I have read a lot of tuts and documentation on form creation and handling in Django but I still am confused on certain aspects of the implementation. Specifically, I cannot understand where I should handle the data sent by the form. Is it on the view that is using the form template or is it on another view?
For example, assume an index template with a single form:
*index.html*
{% load url from future %}
<form action="{% url 'Directories:_results'%}" method="post">
Name: <input type="text" name="txtField" />
<input type="submit" name="submit" />
</form>
So now for my view i have two versions:
#1 version (1 view): The same view displays and handles the form
def index(request):
if request.method == 'POST': # If the form has been submitted...
form = dbForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
field = form.cleaned_data['txtField']
#doSomething
else:
form = dbForm() #unbound form
return render(request, 'Directories/index.html', {'form': form})
#2 version (2 views): One view to display the form and one view to handle the form data
#the view that creates the form (unbound)
def index(request):
form = dbForm()
return render(request, 'Directories/index.html', {'form':form})
#the view that handles the data sent during form submission in the index template.
def results(request):
if request.method == 'POST':
form = dbForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
field = form.cleaned_data['txtField']
#doSomething
else:
form = dbForm() #unbound form
return render(request, 'Directories/index.html', {'form': form})
and here is my urls.py:
from django.conf.urls import patterns, url
from Directories import views
urlpatterns = patterns('',
url(r'^$', views.index, name='_index'),
url(r'^results$', views.results, name='_results'),)
As you can see data handling is performed differently in each version and as a result I want to know if any of these is wrong and if both are correct then which one is considered the best practice.
Generally a form will post to the same view it is being displayed on.
You can simplify the view logic like so:
def index(request):
form = dbForm(data=request.POST or None)
if form.is_valid(): # All validation rules pass
field = form.cleaned_data['txtField']
#doSomething
return redirect(success_url)
return render(request, 'Directories/index.html', {'form': form})
Note that it is usually good if you redirect after a successful form post, even if you redirect back to the same view. This prevents the user from being prompted to 'resend form data' if they refresh the page.
You should look at the docs for rendering a form in the template:
https://docs.djangoproject.com/en/dev/topics/forms/#looping-over-the-form-s-fields
If you don't render the field errors, for example, the user will never know what the problem was.
An example of a form that would post to a different view is if say your base template has a 'search' form which appears on every page. When you post this form you don't want to come back to the current view, you want to go to the 'search results' view.
Generally, one view corresponds to one url. Also, same url should show the form and accept the submitted form. With this logic, your first approach is better. One view shows and accepts the form.
However, there are cases where view to show form is different than the one accepts it. For example, a page that has multiple forms. Each form can be submitted to different view. But a different view can be implemented to handle that url and show such forms.
There's nothing wrong with either, it depends on what you want to do. By default forms send the data to the same request but you can send the data to a different view if that's more convenient
For most cases it's usually simpler to use the same view. Using two views is good if you're using an external tool\app\whatever or if you want tighten your security (having the second view only accept requests with post data for example etc.), but will require extra steps (error handling, succesful redirect)
The first thing to understand is that the view that processes the form is usually also the one that shows the form in the first place -- because it has to show the form again in case of errors.
In your template, you build the form HTML entirely by hand. That's unusual, because in case of errors (usually a required field that wasn't filled in) you want to render the form again, with all the values already entered present, and with a nice error message. Django's form rendering ( {{ form.as_p }} and the like) do that for you, you don't get it if you write the HTML by hand like this. In fact your view misses an else: clause on the is_valid(), leading it to
So usually the view does both, except for the second thing to understand: after a successful POST, you always redirect to a succes page, or possibly to the same page (which will show an empty form again). Mostly so the user can't accidentally re-submit the form with the refresh button then.
So your 1st is typical, except that you also need to finish with returning a ResponseRedirect in the is_valid() case, and should render more of the form in your template. No need for a second view.
Related
I am using django-allauth for local account management. I have customized templates ,login.html and signup.html. Both of these templates are placed in templates/account/ dir and both are accessible properly.
site root i.e localhost:8000 points to index.html which includes using {% include%} both the templates on main page.
form action for signup form in signup.hmtl is set to action="{% url 'account_signup' %}" and that of login.html is set to "{% url 'account_login' %}"
Both the templates appears OK on the main page. The problem arises when I try to use these forms for sigin/login. Instead processing the POST for signup or login I am redirected to locahost:8000/accounts/signup/ for signup and localhost:8000/accounts/login/ for login. I guess I am using the right urls that is account_signup and account_login
I have all settings for allauth. Is this is the default behaviour or I'm missing some thing out? Thanking in anticipation
Well I managed to get rid of the problem after spending some hours. Just in case that some one else caughtup in the same situation I would like to share my solution. The problem was forms have been instantiated by View by using prefix SignUpForm(prefix=signupform) and for that reason allauth class AjaxCapableProcessFormViewMixin(object) (which I don't know how it works) was unable to get data from the fields and its if form.is_valid() was always false as the form has error messages dictionary was containing this field is required for all the fields in the form. Jus to test I removed Prefixing from the form instantiation and it worked but make me feel strange as these kind of hidden errors can take upto indefinite time to resolve especially for some niwbie like me
class AjaxCapableProcessFormViewMixin(object):
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
response = self.form_valid(form)
else:
response = self.form_invalid(form)
return _ajax_response(self.request, response, form=form)
I am trying to show the user a summary of a form before the form is submitted and saved to the server.
I have a form on index.html and within that form, I have this input button:
<input type="submit" value="Show summary" name="summary"/>
On the summary.html page, I have another input button within a dummy form. I use this input button to get the form data saved.
<input type="submit" value="Save to DB" name="save_db" />
Here are code snippets of my the methods in my views:
def index(request):
form = JobForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
return summary(request)
return render_to_response('index.html', {'form': form}, context_instance=RequestContext(request))
def summary(request):
if request.method == 'POST':
save_to_db(request) # The problem here is that this is a new request. I lost the previous request with all the data
return render_to_response('thanks.html', context_instance=RequestContext(request))
xml = getxml(request) # form data is serialized
return render_to_response('summary.html', {'xml': xml}, context_instance=RequestContext(request))
From the above, I am able to get a summary of the form data. However, as the inline comment suggests, when I click the "Save to DB" submit button, the previous request with all the form data is lost.
I have looked into Sessions and I could not find a proper of doing the above. I also tried to pass the request as a parameter to the the summary page. However, I think this is not the right way of doing it.
I would appreciate your input!
Thanks.
Have you tried Django Form Preview? Seems that is exactly you are looking for:
Django comes with an optional “form preview” application that helps
automate the following workflow: “Display an HTML form, force a
preview, then do something with the submission.” To force a preview of
a form submission, all you have to do is write a short Python class.
Cheers!
On my Django-powered site, I have a search page with several optional fields. The search page is a Django form, and my view function is the typical:
def search(request):
form = SearchForm(request.GET or None)
if form.is_valid():
return form.display_results(request)
return render(request, 'search.html', {'form': form})
Form.display_results() uses the fields that are provided to query the DB and render a response. My search.html includes:
<form action="/search/" method="get">{% csrf_token %}
<!-- Render the form fields -->
<input type="submit" value="Search" />
<input type="reset" value="Reset form" />
</form>
Since most searches will have several blank fields, I'd like not to include them in the GET request emitted by the submit button on search.html. Current searches look something like:
http://mysite/search/?csrfmiddlewaretoken=blah&optional_field1=&optional_field2=&optional_field3=oohIWantThisOne
And I'd like them to look like:
http://mysite/search/?csrfmiddlewaretoken=blah&optional_field3=oohIWantThisOne
Of course, I have a several more fields. This would be nice to have because it would make search URLs more easily human-parsable and sharable.
You could use jQuery with an button trigger. Give the form and submit button ids.
$("#button_id").click(function(){
$("input").each(function(){
if($(this).val() == '') {
$(this).remove();
}
});
$("#form_id").submit();
});
That (or something similar) should remove all the empty fields before the submit.
You could also POST the form. Then build the search url and redirect with empty values removed.
See Hide empty fields from GET form by Bill Erickson:
jQuery(document).ready(function($){
// Remove empty fields from GET forms
// Author: Bill Erickson
// URL: http://www.billerickson.net/code/hide-empty-fields-get-form/
// Change 'form' to class or ID of your specific form
$("form").submit(function() {
$(this).find(":input").filter(function(){ return !this.value; }).attr("disabled", "disabled");
return true; // ensure form still submits
});
// Un-disable form fields when page loads, in case they click back after submission
$("form").find(":input").prop("disabled", false);
}
disclaimer: This is a very old question, and the only one I could find that matches the problem I ran into. It's entirely possible that my solution didn't exist at the time, and an even better way has since been added.
I'm using Django 3.2. I didn't want to use js/jQuery, nor did I want to use a POST form, so here's what I came up with. In a nutshell, it just checks the GET data to see if there are any default values present, and simply redirects to a URL that doesn't have them.
In your view:
from django.shortcuts import redirect
def myView(request):
if not is_clean_form(request.GET):
return redirect(whatever_url_path + clean_url_parameters(request.GET))
else:
# whatever your view does normally
Helper functions (consider making these static methods of your Form class, to keep all that type of stuff together):
from urllib.parse import urlencode
# fields and their default value (eg. empty string)
default_form_values = [
("some_field_name", ""),
# ...
]
def is_clean_form(form_dict):
for field, default_value in default_form_values:
if field in form_dict and form_dict[field] == default_value:
return False
return True
def clean_url_parameters(form_dict):
return "/?" + urlencode([
(field, form_dict[field]) for (field, default_value) in default_form_values
if field in form_dict and form_dict[field] != default_value
])
I have a page, index.html, that contains both a login and registration form. I have a couple of questions about getting this to work properly
My URLConfig looks like this:
urlpatterns = patterns('djangoproject1.authentication.views',
(r'^$',direct_to_template,{'template':'authentication/index.html'}),
(r'^register/$','register'),
)
1) Using the Django book is a guide, my form looks like this:
<h1>Register</h1>
<form action="/register/" method="post">
{{ form.as_p }}
<input type="submit" value="Register">
</form>
Of course, since the file is index.html, the form doesn't appear when I just go to the page. Do I need a "view" to handle visiting index.html rather than a direct_to_template?
2) My Register code looks like this:
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
new_user = form.save()
return HttpResponseRedirect("/register/success/")
else:
form = UserCreationForm()
return render_to_response("authentication/index.html", {'form': form})
This is the django authentication built-in stuff. Do people actually use it? It seems limited. I know I can add more fields to the Django User by using a user profile or something, but what about the UserCreationForm? Should I roll my own form? Should it inherit from UserCreationForm somehow?
direct_to_template by itself can neither produce nor handle forms -- it simply renders a request directly to a template, as its name describes.
You might look into django-registration for registration.
If you're putting two forms on the same page, you'll need a custom view that is capable of rendering and handling both forms, though multi-form pages are notoriously tricky to work with properly. If you have separate forms (and submit buttons), you can add a unique name to each submit input and determine which form (class) to validate and handle based on if name in request.POST.
edit:
After looking more closely at your code, I see that your registration form redirects to a different view; that simplifies things, but you'll still need a custom view for your home page that passes both login and registration forms to the template for rendering.
Alternatively, if you're simply redirecting to pages that handle each form, you can add those forms using direct_to_template's extra_context parameter directly in your urls.py:
from wherever import LoginForm, RegistrationForm
urlpatterns = patterns('djangoproject1.authentication.views',
(r'^$',
direct_to_template,
{
'template': 'authentication/index.html',
'extra_context': {
'reg_form': RegistrationForm(),
'login_form': LoginForm()
}
}
),
(r'^register/$', 'register'),
)
This approach isn't the cleanest, but it's an option if you really wanted to use generic views.
It sounds like you'll probably want to use a different generic view instead of direct_to_tepmlate. Take a look at the create object generic view. I usually just create a view, typically I end up needing to do more than what a generic view will allow me to do easily.
I have a page which displays a form that a logged-in user can use to edit their profile.
The first time the user loads the corresponding view, with a GET request, the form should display the values of the users existing profile record.
Because I'd like to have granular control over the markup used for the form fields, I'd like to retrieve the field values only, and manually write the necessary input element markup inserting the retrieved field values as the input elements' 'value' attribute (rather than have django render the entire input elements).
So far I've been trying to use directives like {{ form.myfield.data }} in my template to access the field data, but this is returning "None", instead of the profile details as i expected.
If the user has just posted a valid form though, the form fields do contain the expected profile details.
I hope someone can help me understand what i'm doing wrong here:
Here's my view:
#login_required
def edit(request):
# get logged-in user's profile
obj=Profile.objects.get(user=request.user)
if request.method == 'POST': # If the form has been submitted...
form = ProfileForm(request.POST, request.FILES, instance=obj)
if form.is_valid(): # All validation rules pass
form.save()
request.flash['success'] = "Your profile has been updated." # Puts a string to the flash scope
else:
request.flash['error'] = 'Please correct the problems below and try again' # Puts a string to the flash scope
else:
# if no form was submitted
# derive the form data from the logged-in users existing profile
form = ProfileForm(instance=obj)
return render_to_response('app_profile/edit.html',
{
'form': form,
'user': request.user,
'ancestor_urls': ['app_profile.views.edit',],
},context_instance=RequestContext(request)
)
And here's an excerpt from the corresponding template. This input element deals with a field called 'display_name'.
<input type="text"
class="textInput large {% if form.display_name.errors %}error{% endif %}"
maxlength="50"
size="35"
value="{{ form.display_name.data }}"
id="id_display_name"
name="display_name">
The way to do what you want to do is to write custom form widgets, instead of doing that in the template.
You can either create an entirely custom widget, or you can just add/change the attributes on the form level when you init the ModelForm
I guess you can solve some of your problems without making a custom widget using the widgets attrs argument: http://docs.djangoproject.com/en/dev/ref/forms/widgets/#django.forms.Widget.attrs, also you have some styling options using the input's id!