How do I use error_messages on models in Django - django

I understand form the documentation http://docs.djangoproject.com/en/dev/ref/models/fields/ that you can add error_messages to a model field and supply your own dict of error messages. However, what are they keys of the dict you are supposed to pass?
class MyModel(models.Model):
some_field = models.CharField(max_length=55, error_messages={'required': "My custom error"})
If it is easier to do this on the modelform that is used that would also work, however. I would rather not have to create explicitly creating each field and their type again. This is what I was trying to avoid:
class MyModelForm(forms.ModelForm):
some_field = forms.CharField(error_messages={'required' : 'Required error'})
Update 2: Test code used in my project
My Model:
class MyTestModel(models.Model):
name = models.CharField(max_length=127,error_messages={'blank' : 'BLANK','required' : 'REQUIRED'})
My Form:
class EditTestModel(ModelForm):
class Meta:
model = MyTestModel
My View:
tf = EditTestModel({'name' : ''})
print tf.is_valid() # prints False
print tf.full_clean() # prints None
print tf # prints the form, with a <li> error list containg the error "This field is required"
<tr><th><label for="id_name">Name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input id="id_name" type="text" name="name" maxlength="127" /></td></tr>

You're right, those docs are not very useful. It's a recent addition after all!
My guess is that the normal usage of error_messages is for ModelForms, so I'd look here for a list of acceptable error keys per field: http://docs.djangoproject.com/en/dev/ref/forms/fields/#error-messages
But, if you want to be really safe and not assume anything...
The most reliable way for now is going to be looking at the source at django/db/models/fields/__init__.py where you'll see each of the default_error_messages that can be specified and the actual calls to self.error_messages['invalid']
# Field (base class)
default_error_messages = {
'invalid_choice': _(u'Value %r is not a valid choice.'),
'null': _(u'This field cannot be null.'),
'blank': _(u'This field cannot be blank.'),
}
# AutoField
default_error_messages = {
'invalid': _(u'This value must be an integer.'),
}
Here's the doc on model validation:
http://docs.djangoproject.com/en/dev/ref/models/instances/#validating-objects
Update:
Just tested this in a shell session and it appears to be working. Whats up?
I just defined a simple model:
class SubscriptionGroup(models.Model):
name = models.CharField(max_length=255, error_messages={'blank': 'INVALID!!11', 'null': 'NULL11!'})
# shell
>>> s = SubscriptionGroup()
>>> s.full_clean()
ValidationError: {'name': [u'INVALID!!11']}

(Bit late to this one, but I have been through the same issues myself, so using this as a note_to_self as much as anything.)
You can specify error_messages on both models and modelforms. The keys you should / can use are defined here for the form fields. The issue seems to be (to me) the inter-relationship between forms and the related model, and which error message appears, and when. The key to this is understanding that forms and models are actually very loosely-coupled, and that there really is no magic happening.
If you have a model field called 'quote', with a max_length of 140, and a modelform associated with this model, the error messages will work thus:
If you don't explicitly add a max_length attribute to the modelform, and then validate the form (calling is_valid() or errors), the error message that comes back will be from the model.
If you add a set of error_messages ('required','max_length') to the model, these will appear in the errors collection.
If you add a set of error_messages to the modelform, they will not appear, as it is the model that is failing validation, not the form.
If you then add a max_length attribute to the modelform, you will see the modelform errors surfacing (and overriding the model error_messages.)
So - fairly simple in summary - the error messages map to the object that is being validated - which can be either the model or the modelform.

I tried. It will not work if you defined it in models.
You must define error_messages in your forms like this
name = forms.CharField(error_messages={'required': 'this field is required'})

Related

Django ForeignKey, hide from user select options in dropdown menu

I have standard Django models with ForeignKey.
Django docs:
"ForeignKey is represented by django.forms.ModelChoiceField, which is a ChoiceField whose choices are a model QuerySet."
and
"If the model field has choices set, then the form field’s widget will be set to Select, with choices coming from the model field’s choices."
Now I have dropdown menu with choices.
I don't want dropdown menu where user can see options. I want CharField(textfield or similar) where user type, but still
that must be one of the options from the database for that field. He must type a valid entry.
I tried:
class TransakcijeForm(forms.ModelForm):
model = models.Transakcije
fields = .....
labels = .....
widgets ={'subscriber':forms.TextInput()}
but I receive the message:
"Select a valid choice. That choice is not one of the available choices."
(entry is correct and it works with dropdown menu)
This is my first question here and I'm sorry if I miss the form.
The reason you are getting that error is because your form is still treating the subscriber field as a ModelChoiceField because you are only overriding what widget is rendered to html. You need to change the actual field type of your field. You can define your form like this:
from django.core.exceptions import ValidationError
class TransakcijeForm(forms.ModelForm):
subscriber = forms.CharField()
class Meta:
model = models.Transakcije
fields = ....
labels = ....
def clean_subscriber(self):
subscriber_id = self.cleaned_data['subscriber']
try:
# adjust this line to appropriately get the model object that you need
subscriber = SubscriberModel.objects.get(id=subscriber_id)
return subscriber
except:
raise ValidationError('Subscriber does not exist')
The line subscriber = forms.CharField() will change the form to treat the field as a CharField rather than a ModelChoiceField. Doing this will cause the form to return the subscriber field value as a string, so you will need to get the appropriate model object based on the value of the field. That is what the clean_subscriber(self) function is for. It needs to be named like clean_<field name>(). That function will take the string that is returned by the form, try and find the correct model object and return it if an object is found. If it finds no matching objects it will raise a ValidationError so the form doesn't submit with a bad value.

