Blank field not working correctly in Django model - django

I have a Django model described as follows-
class Building(models.Model):
name = models.CharField(max_length=25,unique=True,blank=False)
country = models.CharField(max_length=20,blank=False)
city = models.CharField(max_length=20,blank=False)
def __str__(self):
return self.name
All of the fields are required as blank is set to False in each of them but still, I am able to save objects for this model by leaving city and country as blank.

blank=True is enforced at the ModelForm layer, as is specified in the documentation on the blank=… parameter [Django-doc]:
Note that this is different than null. null is purely database-related, whereas blank is validation-related. If a field has blank=True, form validation will allow entry of an empty value. If a field has blank=False, the field will be required.
So a ModelForm that uses this model will raise errors when you do not enter a value for name, city, and country. But not the model layer. In fact the model layer does not validate any constraints, unless you call .clean(), or the database rejects the values.

Related

How to allow a form to submit, but prevent blank fields from being saved using Django Admin 3.x?

I am using Django Admin, and have a model like this:
class Item(models.Model):
id = models.CharField(max_length=14, primary_key=True)
otherId = models.CharField(max_length=2084, blank=True)
I want id to be required and unique, and I want otherId to be optional on the Admin form, but if otherId is provided, it has to be unique.
The problem I am running into is, whenever I create an instance of Item using the Admin form and I do not provide an otherId, Django tries to save the otherId field as a blank value, but this means the second time I try to save an instance with a blank otherId value it violates the column's unique constraint and fails.
I need Django to check if the otherId field is falsey before saving, and if it is falsey, do not save that empty value along with the model. Is this possible?
You should add unique=True to otherId field.
otherid = models.CharField(max_length=2084, blank=True, null=True, unique=True)
Django ignore unique or not if otherId is blank.
I failed to understand the question very well but i think you need to override the save method of the django model and provide custom logic you stated above.
class Item(models.Model):
id = models.CharField(max_length=14, primary_key=True)
otherId = models.CharField(max_length=2084, blank=True)
def save(self, *args, **kwargs):
# handle you logic here
# check if self.id is empty string and do something about it
super(Item, self).save(*args, **kwargs)
For every model django also auto create a field id for primary key which is auto generated and incremented.
For disabling submission of blank field you must make the null and blank property False. Check the code.
Also note that the id field is automatically added in django so you need not mention that.
class Item(models.Model):
otherId = models.CharField(max_length=2084, blank=False, null=False)

Force model fields in Django

When creating a model in Django like this example:
class Musician(models.Model):
first_name = models.CharField(max_length=50, primary_key=True)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
I noticed some problem (not sure if that's best word) with this approach. There is nothing preventing you from creating something like:
musician = Musician()
musician.save()
effectively having primary_key value equal to None. I would like to force user to set first_name, but frankly speaking I cannot find any simple solution for that.
Is there a way to achieve this?
First of all, don't set first_name as primary key. Just leave the default primary key as the id field. A primary key needs to be unique (a first_name isn't) and should not be something a user enters.
Second, it's true that you cannot enforce a CharField to not be empty at the database level. But you can enforce it at the code level, so that anytime you create a Django Form and validate it, it will raise an error.
In fact, Django does it automatically for you, in your case. By default first_name is a required field, since you didn't set blank=True.
So if you do:
musician = Musician()
musician.full_clean()
this raises a ValidationError.
If you create a form for your model (which is what you need if you want users to create a Musician):
class MusicianForm(ModelForm):
class Meta:
model = Musician
fields = '__all__'
form = MusicianForm(data={})
form.instance.first_name
# ''
form.is_valid()
# False
form.save()
# ValueError: The Musician could not be created because the data didn't validate.
You'll also see that if you register Musician in admin.py for django admin site, you can't leave any of the fields empty. It just won't save.

Django - ChoiceField Spanning Multiple Forms

