Untuk sejumlah alasan ^, saya ingin menggunakan UUID sebagai kunci utama dalam beberapa model Django saya. Jika saya melakukannya, apakah saya masih dapat menggunakan aplikasi luar seperti "contrib.comments", "django-voting" atau "django-tagging" yang menggunakan hubungan umum melalui ContentType?
Menggunakan "django-voting" sebagai contoh, model Vote terlihat seperti ini:
class Vote(models.Model):
user = models.ForeignKey(User)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
object = generic.GenericForeignKey('content_type', 'object_id')
vote = models.SmallIntegerField(choices=SCORES)
Aplikasi ini tampaknya mengasumsikan bahwa kunci utama untuk model yang dipilih adalah bilangan bulat.
Aplikasi komentar bawaan tampaknya mampu menangani PK non-integer, meskipun:
class BaseCommentAbstractModel(models.Model):
content_type = models.ForeignKey(ContentType,
verbose_name=_('content type'),
related_name="content_type_set_for_%(class)s")
object_pk = models.TextField(_('object ID'))
content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
Apakah masalah "diasumsikan PK dengan bilangan bulat" ini merupakan situasi umum untuk aplikasi pihak ketiga yang akan membuat penggunaan UUID menyebalkan? Atau, mungkin, apakah saya salah membaca situasi ini?
Adakah cara untuk menggunakan UUID sebagai kunci utama di Django tanpa menyebabkan terlalu banyak masalah?
^ Beberapa alasan: menyembunyikan jumlah objek, mencegah url "perayapan id", menggunakan beberapa server untuk membuat objek yang tidak bertentangan, ...
default
.django_extensions.db.fields.UUIDField
seperti yang disebutkan oleh mitchf, Anda tidak akan mendapatkan masalah dengan migrasi Django-Selatan - bidang yang disebutkan olehnya memiliki dukungan bawaan untuk migrasi Selatan.Seperti yang terlihat dalam dokumentasi , dari Django 1.8 ada bidang UUID bawaan. Perbedaan kinerja saat menggunakan UUID vs integer dapat diabaikan.
import uuid from django.db import models class MyUUIDModel(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
Anda juga dapat memeriksa jawaban ini untuk informasi lebih lanjut.
sumber
Saya mengalami situasi yang mirip dan menemukan dalam dokumentasi resmi Django , bahwa
object_id
tidak harus dari tipe yang sama dengan primary_key dari model terkait. Misalnya, jika Anda ingin hubungan generik Anda valid untuk ID IntegerField dan CharField , cukup setel Andaobject_id
menjadi CharField . Karena bilangan bulat dapat dipaksakan menjadi string, itu akan baik-baik saja. Hal yang sama berlaku untuk UUIDField .Contoh:
class Vote(models.Model): user = models.ForeignKey(User) content_type = models.ForeignKey(ContentType) object_id = models.CharField(max_length=50) # <<-- This line was modified object = generic.GenericForeignKey('content_type', 'object_id') vote = models.SmallIntegerField(choices=SCORES)
sumber
Masalah sebenarnya dengan UUID sebagai PK adalah fragmentasi disk dan degradasi sisipan yang terkait dengan pengidentifikasi non-numerik. Karena PK adalah indeks berkerumun, jika tidak bertambah secara otomatis, mesin DB Anda harus menggunakan drive fisik Anda saat memasukkan baris dengan id dengan ordinalitas lebih rendah, yang akan terjadi sepanjang waktu dengan UUID. Ketika Anda mendapatkan banyak data di DB Anda, mungkin perlu beberapa detik atau bahkan menit hanya untuk memasukkan satu record baru. Dan disk Anda pada akhirnya akan menjadi terfragmentasi, membutuhkan defragmentasi disk secara berkala. Ini semua sangat buruk.
Untuk mengatasinya, saya baru-baru ini membuat arsitektur berikut yang menurut saya layak untuk dibagikan.
UUID Pseudo-Primary-Key
Metode ini memungkinkan Anda memanfaatkan manfaat UUID sebagai Kunci Utama (menggunakan indeks UUID yang unik), sambil mempertahankan PK yang bertambah otomatis untuk mengatasi fragmentasi dan menyisipkan masalah penurunan kinerja karena PK non-numerik.
Bagaimana itu bekerja:
pkid
di Model DB Anda.id
bidang UUID berindeks unik untuk memungkinkan Anda menelusuri berdasarkan id UUID, bukan kunci utama numerik.to_field='id'
) untuk memungkinkan kunci asing Anda mewakili Pseudo-PK dengan benar, bukan ID numerik.Intinya, Anda akan melakukan hal berikut:
Pertama, buat Model Dasar Django abstrak
class UUIDModel(models.Model): pkid = models.BigAutoField(primary_key=True, editable=False) id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) class Meta: abstract = True
Pastikan untuk memperluas model dasar, bukan model.Model
class Site(UUIDModel): name = models.CharField(max_length=255)
Pastikan juga ForeignKeys Anda mengarah ke
id
bidang UUID, bukanpkid
bidang yang bertambah otomatis :class Page(UUIDModel): site = models.ForeignKey(Site, to_field='id', on_delete=models.CASCADE)
Jika Anda menggunakan Django Rest Framework (DRF), pastikan juga untuk membuat kelas ViewSet Basis untuk menyetel bidang pencarian default:
class UUIDModelViewSet(viewsets.ModelViewSet): lookup_field = 'id'
Dan perluas itu sebagai ganti ModelViewSet dasar untuk tampilan API Anda:
class SiteViewSet(UUIDModelViewSet): model = Site class PageViewSet(UUIDModelViewSet): model = Page
Catatan lebih lanjut tentang mengapa dan bagaimana dalam artikel ini: https://www.stevenmoseley.com/blog/uuid-primary-keys-django-rest-framework-2-steps
sumber
ini dapat dilakukan dengan menggunakan model abstrak dasar kustom, menggunakan langkah-langkah berikut.
Pertama buat folder di proyek Anda, beri nama basemodel, lalu tambahkan a abstractmodelbase.py dengan yang berikut ini:
from django.db import models import uuid class BaseAbstractModel(models.Model): """ This model defines base models that implements common fields like: created_at updated_at is_deleted """ id=models.UUIDField(primary_key=True, ,unique=True,default=uuid.uuid4, editable=False) created_at=models.DateTimeField(auto_now_add=True,editable=False) updated_at=models.DateTimeField(auto_now=True,editable=False) is_deleted=models.BooleanField(default=False) def soft_delete(self): """soft delete a model instance""" self.is_deleted=True self.save() class Meta: abstract=True ordering=['-created_at']
kedua: di semua file model Anda untuk setiap aplikasi lakukan ini
from django.db import models from basemodel import BaseAbstractModel import uuid # Create your models here. class Incident(BaseAbstractModel): """ Incident model """ place = models.CharField(max_length=50,blank=False, null=False) personal_number = models.CharField(max_length=12,blank=False, null=False) description = models.TextField(max_length=500,blank=False, null=False) action = models.TextField(max_length=500,blank=True, null=True) image = models.ImageField(upload_to='images/',blank=True, null=True) incident_date=models.DateTimeField(blank=False, null=False)
Jadi insiden model di atas melekat pada semua bidang dalam model baseabstract.
sumber
Pertanyaannya dapat dirumuskan ulang sebagai "adakah cara agar Django menggunakan UUID untuk semua id basis data dalam semua tabel alih-alih bilangan bulat yang bertambah otomatis?".
Tentu, saya bisa melakukan:
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
di semua tabel saya, tetapi saya tidak dapat menemukan cara untuk melakukan ini untuk:
Jadi, ini tampaknya menjadi fitur Django yang hilang.
sumber