2010-09-21 22 views
25

Tôi đang ở trên đỉnh của công việc bắt đầu trên một ứng dụng web mới. Một phần của điều này sẽ cung cấp cho người dùng các trang mà họ có thể tùy chỉnh trong mối quan hệ một đến nhiều. Các trang này tự nhiên cần có URL duy nhất.Tạo ID/PK không tuần tự cho Mô hình Django

Còn lại thiết bị của riêng mình, Django thường sẽ chỉ định ID AUTOINCREMENT tiêu chuẩn cho một kiểu máy. Trong khi điều này hoạt động tuyệt vời, nó không nhìn tuyệt vời và nó cũng làm cho các trang rất dễ dự đoán (cái gì đó không phải là mong muốn trong trường hợp này).

Thay vì 1, 2, 3, 4, tôi muốn các chuỗi có chữ số được tạo ngẫu nhiên, chiều dài được thiết lập (ví dụ: h2esj4). 6 điểm của một tập hợp có thể có 36 ký tự nên cho tôi hơn hai tỷ kết hợp mà nên là quá đủ ở giai đoạn này. Tất nhiên nếu tôi có thể mở rộng điều này sau này, điều đó cũng sẽ tốt.

Nhưng có hai vấn đề:

  1. chuỗi ngẫu nhiên thỉnh thoảng giải thích rõ ràng từ xấu hoặc cụm từ gây khó chịu khác. Có cách nào tốt để tránh điều đó không? Để công bằng tôi có thể có thể giải quyết cho một chuỗi số nhưng nó có một hit nặng về khả năng đụng độ.

  2. Làm cách nào để tải Django (hoặc cơ sở dữ liệu) để thực hiện việc nâng hạng nặng khi chèn? Tôi không muốn chèn và sau đó làm việc ra chìa khóa (vì đó sẽ không phải là một chìa khóa). Tôi cho rằng có các vấn đề đồng thời cần phải nhận thức quá mặc dù nếu hai trang mới được tạo ra cùng một lúc và lần thứ hai (chống lại tất cả các tỷ lệ cược) kỳ diệu có cùng khóa như trước khi lần đầu tiên được cam kết.

Tôi không thấy điều này trở thành một triệu dặm khác nhau từ cách URL rút gọn tạo ID của họ. Nếu có một Django thực hiện phong nha của một, tôi có thể piggyback off đó.

+1

là một lưu ý: 'sh URL orteners 'thường tạo ra các URL tuần tự :). –

Trả lời

9

Đây là những gì tôi đã kết thúc. Tôi đã tạo ra một mô hình trừu tượng. Trường hợp sử dụng của tôi cho điều này là cần một số mô hình tạo ra các sên ngẫu nhiên của riêng họ.

Một con sên trông giống như AA##AA vì vậy đó là 52x52x10x10x52x52 = 731,161,600 kết hợp. Có lẽ là một nghìn lần nhiều hơn tôi sẽ cần và nếu đó là một vấn đề, tôi có thể thêm một lá thư cho kết hợp nhiều hơn 52 lần.

Sử dụng đối số default sẽ không cắt nó vì mô hình trừu tượng cần kiểm tra các va chạm sên trên đứa trẻ. Thừa kế là cách dễ nhất, có thể là duy nhất để làm điều đó.

from django.db import models 
from django.contrib.auth.models import User 

import string, random 

class SluggedModel(models.Model): 
    slug = models.SlugField(primary_key=True, unique=True, editable=False, blank=True) 

    def save(self, *args, **kwargs): 
     while not self.slug: 
      ret = [] 
      ret.extend(random.sample(string.letters, 2)) 
      ret.extend(random.sample(string.digits, 2)) 
      ret.extend(random.sample(string.letters, 2)) 

      newslug = ''.join(ret) 
      if self.objects.filter(pk=newslug).count(): 
       self.slug = newslug 

     super(SluggedModel, self).save(*args, **kwargs) 

    class Meta: 
     abstract = True 
+1

Thú vị. Gần đây tôi đã quyết định chuyển sang một phương pháp tạo UUID cho một số pk nhưng tôi cũng có thể xem xét điều này. Đoạn của bạn sẽ thực sự hoạt động giống như cách tôi nghĩ. Chỉ cần thay thế 4 dòng bạn tạo ra 'ret' với một cái gì đó như '' 'ret = uuid.uuid1()' '' –

+0

Im cố gắng sử dụng phương pháp của bạn, nhưng tôi nhận được quản lý không thể truy cập thông qua lỗi trường hợp ClassName. Làm sao bạn vượt qua được điều đó? – zsquare

+1

Đây là một chủ đề cũ, nhưng một điều cho bất cứ ai tình cờ khi điều này và đang sử dụng MySQL để cảnh giác là MySQL là trường hợp mặc định không nhạy cảm trên chuỗi phù hợp, vì vậy ids của "AB12AB" và "ab12ab" sẽ được tìm thấy trừ khi bạn nói rõ ràng với MySQL để sử dụng trường hợp trùng khớp nhạy cảm: http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html – umbrae

4

Có thể bạn cần phải xem Python UUID, nó có thể tạo ra các ký tự dài ngẫu nhiên. Nhưng bạn có thể cắt nó và sử dụng số lượng ký tự mà bạn muốn với ít kiểm tra để đảm bảo rằng nó độc đáo ngay cả sau khi cắt.

UUIDField đoạn mã có thể giúp bạn nếu bạn không muốn tự mình tạo ra UUID.

