how can i allow empty fields in my modelform while still using unique = true?

  • Last Update :
  • Techknowledgy :

Change the field to allow NULL, by adding null = True in your model:

rowname = models.CharField(..., blank = True, null = True, unique = True)

Change empty strings to None in the form:

class ModelNameForm(forms.ModelForm):
   class Meta:
   model = ModelName
def clean_rowname(self):
   return self.cleaned_data['rowname'] or None

class ModelNameAdmin(admin.ModelAdmin):
   form = ModelNameForm

Suggestion : 2

Change the field to allow NULL, by adding null = True in your model: rowname = models.CharField(..., blank = True, null = True, unique = True) ,Change the field to allow NULL, by adding null = True in your model:,How can I allow empty fields in my ModelForm while still using unique = True?,Updating models with unique fields in Django using ModelForm

Change the field to allow NULL, by adding null = True in your model:

rowname = models.CharField(..., blank = True, null = True, unique = True)

Change empty strings to None in the form:

class ModelNameForm(forms.ModelForm):
   class Meta:
   model = ModelName
def clean_rowname(self):
   return self.cleaned_data['rowname'] or None

class ModelNameAdmin(admin.ModelAdmin):
   form = ModelNameForm

Suggestion : 3

Last Updated : 18 Oct, 2021,GATE CS 2021 Syllabus

1._
field_name = models.Field(blank = True)

After running makemigrations and migrate on Django and rendering the above model, let us try to create an instance using None from Django shell. To start Django shell, enter the command, 
 

Python manage.py shell

Suggestion : 4

The ModelForm.clean() method sets a flag that makes the model validation step validate the uniqueness of model fields that are marked as unique, unique_together or unique_for_date|month|year.,Validation on a ModelForm Overriding the clean() method Interaction with model validation Considerations regarding model’s error_messages ,The model’s clean() method will be called before any uniqueness checks are made. See Validating objects for more information on the model’s clean() hook.,Error messages defined on model fields are only used when the ValidationError is raised during the model validation step and no corresponding error messages are defined at the form level.

>>> from django.forms
import ModelForm
   >>>
   from myapp.models
import Article

# Create the form class. >>>
class ArticleForm(ModelForm):
   ...class Meta:
   ...model = Article
   ...fields = ['pub_date', 'headline', 'content', 'reporter']

# Creating a form to add an article. >>>
   form = ArticleForm()

# Creating a form to change an existing article. >>>
   article = Article.objects.get(pk = 1) >>>
   form = ArticleForm(instance = article)
from django.db
import models
from django.forms
import ModelForm

TITLE_CHOICES = [
   ('MR', 'Mr.'),
   ('MRS', 'Mrs.'),
   ('MS', 'Ms.'),
]

class Author(models.Model):
   name = models.CharField(max_length = 100)
title = models.CharField(max_length = 3, choices = TITLE_CHOICES)
birth_date = models.DateField(blank = True, null = True)

def __str__(self):
   return self.name

class Book(models.Model):
   name = models.CharField(max_length = 100)
authors = models.ManyToManyField(Author)

class AuthorForm(ModelForm):
   class Meta:
   model = Author
fields = ['name', 'title', 'birth_date']

class BookForm(ModelForm):
   class Meta:
   model = Book
fields = ['name', 'authors']
from django
import forms

class AuthorForm(forms.Form):
   name = forms.CharField(max_length = 100)
title = forms.CharField(
   max_length = 3,
   widget = forms.Select(choices = TITLE_CHOICES),
)
birth_date = forms.DateField(required = False)

class BookForm(forms.Form):
   name = forms.CharField(max_length = 100)
authors = forms.ModelMultipleChoiceField(queryset = Author.objects.all())
from django.core.exceptions
import NON_FIELD_ERRORS
from django.forms
import ModelForm

class ArticleForm(ModelForm):
   class Meta:
   error_messages = {
      NON_FIELD_ERRORS: {
         'unique_together': "%(model_name)s's %(field_labels)s are not unique.",
      }
   }
>>> from myapp.models
import Article
   >>>
   from myapp.forms
import ArticleForm

# Create a form instance from POST data. >>>
   f = ArticleForm(request.POST)

# Save a new Article object from the form 's data. >>>
   new_article = f.save()

