I have a Django model which looks like this:
class MyModel(models.Model): parent = models.ForeignKey(ParentModel) name = models.CharField(blank=True, max_length=200) ... other fields ... class Meta: unique_together = ("name", "parent")
This works as expected; If there is the same name
more than once in the same parent
then I get an error: "MyModel with this Name and Parent already exists."
However, I also get an error when I save more than one MyModel
with the same parent
but with the name
field blank, but this should be allowed. So basically I don't want to get the above error when the name
field is blank. Is that possible somehow?
If a string-based field has null=True , that means it has two possible values for “no data”: NULL, and the empty string. In most cases, it's redundant to have two possible values for “no data;” the Django convention is to use the empty string, not NULL.
null=True will set the field's value to NULL i.e., no data. It is basically for the databases column value. blank=True determines whether the field will be required in forms. This includes the admin and your own custom forms.
unique_together may be deprecated in the future. This is a list of lists that must be unique when considered together. It's used in the Django admin and is enforced at the database level (i.e., the appropriate UNIQUE statements are included in the CREATE TABLE statement).
The Django convention is to use the empty string, not NULL. The default values of null and blank are False. Also there is a special case, when you need to accept NULL values for a BooleanField , use NullBooleanField instead.
Firstly, blank (empty string) IS NOT same as null ('' != None
).
Secondly, Django CharField when used through forms will be storing empty string when you leave field empty.
So if your field was something else than CharField you should just add null=True
to it. But in this case you need to do more than that. You need to create subclass of forms.CharField
and override it's clean
method to return None on empty string, something like this:
class NullCharField(forms.CharField): def clean(self, value): value = super(NullCharField, self).clean(value) if value in forms.fields.EMPTY_VALUES: return None return value
and then use it in form for your ModelForm:
class MyModelForm(forms.ModelForm): name = NullCharField(required=False, ...)
this way if you leave it blank it will store null in database instead of empty string (''
)
Using unique_together
, you're telling Django that you don't want any two MyModel
instances with the same parent
and name
attributes -- which applies even when name
is an empty string.
This is enforced at the database level using the unique
attribute on the appropriate database columns. So to make any exceptions to this behavior, you'll have to avoid using unique_together
in your model.
Instead, you can get what you want by overriding the save
method on the model and enforcing the unique restraint there. When you try to save an instance of your model, your code can check to see if there are any existing instances that have the same parent
and name
combination, and refuse to save the instance if there are. But you can also allow the instance to be saved if the name
is an empty string. A basic version of this might look like this:
class MyModel(models.Model): ... def save(self, *args, **kwargs): if self.name != '': conflicting_instance = MyModel.objects.filter(parent=self.parent, \ name=self.name) if self.id: # This instance has already been saved. So we need to filter out # this instance from our results. conflicting_instance = conflicting_instance.exclude(pk=self.id) if conflicting_instance.exists(): raise Exception('MyModel with this name and parent already exists.') super(MyModel, self).save(*args, **kwargs)
Hope that helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With