DJANGO - change a form value with request.POST? - django

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.

Related

Flask WTForms Dynamic Default Value

I want to have a dynamic default value for a form. This will be whatever was inputted last time it was used.
I saw this answer that says to modify the default value after creation and then use the process() method. This works in updating it (I see the default displayed on the web page), but I get an error when the form submits saying The CSRF token is missing. In chrome devtools I see that the form has a input_id= 'csrf_token' and a value so not sure what is happening?
class weight(FlaskForm):
weight = StringField('Enter your weight', validators=[DataRequired()])
submit = SubmitField('Submit')
last_weight = get_last_weight() # query database function, returns float (e.g 85.4)
form = weight()
form.weight.default = last_weight
form.process()
A related question, is this the best way to set a dynamic default? I tried
StringField('Enter your weight', validators=[DataRequired()], default=get_last_weight())
as the docs say
default – The default value to assign to the field, if no form or object input is provided. May be a callable.
so I think the function is a callable (tell me if I'm wrong!) but I get an error saying that the function is being used outside of the application context, so I think the function is being called too early. It seems like this would be a common task so guessing I have missed something.
Thanks!
This is how I like to handle dynamic form building
def buildWeightForm(last_weight):
class weight(FlaskForm):
weight = StringField('Enter your weight', default=last_weight, validators=[DataRequired()])
submit = SubmitField('Submit')
return weight()
For your 2nd question, if you're getting an error doing this
StringField('Enter your weight', validators=[DataRequired()], default=get_last_weight())
Try the following (removing your parentheses from your function call):
StringField('Enter your weight', validators=[DataRequired()], default=get_last_weight)

Way to pass information from a GET parameter to a POST form in django?

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.

Django 1.6 request POST/GET empty

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

Changing field values in form.clean_xxx

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

Django: Passing a request directly (inline) to a second view

I'm trying to call a view directly from another (if this is at all possible). I have a view:
def product_add(request, order_id=None):
# Works. Handles a normal POST check and form submission and redirects
# to another page if the form is properly validated.
Then I have a 2nd view, that queries the DB for the product data and should call the first one.
def product_copy_from_history(request, order_id=None, product_id=None):
product = Product.objects.get(owner=request.user, pk=product_id)
# I need to somehow setup a form with the product data so that the first
# view thinks it gets a post request.
2nd_response = product_add(request, order_id)
return 2nd_response
Since the second one needs to add the product as the first view does it I was wondering if I could just call the first view from the second one.
What I'm aiming for is just passing through the request object to the second view and return the obtained response object in turn back to the client.
Any help greatly appreciated, critism as well if this is a bad way to do it. But then some pointers .. to avoid DRY-ing.
Thanx!
Gerard.
My god, what was I thinking. This would be the cleanest solution ofcourse:
def product_add_from_history(request, order_id=None, product_id=None):
""" Add existing product to current order
"""
order = get_object_or_404(Order, pk=order_id, owner=request.user)
product = Product.objects.get(owner=request.user, pk=product_id)
newproduct = Product(
owner=request.user,
order = order,
name = product.name,
amount = product.amount,
unit_price = product.unit_price,
)
newproduct.save()
return HttpResponseRedirect(reverse('order-detail', args=[order_id]) )
A view is a regular python method, you can of course call one from another giving you pass proper arguments and handle the result correctly (like 404...). Now if it is a good practice I don't know. I would myself to an utiliy method and call it from both views.
If you are fine with the overhead of calling your API through HTTP you can use urllib to post a request to your product_add request handler.
As far as I know this could add some troubles if you develop with the dev server that comes with django, as it only handles one request at a time and will block indefinitely (see trac, google groups).