Initialising the flask-wtf form when the page loads - python

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.

Related

Flask table and LinkCol, ButtonCol - passing value as URL parameter instead of path to endpoint possible?

I have a function defined as follows:
#app.route('/products', methods=['GET'])
#app.route('/products/<id>', methods=['GET', 'PUT'])
def foo():
id = request.args.get('id', type=int)
if id != None and request.method in ['GET', 'PUT']:
# route to editing product template
else:
# display products as table
I have defined a Flask table like this:
class ProductsTable(Table):
id=Col('ID', show=False)
price=Col('Price')
available_online=Col('Available online?')
available_num=Col('In stock')
edit=ButtonCol('Edit', url_kwargs=dict(id='id'), endpoint='/products')
I query an SQLite DB and fill the table. Displaying it works fine however I am struggling with the editing functionality.
Right now the way it works once the user presses the Edit button the URL changes to http://localhost:5000/products/1, http://localhost:5000/products/2 etc. depending on the ID of the item in the DB and respectively in the table (since it just displays the DB data).
However I would like to find out if it's possible to pass the ID as an URL parameter so that the URL for editing a specific product by pressing the Edit button becomes http://localhost:5000/products?id=1 http://localhost:5000/products?id=2 etc.
I tried passing '/products/<id>' to the endpoint argument but I get the following error
werkzeug.routing.BuildError: Could not build url for endpoint '/products/<id>' with values ['id']. Did you mean 'products' instead?
I also tried '/products?<id>', '/products?id=<id>' and also without the / in front of products but the result was the same. The only valid endpoint here is just products.
Is this possible and if yes, how can I achieve this behaviour?
I use the following format:
edit = LinkCol('Edit', 'app.foo', url_kwargs=dict(id='id'))
Equivalent to url_for('app.foo', id=id)

Pass data between different views in Django

I have a basic view that retrieves some data, renders my page and sends some data to this page:
def myview(request)
one = values.objects.get(user=request.user).address
two = values.objects.get(user=request.user).number
return render(request, "main/mytemplate.html",
context={'address': one, 'numbers': two})
So the values retrieved by those two queries are shown on my page.
Now, on the same page, called mytemplate.html, i'm using another view, which is supposed to handle a form and some other operations:
def secondview(request):
if request.method == 'POST':
if 'button1' in request.POST:
form = MyForm(request.POST)
# check whether it's valid:
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
profile.save()
return HttpResponseRedirect(request.path_info)
else:
form = MyForm()
return HttpResponse('it works!')
How can i use the data retrieved by those two queries in the second view? The queries are executed when the page is loaded by the first view. Then, in the same page the second view is used. I want to use the two variables one and two in the second view. Is there a way to do this in Django?
Why don't you make the same queries in the second view? Because i would like the second form to be as fast as possible in terms of reload, without having to do a DB query each time that view is used. Also, since i already retrieved those values when the page is opened, it would be a waste to do that again.
I don't know if this question is clear enough, but the core of it is: can i pass variables/data between two views in django?
You have few options:
Simplest way: include this data in request to the second view (as part of the form data, see an example below). You might even use a single view: if POST was send - store data else do request and show it on a page.
Use cache for that (see an example below) - But I'd recommend to use Django built-in package. Here is a basic example how to use it
Use Django Sessions (see an example below) - it is working option despite of that they have another purpose. When customer is loaded Django will load full session record, so you'll have all data in request.session variable. But that is bad practice: you can get a lot of data duplication and increased database memory consumption.
Use API (e.g. using DjangoRestFramework) together with usual Django app. So you'll just get data you need, and when you need. These API requests can also be cached so it is fast solution.
Yes, you can use session to pass data across views. A session works like a temporary server storage and keeps the needed data in a dictionary form.
For instance, add the following lines to myview:
request.session['one'] = one
request.session['two'] = two
Then, retrieve the data in secondview by referring to the session:
one = request.session['one']
two = request.session['two']
you can use cookies. but if you want more secure your request i suggest to you using redis and the python client for redis
file settings.py
redis = redis.Redis(host='localhost', port=6379, db=0)
file views.py
def view1(request):
redis.set("foo", "boo")
def view2(request):
boo = redis.get("foo")
Why not just saving the results of the two queries as hidden fields in the form rendered by the first template ?
<form ...>
<input type="hidden" id="address" name="address" value="{{address}}">
<input type="hidden" id="numbers" name="numbers" value="{{numbers}}">
...
Then, you can either add 'address' and 'numbers' form fields to MyForm
address = forms.CharField(widget=forms.HiddenInput(), required=False)
...
or just retrieve the values from request.POST

Database based forms in Flask

