Django's Form Submission
Classic Form Submission in an AngularJS context
This example shows how to use a classic Django Form inside an AngularJS application.
How does it work?
By inheriting from the mixin class Bootstrap3FormMixin
, Django renders the form in
a way, compatible with Twitter Bootstrap. Here the correct CSS classes are added to the
<input>
elements, which are embedded inside
<div class="form-group">
containers. If you omit Bootstrap3FormMixin
,
then the Form is rendered, as Django would by default.
When this form is rejected by the server, the list of errors is rendered using AngularJS's built
in Form Controller using the
directive ng-show="formname.fieldname.$pristine"
. This in contrast to Django's
internal behavior has the advantage, that the field's error message disappears as soon as the
user starts typing.
Passwords can, for obvious reasons only be validated by the server. Here for demonstration purpose, this is performed by the password field itself, but don't do this in a productive environment!
from django.forms import widgets
from django.core.exceptions import ValidationError
from djng.forms import fields
from djng.styling.bootstrap3.forms import Bootstrap3Form
def validate_password(value):
# Just for demo. Do not validate passwords like this!
if value != 'secret':
raise ValidationError('The password is wrong.')
class SubscribeForm(Bootstrap3Form):
use_required_attribute = False
CONTINENT_CHOICES = [('am', 'America'), ('eu', 'Europe'), ('as', 'Asia'), ('af', 'Africa'),
('au', 'Australia'), ('oc', 'Oceania'), ('an', 'Antartica')]
TRAVELLING_BY = [('foot', 'Foot'), ('bike', 'Bike'), ('mc', 'Motorcycle'), ('car', 'Car'),
('public', 'Public Transportation'), ('train', 'Train'), ('air', 'Airplane')]
NOTIFY_BY = [('email', 'EMail'), ('phone', 'Phone'), ('sms', 'SMS'), ('postal', 'Postcard')]
first_name = fields.CharField(
label='First name',
min_length=3,
max_length=20)
last_name = fields.RegexField(
r'^[A-Z][a-z -]?',
label='Last name',
error_messages={'invalid': 'Last names shall start in upper case'})
sex = fields.ChoiceField(
choices=(('m', 'Male'), ('f', 'Female')),
widget=widgets.RadioSelect,
error_messages={'invalid_choice': 'Please select your sex'})
email = fields.EmailField(
label='E-Mail',
required=True,
help_text='Please enter a valid email address')
subscribe = fields.BooleanField(
label='Subscribe Newsletter',
initial=False, required=False)
phone = fields.RegexField(
r'^\+?[0-9 .-]{4,25}$',
label='Phone number',
error_messages={'invalid': 'Phone number have 4-25 digits and may start with +'})
birth_date = fields.DateField(
label='Date of birth',
widget=widgets.DateInput(attrs={'validate-date': '^(\d{4})-(\d{1,2})-(\d{1,2})$'}),
help_text='Allowed date format: yyyy-mm-dd')
continent = fields.ChoiceField(
label='Living on continent',
choices=CONTINENT_CHOICES,
error_messages={'invalid_choice': 'Please select your continent'})
weight = fields.IntegerField(
label='Weight in kg',
min_value=42,
max_value=95,
error_messages={'min_value': 'You are too lightweight'})
height = fields.FloatField(
label='Height in meters',
min_value=1.48,
max_value=1.95,
step=0.05,
error_messages={'max_value': 'You are too tall'})
traveling = fields.MultipleChoiceField(
label='Traveling by',
choices=TRAVELLING_BY,
help_text='Choose one or more carriers',
required=True)
notifyme = fields.MultipleChoiceField(
label='Notify by',
choices=NOTIFY_BY,
widget=widgets.CheckboxSelectMultiple,
help_text='Must choose at least one type of notification')
annotation = fields.CharField(
label='Annotation',
required=True,
widget=widgets.Textarea(attrs={'cols': '80', 'rows': '3'}))
agree = fields.BooleanField(
label='Agree with our terms and conditions',
initial=False,
required=True)
password = fields.CharField(
label='Password',
widget=widgets.PasswordInput,
validators=[validate_password],
help_text='The password is "secret"')
confirmation_key = fields.CharField(
max_length=40,
required=True,
widget=widgets.HiddenInput(),
initial='hidden value')
from django.views.generic.edit import FormView
from django.core.urlresolvers import reverse_lazy
class SubscribeView(FormView):
template_name = 'subscribe-form.html'
form_class = SubscribeForm
success_url = reverse_lazy('form_data_valid')
<script type="text/javascript">
angular.module('djangular-demo', ['djng.forms']);
</script>
<form method="post" action="." validate>
{% csrf_token %}
{{ form.as_div }}
<button type="submit" class="btn btn-primary">Subscribe</button>
</form>
Use this setting, if you want your forms behave the way intended by Django.
Here the only exception is, that errors from a previous and failed form validation disappear, as
soon as the user changes that field.
In this setting, AngularJS adds a dummy
ng-model
attribute to each input field.