django modelform with bootstrap - django

I want to format my modelforms with bootstrap, and without any additional packages (just using the bootstrap source files). A particular form that I want configured:
class FoodForm(forms.ModelForm):
class Meta:
model = Food
fields = ['name', 'company']
exclude = ('user', 'edit')
The 'name' is a text field I'd want to be a bootstrap text field, and 'company' is a selection field (from a foreign key) that I'd want to be a bootstrap dropdown.
The current setup of the form template:
{% extends "mainsite/base.html" %}
{% block content %}
<form method="POST" class="post-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Save</button>
</form>
</form>
{% endblock %}
What's best practice for formatting any django modelform field into bootstrap?

The trick to bootstrap fields is injecting the form-control class into each field, and making sure each field lives inside a form-group dom element. To inject that class into each one of your fields, you could so something like:
class FoodForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(FoodForm, self).__init__(*args, **kwargs)
for field in iter(self.fields):
self.fields[field].widget.attrs.update({
'class': 'form-control'
})
Now, in your django template you can iterate through your forms fields, placing each one into a bootstrap'd form-group. For example:
<form method="POST" class="post-form">
{% for field in form %}
<div class="form-group">
{{ field }}
</div>
{% endfor %}
</form>
I'll also say that this setup (Django + Bootstrap) is super common now adays, so googling "Bootstrap forms with django" should yield a wealth of knowledge on customizing this even further. Good luck!

Simply use Django Crispy Forms
It is fairly easy and straight forward

Related

Django - Adding custom class to label_tag

I am trying to figure out a way to add a class to my tag on my form. I want to accomplish this without something like crispyforms and if possible not having to edit the html. Based on my knowledge and googling I have spent a few hours trying to figure this out but I cannot haha. I basically just want to output a class in the label element like so:
<label class='text-muted' for="id_street_address">Street Address:</label>
My model is as follows:
class CheckoutForm(forms.Form):
street_address = forms.CharField(widget=forms.TextInput(
attrs={
'class': 'form-control'
}
))
apartment = forms.CharField(required=False, widget=forms.TextInput(
attrs={
'class': 'form-control'
}
))
country = CountryField(blank_label='(select country)')
zip_code = forms.CharField(widget=forms.TextInput(
attrs={
'class': 'form-control'
}
))
same_billing_address = forms.BooleanField(widget=forms.CheckboxInput())
save_info = forms.BooleanField(widget=forms.CheckboxInput())
payment_option = forms.BooleanField(widget=forms.RadioSelect())
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['street_address'].label = 'Street Address'
self.fields['apartment'].label = 'Address 2 (optional)'
self.fields['zip_code'].label = 'Zip Code'
I render my form in the template like this:
<form>
{% csrf_token %}
{% for field in form %}
<div class="form-group px-3 my-3">
{{ field.label_tag }}
{{ field }}
{{ field.help_text }}
{{ field.errors }}
</div>
{% endfor %}
</form>
Is it possible to do this? Any help is greatly appreciated
The class that actually renders the form field is the class BoundField. This bound field has a method label_tag [Django docs] which you are using to render the label, this method does accept some optional parameters one of which is attrs using which we should be able to add the class attribute you want. The problem here though is:
We cannot pass parameters to the method in the template.
Overriding it would involve overriding the form fields too, which means overriding multiple form fields.
One solution would be to implement a custom template tag / filter to call the method for us, which is what I will describe in this answer. First create a directory templatetags in some suitable app of yours, add a __init__.py file to it, and a file in which we will write our template tag (let us say form_helpers.py). Hence your app directory will now look something like:
your_app/
__init__.py
models.py
templatetags/
__init__.py
form_helpers.py
views.py
Next in form_helpers.py add the following code:
from django import template
register = template.Library()
#register.simple_tag
def render_label(bound_field, css_class):
return bound_field.label_tag(attrs={'class': css_class})
Next in your template you can then write:
{% load form_helpers %}
<form>
{% csrf_token %}
{% for field in form %}
<div class="form-group px-3 my-3">
{% render_label field css_class="text-muted" %}
{{ field }}
{{ field.help_text }}
{{ field.errors }}
</div>
{% endfor %}
</form>
Note: There is a great package for rendering forms manually called django-widget-tweaks, if you use it you could have simply used its add_required_class filter.

Check for errors and other values at the widget level - maybe using custom form field

