2013-06-23 14 views
5

Tôi đã nhận được lỗi lạ nhất từ ​​trước tới nay. Tôi có một người mẫuDjango unique, null và trống CharField cho lỗi 'đã tồn tại' trên trang Quản trị

class Person(models.Model): 
    user = models.OneToOneField(User, primary_key=True) 
    facebook_id = models.CharField(max_length=225, unique=True, null=True, blank=True) 
    twitter_id = models.CharField(max_length=225, unique=True, null=True, blank=True) 
    suggested_person = models.BooleanField(default=False) 

Gần đây tôi đã thêm trường twitter_id. Khi tôi truy cập vào trang quản trị Django, và cố gắng thay đổi 'người' thành một suggested_person, tôi nhận được lỗi sau:

Person with this Twitter id already exists. 

tôi thấy lỗi này là cực kỳ lạ bởi vì lĩnh vực Facebook_id được thiết kế chính xác cùng như trường Twitter_id.

Điều gì có thể là lý do cho điều này?

Trả lời

1

Vì bạn có null=True, blank=Trueunique=True, django đang xem xét None hoặc để trống làm mục nhập duy nhất. Loại bỏ ràng buộc duy nhất và xử lý phần duy nhất trong mã.

+1

Tôi đã xóa trường Twitter_id và sau đó thêm lại bằng cách sử dụng miền Nam.Nó hoạt động tốt ngay bây giờ mà không cho tôi bất kỳ vấn đề nào – noahandthewhale

+2

