How to add bootstrap class is-invalid to Django input forms (only if the form/field is not correctly completed).
My forms.py
class BasicUserDataForm(forms.Form):
error_css_class = 'is-invalid'
user_name = forms.CharField(max_length=20, widget=forms.TextInput(attrs={'placeholder': 'Username',
'class': 'form-control'}))
My templates.html
<div class="form-group col-md-6">
<!-- Label -->
<label for="id_user_name">Username*</label>
<div class="input-group mb-0">
<div class="input-group-prepend">
<div class="input-group-text">#</div>
</div>
{{ form.user_name }}
</div>
{% if form.user_name.errors|striptags %}<small class="text-danger"><b>{{ form.user_name.errors|striptags }}</b></small>{% endif %}
</div>
I try:
1.)
class BasicUserDataForm(forms.Form):
user_name = forms.CharField(max_length=20, widget=forms.TextInput(attrs={'placeholder': 'Username',
'class': 'form-control'}))
2.)
class BasicUserDataForm(forms.Form):
user_name = forms.CharField(max_length=20, widget=forms.TextInput(attrs={'placeholder': 'Username',
'class': 'form-control {% if form.user_name.errors %}is-invalid{% endif %}'}))
3.) According to the documentation
class BasicUserDataForm(forms.Form):
error_css_class = 'is-invalid'
[...]
4.)
.error input, .error select {
border: 2px red solid;
}
It still doesn't give any results. I would like to get the effect that gives:
user_name = forms.CharField(max_length=20, widget=forms.TextInput(attrs={'placeholder': 'Username',
'class': 'form-control is-invalid'}))
Create a file, yourapp_template-filters.py and then place it in templatetags folder in your app:
from django import template
register = template.Library()
#register.filter(name='add_attr')
def add_attribute(field, css):
attrs = {}
definition = css.split(',')
for d in definition:
if ':' not in d:
attrs['class'] = d
else:
key, val = d.split(':')
attrs[key] = val
return field.as_widget(attrs=attrs)
To use it in a Django template, firstly add this widget in the top of the template, similar to the load static widget:{% load yourapp_template-filters %}
And now you can use it on any form widget as a function, like so:
{% if form.user_name.errors %}
{{ form.user_name|add_attr:"form-control is-invalid" }}
{% else %}
{{form.user_name|add_attr:"form-control"}}
{% endif %}
Try adding a Meta class to your form and assigning the proper model. For instance:
class BasicUserDataForm(forms.Form):
error_css_class = 'is-invalid'
user_name = forms.CharField(max_length=20, widget=forms.TextInput(attrs={'placeholder': 'Username', 'class': 'form-control'}))
class Meta:
model = User
#ehsan nnnnn's answer is great! I tried it, and it works.
I want to clarify and extend it a little bit.
Initialization:
Source: https://docs.djangoproject.com/en/4.0/howto/custom-template-tags/
The app should contain a templatetags directory, at the same level as models.py, views.py, etc. Do not forget to add init.py in the directory! (as stated in the doc)
Attribute declaration:
The code provided by #ehsan nnnnn is helpful if you completely override the attributes section. I have based my code entirely on #ehsan nnnnn's solution and extended it to preserve the previous attributes.
NOTE: I am using Python 3.10.1; if you are somehow still using a version before f-string or do not like f-strings, correct it to whatever your heart desires.
Here is my solution:
# templatetags\add_attr.py
from django import template
register = template.Library()
#register.filter(name='add_attr')
def add_attribute(field, css):
attrs = field.subwidgets[0].data['attrs'] # Accessing the already declared attributes
definition = css.split(',')
for d in definition:
if ':' not in d:
attrs['class'] += f" {d}" # Extending the class string
else:
key, val = d.split(':')
attrs[key] += f'{val}' # Extending the `key` string
return field.as_widget(attrs=attrs)
{% comment %} HTML template {% endcomment %}
{% if form.username.errors %}
{{form.username|add_attr:'is-invalid'}}
{% else %}
{{ form.username }}
{% endif %}
Tested on:
Python 3.10.1
Django 4.0
Bootstrap 5.1
Related
Rendered django model object cannot give any output in template. My Code is:
model.py:
class MyModel(models.Model):
address = models.GenericIPAddressField(
max_length=15,
verbose_name=_('address')
)
Path = models.CharField(
max_length=300,
verbose_name='path',
)
activate = models.CharField(max_length=5, default='off', )
views.py:
class MyView(ListView):
model = models.MyModel.objects.all()[0]
template_name = '../on_off.html'
context_object_name = 'obj_list'
admin.py:
class MyAdmin(admin.ModelAdmin):
list_display = ('address', 'Path', )
exclude = ('activate', )
change_list_template = '../on_off.html'
on_off.html:
{% extends 'admin/change_list.html' %}
{% block object-tools %}
{% if obj_list %}
<form id="on_off_status" method="POST">
<label id="onoff" class="switch">
{% csrf_token %}
<input type="checkbox" id="togBtn" value=" {{ obj_list.activate }} ">
<div class="slider round"></div>
</label>
</form>
{% endif %}
...
In Django model, the system accepts only one object and I want to give to toggle switch button value in template using activate field in MyModel model. But it is not rendered in template, I couldn't get any object data even if I try <h1> {{ obj_list }} </h1>, it turns <h1> </h1> . Any help or suggestion appreciated.
Render the field manually
<label id="onoff" class="switch">
{% csrf_token %}
{{ obj_list.activate }}
<div class="slider round"></div>
</label>
Instead of using the views.py, django-admin provide a function named changelist_view(). Now it is working, I deleted the funtion in views.py and I added changelist_view() function to my admin.py looks like:
class MyAdmin(admin.ModelAdmin):
list_display = ('address', 'Path',)
exclude = ('activate',)
change_list_template = '../nfs_on_off.html'
def changelist_view(self, request, extra_context=None):
object = self.model.objects.all()
context={
'obj_list': object,
}
return super(MyAdmin, self).changelist_view(request, context)
despite of the hours of struggle i am unable to bootstrap a django form created with UserCreationForm. i want to add bootstrap classes to the tag but due to my poor knowledge of django class based views i am unable to have a workaround.
urls.py
urlpatterns = [
path('signup/', views.SignUp.as_view(), name='signup'),
path('', include('django.contrib.auth.urls')),
]
views.py
class SignUp(CreateView):
form_class = CustomUserCreationForm
success_url = reverse_lazy('accounts:login')
template_name = 'accounts/signup.html'
forms.py
class CustomUserCreationForm(UserCreationForm):
class Meta:
fields = ('first_name', 'last_name', 'email', 'age', 'height', 'avatar', 'password1', 'password2')
model = get_user_model()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['password2'].label = "Confirm Password"
Current Output
enter image description here
Output I want
enter image description here
Please take a look at this
https://simpleisbetterthancomplex.com/tutorial/2018/08/13/how-to-use-bootstrap-4-forms-with-django.html
or
https://django-bootstrap3.readthedocs.io/en/latest/templatetags.html
And also when you are rendering your field in your template, For example
{% bootstrap_field form.myfield form_group_class='custom-class-name' %}
You can add a custom class like so. and it 'll show up as a class in your HTML when you inspect so you can use it for your CSS
So, I am posting an answer to my own question so that someone looking for that same thing will find the solution in one place. What exactly I was looking for is;
forms.py
class CustomUserCreationForm(UserCreationForm):
class Meta:
fields = ('first_name', 'last_name', 'email', 'age', 'height', 'avatar', 'password1', 'password2')
model = get_user_model()
first_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'First Name',
'class': 'form-control',
}))
last_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Last Name',
'class': 'form-control',
}))
email = forms.CharField(widget=forms.EmailInput(attrs={'placeholder': 'Email',
'class': 'form-control mb-4',
}))
age = forms.IntegerField(widget=forms.NumberInput(attrs={'placeholder': 'Age',
'class': 'form-control',
}))
height = forms.IntegerField(widget=forms.NumberInput(attrs={'placeholder': 'Height(cm)',
'class': 'form-control',
}))
avatar = forms.ImageField(widget=forms.FileInput(attrs={'placeholder': 'Avatar',
'class': 'form-control mb-4',
}))
password1 = forms.CharField(widget=forms.PasswordInput(attrs={'placeholder': 'Password',
'class': 'form-control mb-4',
}))
password2 = forms.CharField(widget=forms.PasswordInput(attrs={'placeholder': 'Confirm Password',
'class': 'form-control mb-4',
}))
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['password2'].label = "Confirm Password"
views.py
class SignUp(CreateView):
form_class = CustomUserCreationForm
success_url = reverse_lazy('accounts:login')
template_name = 'accounts/register.html'
accounts/register.html
{% extends 'base.html' %}
{% block title %}
SignUp
{% endblock title %}
{% block content %}
<div style="margin: 75px 0px 0px 0px; background: rgb(87,77,255);
background: linear-gradient(276deg, rgba(87,77,255,0.8830882694874825) 24%, rgba(7,96,255,0.5385504543614321) 77%);" class="bg_cover">
<form class="text-center col-md-5" method="POST" enctype="multipart/form-data">
<p class="h4 mb-4">SignUp</p>
{% csrf_token %}
<div class="form-row mb-4">
<div class="col">
{{ form.first_name }}
</div>
<div class="col">
{{ form.last_name }}
</div>
</div>
{{ form.email }}
<div class="form-row mb-4">
<div class="col">
{{ form.age }}
</div>
<div class="col">
{{ form.height }}
</div>
</div>
{{ form.avatar }}
{{ form.password1 }}
{{ form.password2 }}
<button class="btn-block btn btn-primary my-4" type="submit">Sign up</button>
</form>
</div>
{% endblock content %}
results
You can subclass the UserCreationFrom as i did in the CustomUserCreationForm class, then all you have to do is add your desired attributes(bootstrap class names, id's etc) to the form fields you want to modify(beautify). Speaking of "accounts/register.html" written in the SignUp view, i am explicitly telling the django where to find the template. In the template you can render the entire form by {{ form.as_p }} or render individual fields as i did in the template!
Hopefully, my answer would save you from headache i went through to find that!
I am trying to write a Bootstrap Form with Django ModelForm. I have read the Django Documentation Django Documentation about Forms, so I have this code:
<div class="form-group">
{{ form.subject.errors }}
<label for="{{ form.subject.id_for_label }}">Email subject:</label>
{{ form.subject }}</div>
The {{form.subject}} is rendered by Django, for example in CharField field model, as input tag,
<input type="text"....> etc.
I need add "form-control" class to every input in order to get Bootstrap input appearance (without third-party packages). I found this solution Django add class to form <input ..> field. Is there any way to add a class to every field by default without specifying it in every attribute of the class of Form class?
class ExampleForm(forms.Form):
name = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
email = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
address = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
country = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
and so on ..
If you can't use a third-party app and want to add a class (e.g., "form-control") to every field in a form in a DRY manner, you can do so in the form class __init__() method like so:
class ExampleForm(forms.Form):
# Your declared form fields here
...
def __init__(self, *args, **kwargs):
super(ExampleForm, self).__init__(*args, **kwargs)
for visible in self.visible_fields():
visible.field.widget.attrs['class'] = 'form-control'
You might need to handle checking for existing classes in attrs too, if for some reason you'll be adding classes both declaratively and within __init__(). The above code doesn't account for that case.
Worth mentioning:
You specified that you don't want to use third-party packages. However, I'll take one second to mention that one of the simplest ways of automatically making forms render in the style of Bootstrap is to use django-crispy-forms, like this:
# settings.py
CRISPY_TEMPLATE_PACK = 'bootstrap3'
# forms.py
from crispy_forms.helper import FormHelper
class ExampleForm(forms.Form):
# Your declared form fields here
...
helper = FormHelper()
# In your template, this renders the form Bootstrap-style:
{% load crispy_forms_tags %}
{% crispy form %}
you can add CSS classes in forms.py
subject = forms.CharField(label='subject',
max_length=100,
widget=forms.TextInput(
attrs={'class': "form-control"}))
Since it took me more hours, than I would like to (django newbie), to figure this out, I will place my outcome here aswell.
Setting widget to each field just to add one class over and over again is against programming rule of repeating and leads to many unneccessary rows. This especially happens when working with bootstrap forms.
Here is my (working) example for adding not only bootstrap classes:
forms.py
class CompanyForm(forms.Form):
name = forms.CharField(label='Jméno')
shortcut = forms.CharField(label='Zkratka')
webpage = forms.URLField(label='Webové stránky')
logo = forms.FileField(label='Logo')
templatetags/custom_tags.py
from django import template
from django.urls import reverse
register = template.Library()
#register.filter('input_type')
def input_type(ob):
'''
Extract form field type
:param ob: form field
:return: string of form field widget type
'''
return ob.field.widget.__class__.__name__
#register.filter(name='add_classes')
def add_classes(value, arg):
'''
Add provided classes to form field
:param value: form field
:param arg: string of classes seperated by ' '
:return: edited field
'''
css_classes = value.field.widget.attrs.get('class', '')
# check if class is set or empty and split its content to list (or init list)
if css_classes:
css_classes = css_classes.split(' ')
else:
css_classes = []
# prepare new classes to list
args = arg.split(' ')
for a in args:
if a not in css_classes:
css_classes.append(a)
# join back to single string
return value.as_widget(attrs={'class': ' '.join(css_classes)})
reusable_form_fields.html (template)
{% load custom_tags %}
{% csrf_token %}
{% for field in form %}
<div class="form-group row">
{% if field|input_type == 'TextInput' %}
<div for="{{ field.label }}" class="col-sm-2 col-form-label">
{{ field.label_tag }}
</div>
<div class="col-sm-10">
{{ field|add_classes:'form-control'}}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% else %}
...
{% endif %}
</div>
{% endfor %}
Crispy forms are the way to go . Tips for Bootstrap 4. Adding to #Christian Abbott's answer, For forms , bootstrap says, use form-group and form-control .
This is how it worked for me .
My forms.py
class BlogPostForm(forms.ModelForm):
class Meta:
model = models.Post
fields = ['title', 'text', 'tags', 'author', 'slug']
helper = FormHelper()
helper.form_class = 'form-group'
helper.layout = Layout(
Field('title', css_class='form-control mt-2 mb-3'),
Field('text', rows="3", css_class='form-control mb-3'),
Field('author', css_class='form-control mb-3'),
Field('tags', css_class='form-control mb-3'),
Field('slug', css_class='form-control'),
)
My post_create.html
{% extends 'blog/new_blog_base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<form method='POST' enctype="multipart/form-data">
{% csrf_token %}
{{ form.media }}
{% crispy form %}
<hr>
<input type="submit" name="Save" value="Save" class='btn btn-primary'> <a href="{% url 'home' %}" class='btn btn-danger'>Cancel</a>
</form>
</div>
{% endblock %}
Note : If you are using CK Editor RichTextField() for your model field , then that field wont be affected . If anyone knows about it , do update this .
You can also explicity mention the field that you want to apply the class to
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = ['avatar','company']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['avatar'].widget.attrs.update({'class': 'form-control'})
self.fields['company'].widget.attrs.update({'class':'form-control'})
I found it easier to identify the element via css and add the styling there. With django forms you get a unique id for each form field (user form prefixes if you display the form multiple times in your template).
# views.py
def my_view_function(request):
form_a = MyForm(prefix="a")
form_b = MyForm(prefix="b")
context = {
"form_a": form_a,
"form_b": form_b
}
return render(request, "template/file.html", context)
style
// file.css
form input#by_id {
width: 100%;
}
This is a answer complemeting #Christian Abbott correct answer.
If you use a lot of forms, a option for not having to override init every single time may be to create your own form class:
class MyBaseForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for visible in self.visible_fields():
visible.field.widget.attrs['class'] = 'form-control'
Then you can inherit from this class and it is going to automatically make the styles for you.
class ExampleForm(MyBaseForm):
# Your declared form fields here
...
Same thing can be done with ModelForm by simply creating a MyBaseModelForm that inherits from ModelForm.
This is very practical:
class CreateSomethingForm(forms.ModelForm):
class Meta:
model = Something
exclude = []
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control'
In this way you don't have to go field by field.
One way is to create base form class and manually update the field's attribute inside __init__ method.
Another is by using already existing libraries like this one:
https://github.com/dyve/django-bootstrap3
There are plenty of these libraries around github. Look around.
Ok some time has passed but i had the same issues. I came to this solution:
class FormCssAttrsMixin():
cssAttrs = {}
def inject_css_attrs(self):
# iterate through fields
for field in self.fields:
widget = self.fields[field].widget
widgetClassName = widget.__class__.__name__
# found widget which should be manipulated?
if widgetClassName in self.cssAttrs.keys():
# inject attributes
attrs = self.cssAttrs[widgetClassName]
for attr in attrs:
if attr in widget.attrs: # attribute already existing
widget.attrs.update[attr] = widget[attr] + " " + attrs[attr] # append
else: # create attribute since its not existing yet
widget.attrs[attr] = attrs[attr]
class MyForm(FormCssAttrsMixin, forms.Form):
# add class attribute to all django textinputs widgets
cssAttrs = {"TextInput": {"class": "form-control"}}
name = forms.CharField()
email = forms.CharField()
address = forms.CharField()
country = forms.CharField()
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.inject_css_attrs()
With this Mixin class you can manipulate the attributes of form widgets in a generic way. Simply add a dictionary as class variable which contains the desired attributes and values per widget.
This way you can add your css classes at the same location where you define your fields. Only downside is, that you have to call the "inject_css_attrs" method somewhere but i think that is ok.
A generalized version of #christian-abbott response:
class ExampleForm(forms.Form):
_HTML_CLASSES = ('form-control', 'something-else')
# Your declared form fields here
...
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for visible in self.visible_fields():
missing_classes = list(self._HTML_CLASSES)
if 'class' in visible.field.widget.attrs:
current_classes = visible.field.widget.attrs['class'].split(' ')
for current_class in current_classes:
if current_class in missing_classes:
missing_classes.remove(current_class)
else:
current_classes = []
visible.field.widget.attrs['class'] = ' '.join(current_classes + missing_classes)
If you just need to change the class for bootstrap purposes, you can just add a script to the template.
<script>
const elementsInputs = document.querySelectorAll('input[id^="id_"]');
elementsInputs.forEach(element => {
element.classList.add("form-control");
});
const elementsLabels = document.querySelectorAll('label[for^="id_"]');
elementsLabels.forEach(element => {
element.classList.add("form-label");
});
</script>
then the form fields in the template should be something like:
<div class="fieldWrapper">
{{ form.subject.errors }}
{{ form.subject.label_tag }}
{{ form.subject }}
</div>
as described in Django.
You can add classes in your forms.py inside the Meta class:
class Form(forms.ModelForm):
class Meta:
model = ModelForm
fields = "__all__"
widgets = {
'name': forms.TextInput(attrs={'class':'form-control'})
}
I understood "no third-party libs", but this one django-widget-tweaks
really WORTH MENTIONING
is simple, DRY and powerfull.
give you full control over the widget rendering doesnt matter which css framework you are using ... still simple
you manage many html attributes you want on HTML not Django forms.
User template "filters" not template tags (as a "normal" form var)
You control the input and labels
django-widget-tweaks
-> https://github.com/jazzband/django-widget-tweaks
Sample ...
{{form.hours|attr:"class:form-control form-control-sm"}}
You can do it without any external libraries or code changes, right in the template. Like this:
{% for field in form %}
<div class="input_item">
<p class="title">{{ field.label }}:</p>
<div class="form-group">
<{{ field|cut:"<"|cut:">" }} class="form-control">
</div>
</div>
{% endfor %}
However, it is not the best solution. If you can create templatetag - go for it.
you can use row-cols-5
<div class="row row-cols-5">
<div class="col">1</div>
<div class="col">2</div>
<div class="col">3</div>
<div class="col">4</div>
<div class="col">5</div>
</div>
I know that author asked about Bootstrap for own Form, but there is an additional way to include Bootstrap class tag in Django form for authentication, password reset etc.
If we create template with standard form:
<form action="" method="post">
{% csrf_token %}
{{ form }}
</form>
then in browser source code we can see all the form fields with the tags:
<form action="" method="post">
<input type="hidden" name="csrfmiddlewaretoken" value="xxx">
<tr><th><label for="id_old_password">Old password:</label></th><td><input type="password" name="old_password" autofocus required id="id_old_password"></td></tr>
<tr><th><label for="id_new_password1">New password:</label></th><td><input type="password" name="new_password1" required id="id_new_password1"></td></tr>
<tr><th><label for="id_new_password2">New password confirmation:</label></th><td><input type="password" name="new_password2" required id="id_new_password2"></td></tr>
</form>
Variable {{ form }} in our template now can be replaced with this code and Bootstrap classes we needed:
<div class="fieldWrapper form-group" aria-required="true">
<label for="id_old_password">Old password:</label><span class="required">*</span>
<input type="password" **class="form-control"** name="old_password" autofocus required id="id_old_password">
</div>
Maybe it could be useful for redesign built-in static forms.
So I want to use this getattribute function (found on this link) https://snipt.net/Fotinakis/django-template-tag-for-dynamic-attribute-lookups/
in my django templates. I created a templatetags folder in my app folder where my models.py is. I also created and saved an empty inint.py file in the templatetags folder. I then created a file called getattribute.py in the template tags folder and copy-pasted the snippet found in the link above into the getattribute.py file and saved the file.
This is what my template looks like:
<html>
<body>
<form method="post" action="">{% csrf_token %}
{{ form.first_name }} {{form.last_name }} <br>
{{ form.username }} {{ form.password }} <br>
<input type="submit" value="Register"/>
</form>
{% load getattribute %}
{% for field, error in form.errors.items %}
{% if forloop.counter == 1 %}
{% with field_obj=form|getattribute:field %}
{{ field_obj.label }}{{ error | striptags }}
{% endwith %}
{% endif %}
{% endfor %}
</body>
</html>
This is how my models.py looks.
class Users(models.Model):
alpha_field = RegexValidator(regex=r'^[a-zA-Z]+$', message='Name can only contain letters')
user_id = models.AutoField(unique=True, primary_key=True)
username = models.SlugField(max_length=50, unique=True)
first_name = models.CharField(max_length=50, verbose_name='first Name', validators=[alpha_field])
last_name = models.CharField(max_length=50, validators=[alpha_field])
password = models.SlugField(max_length=50)
My forms.py is this.
class UsersForm(forms.ModelForm):
class Meta:
model = Users
widgets = {'password':forms.PasswordInput()}
def __init__(self, *args, **kwargs):
super( UsersForm, self ).__init__(*args, **kwargs)
self.fields[ 'username' ].widget.attrs[ 'placeholder' ]="Username"
self.fields[ 'first_name' ].widget.attrs[ 'placeholder' ]="First Name"
self.fields[ 'last_name' ].widget.attrs[ 'placeholder' ]="Last Name"
self.fields[ 'password' ].widget.attrs[ 'placeholder' ]="Password"
self.fields['first_name'].label='first Name'
and this is my views.py
def home_page(request):
form = UsersForm()
if request.method == "POST":
form = UsersForm(request.POST)
if form.is_valid():
form.save()
c = {}
c.update(csrf(request))
c.update({'form':form})
return render_to_response('home_page.html', c)
Now, when I run the server, the form is displayed with no errors. However, if I purposely not fill out the first name section and hit submit, it says "This field is required."
I want it to say the verbose name and the snippet is supposed to make it say the verbose name if, in my template, I use {{ field_obj.label }}, right? But it is not display the verbose name for some reason. I'm guessing it is because I'm not using the templatetags properly?
In your form, it may help to have a clean method:
def clean(self):
first_name = self.cleaned_data.get('first_name')
if password is None:
raise forms.ValidationError('This is a custom error message.')
return self.cleaned_data
Then, in your template, you can have code like:
{{ form.first_name }}
{% if form.first_name.errors %}
{{form.first_name.errors.as_text}}
{% endif %}
Otherwise, django's default form validation will treat all your inputs as required, and provide that generic message.
Another option, from the docs, is to define error messages at the form field level: https://docs.djangoproject.com/en/dev/ref/forms/fields/
name = forms.CharField(error_messages={'required': 'Please enter your name'})
This will add the custom error message based on the error type, so in your case, you could use something like:
first_name = models.CharField(max_length=50, verbose_name='first Name', validators=[alpha_field], error_messages={'required': 'Please enter a first name'})
So I have a model called Users and it has a field called first_name.
class Users(models.Model):
alpha_field = RegexValidator(regex=r'^[a-zA-Z]+$', message='Name can only contain letters')
user_id = models.AutoField(unique=True, primary_key=True)
username = models.SlugField(max_length=50, unique=True)
first_name = models.CharField(max_length=50, verbose_name='first Name', validators=[alpha_field])
last_name = models.CharField(max_length=50, validators=[alpha_field])
password = models.SlugField(max_length=50)
and then I created a UsersForm and then in my template page, when displaying any error messages, it doesn't use the verbose name, it uses first_name. For example, my template code for display errors is
{% for field, error in form.errors.items %}
{% if forloop.counter == 1 %}
{{ field }}{{ error | striptags }}
{% endif %}
{% endfor %}
If there is an error in the first_name field, like if I didn't fill it out and still clicked submit, it would display this
"first_nameThis field is required"
How do I make it display
"First NameThis field is required" instead?
Not that it might make a different but do note that I am using south and schemamigration to update the database, it originally did not have a verbose name but I recently added it and then just saved the file (I didn't do a schemamigration and then migrate the app because it said that no changes seem to have been made).
My UsersForm is this:
from django import forms
from models import Users
class UsersForm(forms.ModelForm):
class Meta:
model = Users
widgets = {'password':forms.PasswordInput()}
def __init__(self, *args, **kwargs):
super( UsersForm, self ).__init__(*args, **kwargs)
self.fields[ 'username' ].widget.attrs[ 'placeholder' ]="Username"
self.fields[ 'first_name' ].widget.attrs[ 'placeholder' ]="First Name"
self.fields[ 'last_name' ].widget.attrs[ 'placeholder' ]="Last Name"
self.fields[ 'password' ].widget.attrs[ 'placeholder' ]="Password"
self.fields['first_name'].label='first Name'
my view is here:
def home_page(request):
form = UsersForm()
if request.method == "POST":
form = UsersForm(request.POST)
if form.is_valid():
form.save()
c = {}
c.update(csrf(request))
c.update({'form':form})
return render_to_response('home_page.html', c)
form.errors is a dictionary of field NAMES as the keys and error messages as the values. It will not be the verbose_name. You need to get the field from the form, then do field.label for the verbose_name. If you use this snippet for getting an attribute on an object dynamically in a template: https://snipt.net/Fotinakis/django-template-tag-for-dynamic-attribute-lookups/, you can do something like this to get the verbose_name:
{% load getattribute %}
{% for field, error in form.errors.items %}
{% if forloop.counter == 1 %}
{% with field_obj=form|getattribute:field %}
{{ field_obj.label }}{{ error | striptags }}
{% endwith %}
{% endif %}
{% endfor %}
I had similar problem some time ago, this snippet helped me out:
from django import template
from django import forms
from django.forms.forms import NON_FIELD_ERRORS
from django.forms.util import ErrorDict
register = template.Library()
#register.filter
def nice_errors(form, non_field_msg='General form errors'):
nice_errors = ErrorDict()
if isinstance(form, forms.BaseForm):
for field, errors in form.errors.items():
if field == NON_FIELD_ERRORS:
key = non_field_msg
else:
key = form.fields[field].label
nice_errors[key] = errors
return nice_errors
http://djangosnippets.org/snippets/1764/