2009-12-07 4 views
22

Hãy xem xét các tình huống sau đây: -lĩnh vực mô hình độc đáo trong Django và độ nhạy trường hợp (postgres)

Giả sử ứng dụng của tôi cho phép người dùng tạo ra các bang/tỉnh trong cả nước của họ. Chỉ để rõ ràng, chúng tôi chỉ xem xét các ký tự ASCII tại đây.

Ở Hoa Kỳ, người dùng có thể tạo trạng thái có tên là "Texas". Nếu ứng dụng này đang được sử dụng trong nội bộ, giả sử người dùng không quan tâm nếu đó là viết "texas" hoặc "Texas" hoặc "teXas"

Nhưng quan trọng là hệ thống sẽ ngăn chặn việc tạo "texas" nếu "Texas" đã có trong cơ sở dữ liệu.

Nếu mô hình này là như sau:

class State(models.Model): 
    name = models.CharField(max_length=50, unique=True) 

Sự độc đáo sẽ là trường hợp nhạy cảm trong postgres; nghĩa là, postgres sẽ cho phép người dùng tạo cả "texas" và "Texas" vì chúng là được coi là duy nhất.

Điều gì có thể được thực hiện trong tình huống này để ngăn chặn hành vi như vậy. Làm thế nào để một đi về việc cung cấp đựng pin- insenstitive độc đáo với Django và Postgres

Ngay bây giờ tôi đang làm những điều sau đây để tránh tạo ra các bản sao đựng pin- không nhạy cảm.

class CreateStateForm(forms.ModelForm): 
    def clean_name(self): 
     name = self.cleaned_data['name'] 
     try: 
      State.objects.get(name__iexact=name) 
     except ObjectDoesNotExist: 
      return name 
     raise forms.ValidationError('State already exists.') 

    class Meta: 
     model = State 

Có một số trường hợp tôi sẽ phải thực hiện kiểm tra này và tôi không muốn viết kiểm tra iexact tương tự ở mọi nơi.

Chỉ cần tự hỏi nếu có tích hợp hoặc cách tốt hơn? Có lẽ db_type sẽ giúp đỡ? Có lẽ một số giải pháp khác tồn tại?

+0

Tôi nghĩ bạn đã làm đúng. Ít nhất đó là cách tôi làm điều đó với các thẻ, do đó, đám mây thẻ của tôi không kết thúc bằng "thẻ", "Thẻ" và "TAG" tất cả ngay bên cạnh nhau. –

+1

Có nhưng ở đây Django được xây dựng trong độc đáo không được giúp đỡ nhiều, phải không? Sau khi tất cả tôi phải làm kiểm tra độc đáo bản thân mình ở khắp mọi nơi. – chefsmart

+1

Tôi sẽ mã ứng dụng để đảm bảo rằng chuỗi đã nhập cơ sở dữ liệu chỉ bằng chữ thường. Sau đó kiểm tra lại. – DrBloodmoney

Trả lời

28

Bạn có thể xác định trường mô hình tùy chỉnh bắt nguồn từ models.CharField. Trường này có thể kiểm tra các giá trị trùng lặp, bỏ qua trường hợp.

Tuỳ chỉnh lĩnh vực tài liệu là ở đây http://docs.djangoproject.com/en/dev/howto/custom-model-fields/

Nhìn vào http://code.djangoproject.com/browser/django/trunk/django/db/models/fields/files.py cho một ví dụ về làm thế nào để tạo ra một lĩnh vực tùy chỉnh bằng cách subclassing một lĩnh vực hiện có.

Bạn có thể sử dụng các mô-đun citext của PostgreSQL https://www.postgresql.org/docs/current/static/citext.html

Nếu bạn sử dụng mô-đun này, lĩnh vực tùy chỉnh có thể định nghĩa "db_type" như CITEXT cho cơ sở dữ liệu PostgreSQL.

Điều này sẽ dẫn đến việc so sánh phân biệt chữ hoa chữ thường cho các giá trị duy nhất trong trường tùy chỉnh.

+0

Đây là một giải pháp thú vị, và có vẻ như Django-istic hơn các giải pháp khác được đề cập ở đây. – chefsmart

+0

xem câu trả lời của tôi bên dưới cho các bước rõ ràng – eyaler

+0

Cảm ơn @MichielB – Mayuresh

1

Bạn có thể làm điều này bằng cách ghi đè phương thức lưu của Mô hình - xem docs. Về cơ bản, bạn có thể làm điều gì đó như:

class State(models.Model): 
    name = models.CharField(max_length=50, unique=True) 

    def save(self, force_insert=False, force_update=False): 
     if State.objects.get(name__iexact = self.name): 
      return 
     else: 
      super(State, self).save(force_insert, force_update) 

Ngoài ra, tôi có thể sai về điều này, nhưng nhánh SoC mô hình sắp tới sẽ cho phép chúng tôi thực hiện việc này dễ dàng hơn.

