Forms¶
A basic form¶
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
Processing a form in a view function¶
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
})
https://docs.djangoproject.com/en/stable/topics/forms/modelforms/
A model form¶
from django.forms import ModelForm, ValidationError
# Create the form class.
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ('name', 'title')
# or
exclude = ('birth_date')
def clean_fieldname(self):
if 'fieldname' in self.cleaned_data:
data = self.cleaned_data['fieldname']
if not /valid/:
raise ValidationError("msg")
return data
def clean(self):
data = self.cleaned_data
if not /valid/:
raise ValidationError("msg")
return data
# Creating a form to add an article.
form = ArticleForm()
...
new_article = form.save()
# Creating a form to change an existing article.
article = Article.objects.get(pk=1)
form = ArticleForm(instance=article)
...
form.save()
# Create a form to edit an existing Article, but use POST data to populate the form.
a = Article.objects.get(pk=1)
f = ArticleForm(request.POST, instance=a)
f.save()
Render form in template¶
<!-- Using table - avoid that part - but this does show how to render the fields individually -->
<form {% if form.is_multipart %}enctype="multipart/form-data"{% endif %} action="" method="post" class="uniForm">{% csrf_token %}
<table>
<fieldset>
{% if form.non_field_errors %}
<tr><td colspan="2">{{ form.non_field_errors }}</td></tr>
{% endif %}
{% for field in form %}
<tr{% if field.field.required %} class="required"{% endif %}>
<th style="text-align: left"><label for="{{ field.id_for_label }}">{{ field.label }}:</label></th>
<td>{% if field.errors %}
{{ field.errors }}<br/>
{% endif %}
{{ field }}
or even
<input id="{{ field.id_for_label }}"
name="{{ field.html_name }}"
value="{{ field.value }}"
{% if field.field.max_length != None %}
maxlength="{{ field.field.max_length }}"
{% endif %}
{% if field.field.min_length != None %}
minlength="{{ field.field.min_length }}"
{% endif %}
>
{% if field.help_text %}
<br/><span class="helptext">{{ field.help_text }}</span>
{% endif %}
</td>
</tr>
{% endfor %}
</fieldset>
</table>
<div class="ctrlHolder buttonHolder">
<button type="submit" class="primaryAction" name="submit_changes">Submit changes</button>
</div>
</form>
<!-- Using a list, which is preferred -->
<form {% if form.is_multipart %}enctype="multipart/form-data"{% endif %} action="" method="post" class="uniForm">{% csrf_token %}
<fieldset>
<ul>
{{ form.as_ul }}
<li>
<div class="ctrlHolder buttonHolder">
<button type="submit" class="primaryAction" name="submit_changes">Submit changes</button>
</div>
</li>
</ul>
</fieldset>
</form>
Read-only form¶
Call this on the form:
def make_form_readonly(form):
"""
Set some attributes on a form's fields that, IN COMBINATION WITH TEMPLATE CHANGES,
allow us to display it as read-only.
"""
# Note that a new BoundField is constructed on the fly when you access
# form[name], so any data we want to persist long enough for the template
# to access needs to be on the "real" field. We just use the BoundField
# to get at the field value.
for name in form.fields:
field = form.fields[name]
bound_field = form[name]
if hasattr(field.widget, 'choices'):
try:
display_value = dict(field.widget.choices)[bound_field.value()]
except KeyError:
display_value = ''
else:
display_value = bound_field.value()
field.readonly = True
field.display_value = display_value
Do things like this in the templates:
{# Date field #}
{% if field.field.readonly %}
<span class="form-control">{{ field.value|date:'c' }}</span>
{% else %}
<input type="date" class="form-control" id="{{ field.id_for_label }}" name="{{ field.html_name }}" value="{{ field.value|date:'c' }}">
{% endif %}
{# input fields #}
{% if field.field.readonly %}
<span class="form-control">{{ field.value }}</span>
{% else %}
<input type="{% block input_field_type %}text{% endblock %}" class="form-control" id="{{ field.id_for_label }}" name="{{ field.html_name }}" value="{{ field.value }}" {% if field.field.widget.attrs.placeholder %}placeholder="{{ field.field.widget.attrs.placeholder }}"{% endif %} {% block input_attrs %}{% endblock %}>
{% endif %}
{# select fields #}
{% if field.field.readonly %}
<span class="form-control">{{ field.field.display_value }}</span>
{% else %}
<select class="form-control" id="{{ field.id_for_label }}" name="{{ field.html_name }}" placeholder="">
{% for val, label in field.field.widget.choices %}
<option value="{{ val }}"{% if field.value|stringformat:'s' == val|stringformat:'s' %} selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
{% endif %}
Upload files¶
Template:
<form action="{% url 'links:import_bookmarks' %}" enctype="multipart/form-data" method="post">
{% csrf_token %}
{{ form }}
<button class="btn btn-primary" type="submit">Import file</button>
</form>
Form class:
class ImportForm(forms.Form):
file = forms.FileField()
View:
@login_required
def import_view(request):
if request.method == "POST":
form = ImportForm(request.POST, request.FILES)
if form.is_valid():
handle_uploaded_import(request, request.FILES["file"])
return redirect("links:bookmarks")
messages.error(request, "Form not valid")
else:
form = ImportForm()
return render(request, "import.html", {"form": form})
def handle_uploaded_import(request, file):
messages.info(request, "importing pages & sections")
with transaction.atomic():
data = json.load(file)
# ...