여러 가지 이유로 ^, 일부 Django 모델에서 UUID를 기본 키로 사용하고 싶습니다. 이렇게하면 ContentType을 통해 일반 관계를 사용하는 “contrib.comments”, “django-voting”또는 “django-tagging”과 같은 외부 앱을 계속 사용할 수 있습니까?
“django-voting”을 예로 사용하면 투표 모델은 다음과 같습니다.
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)
이 앱은 투표중인 모델의 기본 키가 정수라고 가정하는 것 같습니다.
기본 제공 댓글 앱은 정수가 아닌 PK를 처리 할 수있는 것 같습니다.
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")
이 “정수 PK 가정”문제가 UUID 사용을 어렵게 만드는 타사 앱의 일반적인 상황입니까? 아니면 내가이 상황을 잘못 읽었 을까요?
Django에서 너무 많은 문제를 일으키지 않고 UUID를 기본 키로 사용하는 방법이 있습니까?
^ 몇 가지 이유 : 개체 수 숨기기, URL “ID 크롤링”방지, 여러 서버를 사용하여 충돌하지 않는 개체 만들기, …
답변
UUID 기본 키는 일반 관계뿐만 아니라 일반적으로 효율성에도 문제를 일으킬 수 있습니다. 모든 외래 키는 기계어보다 저장하고 결합하는 데 훨씬 더 비쌉니다.
그러나 UUID가 기본 키가 될 필요는 없습니다. 모델에 uuid 필드를 추가하여 보조 키로 만드십시오 unique=True
. 암시 적 기본 키를 정상적으로 (시스템 내부) 사용하고 UUID를 외부 식별자로 사용합니다.
답변
문서에서 볼 수 있듯이 Django 1.8에는 내장 UUID 필드가 있습니다. UUID와 정수를 사용할 때의 성능 차이는 무시할 수 있습니다.
import uuid
from django.db import models
class MyUUIDModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
답변
비슷한 상황에 처해 공식 Django 문서 에서 관련 모델 object_id
의 primary_key 와 동일한 유형일 필요가 없다는 것을 알게되었습니다 . 예를 들어 IntegerField 및 CharField ID 모두에 대해 일반 관계를 유효하게하려면 CharField로 설정 object_id
하면 됩니다 . 정수는 문자열로 강제 변환 될 수 있으므로 괜찮습니다. 동일은 간다 UUIDField .
예:
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)
답변
PK로서 UUID의 실제 문제는 숫자가 아닌 식별자와 관련된 디스크 조각화 및 삽입 성능 저하입니다. PK가 클러스터형 인덱스이기 때문에 자동 증가되지 않는 경우 DB 엔진은 ID가 더 낮은 순서의 행을 삽입 할 때 물리적 드라이브를 재지 정해야하며, 이는 UUID에서 항상 발생합니다. DB에 많은 데이터가있는 경우 새 레코드 하나를 삽입하는 데 몇 초 또는 몇 분이 걸릴 수 있습니다. 그리고 디스크는 결국 조각화되어주기적인 디스크 조각 모음이 필요합니다. 이건 정말 나쁘다.
이를 해결하기 위해 최근에 공유 할 가치가 있다고 생각하는 다음 아키텍처를 생각해 냈습니다.
UUID 의사 기본 키
이 방법을 사용하면 UUID의 이점을 기본 키 (고유 인덱스 UUID 사용)로 활용하는 동시에 자동 증가 된 PK를 유지하여 조각화를 해결하고 숫자가 아닌 PK를 갖는 성능 저하 문제를 삽입 할 수 있습니다.
작동 원리 :
pkid
DB 모델에서 호출되는 자동 증가 기본 키를 생성합니다 .id
숫자 기본 키 대신 UUID ID로 검색 할 수 있도록 고유 색인 UUID 필드를 추가하십시오 .to_field='id'
외래 키가 숫자 ID 대신 의사 PK를 적절하게 나타낼 수 있도록 ForeignKey를 UUID (사용 )로 지정합니다.
기본적으로 다음을 수행합니다.
먼저 추상 Django 기본 모델을 만듭니다.
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
모델 대신 기본 모델을 확장해야합니다.
class Site(UUIDModel):
name = models.CharField(max_length=255)
또한 ForeignKeys id
가 자동 증가 pkid
필드 대신 UUID 필드를 가리키는 지 확인하십시오 .
class Page(UUIDModel):
site = models.ForeignKey(Site, to_field='id', on_delete=models.CASCADE)
Django Rest Framework (DRF)를 사용하는 경우 기본 검색 필드를 설정하는 기본 ViewSet 클래스도 만들어야합니다.
class UUIDModelViewSet(viewsets.ModelViewSet):
lookup_field = 'id'
그리고 API 뷰에 대한 기본 ModelViewSet 대신 확장하십시오.
class SiteViewSet(UUIDModelViewSet):
model = Site
class PageViewSet(UUIDModelViewSet):
model = Page
이 기사의 이유와 방법에 대한 추가 정보 : https://www.stevenmoseley.com/blog/uuid-primary-keys-django-rest-framework-2-steps
답변
이것은 다음 단계를 사용하여 사용자 정의 기본 추상 모델을 사용하여 수행 할 수 있습니다.
먼저 프로젝트에 basemodel이라는 폴더를 만들고 다음과 같이 abstractmodelbase.py를 추가하십시오.
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']
두 번째 : 각 앱의 모든 모델 파일에서 다음을 수행하십시오.
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)
따라서 위의 모델 사건은 기본 추상 모델의 모든 분야에 내재되어 있습니다.
답변
이 질문은 “장고가 자동 증가 정수 대신 모든 테이블의 모든 데이터베이스 ID에 대해 UUID를 사용하도록하는 방법이 있습니까?”로 다시 표현할 수 있습니다.
물론입니다.
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
내 모든 테이블에서이 작업을 수행하는 방법을 찾을 수 없습니다.
- 타사 모듈
- Django는 ManyToMany 테이블을 생성했습니다.
따라서 이것은 Django 기능이 누락 된 것으로 보입니다.