Forms are one of the best features of Django. (After models, admin, url routing etc :) ). Here is a quick tutorial describing how to do things with Django forms.
- Basic form
Prob. You want to show a form, validate it and display it.
Ans. Create a simple form.
class UserForm(forms.Form): username = forms.CharField() joined_on = forms.DateField()
This wil take care that the form is displayed with two text field, and a value for them are filled in, and the second field has correct formatting for a date.
Prob. Create a form which has values populated depending upon a runtime value, eg you might want to show only the values corresponding to the current subdomain.
Ans. Create a form with an custom
class UserForm(forms.Form): username = forms.CharField() plan = forms.ModelChoiceField(queryset = Plan.objects.none()) def __init__(self, subdomain, *args, **kwargs): self.default_username = default_username super(UserForm, self).__init__(*args, **kwargs) self.fields['plan'].queryset = Plan.objects.filter(subdomain = subdomain)
Here in the
__init__ we are overriding the default queryset on field
plan. Any of the attributes can similarly be overridden.
self.fields is populated only after
super(UserForm, self).__init__(*args, **kwargs) is called.
Prob. The same form is used in multiple views, and handles the
Ans. Create a form with a custom .save()
class UserForm(forms.Form): username = forms.CharField() def save(self): data = self.cleaned_data user = User.objects.create(username = data['username']) #create a profile UserProfile.objects.create(user = user, ...some more data...)
You could have called this method anything, but this is generally called save, to maintain similarity with
Prob. You need to create a form with fields which have custom validations.
Ans. Create a form with custom clean_fieldname
class UserForm(forms.Form): username = forms.CharField() def clean_username(self): data = self.cleaned_data try: User.objects.get(username = data['username']) except User.DoesNotExist: return data['username'] raise forms.ValidationError('This username is already taken.')
Here we can validate that the usernames are not repeated.
Prob. You want to create a field which has cross field validations.
Ans. Create a field with a .clean
class UserForm(forms.Form): username = forms.CharField() password1 = forms.PasswordField() password2 = forms.PasswordField() def clean(self): data = self.cleaned_data if "password1" in data and "password2" in data and data["password1"] != data["password2"]: raise forms.ValudationError("Passwords must be same")
You need a form the fields of which depends on some value in the database. Eg. The field to be shown are customisable per user.
Create a form dynamically
def get_user_form_for_user(user): class UserForm(forms.Form): username = forms.CharField() fields = user.get_profile().all_field() #Use field to find what to show.
Prob. You need to create a Html form which writes to multiple Django models.
Ans. Django forms are not a one to one mapping to Html forms.
#in forms.py class UserForm(forms.ModelForm): class Meta: model = User fields = ["username", "email"] class UserProfileForm(forms.ModelForm): class Meta: model = UserProfile #in views.py def add_user(request): ... if request.method == "POST": uform = UserForm(data = request.POST) pform = UserProfileForm(data = request.POST) if uform.is_valid() and pform.is_valid(): user = uform.save() profile = pform.save(commit = False) profile.user = user profile.save() .... ... #in template <form method="post"> <input type="submit" ...> </form>
Prob. You want to use multiple forms of same type on one page.
a. If you want a datagrid style ui, use formset.
from django.forms.formsets import formset_factory forms = formset_factory(UserForm, extra = 4) # [Formsets are described much more comprehensively here](http://docs.djangoproject.com/en/dev/topics/forms/formsets/)
b. If you do not need a datagrid style ui, use
prefix argument to forms.
Eg. you have a survey app, and you want a page with all questions from that survey displayed. #IN views.py def survey(request, survey_slug) ... questions = survey.questions.all() question_forms =  for question in questions: qform = QuestionForm(question=question, prefix = question.slug) question_forms.append(qform) ... if request.method == "POST": for question in questions: qform = QuestionForm(question=question, prefix = question.slug, data = request.POST) #Validate and do save action ... ...