I need to display content of two templates (from different views) on 1 webpage (these are login and registration form side by side, sooner or later i will include other apps).
So I created index.html for displaying both templates - login.html and register.html - by usage of {% include %} templatetag. However, when it came to validation of registration form after i typed incorrect values i got redirected to template that I've imported.
How I can solve my issue of having forms from two completely seperate views in other (third) template?
How I can solve my issue of having forms from two completely seperate
views in other (third) template?
You should specify the action attribute of your form.
You can use a context variable (for example 'type') to distinguish between the two forms in same template.
In view1:
if login_form.is_valid():
# do something
return render_to_response("home.html", context_instance=RequestContext(request))
else:
return render_to_response("index.html", {"login_form": login_form, "type": "login"}, context_instance=RequestContext(request))
In view2:
if register_form.is_valid:
# do something
return render_to_response("home.html", context_instance=RequestContext(request))
else:
return render_to_response("index.html", {"register_form": register_form, "type": "register"}, context_instance=RequestContext(request))
Related
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.
I am using inclusion_tags to generate portions of my pages that repeat in many different places across my site.
templatetags/tags.py
#register.inclusion_tag('chunk_template.html')
def output_chunk(object, edit):
... # Lots of set up work here that I don't want to duplicate in the view
return { ... }
Upon AJAX submit of a form on the page, I need to refresh the very same HTML outputted by output_chunk(). To avoid completely rewriting output_chunk() in a view, I did the following as recommended in this answer about how to use templatetags in views:
views.py
def chunk(request, ...):
context = Context({..., 'request': request })
template_string = """
{% load output_chunk from tags %}
{% output_chunk ... %}
"""
t = Template(template_string)
return HttpResponse(t.render(context))
This is all working fine, except chunk_template.html calls {% csrf %}, which works when I call the template tag the standard way, but not when I call in this somewhat hacky way (to avoid writing the same code twice).
(For simpler template tags, this works fine when I call return render (request, template_name, context) from within the view.)
So, is there a better way to call the template tag from within the view to get all the middleware invoked properly? Or is there something I can add to this hack to make it work properly?
I don't understand the core of the problem, but you can always manually pull the token (the middleware calls this function).
from django.middleware.csrf import get_token
csrf = get_token(request)
Need to make the context a RequestContext.
context = RequestContext(request, {....})
In the past, I named all my forms as form and at the base.html template I used {{ form.errors }} to catch all the errors,
Now I have variously named different forms at many different views yet I want to keep this form error catching generic, how can I detect all passed forms and iterate through them ?
Thanks
Nothing is available to your template unless it's in the context. Since you manage the context for each view, I'm not sure what the problem is here. It doesn't matter what your form is named in the view, you can simply pass it to the context as "form":
render_to_response('template.html', {
'form': some_crazy_form_name,
}, context_instance=RequestContext(request))
If you have one view with multiple forms, you could simply provide a context variable called 'forms' and make it a list:
render_to_response('template.html', {
'forms': [first_form, second_form]
}, context_instance=RequestContext(request))
Or even better as a dictionary, so you could know which form is which:
render_to_response('template.html', {
'forms': {
'alpha': first_form,
'beta': second_form,
},
}, context_instance=RequestContext(request))
The idea, is that you can pretty much do whatever you like. Just set the context up how you want and then use it in your template. Where's the problem?
{% include 'django.contrib.auth.views.login' %}
I don't want to write everything by hand.. I hate this really, django full of automatic stuff.
Goal is to include registration/login.html into base.html, so that I could have this form in every page
If I include only template itself (registration/login.html), problem appears that "form.login", I mean "form" var is not defined because this one comes from VIEW which called when you going to login url. So how can I call that view MANUALLY with include or at least to grab django.contrib.auth.views.login variables by my self in my own view and pass then to base.html?
P.s. It's not just about login form, I think there will be more situations like this
I have found better solution in #django irc.
They called inclusion tags
I'll give you my code, because I got lot's of problem learning new stuff in django =)
file: templatetags/form_login.py
from django import template
register = template.Library()
from django.contrib.auth.forms import AuthenticationForm
#register.inclusion_tag('registration/login.html')
def form_login():
return { 'form': AuthenticationForm() }
Now you can have your form anywhere, this will prerender template and THAT'S IT! no stupid context processors which requires to modify whole project settings.py, which is really sux if you writing stand alone little application..
If you need login-form on every page
Create a context processor:
def login_form_processor(request):
return {
'login_form': LoginForm(request.POST or None)
}
Add it to settings.CONTEXT_PROCESSORS.
Include the template for login form:
{% with login_form as form %}
{% include "registration/login.html" %}
{% endwith %}
You can also make you form lazy-loading, so form will not be created until it is used for the first time.
from django.utils improt functional
def login_form_processor(request):
create_login_form = lambda: LoginForm(request.POST or None)
return {
'login_form': functional.lazy(create_login_form, LoginForm)
}
But I guess you won't want the lazy-loading feature, because login-form is cheap to initialize.
Reusing views
Concerning the "grabbing variables" part from your question: you cannot grab variable from view. Django view is method which returns response object. You can not get variables from response. However some of views accept extra_context and other attributes. Those attributes allow you to configure those views in urls, or to wrap them with your own view, for example:
def my_login_view(request):
some_extra_data = get_some_data()
extra_context = {
'some_extra_var': some_extra_data
}
return login_view(request, extra_context=extra_context, template="my_template.html")
This is not exactly grabbing the variables from views, more like augmentation of existing views.
If you expect to have more situations like this, do less data-porcessing in views. Call some methods which checks for permissions. Collect some data from context-processors. Return rendered response. Now you can reuse the data in other views.
You can specify the action on the form html to point to the URL that accesses the corresponding view.
If you want a form, say called as login_form always populated in all templates, then put it in the context_processors.
Browsing the code for django.contrib.auth.views, you will see that the variables form, site and *site_name* are passed to the template.
Either you (1) provide your custom registration form or (2) you can just import django.contrib.auth.forms.AuthenticationForm in your view if you want to use it.
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.