I am aware that on djangoproject.com there is a explanation that you should use NullBooleanField for a checkbox in Django, however, I am wondering if it would be better to use something like this?
models.CharField(null=True, choices=(('Y','YES'),))
Then null would just evaluate to not checked and yes would evaluate to checked. This seems to make more sense to me rather than NullBooleanField where you would have essentially
models.CharField(null=True, choices=(('1','UNKNOWN'),('2','YES'),('3','NO'),))
my interpretation of NullBooleanField could be (and probably is) incorrect, but when I render a crispy forms, it gave me a select box, so wouldn't it be represented this way in the db?
If your logic only requires True/False, and not Null, then it would be better to use a BooleanField instead of a NullBooleanField, with default=False.
A NullBooleanField is indeed essentially a field with Yes/No/Unknown choices.
Note that a CharField never saves NULL but always an empty string, so null=True would have no effect on a CharField.
Related
I have an input box that must accept the following parameters:
Empty Value ('')
ZERO (0)
Fail value ('x')
I know that I can create a custom "fail" BooleanField that gets set to True if "x" is typed. However, I would like to know if it is possible add these values inside the DecimalField. Maybe, use CharField with Decimal Validation. However, trying to use it with CharField and Decimal Validation failed. So, I am not sure how else I can achieve this.
Thanks!
Could you do something with validators like:
extended_decimal_field = models.CharField(
max_length=10,
validators=[RegexValidator('^\d{8}[.]{1}\d{2}|['X']{1}$')]
)
I don't think this is exactly right but might be right track?
I have a button that, when clicked, should save in the database that the user has drunk water. I just wanted to check whether NullBooleanField would be the correct way to define this.
A broader question that if answered would be useful to the community is a list of optimal circumstances under which to use NullBooleanField. But I'm not asking that here. Just in case you wanted a better challenge.
Thank you in advance.
The question you need to answer to find out whether you should use the BooleanField or the NullBooleanField is actually concerning the possible states of the value of the field you want to represent in your model:
2 possible states:
user has drunk water
user has not drunk water
→ use BooleanField
3 possible states:
user has drunk water
user has not drunk water
it is not known whether the user has or has not drunk water
→ use NullBooleanField.
UPDATE:
NullBooleanField is deprecated in version 3.1. Instead use BooleanField with null=True.
Django 2.1 introduced null=True for BooleanField. Using NullBooleanField is now discouraged.
So use, x = BooleanField(null=True) instead of x = NullBooleanField()
Here's a simple use case: If you only need to record the "Yes" or "No" status, use Boolean without null. But if you want to have 3 conditions say, "Yes", "No", and "Don't Know", use it with null=True.
I think you should use NullBooleanField only when you have three possible choices: Unknown, Yes (True) and No (False).
In your case you have only two possible values - Yes (user has drunk water) and No (user has NOT drunk water) so a BooleanField would be better.
One more reason to use a BooleanField in your case is because the default form widget for this field is a CheckboxInput (docs), while the default form widget for a NullBooleanField is a NullBooleanSelect (docs). And since you use a checkbox, a BooleanField would do the job better.
Take advantage of the NULL properties
I use it quite often when I need to enforce some specific constrains in my data, but allow some others. Multiple NULL values can coexist in a column defined UNIQUE. Let's take an address model implementation as an example:
The business rules are:
A user can have up to 1 billing address
A user can have multiple shipping addresses
One way to implement that is by making a single address table with a foreign key to the user and an extra flag that indicates if that address is a billing address or not:
class Address(models.Model):
... # <- address fields
user = models.ForeignKey(User, on_delete=models.CASCADE)
billing_address = models.NullBooleanField(default=None)
You can now simply enforce the business rules at a database level by making user and billing_address unique together.:
class Meta:
constraints = [
models.UniqueConstraint(
fields=['user', 'billing_address'],
name='ensure single billing address'
)
]
The trick to make this work is that the billing_address must be True when the address is a billing address but it should be None (instead of False) when the address is a shipping address.
You can further enforce the validation by adding another constraint to make sure that no False values are added. But this is usually not necessary and could be done at the application level:
class Meta:
constraints = [
models.UniqueConstraint(
fields=['user', 'billing_address'],
name='ensure single billing address'
),
models.CheckConstraint(
check=~Q(billing_address=False),
name='no False allowed'
)
]
Biggest advantage of using NullBooleanField for PostgreSQL database is your table won't be re-written, any new field without null=True will cause a table re-write, it's fine for small tables but could take a significant amount of time and resource for large tables and you won't be able to write to your table during a re-write
I there any way to check if any field Null or Empty without checking each and every field. i have seen this Django check to see if field is blank?. But in this case we have to check each and every field.
if i am wrong please correct me. Thanks for the help!
There's no way around checking all the fields, but you could write some helpers.
any_blank_fields = all((field.blank for field in Model._meta.fields))
Oh, you're talking about Falsy values, not blank=True. In that case, you really have no choice but to do some loops, or a list comprehension.
instance = Model.objects.latest('pk')
all_fields_filled = all((getattr(instance, field.name) for field in
instance._meta.fields))
I guess this question is very relevant outside Django ORM too. Since I am working on it, I will put it this way.
Say, I have a django model abstract class having the below field
content = models.TextField()
One inherited model definitely requires it and another takes it based on the choice (another field). I see there are two ways to go:
use null=True, blank=True in abstract class, make necessary conditional validation statements in inherited classes
use default='' in abstract class and make necessary conditional validation
There are two things I'd like to achieve here in this case
Form validation should be conditional
Database validation should be conditional
Which is more preferred approach? and Why not the other?
From django docs:
Avoid using null on string-based fields such as CharField and
TextField because empty string values will always be stored as empty
strings, not as NULL. 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.
You dont have to specify empty string for default because that is what django use for "no-data" anyway. Avoid null. I recommend you specify the field as blank=True (no null) and enforce your validation at form level.
I have two models as
class Employer(models.Model):
..
..
class JobTitle(models.Model):
type = models.IntegerField(null=False, choices=JobTitles.CHOICES,blank=True,default=0)
employer = models.OneToOneField(Employer,unique=True,null=False)
I have defined admin for Employer and defined JobTitle as inline class.
However, when I saved an Employer object without selecting JobTitle, it raises the error invalid literal for int() with base 10: '' due to the type field of JobTitle.
I would like to set type to 0, as I defined default=0, if I don't select any jobtitle when employer is saved.
How can I achieve this ?
So, I'm inferring this from the code you posted, but I think that you're mixing strategies here. I assume based on the way you've constructed your question that you want to me able to make a job title with no type. Alternatively, you want one job type to be the default selection. I further infer, although now I'm stretching a bit, that you have a custom "No Type" choice (you didn't show us your JobTitles.CHOICES tuple so I have to guess) that corresponds to the 0 value that you've set as the default.
If you want to have a default job type (even if you're calling it "No Job Type" or something similar), then you should set it using the default keyword argument (as you have done) and you should set null=False and blank=False (alternatively, omit both, as these are the default values).
However, if you want it to be possible that there is a job title with no type, then unless you have some reason in your implementation to do something else, the purest way to represent this in data is to use the null SQL value. If you want to go that route, the correct way to do it is to set blank=True and null=True, and then either set default=None or omit that keyword argument entirely.
That should get you the behavior that you seek.
For Model arguments, default is used when Django save your data to Database...
On the other hand, your error occurs during data parsing. When you submit your form, django parses data to relevant type if needed. When your html form is submitted, django recieves string data like
{'title':'12', 'employer_id':'23'...}
So, django have to parse type into ineger before it save it to the database... Since your forms sends a nullstring '' , int('') simply fails with TypeError.
What you must do is removing blank=True. Or somehow you must override the default behaviour of combobox default value and set it 0 instead of ''.