I use a function-based view and try to validate a form which is submitted over Ajax (with jquery.form plugin):
Content-Type:application/json; charset=UTF-8
X-CSRFToken:jRr4oOBHQS5mtwopN69xHocjWJBYuJHa
X-Requested-With:XMLHttpRequest
Request payload:
csrfmiddlewaretoken=jRr4oOBHQS5mtwopN69xHocjWJBYuJHa&code=123456
now, in the view function, I have request.GET/POST empty, but request.body as a string, and I can't validate the form.
form = CodeCheckForm(parse_qs(request.body))
form.is_valid()
In the second line, the clean* functions aren't called, which is really weird.
Changing to use data or initial doesn't help either:
form = CodeCheckForm(data=parse_qs(request.body))
What am I doing wrong?
EDIT: among the answers, the decisive was to change the content type. In jquery.forms plugin I set contentType option to application/x-www-form-urlencoded; charset=UTF-8.
EDIT 2: there are 2 ways to supply arbitrary data around the standard way, but they weren't suitable in my case:
1) form = MyForm(parse_qs(request.body)) is almost correct, but Django forms expect this to be a QueryDict, and have some properties, while this is a usual dict. Form class raises exception and crashes the view.
2) form = MyForm(data=parse_qs(request.body)) works, but does not call clean* functions. This is intentional, as Django developers made this way as a way around clean functions. You're supposed to validate the data yourself, and then submit it this way. Django form then does not clean it in any way and decides the form is not validated, hence form.is_valid() will be False.
The GET and POST only contain form data. They are empty for you because your content type is 'application/json'. It's not clear to me why you've used this content type, since the payload looks like form encoded data to me, not json.
If you manually parse the payload, use the data argument. The initial argument is only used to show initial values. If you don't bind the form to data, then the form is unbound, and will never be valid.
I'm not sure why the following line didn't work. What is the value of parse_qs(request.body)?
form = CodeCheckForm(parse_qs(request.body))
Related
This is from print(request.POST)
<QueryDict: {'csrfmiddlewaretoken': ['2Tg4HgJ07qksb3hPUDWSQYueYOjYOkQcmzll9fnjbJ0GZHkWHdM8DtYqZB4uv3Fv'], 'username': ['boycececil'], 'password': ['password']}>
This is from print(request.POST.get('username'))
boycececil
So as you can see, list (from one of the values of QueryDict)-> string(from the get function), that's magic! Isn't it?
So, somebody know what's going on?
What the value type of the value of request.POST in django?
The type is django.http.request.QueryDict
So as you can see, list -> string, that's magic! Isn't it?
No, it isn't magic. This is just the documented behavior of QueryDict:
"QueryDict.__getitem__(key)
Returns the value for the given key. If the key has more than one value, it returns the last value. ..."
Note: if you want all values for a key, you can call getlist(key) on the QueryDict.
So, the request.POST is a subclass of the dictionary but not a raw dictionary.
It is a subclass of the django MultiValueDict class which is a subclass of dict.
When I call get(key) to the Querydict it return the last value of the list. Sounds like the get method in Querydict overwrite the get method in the raw dictionary.
Yes ... that is what the __getitem__ method does.
By the way, I wonder why we need multiple value for one key.
It is because HTTP allows multiple values for a given parameter in a request. So if multiple values can be provided, django need to be able to make them available via the HttpRequest.POST object.
But since this is an unusual case, they decided to just give your code one value for each parameter ... unless it specifically asks for them all. Django is being helpful.
I have a mailing list system in which a user can click a link to unsubscribe. This link contains the user's email in a GET parameter and the page it points to contains a short form to ask for feedback. This feedback needs to point to the email of the user who submitted it.
The way I tried to achieve this is:
take the email from the GET parameter
put it as initial value in a hidden field on the feedback form
retrieve it from form data when the form is sent
The problem is that if this hidden field is not disabled, the user can meddle with its value and dissimulate his own identity or even claim that the feedback came from another user. But if I set the field as disabled, the request.POST dictionary does not contain the field at all.
I also tried keeping the field enabled and checking for its presence in form.changed_data, but it seems to always be present there even if its value does not change.
This is the form class:
class UnsubscribeForm(forms.Form):
reason = forms.ChoiceField(choices=UnsubscribeFeedback.Reasons.choices)
comment = forms.CharField(widget=forms.Textarea, required=False)
user_email = forms.CharField(widget=forms.HiddenInput, required=False, disabled=False)
This is how I populate user_email in the view when the method is GET:
email = request.GET.get("email", "")
# ...
context["form"] = UnsubscribeForm(initial={"user_email": email})
Note that I also tried disabling the field manually after this line, as well as in the form's init method. The result is the same: if the field is disabled, the value does not get passed.
After setting the initial value, I print()ed it to make sure it was being set correctly, and it is. I also checked the page's source code, which showed the value correctly.
And this is how I check for the value in the POST part of the view, when the data-bound form is being received:
form = UnsubscribeForm(request.POST)
if form.is_valid(): # This passes whether I change the value or not.
if "user_email" in form.changed_data: # This too passes whether I change the value or not.
print("Changed!")
email = form.cleaned_data["user_email"] # This is "" if user_email is disabled, else the correct value.
I have no idea why the initial value I set is being ignored when the field is disabled. As far as I know, a disabled field passes the initial value over regardless of any changes, but here the initial value isn't being passed at all. And as I outlined above, I can't afford to keep this field editable by the user, even if it's hidden.
Django is version 3.0.3, if that matters.
Any solution? Is this a bug?
I found a solution to my problem, though it doesn't quite answer the question of why disabled fields ignore runtime initial values, so in a sense, the question is still open to answers.
In the original question, I crucially neglected to specify (in an effort to make the code minimal and reproducible) that the GET request that includes the user's email address also contains a token I generate with unpredictable data to verify that the email is authentic and corresponds to a subscribed user. In order to successfully meddle with the email, a user would also have to forge a valid token, which is unlikely (and not worth the effort) unless they have access to both my database and codebase (in which case I have worse problems than a feedback form).
I will simply keep the hidden field not disabled and also pass the token along, to verify that the address is indeed valid.
I want to validate form input and if required modify a field used to build a subdomain for a URL. IE take out the illegal characters. Here is a VERY simple example to illustrate the problem.
class MyForm(forms.Form):
def clean_f(self):
f = self.cleaned_data['f']
if f.count('%'):
f = f.replace('%', '')
return f
This doesn't change the form. I want the user to see the 'stripped' value, but it always shows the submitted value.
Is it possible to do this with a simple form clean_xxx method?
Otherwise I will use my AJAX form processor.
Thanks
so i have this code:
post = request.POST.copy()
post['relationshipId'] = theRelationship.id
theStory = StoryForm(post, request = request, initial {'relationshipId' : theRelationship.id})
initially, my code looked like this:
theStory = StoryForm(request.POST, request = request, initial {'relationshipId' : theRelationship.id})
which caused validation problems. The validator would complain that the relationshipId wasn't set. Why would this be?
EDIT: the first block of code works fine, and I am super-happy with it. The question pertains to the second block of code, which was initially what I had (and what i've just spend some time working on) which, to me, is acting "weird"
The first snippet sets the relationshipId field dynamically, instead of taking it from the POST parameters supplied in the web request.
The second snippet will take that value directly from request.POST, so if your form submits an invalid value, or if no value is given, it will not validate.
The initial argument only applies to unbound forms (see https://docs.djangoproject.com/en/1.5/ref/forms/fields/#initial). You could leave it out here, because you are binding the form to post or request.POST.
We've got some clients sending a custom POST of a data blob to our django servers.
They do things in a rather funky way that I'd rather not get into - and we've since moved on from making that particular format the norm. To make further implementations of our upload protocol more streamlined, I was looking to roll a custom UploadHandler in django to make our data handling in the views a bit more streamlined.
So, moving forward, we want all code in the views to access our POSTs via the:
data = request.FILES['something']
So, for our new submissions, we're handling that dandily.
What I'd like to be able to do is get the upload handler we've made, affectionately called LegacyUploadHandler(), to populate the request.FILES dictionary with the right parts, so the code in our view can access the parts the same way.
So, my question:
How does a custom uploadhandler actually populate the request.FILES dictionary? The django documentation doesn't really give a descriptive example of doing that.
Our particular desire is that we have a singular blob of data coming in. We custom parse it and want it to appear as the request.FILES dictionary.
The current code as it stands right now does this:
def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None):
files_dict = {}
files_dict = magic_parser(input_data.read())
#now what do I do?
I see examples of setting a files MultiValueDict in the http.MultiPartParser, but that seems to be outside the scope/control of where I am in my handlers.
Any ideas of how to actually do the return value? Or am I trying to populate the request.FILES object the completely wrong way?
From handle_raw_input you have to return a tuple of what will be POST and FILES on the requst. So in your case it's something like:
def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None):
files_dict = magic_parser(input_data.read())
return QueryDict(), files_dict
The magic_parser should return a MultiValueDict of the form {'filename': fileobj}. A fileobj is an instance of some suitable django.core.files.File subclass (or may be that class itself).