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.
Related
I have a template expression {% get_comment_list for data.post as comment_list %}, it uses get_comment_list from Django-comments. It gets a list of comment objects for a specified object (in this case the Post object through data.post)
However, I'm currently working on client side Ajax API to retrieve the comments and its related user data. So I cannot use the template html way to get/render the data, but in a view, Python script.
I found that the tag get_comment_list is defined as follows:
# django_comments/templatetags/comments.py
#register.tag
def get_comment_list(parser, token):
return CommentListNode.handle_token(parser, token)
I know how to import custom template tag to a view so Python can use it, like:
#register.simple_tag
def addFoo(value=""):
return value + "Foo."
The above type of tag is simply passing a string value, but the get_comment_list(parser, token) tag requires two parser thing and object thing object, it's not a plain string type.
Now, I have no idea how I can construct that parser and token or pass a model to get_comment_list to get the comment objects. Is it possible to use this type of template tag in a view? Thanks.
im using the form.add_error option of django to add customized errors to
form checks.
when i use the add_error in the ModelForm the errors are shown in the
template, but when i use it in the view it doesn't..
what might be the problem?
the modelform add_error (in the clean method):
self.add_error('field', "fdgdsfgfds.")
the view add_error:
def form_valid(self, form):
form.add_error('field', "sfsfdsfsd.")
return self.form_invalid(form)
in the template:
{{ form.errors }}
{{ field.errors }}
{{ form.non_field_errors }}
{{ form.as_table }}
Django model forms have a certain way of working and this in essence is a good thing because it makes sure your code stays clean and that each one of your methods have a standardized flow control.
The forms clean and clean_fieldname methods should be where all of your validation is done, these methods get called from the forms is_valid method and all contribute with their errors towards statings whether the form is valid or not. See my other answer for a more detailed explanation as to how is_valid actually calls the clean methods
The form_valid and form_invalid are for you to handle in your view to handle what response should be shown. The django generic views by default will render success_url if the form is valid although you may find you want to override these methods to return a JsonResponse or similar to make your form more ajaxxy.
So any time you want to report errors, you need to use your clean methods. You may find in the future that you want to reuse a form and you don't want to repeat yourself in views by having to do validation again, so keeping all the validation contained in the form makes this possible. The views job is to handle where you want to display your form and what other context it would need.
Say you already have a complex HTML form, possible from a designer, front end dev, etc. Is it common practice to not use dynamic forms (based on a Django form) for complicated forms?
I want to do something like this:
1.) Create custom HTML form.
2.) Catch form data through POST request, put it in an object/dictionary.
3.) Do some manipulations with that data to get it in a format acceptable by a Django form.
4.) Pass the manipulated data in to a form object, validate it, etc...
What is a clear solution to this problem? Should I be using Django's dynamic forms for everything? If not - how do I implement the above?
EDIT:
Part of my question has to do with using the forms ONLY for validation. I don't think I made this clear. Here is what I'm trying to do:
template.html
<form method="post">
{% csrf_token %}
<input class="foo" name="bar" type="text" value=""/>
<!-- Some more fields, not rendered through Django form -->
<button type="submit">Create Object</button>
</form>
As you can see, other than the csrf_token there is no Django code here. What I am trying to do in my view is catch the data in the POST in my view, make some changes to the data, then try to bind the new data to a form (not sure if it's possible):
views.py
def my_view(request):
# Some GET code
if request.method == 'POST':
form = ImportedForm(request.POST)
form.data['foo'] = "newValue"
# Now after changing the data, validate it...
If the form and model match nicely then I'll take advantage of the ModelForm functionality.
But most of the time it is not so tidy so, most typically, I do things in about this order:
create a django form with all the field definitions
create django GET view to serve the empty form
create an html template which serves the default html/form
test the blank form
create the POST routine to call validation and reserve the validated (erroneous) form
modify the django form to validate the fields
modify the html form to serve the error messages
test the validation and error messages
modify the POST routine to handle a valid form and do whatever it should do as a result (might involve a redirect and 'thanks' view/template)
Test the whole lot
let the designer loose on the templates
In truth the designer will be involved at some points earlier along the way but in theory I just get it all to work as a "white" then add all the fancy stuff after. That includes javascript validation (ie after all the above).
I ended up doing something like this. It is ugly, and may not be the proper way to do it, but it works...
if request.method == 'POST':
try:
# Create dictionary from POST data
data = {
'foo': request.POST['foo'],
'foobar': request.POST['foobar'],
}
except:
# Handle exceptions
form = ImportedForm(data)
if form.is_valid:
# Continue to validate and save
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, {....})
{% 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.