Django form blank=False

Currently, if a field is required, this can be enforced via the blank = False argument, such as:
models.py
address1 = models.CharField(max_length=255,null=False,blank=False)
However, the validation is performed prior to the POST action, yielding something like this when trying to submit the form containing an empty field:
I would prefer the validation to be done during the post step, like this:
models.py
address1 = models.CharField(max_length=255,null=False,blank=true)
forms.py
class AddressForm(forms.ModelForm):
def __init__(self,*args,**kwargs):
super(AddressForm,self).__init__(*args,**kwargs)
self.fields['address1'].required = True
And this yields the following result when trying to submit the form containing an empty field:
But the problem with this, (as far as I can tell) is that I need to explicitly state the required attribute for each field on a case-by-case basis.
Is there any way that I can associate blank=False as being representative of the required=True attribute, suppressing the first form validation (above), in favour of the second?
ModelForm runs form validation, then model validation:
There are two main steps involved in validating a ModelForm:
Validating the form
Validating the model instance
So you have to manually add the extra form validation that you want before the inherited model validations.
However, default ModelForm field for blank field is already required:
If the model field has blank=True, then required is set to False on
the form field. Otherwise, required=True
You can change the error message. If you use this additional validations a lot, you can use a Mixin:
class BlankToRequiredMixin(object):
def set_required(self):
model = self._meta.model
for field_name,form_field in self.fields.iteritems():
if not model._meta.get_field(field_name).blank:
form_field.error_messages={'required': 'This field is required'} # to make it required in addtion to non-blank set .required=True
Then, to set required=True for all fields that are non-blank in the model:
class AddressForm(forms.ModelForm,BlankToRequiredMixin):
def __init__(self,*args,**kwargs):
super(AddressForm,self).__init__(*args,**kwargs)
self.set_required()
In a similar way you can add other validations to the form fields, based on the model validation attributes. For the appearance, change the widget and set the field widget in the mixin.

django ModelForm doesn't seem to validate BooleanField

