[django] Django 모델에서 UUID를 기본 키로 사용 (일반 관계 영향)

여러 가지 이유로 ^, 일부 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_idprimary_key 와 동일한 유형일 필요가 없다는 것을 알게되었습니다 . 예를 들어 IntegerFieldCharField 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를 갖는 성능 저하 문제를 삽입 할 수 있습니다.

작동 원리 :

  1. pkidDB 모델에서 호출되는 자동 증가 기본 키를 생성합니다 .
  2. id숫자 기본 키 대신 UUID ID로 검색 할 수 있도록 고유 색인 UUID 필드를 추가하십시오 .
  3. 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)

내 모든 테이블에서이 작업을 수행하는 방법을 찾을 수 없습니다.

  1. 타사 모듈
  2. Django는 ManyToMany 테이블을 생성했습니다.

따라서 이것은 Django 기능이 누락 된 것으로 보입니다.


답변