Can I turn a Django form into a GET request string? - django

I want to take a ready-made form (i.e an object of a class derived from django.Forms.form with validated bound data) and urlencode it as though it were submitted with GET. Is there a built-in way?
To show why I'm asking this questino, and why I can't just call urlencode, the output from this should be "box=on".
from django import forms
from urllib import urlencode
class DemoForm(forms.Form):
box = forms.BooleanField(required=False)
instance = DemoForm({"box": True}) # it's irrelevant how this data is supplied
instance.is_valid()
print "encoded:", urlencode(instance.cleaned_data)
In fact it's "box=True", because urlencode isn't encoding the form it's encoding the cleaned values (and believe me, BooleanField is the simplest case).
So I'm asking for a way to encode the form as though it were a GET string. A correct GET string.

Calling urllib's urlencode on the form's cleaned_data won't work well in two cases:
If you are using a ModelChoiceField, the cleaned_data will contain the actual object(s), not the IDs. These will urlencode() to their string representations instead of their primary keys.
If you are using a field that can hold multiple values (such as MultiValueField, MultipleChoiceField or others), urlencode() will lose all but one value from that field. So {'mykey':[1,2,3]} becomes ?mykey=3 instead of ?mykey=1&mykey=2&mykey=3 the way django does it.
To deal with both of these problems, use the form's built-in urlencode function:
form = MyForm(request.POST) #or a dict or whatever
form.is_valid()
querystring = form.data.urlencode()
Note that this is called on data, not cleaned_data. If your form changes values as part of validation, those changes won't be reflected here.

I'm not fully sure what you mean with ready-made form, since a form will normally have no values associated. Or do you mean to take a form a user filled and have it posted as a get?
You can use urllibs encode to create a get-string:
import urllib
print urllib.urlencode({'key1': 'value1', 'key2': 'value2'})
# key1=value1&key2=value2
If you want to take a posted form and create a GET-string from this data:
form = MyForm(request.POST)
if form.is_valid():
print urllib.urlencode(form.cleaned_data)
# name=value&name=value etc
If you want to create a GET for an unbound form:
# this form contains intitial values
# which are shown when printing the form
form = MyForm()
print urllib.urlencode(form.initial)
# name=value&name=value etc

Related

Sending form to another view django

I am building a website and I want various views that will ask the user to request a quote from our page. I want to keep the code as DRY as possible so I am writing a view quote which will receive the quote requests from various views and, if there is a validation error redirect back to the page that made the request. I managed to solve this using the super bad practice 'global variables'. I need a better solution, I would like redirecting to respective view with the current form so I can iterate through the form.errors. Here is my code:
def send_quote(request):
form = Quote(request.POST)
if form.is_valid():
# do stuff when valid
return redirect('Support:thanks', name=name or None)
quote_for = request.POST['for_what']
global session_form
session_form = form
return redirect('Main:' + quote_for) # Here I would like to send form instead of storing in global variable`
You can use the HttpResponseRedirect function, and pass as argument the page that made the request.
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
All the META data is store on a dictionary, if you want to learn more check the documentation.
https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META
If you redirect to the referrer, form.errors will be empty, as redirection is always a GET request.
I can think of two solutions to your problem:
Submit forms asynchronously using JavaScript and so populate the errors
Make all the views containing the form support POST - one way to do this would be to create a base class that inherits from FormView
The second option is a typical way of handling forms in Django - you process both POST and GET inside the same view.
After two days of searching I finally found the answer. Instead of saving form in request.session I just save request.POST and then redirect. Here is the code:
def send_quote(request):
form = Quote(request.POST)
if form.is_valid():
# do stuff when valid
return redirect('Support:thanks', name=name or None)
quote_for = request.POST['for_what']
request.session['invalid_form'] = request.POST
return redirect('Main:endview')
def endview(request):
session_form = request.session.pop('invalid_form', False)
if session_form:
form = Quote(session_form)
# render template again with invalid form ;)
Now I can repeat this with all the views I want and just change the what_for input of each form to match the respective view (Like I intended).

Prevent Django forms highlighting correct fields

After submitting a form that contains errors, the incorrect fields get marked as such. Correctly submitted fields, however, also get marked.
Is there a way to prevent this from happening? I'd prefer it if Django were to only render the incorrect fields differently, and render the correctly submitted fields as normal.
I checked the API offered by the Form object, but there does not seem to be a property that lists these correctly submitted fields.
Django by default only marks the invalid fields, not the valid ones.
Be sure you are passing the POST data to the form in the view when POST.
(incomplete example below)
if request.method == 'POST':
form = YourForm(request.POST)
if form.is_valid():
# your code then redirect
else: #GET
form = YourForm()
You can take a look to this Django example in the docs for a full example.

Proper way to handle PUT requests Django with forms