# Create a form to edit an existing Article, but use
# POST data to populate the form. >>>
   a = Article.objects.get(pk = 1) >>>
   f = ArticleForm(request.POST, instance = a) >>>
   f.save()
# Create a form instance with POST data. >>>
   f = AuthorForm(request.POST)

# Create, but don 't save the new author instance. >>>
   new_author = f.save(commit = False)

# Modify the author in some way. >>>
   new_author.some_field = 'some_value'

# Save the new instance. >>>
   new_author.save()

# Now, save the many - to - many data
for the form. >>>
   f.save_m2m()

Suggestion : 5

This validator can be used to enforce the unique=True constraint on model fields. It takes a single required argument, and an optional messages argument:,This validator can be used to enforce unique_together constraints on model instances. It has two required arguments, and a single optional messages argument:,These validators can be used to enforce the unique_for_date, unique_for_month and unique_for_year constraints on model instances. They take the following arguments:,date_field required - A field name which will be used to determine date range for the uniqueness constrain. This must exist as a field on the serializer class.

As an example of how REST framework uses explicit validation, we'll take a simple model class that has a field with a uniqueness constraint.

class CustomerReportRecord(models.Model):
   time_raised = models.DateTimeField(
      default = timezone.now, editable = False)
reference = models.CharField(unique = True, max_length = 20)
description = models.TextField()

Here's a basic ModelSerializer that we can use for creating or updating instances of CustomerReportRecord:

class CustomerReportSerializer(serializers.ModelSerializer):
   class Meta:
   model = CustomerReportRecord

If we open up the Django shell using manage.py shell we can now

>>> from project.example.serializers import CustomerReportSerializer
>>> serializer = CustomerReportSerializer()
>>> print(repr(serializer))
CustomerReportSerializer():
    id = IntegerField(label='ID', read_only=True)
    time_raised = DateTimeField(read_only=True)
    reference = CharField(max_length=20, validators=[<UniqueValidator(queryset=CustomerReportRecord.objects.all())>])
    description = CharField(style={'type': 'textarea'})

The validator should be applied to serializer classes, like so:

from rest_framework.validators
import UniqueTogetherValidator

class ExampleSerializer(serializers.Serializer):
   #...
   class Meta:
   # ToDo items belong to a parent list, and have an ordering defined
# by the 'position'
field.No two items in a given list may share
# the same position.
validators = [
   UniqueTogetherValidator(
      queryset = ToDoItem.objects.all(),
      fields = ['list', 'position']
   )
]

If you want the date field to be writable the only thing worth noting is that you should ensure that it is always available in the input data, either by setting a default argument, or by setting required=True.

published = serializers.DateTimeField(required = True)

Suggestion : 6

Do not use null=True or blank=True for BooleanField. It should also be pointed out that it is better to specify default values for such fields. If you realise that the field can remain empty, you need NullBooleanField.,This example may not be very illustrative, but imagine that you have 3 or more such boolean fields in your model, and validation control for these field value combinations can be really tiresome.,Do not use FloatField to store information about the quantity of money. Instead, use DecimalField for this purpose. You can also keep this information in cents, units, etc.,Do not duplicate model fields in ModelForm or ModelSerializer without need. If you want to specify that the form uses all model fields, use MetaFields. If you need to redefine a widget for a field with nothing else to be changed in this field, make use of Meta widgets to indicate widgets.

It is reasonable to indicate a related-name in plural as related-name addressing returns queryset. Please, do set adequate related-names. In the majority of cases, the name of the model in plural will be just right. For example:

class Owner(models.Model):
   pass
class Item(models.Model):
   owner = models.ForeignKey(Owner, related_name = 'items')
Article
from model_utils
import Choices

class Article(models.Model):
   STATUSES = Choices(
      (0, 'draft', _('draft')),
      (1, 'published', _('published')))
status = models.IntegerField(choices = STATUSES,
   default = STATUSES.draft)…

If it is justified, replace several BooleanFields with one field, status-like. e.g.

class Article(models.Model):
   is_published = models.BooleanField(
      default = False)
is_verified = models.BooleanField(
   default = False)…

22. Don't use null=true if you don't need it

null = True - Allows column to keep null value.blank = True - Will be used only
if Forms
for validation and not related to the database.In text - based fields, it 's better to keepdefaultvalue.blank=Truedefault='
'

