How can you create a non-empty CharField in Django? - django

I have a simple model which looks like this:
class Group(models.Model):
name = models.CharField(max_length = 100, blank=False)
I would expect this to throw an integrity error, but it does not:
group = Group() # name is an empty string here
group.save()
How can I make sure that the name variable is set to something non-empty? I.e to make the database reject any attempts to save an empty string?

another option that doesn't require you to manually call clean is to use this:
name = models.CharField(max_length=100, blank=False, default=None)
blank will prevent an empty string to be provided in the admin or using a form or serializer (most cases). However as pointed out in the comments, this unfortunately does not prevent things like model.name = "" (manually setting blank string)
default=None will set name to None when using something like group = Group(), thus raising an exception when calling save

From the Django docs in this case, your name will be stored as an empty string, because the null field option is False by default. if you want to define a custom default value, use the default field option.
name = models.CharField(max_length=100, blank=False, default='somevalue')
On this page, you can see that the blank is not database-related.
Update:
You should override the clean function of your model, to have custom validation, so your model def will be:
class Group(models.Model):
name = models.CharField(max_length=100, blank=False)
def clean(self):
from django.core.exceptions import ValidationError
if self.name == '':
raise ValidationError('Empty error message')
Or you can replace ValidationError to something else. Then before you call group.save() call group.full_clean() which will call clean()
Other validation related things are here.

Or you can simply use MinLengthValidator with a 1-char minimum:
from django.core.validators import MinLengthValidator
class Company(BaseModel):
"""Company"""
name = models.CharField(max_length=255,
validators=[MinLengthValidator(1)])

I spent a long time looking for the best solution for this simple (and old) problem, And as of Django 2.2, there is actually a really simple answer, so I'll write it here in case someone still encounters the same problem:
Since Django 2.2, we can define CheckConstraints, so it's easy to define a non-empty string constraint:
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=32)
class Meta:
constraints = [
models.CheckConstraint(check=~models.Q(title=""), name="non_empty_title")
]

Django’s validation system assumes that all fields are required,unless you mention that its ok to leave it blank..
Have you registered the class in Admin ? does it show errors when you leave it blank ??

Related

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.

Adding non-nullable fields without a default in Django 1.7

I created the following model to understand how to work with Django properties:
from django.db import models
class Person(models.Model):
_first_name = models.CharField(max_length=50, db_column='first_name')
#property
def first_name(self):
"""This is the getter. You can add logic too."""
return self._first_name
#first_name.setter
def first_name(self, fname):
self._first_name = fname
Why do I get the following error when I run the "makemigrations" command. I didn't have to specify default values for fields prior to Django 1.7. Does it have anything to do with my use of the property decorator?
Thanks.
You are trying to add a non-nullable field '_first_name' to person without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default...
2) Quit...
It is says that default models.CharField has the attribute blank=False witch means that it can't store an empty string value and it needs a default value in case it needs to store one, default="my default value" witch will be used if it has to store something equivalent to an empty string.
To fix this problem you can do the following
1)Allow empty strings values
You do this by specifying :
_first_name = models.CharField(max_length=50, db_column='first_name', blank=True)
2)Set a default value if it encounters and empty string
You give to him something to use instead of storing an empty string.
You do this by specifying :
_first_name = models.CharField(max_length=50, db_column='first_name', default="My default value")
By default we cannot store a empty string so we use blank=True to allow that.
Try this:
first_name = models.CharField(max_length=50, db_column='first_name',blank=False)

Django Field Regex Validation

I am trying to create a model to store an hashtag.
The validator doesn't seem to be working, making the field accept all inputs, and I can't find the solution.
Here is my model:
class Hashtags(models.Model):
hashtag_validator = RegexValidator(r'^[#](\w+)$', "Hashtag doesn't comply.")
hashtag_id = models.AutoField(primary_key=True)
hashtag_text = models.CharField(max_length=100, validators=[hashtag_validator], unique=True)
def get_id(self):
return self.hashtag_id
def get_text(self):
return self.hashtag_text
You can alter it to the below given code to see it working
hashtag_validator = CharField(
max_length=50,
required=True, #if you want that field to be mandatory
validators=[
RegexValidator(
regex='^[#](\w+)$',
message='Hashtag doesnt comply',
),
]
)
Hope that helps!!
If that is causing problem you can try writing your own validator
from django.core.exceptions import ValidationError
import re
def validate_hash(value):
reg = re.compile('^[#](\w+)$')
if not reg.match(value) :
raise ValidationError(u'%s hashtag doesnot comply' % value)
and change your model field to
hashtag_validator = models.Charfield(validators=[validate_hash])
Very late to the party so I doubt that this is still a problem for OP, but I will leave this here just for posterity and people that happen to come across this post. Probably you are instantiating and saving an object directly, e.g Hashtags(hashtag_text='invalid-tag').save(). This will not call the validators. The validators are only called when full_clean or clean is called, which is only done automatically if you go through a ModelForm. If you instantiate objects manually, either through the constructor or the object collection Hashtags.objects.create the validators will not be called.
In addition to S.Ali answer:
based on example from here
def uncvalidator(value):
"""Custom UNC path validator"""
import re
from django.utils.translation import gettext_lazy as _
UNC_REGEX = r'^local.company/some/share'
regex = re.compile(UNC_REGEX, re.IGNORECASE)
if not regex.match(value):
raise ValidationError(
_('Entered path %(value)s is incorrect.'),
params={'value': value},
)
unc = models.CharField(
validators=[uncvalidator],
)

