The suggested pattern for processing a form in a view seems overly complex and non-DRY to me:
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,
})
That's a lot of conditionals, it repeats the ContactForm() construction, and the whole block is repeated everywhere a view needs to process a form. Isn't there a better way of doing it?
You can avoid the repetition, of course. Mostly, you need to pass in as arguments the class of form and template name to use, a callable to process the cleaned data when a valid form is submitted, and a destination for the redirect after such processing; plus, you need a little extra code to call the form class just once, to produce either a bound or unbound form, and deal with it properly. I.e.:
def process_any_form(request,
form_class, template_file_name,
process_data_callable, redirect_destination):
form = form_class(request.POST if request.method == 'POST' else None)
if form.is_bound and form.is_valid():
process_data_callable(form.cleaned_data)
return HttpResponseRedirect(redirect_destination)
return render_to_response(template_file_name, {'form': form})
You are right it could be better, here is a better alternative (but keep reading):
def contact(request):
form = ContactForm(request.POST or None) # 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
return render_to_response('contact.html', {
'form': form,
})
This snippet comes from a talk called Advanced Django Form Usage from DjangoCon11.
Note that this will process an empty form as valid (even before submission) if all the fields are optional and you don't use CSRF protection. So to eliminate that risk, you better use this one:
def contact(request):
form = ContactForm(request.POST or None) # A form bound to the POST data
if request.method == 'POST' and form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
return render_to_response('contact.html', {
'form': form,
})
The boilerplate way of processing forms mixes two concerns: presenting a form to edit and processing the results. You could break this into two methods, which would introduce some duplication in the form of identical render_to_response() calls. By the time you refactored that, you might end up with something that's less readable than the single-method form above.
When I look at the boilerplate method, I don't see duplication. The two uses of ContactForm() are distinctly different. The two conditionals seem to me to fairly cleanly show the state transitions involved in processing a form (present a blank form, accept submissions until one is valid, process-and-redirect).
Alex's generic handler beat me to it, but FWIW we tend toward a less-generic version of his suggestion:
def contact(request):
post_data = request.POST if request.method == 'POST' else None
form = ContactForm(post_data)
if request.method == 'POST':
# perform normal validation checking, etc
return render_to_response('contact.html', {
'form': form,
})
If post_data is None, then the form is instantiated as being unbounded. Otherwise, bound processing continues as normal. It avoids a duplicated construction of ContactForm, but I agree with Dave's answer that the duplicate construction doesn't bother me as being a duplicate precisely because the construction parameters are different.
I got so tired of this that i wrote my own generic views to handle it. In the process, I discovered that django already has underdocumented generic views for forms processing. They are fairly direct analogues of the documented generic views, but accept forms, and basically follow the same template you used in your example. Ultimately, I found them too inflexible and stupid for my use (I don't want a create_or_update view, I wan't to treat those two actions seperately.)
Edit: You didn't like Fragsworth's answer, which points to the same thing i'm talking about, I assume you wont' like mine either. Here's an example for how it works.
# in urls.py
urlpatterns += patterns("",
(u'^...$', 'django.views.generic.create_update.update', {
'form_class': ContactForm })
)
ContactForm must have a save() method, and thats where your form processing logic goes.
One could write a function that handles the conditionals for all forms. You could do this by passing in a function specific to that form after "is_valid", such as:
def FormHandler(request, CleaningFunction, redirecturl):
if request.method = 'POST':
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
CleaningFunction(form) # Process the data in form.cleaned_data
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return form
Then you would call FormHandler from your view. Note this isn't tested and may have errors.
You can bypass django's forms module and just do it the old fashion way, you get more flexibility without too much loss IMHO.
Last time I looked at django forms was quite a while ago, I don't know if things have changed, but for instance, it doesn't really allow you build an ajax-style form; at least not easily.
Django provides several generic views for creating, editing, and deleting objects. Perhaps you could try these.
Related
Is it okay to have a view that both serves the form and processes the form's action?
For example:
if request.method == 'POST':
(process form)
else:
serve form
Would it be better practice to add an action parameter to the form and redirect to a view specifically for processing the form?
Yes, that's the way to go (you don't need a else statement though). Django and other frameworks do this - the assumed default is GET, then you add a if statement for the POST method.
Don't forget to return a redirect response in case you don't want the form to be reposted. Also, if the form processing is long - you can use a separate method / service. Something like:
def my_view(request):
form = SomeForm(data=request.POST or None)
if request.method == 'POST':
if form.is_valid():
do_something(form) # form processing that could save a couple of objects, send notifications, etc.
messages.success(request, "Some message.")
return HttpResponseRedirect(request.path)
return render(request, ".../template.html", locals())
This way, you keep the business logic separated and you can easily unit test it (in this example, you would unit test do_something(...)).
Yes, this is ok. This is what Django does normally. Note that the generic FormView has got get andpost methods.
(To understand how they're called, read the dispatch method.)
I have a form that I've been working on, currently displaying in a template called search_test.html, that I've finally gotten working right. I want to actually integrate it into my site, but on my site it's not going to be its own page, it's going to be a sidebar that's present on most pages. I know when I do that, this line
return render_to_response('corpus/search_test.html',
{'form': form}, context_instance=RequestContext(request))
is going to give me problems, because I don't want to actually redirect to search_test.html.
What can I replace this 'render_to_response' with, to tell it to just stay on the same page, but still pass the form info? I know this has got to be something simple, but I've looked all over online and in the docs and I can't find a similar example. (The view code for the form is below.)
Thank you.
def concord_test(request):
if request.method == 'POST': # If the form has been submitted...
form = ConcordanceForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
searchterm = form.cleaned_data['searchterm'].encode('utf-8')
search_type = form.cleaned_data['search_type']
category = form.cleaned_data['category']
context, texts_len, results_len = make_concordance(searchterm, search_type, cat=category)
return render_to_response('corpus/concord.html', locals()) # Redirect after POST
else:
form = ConcordanceForm() # An unbound form
return render_to_response('corpus/search_test.html',
{'form': form}, context_instance=RequestContext(request))
In a function page view (as per example)
you can do something like
def other_page(request):
if request.method == 'POST':
return concord_test(request)
else:
form = ConcordanceForm()
#Processing for other_page
object_list = OtherPageModel.objects.all()
return render_to_response('corpus/other_page.html',
{'form': form , 'object_list': object_list }, context_instance=RequestContext(request))
my advice: study class based views , you can have more granular capabilities dealing with 'repetitive' tasks like this & more.
This question may seem obvious, but I have been thrown for a loop the past few days. The vast majority of tutorials and documentation I find on django-forms shows them as their own solitary views.
Take this example from django 1.6 official docs as a typical example, 'Using a form in a view':
from django.shortcuts import render
from django.http import HttpResponseRedirect
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(request, 'contact.html', {
'form': form,
})
The problem I have is that I almost never dedicate a whole page or view just to one form. I can't really {% include %} the form view due to context variables. I also can't {% extend %} it without bringing in way to many items or reworking all of my templates and views.
Is it a good practice to combine form logic along with other references and variables in the same view? I would just like to confirm that yes this is normal and acceptable or that no I am doing things unacceptably. Off-hand there looks like some non DRY boilerplate if I do this in each view that needs a form.
There also appears to be some debate in the blogs & tutorials for form logic. Its hard to tell whether modifying a CBV or using some good ol' FBV is preferred. I just don't want to get any bad habits while I am still new and learning.
This is my currently "working" code for my view:
def home_page(request):
all_sliders = Slider.objects.all()
all_marketing = Marketing.objects.all()
all_features = Feature.objects.all()
skill_set = Skills.objects.all()
#clunky way of adding the form...yes? no?
errors = []
if request.method == 'POST':
form = ContactForm(request.POST)
if not request.POST.get('subject', ''):
errors.append('Enter a subject.')
if not request.POST.get('message', ''):
errors.append('Enter a message.')
if request.POST.get('email') and '#' not in request.POST['email']:
errors.append('Enter a valid e-mail address.')
if not errors:
send_mail(
request.POST['subject'],
request.POST['message'],
request.POST.get('email', 'noreply#mysite.com'),
# email address where message is sent.
['email#mysite.com'],
)
return HttpResponseRedirect('frontpage/thanks/')
else:
form = ContactForm()
context = {'sliders': all_sliders, 'marketing': all_marketing,
'feature': all_features, 'skills': skill_set,
#this is tied to my form logic
'form': form, 'errors': errors,
}
return render(request, 'frontpage/home.html', context)
In closing, I don't want to use an add-on library. I am learning django and want to learn it well before using libraries for basic things like forms.
There is nothing wrong with providing extra context/whatnot to a view with a form. In fact, it's pretty easy to do with CBVs. The docs for the Generic FormView provide a good example of a contact form, and you can extend it to add your extra context by overriding the get_context_data. What you should not do is form validation in the view. Leave that to your form class in forms.py. Here's an example pieced together from the docs:
# forms.py
class ContactForm(forms.Form):
subject = forms.CharField()
message = forms.CharField(widget=forms.Textarea)
email = forms.CharField()
# Whatever fields you had.
def clean(self):
"""This is where you should be checking for required
fields and making sure the submitted data is safe."""
pass
def send_email(self):
# send email using the self.cleaned_data dictionary
send_mail(
self.cleaned_data['subject'],
self.cleaned_data['message'],
self.cleaned_data.get('email', 'noreply#mysite.com'),
['email#mysite.com']
)
# views.py
class HomePageFormView(FormView):
template_name = 'frontpage/home.html'
form_class = ContactForm
success_url = 'frontpage/thanks/'
def get_context_data(self, **kwargs):
context = super(HomePageFormView, self).get_context_data(**kwargs)
context['sliders'] = Slider.objects.all()
context['marketing'] = Marketing.objects.all()
context['feature'] = Feature.objects.all()
context['skills'] = Skills.objects.all()
return context
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
form.send_email()
return super(HomePageFormView, self).form_valid(form)
Is it a good practice to combine form logic along with other
references and variables in the same view? I would just like to
confirm that yes this is normal and acceptable or that no I am doing
things unacceptably.
It is perfectly acceptable to add other things to the context in your views (otherwise, how would you render things?).
The problem comes when you are talking about form logic, and here is where there are some issues with your code/approach.
There is a convention in django (which you are free to violate), is that forms go in a forms.py file that is part of your application.
You can perfectly declare all your form classes in your views.py, there is nothing wrong with this approach but once you start collaborating with others, or start combining public django apps into your code, it is best to use a convention. After all, a software development framework is nothing but a bunch of conventions and some helpers all bundled together nicely.
However, a more serious problem with your logic is that you are not using form validation - and this you must absolutely stop right now.
Forms are - at their core - a way to validate dictionaries, and they are one of the most powerful features of the django framework. They allow you to validate any dictionary and are used anywhere you are working with models or data.
The code you have written is almost exactly what the form validation does - it checks if required fields are missing in the form (or, think of another way - required keys are None in the dictionary) and then adds error messages and marks the form (or dictionary) as invalid.
The basic logic of using forms is like this:
def someview(request):
form = SomeForm() # This creates an unbound (empty) form
if request.method == 'POST':
form = SomeForm(request.POST, request.FILES) # Bind the form to
# POST data
if form.is_valid():
# perform validation
# do something with form's data
data = form.cleaned_data['somefield']
# Or, if its a model form (a form that is tied to a
# model), save the model since the form is validated
obj = form.save()
# All post requests should redirect
return redirect('index')
else:
# The form was not valid, return the form
# to the view, except this time it will
# contain helpful error messages
return render(request, 'form.html', {'form': form})
else:
# Return an empty form to the view
# for the user to fill in, as this is a GET not POST
# request
return render(request, 'form.html', {'form': form})
You can always customize the validation rules for a form either on a field-by-field basis, or on the overall data in the form. This is discussed in the documentation on form and field validation.
Off-hand there looks like some non DRY boilerplate if I do this in
each view that needs a form.
The new CBV have solved this problem by taking care of the repeated logic (one of the benefits of inheritance and classes). The code I pasted above can be minimized to the following when using FormView:
from django.core.urlresolvers import reverse_lazy
from django.views.generic.edit import FormView
class SomeView(FormView):
template_name = 'form.html'
form_class = SomeForm
success_url = reverse_lazy('index')
def form_valid(self, form):
data = form.cleaned_data['somefield']
return super(SomeView, self).form_valid(form)
There also appears to be some debate in the blogs & tutorials for form
logic. Its hard to tell whether modifying a CBV or using some good ol'
FBV is preferred. I just don't want to get any bad habits while I am
still new and learning.
There is nothing wrong with using FBV - they are still perfectly valid django. The benefit you get with CBV is that common functionality is only written once. My advice is to use CBV when you have common logic that you want to modify on a per-view basis. Forms is a good example, displaying models, pagination, rendering simple templates, downloading data (for example, you can have one base view that converts objects to Excel, and then inherit from here in any view that needs to provide a download feature) are good candidates for CBV.
I am using this thing in my views quite a lot but I want to know what exactly does that mean.
What happens when we write request.method == "GET" or request.method == "POST"?
The result of request.method == "POST" is a boolean value - True if the current request from a user was performed using the HTTP "POST" method, of False otherwise (usually that means HTTP "GET", but there are also other methods).
You can read more about difference between GET and POST in answers to the question Alasadir pointed you to. In a nutshell POST requests are usually used for form submissions - they are required if processing a form would change server-side state (for example add user to a database, in case of a registration form). GET is used for normal HTTP requests (for example when you just type an URL into your browser) and for forms that can be processed without any side-effects (for example a search form).
The code is usually used in conditional statements, to distinguish between code for processing a submitted form, and code for displaying an unbound form:
if request.method == "POST":
# HTTP Method POST. That means the form was submitted by a user
# and we can find her filled out answers using the request.POST QueryDict
else:
# Normal GET Request (most likely).
# We should probably display the form, so it can be filled
# out by the user and submitted.
And here is another example, taken straight from Django documentation, using Django Forms library:
from django.shortcuts import render
from django.http import HttpResponseRedirect
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(request, 'contact.html', {
'form': form,
})
request.methodreturns the type of the request method it may be GET,POST,PUT,DELETE etc.
after returning you are comparing it with your string.
comparison operator always provides a boolean value(True or False).
Some times we need to handle the functionality based on the requested method type.
if request.method == "GET":
# functionality 1
elif request.method == "POST":
# functionality 2
elif request.method == "PUT":
# functionality 3
elif request.method == "DELETE":
# functionality 4
for request method GET data is passed along with url.
for request method POST data is passed inside body. In terms of security method type POST is better one.
book_id = Book.objects.get(id=id)
if request.method == 'POST':
book_save == BookForm(request.POST, request.FILES, instance=book_id)
if book_save.is_valid():
book_save.save()
else:
book_save = BookForm(instance=book_id)
y = {
'form':book_save,
}
return render(request, 'pages/update.html', y)
The standard pattern for the view logic for Django forms is thus:
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,
})
This is fine in simple cases but quite easily descends into a complex mass of nested IF statements if your application logic gets a bit more complex.
Can anyone share their own cleaner approaches that avoid nested IF's and logic that depends on fall-through cases?
I'd be particularly interested in answers that don't rely on additional 3rd party apps.
One of the included class based views is FormView (documentation). The two main methods you'd be concerned with are form_valid and form_invalid.
from django.views.generic import FormView
from myapp.forms import MyForm
class MyView(FormView):
template_name = 'edit_something.html'
form_class = MyForm
success_url = '/success/' # you should use `reverse`, but let's stay focused.
def form_valid(self, form):
"""
This is what's called when the form is valid.
"""
return super(MyView, self).form_valid(form)
def form_invalid(self, form):
"""
This is what's called when the form is invalid.
"""
return self.render_to_response(self.get_context_data(form=form))
Alternatively you can override post, get or put methods and handle the form according to each type of request.
This is shortest approach I found:
def contact(request):
# if it's POST request it'll have data else it'll be unbound
form = ContactForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
return render_to_response('contact.html', { 'form': form, })
of course if you don't like nested if you could try reversing logic:
def contact(request):
template = 'contact.html'
if request.method != 'POST': # GET return:
return render_to_response(template, {'form': ContactForm()})
form = ContactForm(request.POST)
if not form.is_valid(): # FAIL return:
return render_to_response(template, {'form': form})
# here logic if user posted form and it's clean:
return HttpResponseRedirect('/thanks/')
For complex scenarios, you should really consider overriding various methods of your form class which are involved in the validation/sanitizing process. The BaseForm class defines a _post_clean() method with no behavior which is particularly meant to be overridden. Overriding _clean() properly is also pretty straight forward, you could do that too if you wanted to do something before clean() is called.
Reading the source of django.forms will give you a much clearer insight on how the forms work and discover the right pattern for your needs.
Here is what i tried so far to make view funcs cleaner:
Separate view functions for GET / POST
Obviously is_valid logic in form as state in prev post.
Overriding model save() method to make if form.is_valid() part cleaner ( you can hide other related objects creates inside one save() )
Move common parts of view-funcs code into separeate function.
Also if your GET part is not CPU or database intense you can do it above if req == POST..
in other words do it always..
I for example often do: ctx['form'] = ContactForm() at the top.. and then again
ContactForm(req.POST) inside if req == POST
BUT
django 1.3 Now have class based view.. And this give one a big opportunity
to make view extendable.. to make mixins.. not to write the save over and over.
I personally want to give it a shot and will try write django stuff in a new style with classes.
The main source of complexity in these cases is handling GETs and POSTs in the same view function. Separate the logic for each method into its own view and things become clearer.
The only duplication will be rendering the form html, but that particular part is concise and meaningful for both cases (i.e. not boilerplate), so that's acceptable.