Sometimes even a separate folder for each FileField will not be enough if a large amount of downloaded files is expected. Storing many files in one folder means the file system will search for the needed file more slowly. To avoid such problems, you can do the following:

def get_upload_path(instance, filename):
   return os.path.join('account/avatars/', now().date().strftime("%Y/%m/%d"), filename)

class User(AbstractUser):
   avatar = models.ImageField(blank = True, upload_to = get_upload_path)
Article
from model_utils
import Choices

class Article(models.Model):
   STATUSES = Choices(
      (0, 'draft', _('draft')),
      (1, 'published', _('published')))
status = models.IntegerField(choices = STATUSES,
   default = STATUSES.draft)…

Suggestion : 7

For my project, I dumped this into an extras.py file that lives in the root of my site, then I can just from mysite.extras import CharNullField in my app's models.py file. The field acts just like a CharField - just remember to set blank=True, null=True when declaring the field, or otherwise Django will throw a validation error (field required) or create a db column that doesn't accept NULL.,Personally I prefer using the CharNullField suggested, for portability to models I might define in the future.,The issue here is that the normalized "blank" value for a form CharField is an empty string, not None. So if you leave the field blank, you get an empty string, not NULL, stored in the DB. Empty strings are equal to empty strings for uniqueness checks, under both Django and database rules.,The OP requires his bar field to be unique if it has a value, and null otherwise. Then it must be that the model itself makes sure this is the case. It cannot be left to external code to check this, because that would mean it can be bypassed. (Or you can forget to check it if you write a new view in the future)

You can force the admin interface to store NULL for an empty string by providing your own customized model form for Foo with a clean_bar method that turns the empty string into None:

class FooForm(forms.ModelForm): class Meta: model = Foo def clean_bar(self): return self.cleaned_data['bar'] or None class FooAdmin(admin.ModelAdmin): form = FooForm

Therefore, as suggested by the from_db_value() documentation and this example, this solution must be changed to:

class CharNullField(models.CharField): ""
"     Subclass of the CharField that allows empty strings to be stored as NULL.     "
""
description = "CharField that stores NULL but returns ''."
def from_db_value(self, value, expression, connection, contex): ""
"         Gets value right out of the db and changes it if its ``None``.         "
""
if value is None: return ''
else: return value def to_python(self, value): ""
"         Gets value right out of the db or an instance, and changes it if its ``None``.         "
""
if isinstance(value, models.CharField): # If an instance, just
return the instance.return value
if value is None: # If db has NULL, convert it to ''.return ''
# Otherwise, just
return the value.return value def get_prep_value(self, value): ""
"         Catches value right before sending to db.         "
""
if value == '': # If Django tries to save an empty string, send the db None(NULL).return None
else: # Otherwise, just pass the value.return value

I think a better way than overriding the cleaned_data in the admin would be to subclass the charfield - this way no matter what form accesses the field, it will "just work." You can catch the '' just before it is sent to the database, and catch the NULL just after it comes out of the database, and the rest of Django won't know/care. A quick and dirty example:

from django.db
import models class CharNullField(models.CharField): # subclass the CharField description = "CharField that stores NULL but returns ''"
__metaclass__ = models.SubfieldBase # this ensures to_python will be called def to_python(self, value): # this is the value right out of the db, or an instance #
if an instance, just
return the instance
if isinstance(value, models.CharField): return value
if value is None: #
if the db has a NULL(None in Python) return ''
# convert it into an empty string
else: return value # otherwise, just
return the value def get_prep_value(self, value): # catches value right before sending to db
if value == '': #
if Django tries to save an empty string, send the db None(NULL) return None
else: # otherwise, just pass the value
return value

The quick fix is to do :

def save(self, * args, ** kwargs): if not self.bar: self.bar = None super(Foo, self).save( * args, ** kwargs)

Note: UniqueConstraint was added since django 2.2

class Foo(models.Model): name = models.CharField(max_length = 40) bar = models.CharField(max_length = 40, unique = True, blank = True, null = True,
   default = None) class Meta: constraints = [# For bar == null only models.UniqueConstraint(fields = ['name'], name = 'unique__name__when__bar__null', condition = Q(bar__isnull = True)), # For bar != null only models.UniqueConstraint(fields = ['name', 'bar'], name = 'unique__name__when__bar__not_null')]