Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django unique, null and blank CharField giving 'already exists' error on Admin page

I've been getting the most weird error ever. I have a Person model

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)

I recently added the twitter_id field. When I access the Django admin page, and try to change the 'person' into a suggested_person, I get the following error:

 Person with this Twitter id already exists.

I find this error to be extremely strange because the Facebook_id field is designed the exact same way as the Twitter_id field.

What could be the reason for this?

like image 837
deadlock Avatar asked Jun 23 '13 01:06

deadlock


4 Answers

None of the answers clearly describe the root of the problem.

Normally in the db you can make a field null=True, unique=True and it will work... because NULL != NULL. So each blank value is still considered unique.

But unfortunately for CharFields Django will save an empty string "" (because when you submit a form everything comes into Django as strings, and you may have really wanted to save an empty string "" - Django doesn't know if it should convert to None)

This basically means you shouldn't use CharField(unique=True, null=True, blank=True) in Django. As others have noted you probably have to give up the db-level unique constraint and do your own unique checks in the model.

For further reference, see here: https://code.djangoproject.com/ticket/4136
(unfortunately no good solution decided at time of writing)

NOTE: As pointed out by @alasdair in a comment, that bug has now been fixed - since Django 1.11 you can use CharField(unique=True, null=True, blank=True) without having to manually convert blank values to None

like image 180
Anentropic Avatar answered Oct 21 '22 05:10

Anentropic


This is an old one but I had a similar issue just now and though I would provide an alternative solution.

I am in a situation where I need to be able to have a CharField with null=True, blank=True and unique=True. If I submit an empty string in the admin panel it will not submit because the blank string is not unique.

To fix this, I override the 'clean' function in the ModelForm, and in there I check if it's a blank string and return the result accordinly.

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']

This fixed the problem for me without having to sacrifice the unique attribute on the model field.

Hope this helps.

EDIT: You need to change where I have put "something_unique_or_null" to the name of your field. For example "clean_twitter_id".

like image 29
LondonAppDev Avatar answered Oct 21 '22 07:10

LondonAppDev


Since you have null=True, blank=True and unique=True, django is considering None or blank as a unique entry. Remove the unique constraint and handle the uniqueness part in the code.

like image 13
karthikr Avatar answered Oct 21 '22 06:10

karthikr


In Django 1.11, form CharFields will have an empty_value argument, which allows you to use None if the field is empty.

Model forms, including the Django admin, will automatically set empty_value=None if the model's CharField has null=True.

Therefore, you will be able to use null=True, blank=True and unique=True together in your model CharField without the unique constraint causing problems.

like image 13
Alasdair Avatar answered Oct 21 '22 07:10

Alasdair