Cũng có một cái nhìn tại này blog post

+0

Điều này không thực sự phá vỡ một trong hai vấn đề tôi đánh dấu trong câu hỏi. Cấp 'UUIDField' giúp trừu tượng một số mã khỏi mô hình của tôi nhưng nó vẫn nằm ngoài cơ sở dữ liệu (nơi tôi thực sự thích nó) và vẫn có khả năng đánh vần các từ thô lỗ. – Oli

20

Có được xây dựng trong Django cách để đạt được những gì bạn muốn. Thêm một trường để mô hình "trang tùy chỉnh" với primary_key=Truedefault= tên của chức năng thế hệ chủ chốt, như thế này:

class CustomPage(models.Model): 
    ... 
    mykey = models.CharField(max_length=6, primary_key=True, default=pkgen) 
    ... 

Bây giờ, đối với mỗi trường hợp mô hình page, page.pk trở thành một bí danh cho page.mykey, đó là được tự động được gán với chuỗi được trả về bởi hàm pkgen() của bạn tại thời điểm tạo ra cá thể đó.
nhanh & thực hiện bẩn:

def pkgen(): 
    from base64 import b32encode 
    from hashlib import sha1 
    from random import random 
    rude = ('lol',) 
    bad_pk = True 
    while bad_pk: 
     pk = b32encode(sha1(str(random())).digest()).lower()[:6] 
     bad_pk = False 
     for rw in rude: 
      if pk.find(rw) >= 0: bad_pk = True 
    return pk 

Xác suất của hai trang bị khóa chính giống hệt nhau là rất thấp (giả sử random() là đủ ngẫu nhiên), và không có vấn đề đồng thời. Và, của couse, phương pháp này là easilly mở rộng bằng cách cắt nhiều ký tự từ chuỗi mã hóa.

+3

Tôi không hiểu điểm b32encode và sha1 trong khái niệm này. Sẽ không phải là một lựa chọn ngẫu nhiên đơn giản của một danh sách các nhân vật tạo ra chỉ là ngẫu nhiên một kết quả, với chi phí ít hơn rất nhiều (và mã)? – Oli

+0

@Oli bạn có thể tạo ra bất kỳ chuỗi nào bạn muốn, điểm là thiết lập một hàm gọi lại để mặc định là cách bạn sẽ gán chuỗi là PK. Có vẻ như giải pháp phù hợp với tôi +1 Upvote – Rasiel

+1

Trong cài đặt có thể sử dụng lại, nó không thể kiểm tra va chạm. Không thể có nhiều hơn một lần một Mô hình với cùng một con sên. Đây là một lỗ hổng trong đối số 'mặc định' không thể lấy thông tin bổ sung (để chuyển lớp tới máy phát). – Oli

1

Oli: Nếu bạn đang lo lắng về chính tả ra những lời thô lỗ, bạn luôn có thể so sánh/tìm kiếm UUIDField của bạn cho họ, bằng cách sử dụng bộ lọc django thô tục, và bỏ qua bất kỳ UUIDs mà có thể triggery.

0

Đây là những gì tôi đã kết thúc bằng UUID.

import uuid 

from django.db import models 
from django.contrib.auth.models import User 


class SluggedModel(models.Model): 
    slug = models.SlugField(primary_key=True, unique=True, editable=False, blank=True) 

    def save(self, *args, **kwargs): 
     if not self.slug: 
      uuid.uuid4().hex[:16] # can vary up to 32 chars in length 
     super(SluggedModel, self).save(*args, **kwargs) 

    class Meta: 
     abstract = True 
2

Django hiện nay bao gồm một UUIDField type, do đó bạn không cần bất kỳ mã tùy chỉnh hoặc gói bên ngoài Srikanth Chundi gợi ý. thực hiện này sử dụng chuỗi HEX với dấu gạch ngang, vì vậy văn bản là khá trẻ em an toàn, khác hơn 1337 biểu như abad1d3a :)

Bạn sẽ sử dụng nó như thế này để bí danh pk đến lĩnh vực uuid như một khóa chính:

import uuid 
from django.db import models 

class MyModel(models.Model): 
    uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) 
    # other fields 

Lưu ý, tuy nhiên, khi bạn định tuyến quan điểm này trong urls.py, bạn cần một regex khác nhau như mentioned here, ví dụ:

urlpatterns = [ 
    url(r'mymodel/(?P<pk>[^/]+)/$', MyModelDetailView.as_view(), 
     name='mymodel'), 
] 
+0

Nhận xét của tôi về câu trả lời đó cũng mang ở đây. UUID là tuyệt vời cho duy nhất, gần ID vô hạn nhưng chúng khá dễ sử dụng. Hãy xem xét —trong bối cảnh của Django và web— đây là cái gì đó * sẽ * được hiển thị và * có thể * được sao chép theo cách thủ công, và các chuỗi ngẫu nhiên cuối cùng cũng đánh vần các từ thề. – Oli

+0

Từ ngữ nào bạn có thể đánh vần trong hệ thống số thập lục phân? Lưu ý các chữ cái chỉ có sẵn là a, b, c, d, e, f. Tuy nhiên, tôi đồng ý rằng các chuỗi dài ngẫu nhiên có thể không phù hợp với mọi trường hợp sử dụng. – metakermit

+1

Bạn hỏi như vậy: 'B00B5' .. Nhưng yeah, HEX chắc chắn là tốt hơn. Chiều dài 36-hulking hulking là vấn đề thực sự ở đây. – Oli

Các vấn đề liên quan