How to keep track of Form field changes in Flask-WTF? - python

I have model with members field as shown below:
class Team(db.Model):
--- some fields ---
members = ListProperty(db.Key) # Using App Engine datastore as backend.
I am using Flask-WTFforms to create form using this model. While creating new Team, user will select some members in the form and save it. User can also edit the Team form and make changes to the members field(Can add or remove members). I wanted to check how many members added or deleted by comparing it with previous data stored in members field. Same applies for other fields also.
I used sessions to achieve this:
def edit_team(key):
k = db.Key(key)
team = db.get(k)
form = TeamForm(obj = team)
if not form.is_submitted(): # Indicates GET request
session[str(g.user.user_id() + 'prev_members'] = form.members.data
if form.validate_on_submit():
form.populate_obj(project)
# I will use session data with new form.members.data to do further processing.
Is this the right way to handle this scenario?
Thank you for any help..

the problem with your code is
team = db.get(k)
form = TeamForm(obj = team)
if the request was GET, the object's data has to be loaded from the model. however, if the request was POST (or PUT, PATCH) it means that you need to read the request body to form to do proper updates on the model. it seems like you're using k variable as a key to the indicator of the model.
k = db.Key(key)
team = db.get(k)
if request.method == "GET":
# create form from the model
form = TeamForm(obj = team)
elif request.method == "POST":
# update model with the form
form = TeamForm() #flask-wtf automatically reads from request object
# validate, update, delete, ...

Related

Django: Modify model field value when form is submitted

I have a model field for each use that keeps track of their score on my website. Every time a user does some I want to add or subtract from their score.
In this particular case, I would like to change the users score when they publish a post on my site. I am able to access the user's score, but not modify it. Here is what I have so far:
def createPost(request):
user = UserProfile.objects.get(user=request.user)
current_score = user.user_score
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
...
user.user_score = current_score - 1
...
else:
form = PostForm()
return render(request,'feed/userpost_form.html',{'form':form})
I am not getting any errors and publishing the post works fine, just not modifying the user's score. Also, I am using Django 1.11
you were missing user.save()
btw another approach could be:
from django.db.models import F
UserProfile.objects.filter(user=request.user).update(user_score=F('user_score')+1)
important feature of above approach in mentioned context is:
Another useful
benefit of F() is that having the database - rather than Python -
update a field’s value avoids a race condition.
https://docs.djangoproject.com/en/1.8/ref/models/expressions/#avoiding-race-conditions-using-f

How to access field names of a ModelForm in Django?

Just to be clear, I'm asking about accessing the fields in views.py
I want to add extra data into the form before it is validated (because it's a required field), and another answer on stackexchange seems to imply I have to create a new form to do so.
Right now my code look something like this:
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = TestForm(request.POST)
data = {}
for ---:
---add to data---
comp = Component.objects.get(name = path)
data['component'] = comp.id
form = TestForm(data)
if form.is_valid():
test = form.save(commit = 'false')
test.save()
return submitTest(request, var)
How could I fill in the parts with dashes?
This is the wrong thing to do. There is no reason to add in a required field programmatically; if you know the value of the field already, there is no reason to include it on the form at all.
I don't know what you mean about having to create another form; instead you should explicitly exclude that field, in the form's Meta class, and set the value on the test object before calling test.save().
Edit after comment I still don't really understand why you have data coming from two separate places, but maybe you should combine them before passing to the form:
data = request.POST.copy()
data['myvalue'] = 'myvalue'
form = MyForm(data)
I figured out what I was doing wrong. In my TestForm modelform I didn't include the 'component' field because I didn't want it to show up on the form. As a result, the 'component' data was being cleaned out during form validation even if I inserted it into the form correctly. So to solve this I just added 'component' into the fields to display, and to hide it on the form I added this line
widgets = {'component': HiddenInput()}
to the TestForm class in forms.py.

Two types of user accounts with django user accounts

I'm creating a web application and I'd like to split the sign-up/registration process between
A. Individuals
and
B. Employers
where each sign-up form contains similar elements but are also different.
What's the best approach to doing this? Inheritance?
Concentrating only on forms + views section ( assuming you are done with the models).
Since all the fields are same for both the entities. You can differentiate when you create the object of the signup form in views method.
def employer_signup(request):
form = CommonSignupForm(request.Post or None)
# do something with it
def individual_signup(request):
form = CommonSignupForm(request.Post or None)
# do something else with it
Now, i assume, max you have to do in this is to set user_type of the user signing up. Following code should be in each method.
user = form.save(commit=false)
user.user_type = 'E' # depends what tags you are using
user.save()

Initialising the flask-wtf form when the page loads

Whenever the flask server is started, all the forms are initialised at that time itself. The data is interconnected between the pages, so for one form, the choices come from the database and those choices can be edited using another form on another page but after the choices are updated on that page, they remain the same for the first form. To get the new values I need to restart the server. Is there any way to refresh the values without restarting the server?
This is how the form looks like
class AddExpenses(Form):
reason = wtforms.StringField('reason', [validators.Required()])
amount = wtforms.IntegerField('amount', [validators.Required()])
allnames = []
allnames = getSalesman()
salesperson = wtforms.SelectField('salesperson', choices=[names for names in allnames])
submitfield = wtforms.SubmitField('Submit')
getSalesman() function is used to query the database and get the choices.
Why not get the choices in the view function that requires the form? For example
#app.route('/index')
def index():
form = AddExpenses()
allnames = getSalesman()
form.salesperson.choices = [names for names in allnames]
...
You may also want to use some caching on getSalesman() if the database will not change often.

Using FormWizard and saving the forms data in between before the completion of the whole process?

I am using FormWizard to complete a set of operation in my app, I have two models Employee and Person, Employee class inherits Person, and all the fields of Person are available for Employee object.
Now I am creating a set of forms using FormWizard, I just wanted to know that. If a user starts entering the data in the forms and fills upto 2 forms out of 4 and is willing to fill the rest of the forms afterwards. So is this possible that the data for the two forms which he filled can be saved in the database.
And the next time he comes can complete the operation form the 3rd form.
If anyone knows that then plz help me out, it would be a great help. Thank You!
what you can do is every step, save out the form state to some serialised object in db ForeignKeyed to the user.
then when hooking up the formwizard, wrap the formwizard view in a custom view which checks if the user has a saved form and if so deserialises and redirects to the appropriate step.
Edit: seems formwizard saves state in POST. only need to save postdata.
models.py:
class SavedForm(Model):
user = ForeignKey(User)
postdata = TextField()
views.py:
import pickle
class MyWizard(FormWizard):
def done(self, request, form_list):
SavedForm.objects.get(user=request.user).delete() # clear state!!
return render_to_response('done.html',)
formwizard = MyWizard([Form1, Form2]) <- class name, not instance name
def formwizard_proxy(request, step):
if not request.POST: #if first visit, get stored data
try:
prev_data = SavedForm.objects.get(user=request.user)
request.POST = pickle.loads(prev_data.postdata)
except:
pass
else: # otherwise save statet:
try:
data = SavedForm.objects.get(user=request.user)
except:
data = SavedForm(user=request.user)
data.postdata=pickle.dumps(request.POST)
data.save()
return formwizard(request)
edit: changed formwizard constructor

Categories

Resources