18👍
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 (''
)
13👍
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.
- [Django]-Anyone using Django in the "Enterprise"
- [Django]-Django makemessages errors Unknown encoding "utf8"
- [Django]-Django DeleteView without confirmation template
2👍
This solution is very similar to the one given by @bigmattyh, however, i found the below page which describes where the validation should be done:
http://docs.djangoproject.com/en/1.3/ref/models/instances/#validating-objects
The solution i ended up using is the following:
from django import forms
class MyModel(models.Model):
...
def clean(self):
if self.name != '':
instance_exists = MyModel.objects.filter(parent=self.parent,
name=self.name).exists()
if instance_exists:
raise forms.ValidationError('MyModel with this name and parent already exists.')
Notice that a ValidationError is raised instead of a generic exception. This solution has the benefit that when validating a ModelForm, using .is_valid(), the models .clean() method above is automatically called, and will save the ValidationError string in .errors, so that it can be displayed in the html template.
Let me know if you do not agree with this solution.
- [Django]-Django InlineModelAdmin: Show partially an inline model and link to the complete model
- [Django]-Docker/Kubernetes + Gunicorn/Celery – Multiple Workers vs Replicas?
- [Django]-Pip install PIL fails
2👍
You can use constraints to set up a partial index like so:
class MyModel(models.Model):
parent = models.ForeignKey(ParentModel)
name = models.CharField(blank=True, max_length=200)
... other fields ...
class Meta:
constraints = [
models.UniqueConstraint(
fields=['name', 'parent'],
condition=~Q(name='')
name='unique_name_for_parent'
)
]
This allow constraints like UniqueTogether to only apply to certain rows (based on conditions you can define using Q).
Incidentally, this happens to be the Django recommended path forward as well: https://docs.djangoproject.com/en/3.2/ref/models/options/#unique-together
Some more documentation: https://docs.djangoproject.com/en/3.2/ref/models/constraints/#django.db.models.UniqueConstraint
- [Django]-TemplateDoesNotExist – Django Error
- [Django]-Django rest framework override page_size in ViewSet
- [Django]-Custom django admin templates not working
-1👍
bigmattyh gives a good explanation as to what is happening. I’ll just add a possible save
method.
def save(self, *args, **kwargs):
if self.parent != None and MyModels.objects.filter(parent=self.parent, name=self.name).exists():
raise Exception('MyModel with this name and parent exists.')
super(MyModel, self).save(*args, **kwargs)
I think I chose to do something similar by overriding my model’s clean method and it looked something like this:
from django.core.exceptions import ValidationError
def clean(self):
if self.parent != None and MyModels.objects.filter(parent=self.parent, name=self.name).exists():
raise ValidationError('MyModel with this name and parent exists.')
- [Django]-Django circular model reference
- [Django]-How to verify if object exist in manytomany
- [Django]-In Django, getting a "Error: Unable to serialize database" when trying to dump data?