Django đang xử lý một trường trống làm chuỗi trống '''', và 'unique = True' không cho phép nhiều mục có giá trị' '' '. Tuy nhiên nó sẽ cho phép nhiều mục nhập với 'twitter_id = None'. – Alasdair

-2

bạn chấp nhận các giá trị trống và mong đợi chúng là duy nhất. điều này có nghĩa chỉ có thể có ONE nhập với một twitter_id trống

bạn có thể

  • hoặc loại bỏ các contraint độc đáo
  • tháo trống = True
  • cho một giá trị mặc định cho trường (nhưng mặc định cần phải là duy nhất)
+0

Giá trị mặc định sẽ không hoạt động. Một lần nữa cùng một vấn đề. Ngoài ra loại bỏ trống sẽ gây ra nhiều vấn đề hơn – karthikr

+0

oh có, hạn chế duy nhất. nhưng giá trị mặc định có thể đến từ một hàm hoặc một cái gì đó. –

16

Đây là phiên bản cũ nhưng tôi đã gặp sự cố tương tự ngay bây giờ và mặc dù tôi sẽ cung cấp giải pháp thay thế.

Tôi đang ở trong một tình huống mà tôi cần để có thể có một CharField với null = True, blank = True và unique = True. Nếu tôi gửi một chuỗi rỗng trong bảng quản trị, nó sẽ không gửi vì chuỗi trống không phải là duy nhất.

Để khắc phục điều này, tôi ghi đè hàm 'sạch' trong ModelForm và trong đó tôi kiểm tra xem đó có phải là một chuỗi trống không và trả về kết quả một cách phù hợp.

class MyModelChangeForm(forms.ModelForm): 

    class Meta: 
     model = models.MyModel 
     fields = ['email', 'name', 'something_unique_or_null',] 

    def clean_something_unique_or_null(self): 
     if self.cleaned_data['something_unique_or_null'] == "": 
      return None 
     else: 
      return self.cleaned_data['something_unique_or_null'] 

Điều này đã khắc phục sự cố cho tôi mà không phải hy sinh thuộc tính duy nhất trên trường mô hình.

Hy vọng điều này sẽ hữu ích.

EDIT: Bạn cần thay đổi nơi tôi đã đặt "something_unique_or_null" vào tên trường của bạn. Ví dụ: "clean_twitter_id".

8

Không có câu trả lời nào mô tả rõ ràng gốc của vấn đề.

Thông thường trong db bạn có thể làm cho một lĩnh vực null=True, unique=True và nó sẽ làm việc ... bởi vì NULL != NULL. Vì vậy, mỗi giá trị trống vẫn được coi là duy nhất.

Nhưng không may cho CharField s Django sẽ tiết kiệm được một chuỗi rỗng "" (vì khi bạn gửi một hình thức tất cả mọi thứ đi vào Django như dây đàn, và bạn có thể thực sự muốn tiết kiệm một chuỗi rỗng "" - Django không biết nếu nó nên chuyển đổi thành None)

Điều này về cơ bản có nghĩa là bạn không nên sử dụng CharField(unique=True, null=True, blank=True) ở Django. Như những người khác đã lưu ý bạn có thể phải từ bỏ ràng buộc duy nhất cấp db và thực hiện các kiểm tra duy nhất của riêng bạn trong mô hình.

Để tham khảo thêm, xem tại đây: https://code.djangoproject.com/ticket/4136
(tiếc là không có giải pháp tốt quyết định tại thời điểm viết bài)

+2

Trong Python, 'None == None'. –

+0

oops, bạn nói đúng, sẽ cập nhật – Anentropic

+3

Vé 4136 hiện đã được sửa. Trong Django 1.11+ bạn có thể sử dụng 'CharField (unique = True, null = True, blank = True)' mà không cần phải tự chuyển đổi giá trị trống thành 'None'. – Alasdair

4

Các gốc rễ của vấn đề là Django tồn các giá trị rỗng như trống rỗng-string, và không phải là rỗng . Để khắc phục điều này, bạn có thể phân lớp CharField như sau:

class CharNullField(models.CharField): 
    description = "CharField that stores NULL" 

    def get_db_prep_value(self, value, connection=None, prepared=False): 
     value = super(CharNullField, self).get_db_prep_value(value, connection, prepared) 
     if value=="": 
      return None 
     else: 
      return value 

Vì vậy get_db_prep_value sẽ làm cho nó chắc chắn rằng rỗng được tồn

1

Điều quan trọng là để giải quyết này ở cấp độ mô hình, chứ không phải ở cấp hình thức, vì dữ liệu có thể nhập thông qua các API, thông qua các kịch bản nhập, từ trình bao, vv Nhược điểm của thiết lập null=True trên CharField là cột có thể kết thúc với cả chuỗi trống và NULL, hơi mơ hồ nhưng không phải là vấn đề trong kinh nghiệm của tôi. Nếu bạn sẵn sàng sống với sự mơ hồ đó, dưới đây là cách thực hiện theo một vài bước sau:

1) Đặt null=True, blank=True trên trường và di chuyển trong thay đổi.

2) Massage dữ liệu của bạn để tất cả các chuỗi sản phẩm nào hiện có được thay đổi để NULLs:

items = Foo.objects.all() 
for item in items: 
    if not item.somefield: 
    item.somefield = None 
    item.save() 

3) Thêm một phương pháp tùy chỉnh save() để mô hình của bạn:

def save(self, *args, **kwargs): 
    # Empty strings are not unique, but we can save multiple NULLs 
    if not self.somefield: 
     self.somefield = None 

    super().save(*args, **kwargs) # Python3-style super() 

4) Đặt unique=True trên lĩnh vực này và di chuyển trong đó.

Bây giờ bạn sẽ có thể lưu trữ somefield làm trống hoặc dưới dạng giá trị duy nhất cho dù bạn đang sử dụng quản trị viên hay bất kỳ phương thức nhập dữ liệu nào khác.

Nếu bạn không muốn có một vài di cư, đây là một ví dụ về làm thế nào để làm điều đó trong một sự chuyển đổi duy nhất:

from __future__ import unicode_literals 
from django.db import migrations, models 

def set_nulls(apps, schema_editor): 
    Event = apps.get_model("events", "Event") 
    events = Event.objects.all() 
    for e in events: 
     if not e.wdid: 
      e.wdid = None 
      e.save() 


class Migration(migrations.Migration): 

    dependencies = [ 
     ('events', '0008_something'), 
    ] 

    operations = [ 
     migrations.AlterField(
      model_name='event', 
      name='wdid', 
      field=models.CharField(blank=True, max_length=32, null=True), 
     ), 
     migrations.RunPython(set_nulls), 
     migrations.AlterField(
      model_name='event', 
      name='wdid', 
      field=models.CharField(blank=True, max_length=32, null=True, unique=True), 
     ), 
    ] 
7

Trong Django 1.11, hình thức CharFields sẽ có một cuộc tranh cãi empty_value, cho phép bạn sử dụng None nếu trường trống.

Các mẫu biểu mẫu, bao gồm cả quản trị viên Django, will automatically set empty_value=None nếu mô hình của CharFieldnull=True.

Vì vậy, bạn sẽ có thể sử dụng null=True, blank=Trueunique=True cùng nhau trong mô hình CharField mà không có ràng buộc duy nhất gây ra sự cố.