How can I access if a field has)errors at the level of widget?
Using default I tried:
{% if widget.attributes.has_errors %} or {% if widget.has_errors %}
but are not working.
I use custom widget templates, I'm thinking to use a custom form Field and overwrite the default field.
I know clean method exist but I don't know how to push to the widget the dynamic(non default) data/attributes I want.
I tried:
class AWidget(forms.Widget):
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
has_errors = context['widget']['attrs'].pop('has_errors', None)
context['widget']['has_errors'] = has_errors
It works for errors but I don't know if is the best option plus I want to pass other values/attributes from Form Field,and I think will be better to try to overwrite the Form Field but I don't know exactly how.
Also accessing individual attributes using:
{{ widget.attrs.maxlength }} or {{ widget.attrs.items.maxlength }}
even if accedes in a for loop works
I know I can add a parent div with a class of error:
<div class="{% if form.field.errors %}pass_error{% endif %}">
{{ form.field }}
</div>
but, that implies big changes at the css level.
I already overwrite all Django widgets with custom widgets, on error I don't need just to change a border color, but to show or not different elements of the widget template and the position of some of them change.
I already modify the based widget to add errors, but I'm looking to do it in a more elegant way at the field level by passing from the field to the widget, parameters depending on error type.
So my question is what I need to overwrite to pass from field to widget errors and other variables ?
Not sure whether this could help in your specific use case ... but just in case, please note that when you build your form in the view, you can add extra parameters as needed, then pass them down to your custom widget.
Working example:
file "forms.py"
from django import forms
def build_ingredient_form(unit):
"""
Ingredient form factory
Here we build the form class dynamically, in order to acces 'unit' via closure.
References:
http://stackoverflow.com/questions/622982/django-passing-custom-form-parameters-to-formset#623030
"""
class IngredientForm(forms.Form):
#quantity = forms.DecimalField(max_digits=10)
quantity = UnitField(unit, required=False)
...
return IngredientForm
file "fields.py"
from django import forms
from .fields import UnitField
class UnitField(forms.CharField):
"""
Custom field to support UnitWidget
References:
- http://tothinkornottothink.com/post/10815277049/django-forms-i-custom-fields-and-widgets-in
"""
def __init__(self, unit, *args, **kwargs):
self.unit = unit
super(UnitField, self).__init__(*args, **kwargs)
self.widget = UnitWidget(unit)
...
file "widgets.py"
from django import forms
from .models import Unit
class UnitWidget(forms.TextInput):
def __init__(self, unit, attrs=None):
if unit is None:
self.unit = Unit()
else:
self.unit = unit
...
Well a widget is how you will render the field's data/value into the HTML rendered template, that's the only function of widgets, look the following example taken from the docs:
>>> name = forms.TextInput(attrs={'required': True})
>>> name.render('name', 'A name')
'<input name="name" type="text" value="A name" required>'
>>>
>>> name = forms.TextInput(attrs={'required': False})
>>> name.render('name', 'A name')
'<input name="name" type="text" value="A name">'
So, widgets are not aware of the data is valid(has errors) or not and should remain that way.
Is not a good idea to handle any data error/validation at the widget level, you want, I can ensure that, that if you change how your field looks like (the widget), your validations keeps working.
Said that ...
How can I access field errors?
When you are rendering a form you can do it field by field lets take this form by example:
class LoginForm(forms.Form):
username = forms.CharField(max_length=255)
password = forms.CharField(widget=forms.PasswordInput)
you can write to temlate:
<form action="." method="get">
<p>{{ loginform.username.label }}: {{ loginform.username }}</p>
<p>{{ loginform.password.label }}: {{ loginform.password}}</p>
<button type="submit">submit</button>
</form>
And this will render something like the following:
Now, suppose your form won't admit passwords with less than 8 characters:
class LoginForm(forms.Form):
username = forms.CharField(max_length=255)
password = forms.CharField(widget=forms.PasswordInput)
def clean_password(self):
password = self.cleaned_data['password']
if len(password) < 8:
raise forms.ValidationError(
"Password must have at least 8 characters, it has only %(password_length)s",
code='invalid password',
params={'password_length': len(password)}
)
return password
You can access the password errors like this:
<form action="." method="get">
{% csrf_token %}
<p>{{ form.username.label }}: {{ form.username }}</p>
<p>{{ form.password.label }}: {{ form.password}}</p>
<ul>
{% for error in form.password.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
<button type="submit">submit</button>
</form>
And now if you type a short password ...
I want the control to look different if there are errors.
You can add some style if there are errors just use {% if ... %} in your template code:
<p>
{{ form.password.label }}:
<span class="{% if form.password.errors %}pass_error{% endif %}">
{{ form.password }}
</span>
</p>
With CSS:
<style>
.pass_error input {
border-color: red;
}
</style>
And this is the result:
Conlusion.
Validate and handle data errors in the form or using validators, use widgets for display the data, of course, you can customize how the data is presented since you can specify a custom template for your widget.
I also recommend django-widget-twaeks if you want to add attributes to your widget in template code. This apps allows you to write code like (example from the app docs):
{% load widget_tweaks %}
<!-- change input type (e.g. to HTML5) -->
{% render_field form.search_query type="search" %}
<!-- add/change several attributes -->
{% render_field form.text rows="20" cols="20" title="Hello, world!" %}
<!-- append to an attribute -->
{% render_field form.title class+="css_class_1 css_class_2" %}
<!-- template variables can be used as attribute values -->
{% render_field form.text placeholder=form.text.label %}

Initial Values in Django model form

I am having a difficult time understanding the Django docs on this one. I have also come across some other threads with the same question, but I cannot seem to get the suggested answers to work for me. I think it is because I am posting text, and it is not considered "clean" data?
I want to autofill two form fields, then when the user hits the submit button, it saves. But for some reason only the boolean value is working, not the text value. Any ideas?
You will also see the fields are hidden from my template. When I show these fields, the initial values are set correctly as I expected, but when I hit submit, only the boolean is saved to database correctly.
EDIT
It works fine when I don't hide the form fields using {{ form }} in my template.
It does not work when I hide the fields using {{ form.field.as_hidden }}
Boolean field is accepted, but the text field is not.
I am trying to autofill this field with a text value, hide it, and submit this value when the submit button is pressed...
views.py
class BuildStopView(LoginRequiredMixin,UpdateView):
model = Build
form_class = StopBuild
template_name = 'build_stop.html'
login_url = 'login'
forms.py
class StopBuild(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(StopBuild, self).__init__(*args, **kwargs)
self.initial['buildEndType'] = 'manuallyStopped'
self.initial['buildActive'] = False
class Meta:
model = Build
fields = ['buildEndType','buildActive']
(template) stop_build.html
{% extends 'base.html' %}
{% block body %}
<style>
div.a {
text-align: center;
}
</style>
<div class = "a">
<h3>Are you sure you want to stop this build manually?</h3>
</div>
<form action="" method="post">{% csrf_token %}
{{ form.field.as_hidden }}
<button class="btn btn-danger ml-2" type="submit">Stop Build Manually</button>
</form>
{% endblock %}
form.field.as_hidden does not output the fields as hidden, in fact it does not do anything at all because you don't have a field called field in your form. You need to refer to the actual fields:
{{ form.buildEndType.as_hidden }}
{{ form.buildActive.as_hidden }}
However, if you want these to always be shown as hidden, you should probably do it in your form definition, by declaring them with HiddenInput widgets.

How to change form layouts in Django 1.8

I have a form
Field Name: [Input Box]
I want
Field Name:
[Input Box]
How can I achieve this?
forms.py
class SearchForm(forms.Form):
search = forms.CharField()
views.py
form = SearchForm()
html_dtc = {'form':form}
return render_to_response('site/home.html', html_dtc)
home.html
<form method='POST' action=''> {% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-success btn-sm">Update</button>
</form>
Thank you :)
You want a custom form rendering. You can read more about it here. For example, the following code would achieve what you're after.
<form method='POST' action=''> {% csrf_token %}
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} <br/>
{{ field }}
</div>
{% endfor %}
<button type="submit" class="btn btn-success btn-sm">Update</button>
</form>
(field.errors are added, because when you are manually rendering the form, you have to take care of error rendering as well)
Try to overide form.as_p()
class SearchForm(forms.Form):
search = forms.CharField()
def as_p(self):
"Returns this form rendered as HTML <p>s."
return self._html_output(
normal_row='<p%(html_class_attr)s>%(label)s <br> %(field)s%(help_text)s</p>',
error_row='%s',
row_ender='</p>',
help_text_html=' <span class="helptext">%s</span>',
errors_on_separate_row=True)
If this is a one off thing you can render your form manually like described here in the documentation.
Otherwise there's the django-floppyforms which gives you great control over how forms and (default) widgets are rendered.
Just define a custom layout, make it the default, use floppyforms custom Form classes (they behave exactly the same) and you're good to go.
As far as I remember some of floppyforms's functionality will also be included in Django 1.9, so look out for that, too.
Use django_crispy_forms: http://django-crispy-forms.readthedocs.org/en/latest/
In the template include {% load crispy_forms_tags %} and for the form:
{% crispy form %}
In addition, you can change the layout of the form easily, by overriding the form_class in the init function of the form:
class ContactForm(forms.Form):
def __init__(self, *args, **kwargs):
super(ContactForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.form_class = 'form-horizontal' # <-- leave out for vertical, or form-inline
self.helper.render_hidden_fields = True
self.helper.layout = Layout(
Div(InlineRadios('applying_for_whom'), css_class="col-xs-12"),
Div(InlineRadios('gender'), css_class='col-xs-12'),
Div('agreed_to_data_transmit', css_class="col-xs-12"),
As a bonus, if you are using bootstrap, set also set CRISPY_TEMPLATE_PACK = 'bootstrap3' so then everything is taken care of automatically for vertical bootstrap.
you can do
<form method='POST' action=''>
{% csrf_token %}
<label>Field Name:</label>
{{ form.search }}
<button type="submit" class="btn btn-success btn-sm">Update</button>
</form>
generally I don't recommend use the HTML code generated by Django, but instead I supply what is needed by the DJANGO form.
but some are required: like the ERRORs, like the CSRF token.
let me add some examples to clarify what I am talking
<form class="*" style="*">
<label /><input name="email" />
<label /><input name="password" />
<form>
basically what I am suggesting is, do not use template tags unless absolute necessary like CSRF.
in this way, you can completely separate the design from the backend logic. you can have front end work indecently on the UI. the interface is the form fields, you have to supply all fields to the backend. like in this case 'email' && 'password' is required at backend

Get the type of field in django template

I am using django forms and I want to use Twitter Bootstrap's css in my html.
so my template looks like this:
{% for field in form %}
<div class="form-group">
{{ field.label_tag }}<!--Same thing as : <label for="{{field.id_for_label}}"></label> -->
<input type="{{field.type}}" class="form-control" id="{{field.auto_id}}" placeholder="Email">
</div>
{% endfor %}
I can't figure out out to get the type value. {{field.type}} .
Is there any way to get the type of the input field in django templates?
Thanks in advance
Update:
The reason I am asking this question is because I want to be able to apply bootstrap classes to the input element. But in Bootstrap3, to use the default css for input types you would have to add form-control class to the input element like so: <input type="text" class="form-control" id="{{field.auto_id}}" placeholder="">.
If I use django's field {{field}} then I can't add the form-control class.
I hope this clarifies some things.
I also saw this app https://github.com/dyve/django-bootstrap3 that looks like it does what I wanted to do. It surprises me that django doesn't allow accessing the form type to allow more flexibility.
According to https://stackoverflow.com/a/31538555/254553
You can use:
{{ [FIELD].field.widget.input_type }}
[FIELD] is your field name.
I don't think you need to worry about the field_type. Django will itself handle that for you depending on the form field.
Lets say we have a ContactForm like:
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField(widget=forms.Textarea)
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
Then {{form.subject}} will automatically create <input> element in the template.
<input id="id_subject" type="text" name="subject" maxlength="100" />
Similarly, {{form.message}} in the template will create:
<input type="text" name="message" id="id_message" />
Though if you really need to get the form field type in the template, you can create a custom template filter like below.
from django import template
register = template.Library()
#register.filter(name='field_type')
def field_type(field):
return field.field.widget.__class__.__name__
Now, in your template, you need to do something like:
{{form.field_name|field_type}}
In the above example, {{form.message|field_type}} will return TextInput.
If you want to access the field type then refer to this answer.
If you want to override the default type of the field, use attrs parameter when defining the widget for the field.
Eg.
field_name = forms.CharField(widget=forms.Textarea(attrs={'type': 'custom type'}))
Also note that you can pass any key value pair to attrs and they will be used as attributes in the html tag when the form is rendered.
I also had this problem. I used Django Widget Tweaks to add classes (https://github.com/kmike/django-widget-tweaks).
Then you can do something like this:
{% for field in form %}
<div class="form-group {% if field.errors %}has-error {% endif %}">
{% render_field field class="form-control" %}
{% if field.errors %}
<span class="help-block">{{ field.errors.0 }}</span>
{% endif %}
</div>
{% endfor %}
I think another way of dealing with this is to use django crispy forms but I have not tried that yet.
You are able to override the __init__ method of the form in order to pass attributes to the HTML without having to know the type of field. Works with both standard forms and modelforms
class CoolForm(forms.Form):
field_name = forms.CharField(...)
def __init__(self, *args, **kwargs):
super(CoolForm, self).__init__(*args, **kwargs)
self.fields['field_name'].widget.attrs = {
'class': 'form-control'
}
You can pass any HTML attribute through to the template this way, for example 'placeholder': 'email#exam.pl'