I am new to Flask and bit confused about the database modeling for it. Please have my apologies if by any mean this isn't a question eligible for post.
I need to create a multi choice model field in Flask and I need to be able to access it from backend admin panel to set values for it. It does shows options in docs with WTF forms to create multiple choice field. I am confused that how to create forms attached to Database. Can someone clear it up for me because I am a Django user and in Django Forms and ModelForms have different approach so trying to understand what would it be in Flask. How to render Database based forms in Flask? How would I create a multi choice field with database created for it. Please help.
What you are looking for is SQLAlchemy built-in ORM to build forms from models or integrated to database. There are other options to overcome limitations of Flask ORM when needed. Following is the example that would give you some clarity.
from flask import Flask, render_template, redirect, flash
from flask.wtf import Form
from flask.ext.sqlalchemy import SQLAlchemy
from wtf.ext.sqlalchemy.orm import model_form
app=Flask(__app__)
app.config['SECRET_KEY'] = 'secret'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/employees.sqlite'
app.config['SQLALCHEMY_ECHO'] = True
# Here you initiate the ext
db=SQLAlchemy(app)
#Let's define a model
class Employee(db.Model)
__tablename__ = 'employee'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False
birthday = db.Column(db.Date, nullable=False
def __repr__(self):
return 'employee %s' %self.name
# Now go to your shell inside your env not in gloabal shell outside your env and run this command.
# From your .py file where you've created all above configration first make an import to db from shell
from file.py import db
#Then create a database with following command in shell
db.create.all()
#Your auto generated database based form below
EmployeeForm() = model_form(Employee, base_class=Form, field_args{'name':{'class':'employee'}})
#Let's create a view with model_form or database based form in your case.
#app.route('/', methods=['GET', 'POST'])
def index()
#request.POST does same in Django or any other Python based web framework like Bottle, Tornado etc
form = EmployeeForm()
try:
if form_validate_on_submit():
employee=Employee() #load the model values
form.populate_obj(Employee) #populates the form with respective values
db.session.add(employee) #gathers the session based data to be added in DB
db.session.commit() #Adds data to DB
flash('New Employee added to database successfully.') #Display a message to end user at front end.
retrun redirect('/') # redirects upon success to your homepage.
except Exception e:
# logs the errors
db.session.rollback()
flash('There was a problem registering new employee. Please contact the site administrator at site#site.com')
employee_list = Employe.query.all() #equailent to django style "item.objects.all() to show list of all existing items.
return render_template('index.html', form=form, employee_list=employee_list)
In last line above you did three things. You got your form variable or context variable like you do in Django as "form" so your end user can enter data.
Then you have your model data that is saved in db as "employee_list=employee_list" that will show all the list to end users. "flash" is just like Django
messaging framework.
Now for multiple choices its model has same as djagno choice arguement for key value like below:
With my experience I would suggest you to install "peewee" a simple ORM for Python connected Databases.
choices = (('key', 'value')('key', 'value'))
employee_type = db.Model(db.String(90), choices=('key1', 'key2)
Hope this helps.

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

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, ...

Decorate GET URL using forms

I have some question:
I use django form, and fields like MultipleChoiceField
in view.py I clean data and get GET URL like this
http://localhost:8000/?category=&style=&sex=&brand=ASICS&brand=Be+Positive&low_price=&high_price=
Give me advise, can I regroup brand field and hide empty.
I want getting something like this:
http://localhost:8000/?brand=1+2
And else one question:
How can I set empty value(empty_label) for forms.ModelMultipleChoiceFIeld
forms.py:
brand = forms.MultipleChoiceField(required=False,
widget=forms.SelectMultiple(attrs={'size':1})
)
def __init__(self,app_label=None, *args, **kwargs):
super(Search, self).__init__(*args, **kwargs)
self.fields['brand'].choices = [('', 'All brands')]+[(brand.name, brand) for brand in Brand.objects.all() ]
views.py:
if request.method == 'GET' and request.GET:
form = SearchForm(app_label, request.GET)
if form.is_valid():
brands = form.cleaned_data['brand']
kwargs.update({"brand__name__in": brands})
This is how the browser submits multiple data. It's part of the HTML specification, trying to change it would be folly and technically I can't understand why you would try to care about how your url GET data looks.
That being said, if you want to change the way it submits you'll need javascript to transform the data on form submit. Django has nothing to do with the matter.
Using jQuery for example:
$('#form').submit(function(){
//Get form data
//Transform into my custom set of vars
//Redirect to form's ACTION with my querystring appended.
});
Please keep in mind you will not get any automatic parsing of the values on the Django side. Normally it would turn it into a list for you, but now you're responsible for parsing the 'value+value+value' yourself.
For empty label in forms you could do this -
class SomeForm(forms.Form):
h=forms.CharField(label=u'',widget=forms.TextInput(attrs={'value':'Search'}))
By keeping label as '', you get the label as empty. The attrs are basically the HTML attributes of the form text field.
UPDATE: I didn't understand the first part of your Q, elaborate...

Categories

Resources