2010-10-14 31 views
54

Vì một số lý do ^, tôi muốn sử dụng UUID làm khóa chính trong một số mô hình Django của tôi. Nếu tôi làm như vậy, tôi vẫn có thể sử dụng các ứng dụng bên ngoài như "contrib.comments", "django-vote" hoặc "django-tagging" có sử dụng quan hệ chung thông qua ContentType không?Sử dụng UUID làm khóa chính trong các mô hình Django (tác động quan hệ chung)

Sử dụng "django-bỏ phiếu" là một ví dụ, mô hình Vote trông như thế này:

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) 

Ứng dụng này dường như được giả định rằng các khóa chính cho mô hình được bình chọn trên là một số nguyên.

Việc xây dựng trong kiến ​​ứng dụng có vẻ là khả năng xử lý PKS phi nguyên, mặc dù:

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") 

Đây có phải là "nguyên-PK-giả định" vấn đề một tình huống phổ biến cho các ứng dụng của bên thứ ba mà sẽ sử dụng UUID làm đau? Hoặc, có thể, tôi đang hiểu sai tình huống này?

Có cách nào để sử dụng UUID làm khóa chính ở Django mà không gây ra quá nhiều sự cố không?


^ Một số lý do: số lượng đối tượng lẩn trốn, ngăn chặn url "id bò", sử dụng nhiều máy chủ để tạo các đối tượng phi mâu thuẫn, ...

Trả lời

39

Một khóa chính UUID sẽ gây ra vấn đề không chỉ với quan hệ chung chung, nhưng với hiệu quả nói chung: mọi khóa ngoại sẽ đắt hơn đáng kể - cả để lưu trữ và tham gia — hơn là từ máy.

Tuy nhiên, không có gì yêu cầu UUID là khóa chính: chỉ cần đặt khóa này là khóa phụ, bằng cách bổ sung mô hình của bạn với trường uuid với unique=True. Sử dụng khóa chính ngầm định như bình thường (bên trong hệ thống của bạn) và sử dụng UUID làm mã định danh bên ngoài của bạn.

+0

Ngoài ra, bạn có thể ghi đè 'save' và tạo UUID của bạn có khi một đối tượng đang được lưu lần đầu tiên (bằng cách kiểm tra nếu đối tượng có một khóa chính). –

+13

Joe Holloway, không cần điều đó: bạn có thể chỉ cần cung cấp hàm tạo UUID làm 'mặc định' của trường. –

+1

Cảm ơn Piet. Giải pháp của bạn là những gì tôi đang làm bây giờ và nó hoạt động để che khuất khóa chính trong URI (mặc dù ứng dụng bình luận vẫn hiển thị nó trong một trường ẩn trong biểu mẫu "tạo bình luận").Không cho tôi lợi thế là có thể dễ dàng tạo ra các hàng cơ sở dữ liệu không va chạm trên các máy chủ riêng biệt. Ồ, tôi đoán tôi sẽ học cách yêu lại khóa chính nguyên. – mitchf

135

Django 1.8 hiện đã tích hợp sẵn trường UUID. Sự khác biệt hiệu suất khi sử dụng một UUID vs số nguyên là không đáng kể.

import uuid 
from django.db import models 

class MyUUIDModel(models.Model): 
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) 
+5

Điều đó có giải quyết được các vấn đề về quan hệ chung không? –

+0

@Keithhackbarth làm thế nào để chúng tôi thiết lập django để sử dụng điều này mỗi khi tự động tạo ID cho bảng? – anon58192932

+2

@ anon58192932 Không thực sự rõ ràng chính xác những gì bạn có nghĩa là "mọi lúc". Nếu bạn muốn UUID được sử dụng cho mọi mô hình, hãy tạo mô hình cơ sở trừu tượng của riêng bạn và sử dụng nó thay vì django.models.Model. –

8

tôi chạy vào một tình huống tương tự và phát hiện ra trong official Django documentation, rằng object_id không phải cùng loại như primary_key của mô hình liên quan. Ví dụ: nếu bạn muốn mối quan hệ chung của mình hợp lệ cho cả hai số IntegerFieldCharField id, chỉ cần đặt object_id thành CharField. Vì các số nguyên có thể coerce thành chuỗi nó sẽ được sử dụng tốt. Tương tự cho UUIDField.

Ví dụ:

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) 
Các vấn đề liên quan