I have a web app that allows users to edit previously submitted data.
I'm currently processing PUT requests by manually updating the data.
I would like to use my forms to validate input but I run into the issue of the other required fields.
For instance, if a user updates a date field and I validate it with my form, it errors out as I'm missing other required fields like name, location, etc since the form was designed to be filled out all at once.
What is the best way to use my forms to validate input but conditionally allow required fields if the request is a PUT or POST with model Forms.
If you're getting errors for missing fields you're not using the modelform correctly, sounds like you are not passing it the existing instance to work on.
You need to use a pattern like this:
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render
def my_view(request, obj_id):
my_object = get_object_or_404(MyModel, pk=obj_id)
update_form = MyModelForm(instance=my_object, data=request.PUT or None)
if update_form.is_valid():
return HttpResponse(status=204) # empty success response
else:
return render(...) # render update_form.errors somehow
And be sure to include the object id in the url that you send your PUT requests to

Checking for content in Django request.POST

I am accepting data via request.POST like this:
if request.method == 'POST':
l = Location()
data = l.getGeoPoints(request.POST)
appid = settings.GOOGLE_API_KEY
return render_to_response('map.html',
{'data': data, 'appid': appid},
context_instance=RequestContext(request))
It accepts data from a bunch of text input boxes called form-0-location all the way up to form-5-location.
What I want to add in is a check to make sure that request.POST contains data in any of those input fields. I think my problem is that I do not know the correct terminology for describing this in Django.
I know how to do it in PHP: look inside $_POST for at least one of those fields to not be empty, but I can't seem to find the right answer via searching for google.
If I don't find any data in those input fields, I want to redirect the user back to the main page.
Have you thought about using Django's Forms?? You can mark fields as "required" when defining a form and Django will take care of validating if said fields have data in them upon submission. They also do other kinds of validation.
if request.method == 'POST' and request.POST:
# Process request
request.POST will be false if the request does not contain any data.
With Django request objects, the POST data is stored like a dictionary, so if you know the keys in the dictionary, you can search for them and check if they're empty or not. Check out these two links for more detail:
http://docs.djangoproject.com/en/dev/ref/request-response/#attributes
http://docs.djangoproject.com/en/dev/ref/request-response/#querydict-objects
And, for example, when you have your request object, and you know you have a key/var called 'form-0-location', you could do:
if request.POST.get('form-0-location'):
print 'field is not None >> %s' % request.POST.get('form-0-location'')
I second the suggestion to use Django Forms. Why not take advantage of Django Forms when you are using Django?
Design a quick form that matches the fields you currently have on the page. Load the form with request.POST data, and use form.is_valid() to determine whether the form is valid or not .
request.POST returns a type of QueryDict (which extends the Dictionary superclass).
This means you can iterate through the keys in this dictionary (all the parameters in POST) and return false when you see one that is empty
(for key in request.POST):
if key k has invalid value (i.e. None or something else):
return false
return true
You could also try try something like
if len(request.POST['data'])<1:
do something if empty
else:
do something if has data

Prepopulate Django (non-Model) Form

I'm trying to prepopulate the data in my django form based on some information, but NOT using ModelForm, so I can't just set the instance.
This seems like it should be really easy, but for some reason I can't find any documentation telling me how to do this. This is my form:
class MyForm(forms.Form):
charfield1 = forms.CharField(max_length=3)
charfield2 = forms.CharField(max_length=3)
choicefield = forms.ModelChoiceField(MyModel.objects.all())
I tried just doing:
form = MyForm()
form.charfield1 = "foo"
form.charfield2 = "bar"
# a model choice field
form.choicefield = MyModel.objects.get(id=3)
which does not work.
Try:
form = MyForm({'charfield1': 'foo', 'charfield2': 'bar'})
The constructor of Form objects can take a dictionary of field values. This creates a bound form, which can be used to validate the data and render the form as HTML with the data displayed. See the forms API documentation for more details.
Edit:
For the sake of completeness, if you do not want to bind the form, and you just want to declare initial values for some fields, you can use the following instead:
form = MyForm(initial={'charfield1': 'foo', 'charfield2': 'bar'})
See the documentation of initial values for details.
There are two ways of populating a Django form.
The first is to pass a dictionary as the first argument when you instantiate it (or pass it as the data kwarg, which is the same thing). This is what you do when you want to use POST data to populate and validate the form.
data_dict = {'charfield1': 'data1', 'charfield2': 'data2', 'choicefield': 3}
form = MyForm(data_dict)
However, this will trigger validation on the form, so only works if you are actually passing in valid and complete data to begin with - otherwise you will start off with errors.
The other way to populate a form is to use the initial parameter (documented here). This gives initial values for the form fields, but does not trigger validation. It's therefore suitable if you're not filling in all values, for example.
form = MyForm(initial=data_dict)
To populate a choicefield via initial, use the pk value.
You can use model_to_dict() to convert an instance to a dictionary, and then populate a form with that. Something like this should work:
from django.forms.models import model_to_dict
...
my_obj = MyModel.objects.get(abc=123)
form = MyForm(initial=model_to_dict(my_obj))
Note: I'm using django version 1.3
For what it's worth, the FormView class-based view way to do this would be to override the FormView's get_initial function. get_initial returns the initial keyword arguments used by get_form_kwargs to instantiate the form.
Docs:
for get_initial, here,
for get_form_kwargs, here.
Sample code:
from django.views.generic.edit import FormView
class MyFormView(FormView):
def get_initial(self):
initial = super(MyFormView, self).get_initial()
# update initial field defaults with custom set default values:
initial.update({'charfield1': 'foo', 'charfield2': 'bar'})
return initial