Flask WTForms Dynamic Default Value - flask

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)

Related

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.

calling a function to obtain a model field value

I'm trying to get a unique value for a field (unique within the db column).
my code (other model fields omitted):
class PlatformUserChildren(models.Model):
dashboard = models.CharField('dashboard URL', max_length=64, unique=True, default=createDashboardCode(self))
def createDashboardCode(self):
stringCheck = False
while stringCheck is False:
newString = str(uuid.uuid4())[:32]
doesStringExist = newString in self.dashboard
if not doesStringExist:
stringCheck = True
return newString
I'm getting name 'self' is not defined as an error.
What should I be passing to the function so that I can check the db column to ensure the value is unique -or- is there a built-in way of doing this?
What I've already tried or looked at:
setting unique=True for the field and using default=uuid.uuid4 - that gives me duplicate values and generates a validation error (SO link)
I'm aware of Django 1.8's UUID field, however i'm on 1.7
The problem lies in the following line (indented for better readability) as you already know and mentioned before:
dashboard = models.CharField(
'dashboard URL',
max_length=64,
unique=True,
default=createDashboardCode(self)
)
In this part:
default=createDashboardCode(self)
you're calling the method createDashboardCode with the argument self. This is wrong, because you never pass self to a method as it is passed by Python. Whenever you call the method createDashboardCode you should do it this way:
createDashboardCode()
That's it, you're not passing the argument self explicitly.
You're getting an error "name 'self' is not defined" because self is not defined. There is no variable self in your code that you can pass to the method.
Now we're one step further, but your problem won't be solved if you just apply this slight change to your code.
The return value from the method createDashboardCode will be assigned to default. That's not what you really want. You have to assign a reference of the method to default:
default = createDashboardCode
Pay attention to the missing brackets. This will call the method every time a new instance of the model is created
Define a function:
def my_function():
print "hello"
and run it in the Python interpreter:
# once like this
my_function()
# and again like this
my_function
and you'll see the difference. That should help you to better comprehend this issue.

DJANGO - change a form value with request.POST?

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.

Some troubles while converting authors id to User model

My Post model have list of authors id
class Post(Document):
authors_id = ListField(IntField(required=True), required=True)
But sometime I need to use default Django User class. How most rapidly I can do it?
(I'm using sqlite for users and sessions and MongoDB (mongoengine ODM) for other. Don't ask why:))
I was tried to write it:
def get_authors(self):
authors = list()
for i in self.authors_id:
authors.append(get_user(IntField.to_python(self.authors_id[i])))
return authors
...and it raises 'list index out of range' exception. (authors id not empty, really). What I'm doing wrong?
Not sure about the to_python method but since you are looping through the authors_id, there is no need to do
authors.append(get_user(IntField.to_python(self.authors_id[i])))
You should be good with
authors.append(get_user(IntField.to_python(i)))
Instead of
IntField.to_python(self.authors_id[i]))
I think you just need to do:
IntField.to_python(i)
In Python the 'for i in some_list' construction gives you elements of the list, not integer indexes.
You said that you were getting this error:
and unbound method to_python() must be called with IntField instance as first argument (got int instance instead)
I got a similar error from MongoEngine. In my case the problem was that I defined the field like this:
foo_id = IntField
The correct way to define it is:
foo_id = IntField()
When I added the parenthesis, the problem went away.

Can you set default values for request.get() (not post.get)?

I have 2 HTML submission fields under one submit button, one of which is optional. I'm using the 'get' method to retrieve submitted data, but I want the optional field to have a default value just in case the user submits an empty form. If I don't do this, I get a MultiValueDictKeyError.
if request.method == 'GET':
# required
name = request.GET['name']
# optional
color = request.GET['color']
I think a possible solution is try / except for every field, but is there a more elegant method? I know for get.post() you can do something like
color = request.POST.get('color', False)
But this doesn't seem to work for just request.get()
Any ideas?
Thanks,
fertileneutrino
Confused here... request.get() won't work, but request.GET.get() should. Did you just mistype or were you actually using request.get()?