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.
Related
New Django user here.
I am trying to restrict access to Django's success_url() upon GET requests. I realize I am not the first to ask this question, however, I am specifically trying to achieve this in conjunction with Django's generic class-based views. Generic views seem cleaner, faster, and more idiomatic. I want to use them as much as possible unless they are absolutely unfit for the job.
Basically, I am creating a simple contact form for non-users who only want to send me a message without creating an account. I've created a contact app to handle these types of contacts. I've created a ModelForm, which I am rendering with a contact.html with Django's FormView. After a person submits the form, they will receive a cool looking thank you message, rendered with a thanks.html, which has its own url.
But I only want them to see the thank you message if they POST the contact form. Currently, you can go on my site and type '/contact/thanks/', and my thanks.html will be rendered whether you've submitted a form or not. Django's success_url apparently defaults to a GET request.
Here's my view:
class MyContact(FormView):
template_name = 'contact.html'
form_class = ContactForm
success_url = 'thanks'
Here's my form:
ContactForm(forms.ModelForm):
class Meta:
model = Contact
fields = ['email_address', 'message_body']
Here's the html form in contact.html:
<form action="" method="POST">{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="send btn btn-secondary">Send Message</button>
</form>
My first thought was to decorate my contact app url with a require_POST() decorator like this:
urlpatterns = [
url(r'^thanks/$', require_POST(views.ThanksPageView.as_view()), name='thanks'),
]
This doesn't work because Django's success_url() defaults to a GET request.
What is the most idiomatic way to approach this? Since Django is a 'batteries included' framework, I do not want to use 'duck-tape', meaning I do not want implement any ad-hoc logic in my views or urls.
Sorry if I've missed anything in the docs or questions archive.
Since you're asking for a idiomatic approach, I would consider the messages framework and specifically the SuccessMessageMixin. This would allow you to add a success message to e.g. the contact form itself and the url-pattern for the /thanks/ page would not be necessary.
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.
I'm trying to use a Django ListView sub-class to generate a page with a form on it. It's an old school manual HTML form, not a Django-generated one (though I do also have a Django-generated form elsewhere on the same page). Since Django bakes CSRF authentication in, I need to include the CSRF token in that form in order to make it work.
However, I'm not having much luck, even after looking at several related Stack Overflow posts (and fixing things accordingly).
Basically I've got a get method on a ListView subclass, and I've used the method decorator to decorate it with the CSRF decorator:
class FooView(ListView):
#method_decorator(ensure_csrf_cookie)
def get(self, request):
# code for otherwise working view
In my template I have:
<form>
{% csrf_token %}
However, when I view the source of the page after it's been rendered, I just see:
<form>
(no CSRF token).
I'm not explicitly adding the CSRF token to the context because I'm using ListView, and as per https://docs.djangoproject.com/en/1.6/ref/contrib/csrf:
If you are using generic views or contrib apps, you are covered already
I'm sure I'm just missing something basic, but any help explaining what that might be would be greatly appreciated.
You need import this:
from django.template import RequestContext
and then use it like so:
def example():
# Some code
return render_to_response('my_example.html', {
'Example_var':my_var
}, context_instance=RequestContext(request))
This will force a {% csrf_token %} to appear.
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
{% 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.