Django auto detecting passed forms to the template - django

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?

Related

How to render django form into html (not in template)

I have a template in which I have
<div id="form-wrapper">
{{form}}
</div>
Now if the user submits (via AJAX) and there are errors, I want to do
$("#form-wrapper").html(data.form);
Basically just replace the whole form to show the errors. But when I write
return JsonResponse({'form': form})
in the view django tells me that my form isn't serializable. Makes sense. So my question is: how does django render forms into html? What function should I call?
Note: I know I can return just the errors instead of the whole form, but that would require more js code in the template to put each field's errors in the correct place. I want the one function that django uses when I say {{form}}.
Thank you!
All the template does when presented with any object is call str() (or unicode()) on it. Internally, the methods that implement those on the form class simply delegate to the as_table() method. So you can just call that directly:
return JsonResponse({'form': form.as_table()})
Note, though, that you might want to add other HTML, in which case a better approach might be to simply render a short template snippet, including the form, to a string - using render_to_string - and send that in the Json response.
You've gotta unload the form since it's an object
if not form.is_valid():
errors = form.errors # You'll get a dict of errors
return JsonResponse(data=errors)
From here use javascript to create new HTML.

Calling a templatetag from a view -- CSRF not gtting populated

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, {....})

Django render templates from seperate app's in one template file

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))

Django: How to include modelform?

{% 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.

Dynamic variables in Django base.html

I have an app that uses flatpages and other constructs that don't take a request object. This causes problems in base.html. Here's a simple example.
If I wanted something like "Welcome {{ request.user.username }}!" at the top of every page, what's the best way to make that happen?
Flatpages use RequestContext in rendering templates. Here's a bit more about RequestContext. Suffice to say, you should be able to write a Context Processor to add request.user to the context of every template. Something like this:
def user(request):
"""A context processor that adds the user to template context"""
return {
'user': request.user
}
Which you would then add to your existing TEMPLATE_CONTEXT_PROCESSORS in settings.py:
TEMPLATE_CONTEXT_PROCESSORS = TEMPLATE_CONTEXT_PROCESSORS + (
'context_processors.user',
)
You just need to make sure all your views bind RequestContext to their templates as well:
return render_to_response('my_template.html',
my_data_dictionary,
context_instance=RequestContext(request))
Here's a good read on Context Processors. They're a very helpful feature.
Context processors.