+0

Điều này về cơ bản giống như những gì tôi đã làm. Trong thực tế, họ cách tôi đang làm nó ngay bây giờ là tốt hơn so với xử lý nó trong vòng tiết kiệm. – chefsmart

+0

Trong lần đọc thứ hai, bạn đã đúng. AFAIK, bằng cách sử dụng các hình thức xác nhận sẽ là cách tốt nhất để đi (như của bây giờ) - trừ khi dữ liệu không được chèn thông qua một hình thức :). –

+0

Hoàn toàn có thể cho dữ liệu không được chèn thông qua biểu mẫu! đặc biệt là nếu bạn đang sử dụng ứng dụng của bên thứ ba. – dbn

3

Ngoài tùy chọn đã đề cập để ghi đè lưu, bạn có thể chỉ lưu trữ tất cả văn bản trong trường hợp thấp hơn trong cơ sở dữ liệu và viết hoa chúng trên màn hình.

class State(models.Model): 
    name = models.CharField(max_length=50, unique=True) 

    def save(self, force_insert=False, force_update=False): 
     self.name = self.name.lower() 
     super(State, self).save(force_insert, force_update) 
5

Về phía Postgres của sự vật, chỉ mục duy nhất chức năng sẽ cho phép bạn thực thi các giá trị duy nhất mà không có trường hợp. citext cũng được lưu ý, nhưng điều này sẽ làm việc với các phiên bản cũ của PostgreSQL và là một kỹ thuật hữu ích nói chung.

Ví dụ:

# create table foo(bar text); 
CREATE TABLE 
# create unique index foo_bar on foo(lower(bar)); 
CREATE INDEX 
# insert into foo values ('Texas'); 
INSERT 0 1 
# insert into foo values ('texas'); 
ERROR: duplicate key value violates unique constraint "foo_bar" 
+0

Tôi đã thử cách này và có thể xác nhận nó hoạt động. Nhưng câu trả lời của Mayuresh cho phép tôi sống trong Django. – chefsmart

+2

Vâng, bạn nên luôn luôn thực thi các ràng buộc của mình trong cơ sở dữ liệu. –

+0

Cách tốt nhất để thực thi 'tự động' này ở cấp cơ sở dữ liệu sau khi Django đã tạo mô hình là gì. Tôi yêu cầu một cách scripted làm điều này thay vì tự chỉnh sửa db sau khi django 'syncdb' – Medorator

6

Hoặc bạn có thể thay đổi các truy vấn Set Manager mặc định để làm phân biệt dạng chữ look-up trên sân. Trong cố gắng để giải quyết một vấn đề tương tự như tôi đi qua:

http://djangosnippets.org/snippets/305/

Mã dán vào đây để tiện theo dõi:

from django.db.models import Manager 
from django.db.models.query import QuerySet 

class CaseInsensitiveQuerySet(QuerySet): 
    def _filter_or_exclude(self, mapper, *args, **kwargs): 
     # 'name' is a field in your Model whose lookups you want case-insensitive by default 
     if 'name' in kwargs: 
      kwargs['name__iexact'] = kwargs['name'] 
      del kwargs['name'] 
     return super(CaseInsensitiveQuerySet, self)._filter_or_exclude(mapper, *args, **kwargs) 

# custom manager that overrides the initial query set 
class TagManager(Manager): 
    def get_query_set(self): 
     return CaseInsensitiveQuerySet(self.model) 

# and the model itself 
class Tag(models.Model): 
    name = models.CharField(maxlength=50, unique=True, db_index=True) 

    objects = TagManager() 

    def __str__(self): 
     return self.name 
+0

Cách tiếp cận này sẽ không làm việc cho các truy vấn phức hợp như name__in = [] hoặc related__name =. – dbn

2

một giải pháp rất đơn giản:

bước
class State(models.Model): 
    name = models.CharField(max_length=50, unique=True) 

    def clean(self): 
     self.name = self.name.capitalize() 
0

Giải pháp từ Suhail làm việc cho tôi mà không cần phải kích hoạt citext, giải pháp khá dễ dàng chỉ có chức năng sạch và thay vì viết hoa tôi đã sử dụng upper(). Giải pháp của Mayuresh cũng hoạt động nhưng đã thay đổi trường từ CharField thành TextField.

class State(models.Model): 

    name = models.CharField(max_length=50, unique=True) 

    def clean(self): 
     self.name = self.name.upper() 
1

Bạn có thể sử dụng tra cứu = 'iexact' trong UniqueValidator trên serializer, như thế này: Phiên bản

class StateSerializer(serializers.ModelSerializer): 
    name = serializers.CharField(validators=[ 
    UniqueValidator(
     queryset=models.State.objects.all(),lookup='iexact' 
    )] 

django: 1.11.6