I'm using django 1.5.4
Here's a minimal example of the issue I'm facing.
The model:
#models.py
from django.db import models
class SampleModel(models.Model):
spam = models.BooleanField()
The form:
#forms.py
from django.forms import ModelForm
from .models import SampleModel
class SampleModelForm(ModelForm):
class Meta:
model = SampleModel
fields = ('spam', )
From the django shell:
>>> data = {} #intentionally blank
>>> form = SampleModelForm(data)
>>> is_valid = form.is_valid() #is_valid is True
>>> form.save() # model instance is created with "spam" set to False by default.
Am I validating the form incorrectly? form.is_valid validates fields of other types correctly.
The docs indicate that all fields are required by default but is_valid returns Truewithout the boolean field key being present.
I need to ensure that the boolean field is present in the input data.
As of now, I'm manually checking if the field is present and is of type bool. Do you think I should override form.is_valid and add this check so that it can be reused for other models too?
It turns out (from code inspection; the docs don't say) that model BooleanFields have blank=True set automatically in their __init__ method, thus making the automatically created model form field not required. This makes sense upon consideration (False counts as blank, so BooleanFields need it to be true) but it's not obvious when you just read the docs.
If you want it to be required to be True, the usual form field overrides apply - declare the field yourself or set its required attribute to be True somewhere before validating (I usually use the form's __init__ method). If you want it to allow True or False but not Python None, it's harder.
Specifically, the standard widget for a BooleanField is a checkbox. Browsers do not submit anything for unchecked checkboxes, so the checkbox widget treats the absence of the field from the submit as False. There's no way to distinguish the user not selecting the checkbox from cases in which the input really is bad. You could use a different widget (say, a RadioSelect) to at least make it possible for the browser to submit something for False, but you still have the problem that the BooleanField's to_python method converts its value to a boolean before validating, so you'd have to subclass and override.
Fortunately, False is not considered empty by the validation code, so if you do that you'll be able to set required=True and don't need custom cleaning methods.

Why is my forms clean method not doing anything?

I have two basic models that use model forms in the Django admin.
Models.py is similar to:
class FirstModel(models.Model):
name = CharField(max_length=100)
url = URLField()
class OtherModel(models.Model):
model = models.ForeignKey(FirstModel)
##Other fields that show up fine and save fine, but include some localflavor
Forms.py looks similar to:
class FirstModelForm(forms.ModelForm):
def clean(self):
#call the super as per django docs
cleaned_data = super(FirstModelForm, self).clean()
print cleaned_data
class Meta:
model = FirstModel
#other modelform is the same with the appropriate word substitutions and one field that gets overridden to a USZipCodeField
These are a stacked inline ModelAdmin with nothing special in the admin.py:
class OtherModelInline(admin.StackedInline):
model = OtherModel
fields = (#my list of fields works correctly)
readonly_fields = (#couple read onlys that work correctly)
class FirstModelAdmin(admin.ModelAdmin):
inlines = [
OtherModelInline,
]
admin.site.register(FirstModel, FirstModelAdmin)
I do have a User model, form and ModelAdmin that subclasses the User and UserCreationForm and overrides it's own clean method.This works exactly as expected.
The problem is with FirstModel and OtherModel. The clean methods I override in the ModelForm subclasses of FirstModelForm and OtherModelForm don't do anything. No exception thrown or a print of the cleaned_data. Just nothing. Everything else works as expected, but it's like my clean method isn't even there.
I got to be missing something simple, but I can't see what is. Any help would be great. Thanks!
By default, Django dynamically generates a model form for your model admins. You must specify that you want to use your custom forms by setting the form attribute.
class OtherModelInline(admin.StackedInline):
model = OtherModel
fields = (...) # if this doesn't work after specifying the form, set fields for the model form instead
readonly_fields = (#couple read onlys that work correctly)
form = OtherModelForm
class FirstModelAdmin(admin.ModelAdmin):
form = FirstModelForm
inlines = [
OtherModelInline,
]
admin.site.register(FirstModel, FirstModelAdmin)
You need to return the cleaned_data from the clean method in the form. If you look at the documentation for cleaning fields that rely on each other you'll notice:
...
# Always return the full collection of cleaned data.
return cleaned_data
It is possible that nothing survived the parent 'clean' method. If you are submitting data that won't validate because of the way your models are set up, cleaned_data will be empty. This is mentioned in the same doc linked by Timmy, where it says:
By the time the form’s clean() method is called, all the individual field clean methods will have been run (the previous two sections), so self.cleaned_data will be populated with any data that has survived so far. So you also need to remember to allow for the fact that the fields you are wanting to validate might not have survived the initial individual field checks.
In this case, if you have a URLField, the field validation is very strict, and unless you define 'verify_exists=False', it will also check if you are putting in a URL that returns a 404. In your case you would need to do this if you wanted to allow that:
class FirstModel(models.Model):
name = CharField(max_length=100)
url = URLField(verify_exists=False)
Outside of that, I have no idea what could be going on.

Django 1.4 - Add custom error message to user fields in form with meta field Model=User

I was trying to add some custom error messages to a form like this one:
class RegistrationForm(forms.ModelForm):
class Meta:
model = User
fields = ('email',)
extra_field = forms.CharField(error_messages={
'required':'Este campo es requerido'
})
I have no problem adding custom error messages to a field that is declared in the form (extra_field), but I'm having trouble adding custom error messages to the fields inherited from the model User via meta class (email).
What I really want is to customize all the error messages of this form. Also, I would like to display them in spanish, not english.
So far I have not found any documentation about how to customize the form fields inherited from the model User, I don't really want to write a new form just because of the error messages.
Thanks in advance!!!
PD: I'm using Django 1.4 and python 2.6.6 in a debian squeeze box.
Ok guys, thank you all for your answers, in fact you helped me a lot!!!
I solved the problem like this:
Suposse I have this form:
class RegistrationForm(forms.ModelForm):
class Meta:
model = User
fields = ('username', 'email')
And I want to add custom error messages to the username field, but I don't want to loose all the validation that already comes with that field.
I added this method to my RegistrationForm class:
def clean_username(self):
data = self.cleaned_data
# My extremely important validation.
if len(data['username']) == 5:
self.fields['username'].error_messages["myerror"] = "The important message"
raise forms.ValidationError(self.fields['username'].error_messages["myerror"])
return data['username']
The advantage of this aproach is that I don't loose the other validation checks that already come with the field in question.
This is important because now I don't have to validate that the user already exists in my database.
Thanks for your responses, have a good day!!!
Turn on internalization in your settings.py
USE_I18N = True
LANGUAGE_CODE = 'es'
Model User doesn't differ from others. Put your code to figure out the issue.
In your meta class fields = ('email') must be tuple or list, i.e. fields = ('email', )
You can override any of the inherited fields by simply redefining them in exactly the same way as you have defined your extra field.