Is it possible in django to have a ChoiceField on a formset level rather than an inline form? So for example if I have a formset for phones and each inline form represents a single Phone model, how can I have a ChoiceField that spans all the inline forms? Something like this where I'm choosing a primary phone:
My models:
class Profile(models.Model):
verified = models.BooleanField(default=False)
primary_phone = models.OneToOneField('Phone', related_name='is_primary', null=True, blank=True)
class Phone(models.Model):
profile = models.ForeignKey(Profile, editable=False)
type = models.CharField(choices=PHONE_TYPES, max_length=16)
number = models.CharField(max_length=32)
#property
def is_primary(self):
return profile.primary_phone == self
I can always remove primary_phone and use a BooleanField in Phone to indicate if it's primary or not, but I'm not sure if this going to help my problem.
I'm also looking for a less-hacky more-django-like approach if possible.
There is no way to have django create this for you automatically. In your ModelForm (that's used in the inline) I'd add a boolean field called is_primary. This field will then show up on each inlined Phone instance (as a checkbox).
On the front end sort it out with javascript so that a user can only select one default at a time. On the back end use some custom validation to double check that only one is_default was submitted, and then update the primary_phone as necessary with form logic.

How to assign default value to Django model Integerfield, while related FormField is optional?

I want to create a Django model Field (IntegerField) with a default value, and also create a form derived from the model, where the field is optional. If it's not set on the form, then when I save the form, I want the default value saved to the DB.
# model.py
class Invoice(models.Model):
# IntegrityError "Column 'expireDays' cannot be null"
expireDays = models.PositiveSmallIntegerField(default=1)
# expireDays = *null* in DB
expireDays = models.PositiveSmallIntegerField(default=1, blank=True, null=True)
# forms.py
class InvoiceForm(forms.ModelForm):
# leaving this line out gives invalid form
expireDays = forms.IntegerField(required=False)
class Meta:
model = Invoice
(I used only one of the field declaration lines at a time. :)
I'm not even sure that I'm declaring the default value correctly. The only reference I could find to it was in an article on handling choices by James Bennett. I have yet to find it in the Django docs (I'm using version 1.2 - maybe it's in 1.3?)
Update - I tried setting the field's default value in the MySql database, to no effect. It seems as if, even when the form does not have a value for the field, it goes ahead and assigns null to the DB, over-riding the MySql default value.
Although I am currently just setting a default value in the view that creates the form - I don't really like that, since it puts the responsibility for the field's integrity in the view, not the DB.
The way I would have thought it would work, is that the field could be set, or not, in the form - if set, that value would be written to the DB, and if not set, the DB default would be used. Instead, if not set, the form is writing a null to the DB. So what's the point of having a default value in the ModelField declaration if it's not used? What exactly does it do?
i you want field to be optional - just leave second definition in the model and do not add anything in the form definition:
class Invoice(models.Model):
expireDays = models.PositiveSmallIntegerField(default=1, blank=True, null=True)
class InvoiceForm(forms.ModelForm):
class Meta:
model = Invoice
update, so in case there is no value set, use 1 as the field value:
class InvoiceForm(forms.ModelForm):
def clean_expireDays(self):
exp_days = self.cleaned_data.get('expireDays')
if exp_days is None:
return self.fields['expireDays'].initial
# above can be: return 1
# but now it takes value from model definition
else:
return exp_days
class Meta:
model = Invoice

Validating an Autocomplete field in Django

I have models similar to the following:
class Band(models.Model):
name = models.CharField(unique=True)
class Event(models.Model):
name = models.CharField(max_length=50, unique=True)
bands = models.ManyToManyField(Band)
and essentially I want to use the validation capability offered by a ModelForm that already exists for Event, but I do not want to show the default Multi-Select list (for 'bands') on the page, because the potential length of the related models is extremely long.
I have the following form defined:
class AddEventForm(ModelForm):
class Meta:
model = Event
fields = ('name', )
Which does what is expected for the Model, but of course, validation could care less about the 'bands' field. I've got it working enough to add bands correctly, but there's no correct validation, and it will simply drop bad band IDs.
What should I do so that I can ensure that at least one (correct) band ID has been sent along with my form?
For how I'm sending the band-IDs with auto-complete, see this related question: Django ModelForm Validate custom Autocomplete for M2M, instead of ugly Multi-Select
You can override the default fields in a ModelForm.
class AddEventForm(forms.ModelForm):
band = forms.CharField(max_length=50)
def clean_band(self):
bands = Band.objects.filter(name=band,
self.data.get('band', ''))
if not bands:
raise forms.ValidationError('Please specify a valid band name')
self.cleaned_data['band_id'] = bands[0].id
Then you can use your autocomplete widget, or some other widget. You can also use a custom widget, just pass it into the band field definition: band = forms.CharField(widget=...)