I can see how to add an error message to a field when using forms, but what about model form?
This is my test model:
class Author(models.Model):
first_name = models.CharField(max_length=125)
last_name = models.CharField(max_length=125)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
My model form:
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
The error message on the fields: first_name and last_name is:
This field is required
How do I change that in a model form?
For simple cases, you can specify custom error messages
class AuthorForm(forms.ModelForm):
first_name = forms.CharField(error_messages={'required': 'Please let us know what to call you!'})
class Meta:
model = Author
New in Django 1.6:
ModelForm accepts several new Meta options.
Fields included in the localized_fields list will be localized (by setting localize on the form field).
The labels, help_texts and error_messages options may be used to customize the default fields, see Overriding the default fields for details.
From that:
class AuthorForm(ModelForm):
class Meta:
model = Author
fields = ('name', 'title', 'birth_date')
labels = {
'name': _('Writer'),
}
help_texts = {
'name': _('Some useful help text.'),
}
error_messages = {
'name': {
'max_length': _("This writer's name is too long."),
},
}
Related: Django's ModelForm - where is the list of Meta options?
Another easy way of doing this is just override it in init.
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
def __init__(self, *args, **kwargs):
super(AuthorForm, self).__init__(*args, **kwargs)
# add custom error messages
self.fields['name'].error_messages.update({
'required': 'Please let us know what to call you!',
})
I have wondered about this many times as well. That's why I finally wrote a small extension to the ModelForm class, which allows me to set arbitrary field attributes - including the error messages - via the Meta class. The code and explanation can be found here: http://blog.brendel.com/2012/01/django-modelforms-setting-any-field.html
You will be able to do things like this:
class AuthorForm(ExtendedMetaModelForm):
class Meta:
model = Author
field_args = {
"first_name" : {
"error_messages" : {
"required" : "Please let us know what to call you!"
}
}
}
I think that's what you are looking for, right?
the easyest way is to override the clean method:
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
def clean(self):
if self.cleaned_data.get('name')=="":
raise forms.ValidationError('No name!')
return self.cleaned_data
I have a cleaner solution, based on jamesmfriedman's answer.
This solution is even more DRY, especially if you have lots of fields.
custom_errors = {
'required': 'Your custom error message'
}
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
def __init__(self, *args, **kwargs):
super(AuthorForm, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].error_messages = custom_errors
You can easily check and put custom error message by overriding clean()method and using self.add_error(field, message):
def clean(self):
super(PromotionForm, self).clean()
error_message = ''
field = ''
# reusable check
if self.cleaned_data['reusable'] == 0:
error_message = 'reusable should not be zero'
field = 'reusable'
self.add_error(field, error_message)
raise ValidationError(error_message)
return self.cleaned_data
Related
Imagine having a simple model like the one bellow:
from utils.validators import name_validator
class Customer(models.Model):
name = models.CharField(verbose_name="Customer Name", validators=[name_validator])
email = models.EmailField(verbose_name="Customer Email")
def __str__(self):
return self.name
Now if I explicitly define a filed on my serializer, both validators and verbose_name are lost. I can use label= and validatos= when defining the field on my serializer but I don't want to repeat myself. What if I have multiple serializer pointing to the same Model?
class CustomerSerilizer(serializers.ModelSerializer):
custom_field_name = serializers.CharField(source="name")
class Meta:
model = Customer
fields = "__all__"
Is there anyway to prevent this from happening?
I'm not sure if it's the perfect way of doing this or not, but I managed to achieve my desired behavior by writing a custom ModelSerializer which sets label and validators if they are not being passed when explicitly defining a field on the serializer.
class CustomModelSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super(CustomModelSerializer, self).__init__(*args, **kwargs)
model = self.Meta.model
model_fields = [f.name for f in model._meta.get_fields()]
for field_name, field_instance in self.fields.items():
source_field = field_instance.source
if source_field in model_fields:
model_field = model._meta.get_field(source_field)
if "label" not in field_instance._kwargs:
field_instance.label = model_field.verbose_name
if "validators" not in field_instance._kwargs:
field_instance.validators.extend(model_field.validators)
I have written this function in Django to override the 'label suffix and form field error message. Within the same function, the lebel suffix is working (the colon is removed) but the error message did not replaced by with customized one.
Here is the form class with the function:
class User_accountModelForm(forms.ModelForm):
# to remove colons from the labels:
def __init__(self, *args, **kwargs):
kwargs.setdefault('label_suffix', '')
super(User_accountModelForm, self).__init__(*args, **kwargs)
# changing error messages:
for field in self.fields.values():
field.error_messages = {'required':'The field {fieldname} is required'.format(fieldname=field.label)}
class Meta:
model = User_account
fields = ['first_name', 'other fields']
any help or clue is appreciated
To override the forms error messages, add it to the forms Meta; see also the ModelForm docs:
class User_accountModelForm(ModelForm):
class Meta:
model = User_account
fields = ['first_name', 'other fields']
error_messages = {
'first_name': {
# for example:
'max_length': _("This writer's name is too long."),
},
}
I am a newbie in Django.
I would like the email field in the subclassed UserCreationForm to be required.
I have tried the commented methods but none has worked so far. I have tried the solution from this but to no avail. Any help would be appreciated.
class MyRegistrationForm(UserCreationForm):
captcha = NoReCaptchaField()
#email = forms.EmailField(required=True, widget=forms.TextInput(attrs={'class': 'mdl-textfield__input'}))
class Meta:
model = User
fields = ('first_name', 'last_name', 'username', 'email', 'password')
#email = {
# 'required': True
#}
widgets = {
'first_name': forms.TextInput(attrs={'class': 'mdl-textfield__input'}),
'last_name': forms.TextInput(attrs={'class': 'mdl-textfield__input'}),
'username': forms.TextInput(attrs={'class': 'mdl-textfield__input'}),
#'email': forms.TextInput(attrs={'class': 'mdl-textfield__input'})
}
def save(self, commit=True):
user = super(MyRegistrationForm, self).save(commit=False)
user.first_name = self.cleaned_data["first_name"]
user.last_name = self.cleaned_data["last_name"]
user.username = self.cleaned_data["username"]
user.email = self.cleaned_data["email"]
#user.user_level = self.cleaned_data["user_level"]
if commit:
user.save()
return user
def __init__(self, *args, **kwargs):
super(MyRegistrationForm, self).__init__(*args, **kwargs)
self.fields['password1'].widget.attrs['class'] = 'mdl-textfield__input'
self.fields['password2'].widget.attrs['class'] = 'mdl-textfield__input'
#self.fields['email'].required=True
This solved the problem: email = forms.CharField(required=True, widget=forms.EmailInput(attrs={'class': 'validate',}))
I checked Django's User model and it has required=False. So, I think you cannot achieve what you are looking for with the default User model based on note section of "Overriding the default fields" in the django documentation. I have inluded the snippet
ModelForm is a regular Form which can automatically generate certain
fields. The fields that are automatically generated depend on the
content of the Meta class and on which fields have already been
defined declaratively. Basically, ModelForm will only generate fields
that are missing from the form, or in other words, fields that weren’t
defined declaratively.
Fields defined declaratively are left as-is, therefore any
customizations made to Meta attributes such as widgets, labels,
help_texts, or error_messages are ignored; these only apply to fields
that are generated automatically.
Similarly, fields defined declaratively do not draw their attributes
like max_length or required from the corresponding model. If you want
to maintain the behavior specified in the model, you must set the
relevant arguments explicitly when declaring the form field.
For example, if the Article model looks like this:
class Article(models.Model):
headline = models.CharField(
max_length=200,
null=True,
blank=True,
help_text='Use puns liberally',
)
content = models.TextField() and you want to do some custom validation for headline, while keeping the blank and help_text values
as specified, you might define ArticleForm like this:
class ArticleForm(ModelForm):
headline = MyFormField(
max_length=200,
required=False,
help_text='Use puns liberally',
)
class Meta:
model = Article
fields = ['headline', 'content'] You must ensure that the type of the form field can be used to set the contents of the corresponding
model field. When they are not compatible, you will get a ValueError
as no implicit conversion takes place.
So try this,
from django.forms import EmailField
from django.core.validators import EMPTY_VALUES
# I used django [emailfield code][2] as reference for the code of MyEmailField
# Also, following comment in django [custom form fields document][2]:
# If the built-in Field classes don’t meet your needs, you can easily create custom Field classes. To do this, just create a subclass of django.forms.Field. Its only requirements are that it implement a clean() method and that its __init__() method accept the core arguments mentioned above (required, label, initial, widget, help_text).
# You can also customize how a field will be accessed by overriding get_bound_field().
class MyEmailField(forms.EmailField):
def __init__(self, *args, **kwargs):
super(MyEmailField, self).__init__(*args, strip=True, **kwargs)
# Clean would be called when checking is_clean
def clean(self,value):
if value in EMPTY_VALUES:
raise Exception('Email required')
value = self.value.strip()
return super(MyEmailField, self).clean(value)
class MyRegistrationForm(UserCreationForm):
captcha = NoReCaptchaField()
# All available arguments listed in django [core fields argument document][2]. Currently they are required, label, label_suffix, initial, widget, help_text, error_messages, validators, localize, disabled
email = MyEmailField(required=True)
class Meta:
model = User
fields = ('first_name', 'last_name', 'username', 'email', 'password')
# other part of your code
PS: I have not tested this code but based on the documentation I think this should take you in a good direction.
Few more references:
Django auth.user with unique email
How to make email field unique in model User from contrib.auth in Django
https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html
Add this to your forms.py file:
class Userform(UserCreationForm):
email = forms.EmailField(required=True)
class meta:
model = User
fields = ('name','email')
I can see how to add an error message to a field when using forms, but what about model form?
This is my test model:
class Author(models.Model):
first_name = models.CharField(max_length=125)
last_name = models.CharField(max_length=125)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
My model form:
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
The error message on the fields: first_name and last_name is:
This field is required
How do I change that in a model form?
For simple cases, you can specify custom error messages
class AuthorForm(forms.ModelForm):
first_name = forms.CharField(error_messages={'required': 'Please let us know what to call you!'})
class Meta:
model = Author
New in Django 1.6:
ModelForm accepts several new Meta options.
Fields included in the localized_fields list will be localized (by setting localize on the form field).
The labels, help_texts and error_messages options may be used to customize the default fields, see Overriding the default fields for details.
From that:
class AuthorForm(ModelForm):
class Meta:
model = Author
fields = ('name', 'title', 'birth_date')
labels = {
'name': _('Writer'),
}
help_texts = {
'name': _('Some useful help text.'),
}
error_messages = {
'name': {
'max_length': _("This writer's name is too long."),
},
}
Related: Django's ModelForm - where is the list of Meta options?
Another easy way of doing this is just override it in init.
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
def __init__(self, *args, **kwargs):
super(AuthorForm, self).__init__(*args, **kwargs)
# add custom error messages
self.fields['name'].error_messages.update({
'required': 'Please let us know what to call you!',
})
I have wondered about this many times as well. That's why I finally wrote a small extension to the ModelForm class, which allows me to set arbitrary field attributes - including the error messages - via the Meta class. The code and explanation can be found here: http://blog.brendel.com/2012/01/django-modelforms-setting-any-field.html
You will be able to do things like this:
class AuthorForm(ExtendedMetaModelForm):
class Meta:
model = Author
field_args = {
"first_name" : {
"error_messages" : {
"required" : "Please let us know what to call you!"
}
}
}
I think that's what you are looking for, right?
the easyest way is to override the clean method:
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
def clean(self):
if self.cleaned_data.get('name')=="":
raise forms.ValidationError('No name!')
return self.cleaned_data
I have a cleaner solution, based on jamesmfriedman's answer.
This solution is even more DRY, especially if you have lots of fields.
custom_errors = {
'required': 'Your custom error message'
}
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
def __init__(self, *args, **kwargs):
super(AuthorForm, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].error_messages = custom_errors
You can easily check and put custom error message by overriding clean()method and using self.add_error(field, message):
def clean(self):
super(PromotionForm, self).clean()
error_message = ''
field = ''
# reusable check
if self.cleaned_data['reusable'] == 0:
error_message = 'reusable should not be zero'
field = 'reusable'
self.add_error(field, error_message)
raise ValidationError(error_message)
return self.cleaned_data
I have two models:
class Manufacturer(models.Model):
name = models.CharField(max_length = 100)
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, blank = True, null = True)
My serialisers:
class ManufacturerSerializer(serializers.ModelSerializer):
class Meta:
model = Manufacturer
fields = ('id', 'name')
class CarSerializer(serializers.ModelSerializer):
manufacturer = Manufact
class Meta:
model = Car
fields = ('id', 'name', 'manufacturer')
def validate(self, attrs):
try:
attrs['manufacturer'] = Manufacturer.objects.get(pk = attrs['manufacturer'])
except Manufacturer.DoesNotExist:
raise ValidationError(_('Not found'))
return attrs
My views:
class CarList(generics.ListCreateAPIView):
queryset = Car.objects.all()
serializer_class = CarSerializer
When I try to add a new Car calling POST /cars/ I get a validation error manufacturer is a required field. Car model expects manufacturer field to be Manufacturer object but to make messages small I pass it the manufacturer_id instead.
I know that should raise a ValidationError so to fix this I added a validate(..) to my CarSerializer so during validation I check if Manufacturer by this ID exists and I assign it to attrs.
Problem is this validate(..) is never called I even tried adding a post() method to CarList view and manually calling is_valid() to no success.
But I still get validation errors which I assume are coming from the Model.
It doesn't work because you are complicating things. The ManufacturerSerializer is not necessary (for this view anyway). By default the 'manufacturer' field will be represented as a PrimaryKeyRelatedField which will resolve your manufacturer_id to a Manufacturer object automatically, so you don't need your validate method either.
Updated serializers:
class CarPostSerializer(serializers.ModelSerializer):
class Meta:
model = Car
fields = ('id', 'name', 'manufacturer')
class CarGetSerializer(CarPostSerializer):
manufacturer = ManufacturerSerializer()
Updated view:
class CarList(generics.ListCreateAPIView):
queryset = Car.objects.all()
def get_serializer_class(self):
if self.request.method == 'POST':
return CarPostSerializer
else:
return CarGetSerializer