How to display a value where there is none in Django admin?

Is there a Djangotastic way to display a default value for a field in the admin when there isn't a value? Like 'n/a', but not to save that to the database?
When I set all the fields in the model below to readonly in the admin, the front-end display looks like the image at the bottom. It feels visually collapsed like it should have a value or a box or something. If there isn't an easy way to do what I am looking for, then is there another solution to make the front-end admin more clear for the user?
class Package(models.Model):
packaging_format = models.CharField(max_length=40)
package_delivery_pattern = models.CharField(max_length=30, blank=True)
package_delivery_comments = models.CharField(max_length=250, blank=True)
package_manifest_filename = models.CharField(max_length=50)
package_description = models.CharField(max_length=250, blank=True)
package_naming_pattern = models.CharField(max_length=50)
Screenshot of fields as displayed in the admin:
What's happening is that your actually saving a empty string '' in your CharFields instead of None values (because of the blank=True). So the Django-admin is showing the string you saved in the db (in this case, nothing).
If you change your CharFields to null=True instead of blank=True, you will be saving NULL in your database instead of an empty string. And that way, you will get the behaviour you want.
EDIT: I know this solution is not recommended (following Django Docs), but that's the behaviour you wanted. Django-admin is just showing you the string you have in the database, which is ''.
Another solution that comes to my mind is to modify the ModelAdmin for your Package model, something like:
class PackageAdmin(admin.ModelAdmin):
readonly_fields = ['show_package_delivery_pattern', ...]
def show_package_delivery_pattern(self, obj):
if obj.package_delivery_pattern:
return obj.package_delivery_pattern
else:
return 'N/A'
# same with all your CharFields..
As of Django 1.9 you can use empty_value_display at the site, model, or field level in the Django admin. At the model level:
class YourModelAdmin(admin.ModelAdmin):
empty_value_display = '---'

Django Form with no required fields

I want to make a form used to filter searches without any field being required. For example given this code:
models.py:
class Message(models.Model):
happened = models.DateTimeField()
filename = models.CharField(max_length=512, blank=True, null=True)
message = models.TextField(blank=True, null=True)
dest = models.CharField(max_length=512, blank=True, null=True)
fromhost = models.ForeignKey(Hosts, related_name='to hosts', blank=True, null=True)
TYPE_CHOICES = ( (u'Info', u'Info'), (u'Error', u'Error'), (u'File', u'File'), (u'BPS', u'BPS'),)
type = models.CharField(max_length=7, choices=TYPE_CHOICES)
job = models.ForeignKey(Jobs)
views.py:
WHEN_CHOICES = ( (u'', ''), (1, u'Today'), (2, u'Two days'), (3, u'Three Days'), (7, u'Week'),(31, u'Month'),)
class MessageSearch(ModelForm): #Class that makes a form from a model that can be customized by placing info above the class Meta
message = forms.CharField(max_length=25, required=False)
job = forms.CharField(max_length=25, required=False)
happened = forms.CharField(max_length=14, widget=forms.Select(choices=WHEN_CHOICES), required=False)
class Meta:
model = Message
That's the code I have now. As you can see it makes a form based on a model. I redefined message in the form because I'm using an icontains filter so I didn't need a giant text box. I redefined the date mostly because I didn't want to have to mess around with dates (I hate working with dates! Who doesnt?) And I changed the jobs field because otherwise I was getting a drop down list of existing jobs and I really wanted to be able to search by common words. So I was able to mark all of those as not required
The problem is it's marking all my other fields as required because in the model they're not allowed to be blank.
Now in the model they can't be blank. If they're blank then the data is bad and I don't want it in the DB. However the form is only a filter form on a page to display the data. I'm never going to save from that form so I don't care if fields are blank or not. So is there an easy way to make all fields as required=false while still using the class Meta: model = Message format in the form? It's really handy that I can make a form directly from a model.
Also this is my first serious attempt at a django app so if something is absurdly wrong please be kind :)
You can create a custom ModelForm that suit your needs. This custom ModelForm will override the save method and set all fields to be non-required:
from django.forms import ModelForm
class SearchForm(ModelForm):
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
for key, field in self.fields.iteritems():
self.fields[key].required = False
So you could declare your forms by simply calling instead of the ModelForm, e.g.:
class MessageForm(SearchForm):
class Meta:
model = Message
You could also pass empty_permitted=True when you instantiate the form, e.g.,
form = MessageSearch(empty_permitted=True)
that way you can still have normal validation rules for when someone does enter data into the form.
I would give a try to the django-filter module :
http://django-filter.readthedocs.io/en/develop/
fields are not required. these are filters actually. It would look like this :
import django_filters
class MessageSearch(django_filters.FilterSet):
class Meta:
model = Message
fields = ['happened', 'filename', 'message', '...', ]
# django-filter has its own default widgets corresponding to the field
# type of the model, but you can tweak and subclass in a django way :
happened = django_filters.DateFromToRangeFilter()
mandatory, hidden filters can be defined if you want to narrow a list of model depending on something like user rights etc.
also : setup a filter on a 'reverse' relationship (the foreignkey is not in the filtered model : the model is referenced elsewhere in another table), is easy, just name the table where the foreign key of the filtered model field is :
# the 'tags' model has a fk like message = models.ForeignKey(Message...)
tags= django_filters.<some filter>(name='tags')
quick extendable and clean to setup.
please note I didn't wrote this module, I'm just very happy with it :)