Dynamically populate the Flask WTForms Select field from a csv file? - python

I am using Flask, Pandas, WTForms.
I want to populate a selectfield with data from a csv file.
I have
routes.py
forms.py
tabular.html
I create form in forms.py then via routes.py display it into tabular.html.
Forms.py
class QueryTableForm(FlaskForm):
state_select = []
district_select = []
country = SelectField(u'Country', choices=state_select)
state = SelectField(u'State', choices=district_select)
submit = SubmitField(u'Search')
Routes.py
#csv imports
country_wise_data = list(pd.read_csv('country_data.csv').values)
state_wise_data = list(pd.read_csv('state_data.csv').values)
district_wise_data = list(pd.read_csv('district_data.csv').values)
#app.route("/tabular", methods=['GET', 'POST'])
#login_required
def tabular():
form = QueryTableForm()
if form.validate_on_submit():
post = Post(title=form.country.data, content=form.state.data)
flash('Your Selection is Registered!', 'success')
return render_template('tabular.html', state_wise_data = state_wise_data,
country_wise_data=country_wise_data, district_wise_data=district_wise_data,
title='Tabular Data', form=form, legend='Tabular',
max=17000, date=date)
tabular.html
<form method="POST" action="">
...
{{ form.country.label }} {{ form.country }}
{{ form.state.label }} {{ form.state}}
...
</form>
csv file format
State,Confirmed,Recovered,Deaths,Active,Last_Updated_Time,,,,,,
USA,3458996,2834473,58245,564746,12-04-2021 22.57,,,,,,
India,1180398,1123133,4815,52128,13-04-2021 19.02,,,,,,
Now I want to pull only the values "USA","India" etc and populate in the selectfield "form.country". I have tried to do a for loop on routes.py. But how do I pass it on to the HTML file ? I also tried to do a for loop via Jinja on the HTML file.
How do I dynamically populate the "choices" attribute of the WTF selectfield from the html file ?

#app.route("/tabular", methods=['GET', 'POST'])
#login_required
def tabular():
form = QueryTableForm()
form.country.choices = country_wise_data
if form.validate_on_submit():
post = Post(title=form.country.data, content=form.state.data)
flash('Your Selection is Registered!', 'success')
return render_template('tabular.html', state_wise_data = state_wise_data,
country_wise_data=country_wise_data, district_wise_data=district_wise_data,
title='Tabular Data', form=form, legend='Tabular',
max=17000, date=date)

Related

Using data returned from forms in Django

Sorry for being a little new. My goal is to use the data collected from a user form to output a graph. The user inputs an initial 'price' then submits this number, it then goes into my formulas written in python and then django should display a chart in the same HTML file. I'm currently stuck on how to use data from 'POST'.
For example, if I'd like to take the cleaned_data from 'strike1' and multiply it by 4, then display it on the webpage, how would I go about doing that?
views.py
from django.shortcuts import render
from .forms import DataForm
# Create your views here.
def data(request):
if request.method == 'POST':
form = DataForm(request.POST)
if form.is_valid():
strike1 = form.cleaned_data['strike1']
strike2 = form.cleaned_data['strike2']
strike3 = form.cleaned_data['strike3']
strike4 = form.cleaned_data['strike4']
strategy = form.cleaned_data['strategy']
price = range(0,50)
premium1 = form.cleaned_data['premium1']
premium2 = form.cleaned_data['premium2']
premium3 = form.cleaned_data['premium3']
premium4 = form.cleaned_data['premium4']
contracts = form.cleaned_data['contracts']
form = DataForm()
return render(request, 'form.html', {'form': form})
forms.py
from django import forms
class DataForm(forms.Form):
strategy = forms.ChoiceField(
choices=[('Long Call', 'Long Call'), ('Long Put', 'Long Put'), ('Short Call', 'Short Call',),
('Short Put', 'Short Put'), ('Bull Call Spread', 'Bull Call Spread'),
('Bear Put Spread', 'Bear Put Spread'), ('Straddle', 'Straddle'),
('Butterfly Spread', 'Butterfly Spread'),
('Box Spread', 'Box Spread'), ('Iron Condor', 'Iron Condor')])
strike1 = forms.FloatField()
strike2 = forms.FloatField()
strike3 = forms.FloatField()
strike4 = forms.FloatField()
premium1 = forms.FloatField()
premium2 = forms.FloatField()
premium3 = forms.FloatField()
premium4 = forms.FloatField()
contracts = forms.IntegerField()
form.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Website</title>
</head>
<body>
<form method="'post">
{% csrf_token %}
{{ form }}
<button type="Submit">Generate</button>
</form>
</body>
</html>
I'd say the cleanest way to do it, is to build up a new "content" and render a new page in the POST processing section. For example:
from django.shortcuts import render
from .forms import DataForm
# Create your views here.
def data(request):
if request.method == 'POST':
form = DataForm(request.POST)
if form.is_valid():
# ... do you processing as it
return render(request, 'output.html', {'data': whatever})
form = DataForm()
return render(request, 'form.html', {'form': form})
You could also just let it call through to the original render function, but you'll need to include whatever information you want in the context like {"form: form} already is.
U need to create 2 views for using data.(basicly)
One for form, one for your formulas.
in urls.py
path('formpage/', views.formpage, name='form_view'),
path('result/', views.result, name='MY_calculator'),
in views.py
def formpage(request):
return render(request, '-----Template-----',)
def result(request):
x=request.GET.get('xval')
y=request.GET.get('yval')
result=x+y
return render(request, '-----Result Template----', {'result': result})
and form action for template. ("" instead of)
<form action="{% url 'MY_calculator' %}">
You can set initial values to your form and render them to the same template within the same URL.
Here is an example:
def data(request):
if request.method == 'POST':
form = DataForm(request.POST)
if form.is_valid():
strike1 = form.cleaned_data['strike1'] * 2
strike2 = form.cleaned_data['strike2'] * 3
# Set initials
# strike1 & strike2 new values will be rendered as default values
# in your form
form = DataForm(initial={'strike1': strike1, 'strike2': strike2})
return render(request, 'form.html', {'form': form})
form = DataForm()
return render(request, 'form.html', {'form': form})
Also, you must pay attention, in your template HTML you wrote "'post" instead of "post".

How to expose WTForms custom validation function variable to Flask view?

I have a WTForms custom validation function that returns a variable containing a hash:
def dupe_check(self, field):
"""
Check for duplicate images.
"""
def get_hash():
"""
Get hash of image.
"""
f = field.data
img = Image.open(f)
imghash = imagehash.dhash(img)
f.seek(0)
return imghash
imghash = str(get_hash())
hashcheck = str(Sights.query.filter(Sights.image_hash == imghash).first())
if hashcheck == imghash:
raise ValidationError('Duplicate image detected!')
class AddImageForm(FlaskForm):
sights_image = FileField('image', validators=[FileRequired(), FileAllowed(images, 'Images only!'), dupe_check])
What is the best way to expose the imghash variable which lives in forms.py to a Flask view which lives in views.py?
Should imghash go into a WTForms HiddenField? Should I create a session variable? Should I turn the dupe_check function into a Class?
My goal is to write the hash into DB during Flask view, but not have to regenerate the hash since it is already created during validation.
In your dupe_check(self, field) self is the form instance so you can store your calculated hash value in the form and use it later on in your processing.
Here's a self-contained example, note I've created a class validator rather then using a simple method.
from flask import Flask, request, flash, render_template_string, redirect
from flask_wtf import FlaskForm
from wtforms import ValidationError, StringField
app = Flask(__name__)
app.config['SECRET_KEY'] = '0123456789'
def get_hash(data):
# Use data to compute hash
image_hash = 1000000
return image_hash
class HashValidator(object):
def __init__(self, message=None):
if not message:
message = u'Duplicate image detected!'
self.message = message
def __call__(self, form, field):
# Pass field data to hash function
_img_hash = get_hash(field.data)
# Store the hash in the form
form.imghash = _img_hash
# calculate if existing hash exists
# hashcheck = str(Sights.query.filter(Sights.image_hash == _img_hash).first())
hashcheck = 1000000
if hashcheck == _img_hash:
raise ValidationError(self.message)
class AddImageForm(FlaskForm):
# sights_image = FileField('image', validators=[HashValidator()])
# Use an Stringfield to make the sample easier
sights_image = StringField('image', validators=[HashValidator()])
_template = '''
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<form action="/" method="POST">
{{form.csrf_token}}
{{form.sights_image}}
{% if form.sights_image.errors %}
<ul>{% for error in form.sights_image.errors %}<li>{{ error }}</li>{% endfor %}</ul>
{% endif %}
<button type='submit'>Submit</button>
</form>
'''
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
_image_form = AddImageForm()
if _image_form.validate_on_submit():
flash("Validated: get_hash returned {}".format(_image_form.imghash))
return redirect('/')
else:
# didn't validate template will show error
flash("Error. get_hash returned {}".format(_image_form.imghash))
return render_template_string(_template, form=_image_form)
else:
_image_form = AddImageForm()
return render_template_string(_template, form=_image_form)
if __name__ == '__main__':
app.run(port=9876, debug=True)
In this case, you can turn the function get_hash() into a method of the form class:
class AddImageForm(FlaskForm):
sights_image = FileField('image', validators=[...])
def get_hash(self):
f = self.sights_image.data
img = Image.open(f)
imghash = imagehash.dhash(img)
f.seek(0)
return imghash
Then in view function, you can simply call it with the form, such as:
from flask import request
#app.route('/', methods=['GET', 'POST'])
def test():
form = AddImageForm()
if request.method == 'POST':
hash = form.get_hash()

Django: edit the choices set of forms.Select widget with dB data linked to current user in templates

Dear Python community,
Could you please share some insights with a newbie like myself regarding the following topic:
I would like to dynamically modify the inputs into form field input, specifically
forms.ChoiceField(choices=((text, name), widget=forms.Select())
As I was not able to access requestfrom class in forms.py, I'd like to try editing the choices from Django template engine. Is it possible to edit the choices, taking the parameters from views.py method using jinja?
The question is conceptual, a few lines of code as an example would be enough, I'll pick it up.
The tricky part is - the data should be dependent on logged-in User's created model instances.
If there's no actual way to do it via python, but js only - please let me know so I don't dry to do the impossible.
Thank you!
Code sample for reference:
forms.py
class InformForm(forms.Form):
flight_number = forms.CharField(5, widget=forms.TextInput())
date = forms.DateField(widget=forms.DateInput(attrs={'class': 'datepicker'}))
template = forms.ChoiceField(choices=tuple([(template.text, template.name) for template in Template.objects.all()]),
widget=forms.Select(attrs={'id': 'select_box',
'onchange': 'javascript:changer();'}))
text = forms.CharField(widget=forms.Textarea(attrs={'id': 'txt_box', 'class': 'latin',
'maxlength': "160", 'onchange': 'javascript:validateTextArea();'}))
template
<form class="form-signin form-container" method="post" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="form-element-wrapper">
<div class="error-form-element">
<span class="error-span">{{field.errors}}</span>
</div>
<div class="form-label">{{field.label_tag}}</div>
<div class="form-data">{{field}}</div>
</div>
{% endfor %}
<button id="cr_inf" type="submit" class="btn btn-lg btn-primary btn-block stl-color"><span id="loader" class=""></span>Create inform</button>
</form>
views.py
class InformFill(View):
form_class = InformForm
temlate_name = 'distrib_db/inform_fill.html'
def get(self, request):
if request.user.is_authenticated():
form = self.form_class(None)
return render(request, self.temlate_name, context={'form': form})
else:
return redirect('distrib_db:login')
def post(self, request):
if request.user.is_authenticated():
form = self.form_class(user=request.user, data=request.POST)
if form.is_valid():
inform = Inform(flt_numbr=form.cleaned_data['flight_number'], date=form.cleaned_data['date'],
template=form.cleaned_data['text'], request=request)
inform.save()
date = form.cleaned_data['date']
flt_numbr = form.cleaned_data['flight_number']
try:
emails, contacts = get_mail_cnt(date, flt_numbr)
# inform = get_object_or_404(Inform, pk=request['pk'])
paxdata = PaxData(inform=inform, emails=' '.join(emails), contacts=' '.join(contacts))
paxdata.save()
return redirect('/inform/{0}/'.format(inform.pk))
# 'distrib_db:detail', context={'pk': inform.id}
except Exception as e:
return render(request, 'distrib_db/sample.html',
context={'date': date, 'flight_number': flt_numbr, 'error': e})
# return render(request, 'distrib_db/sample.html', context={'date': date, 'flt_numbr': flt_numbr})
return render(request, self.temlate_name, context={'form': form})
else:
return redirect('distrib_db:login')
QuerySet issue:
>>> usr = User.objects.filter(username='aleks')
sample = tuple([(template.text, template.name) for template in usr.template_set.all()])
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'QuerySet' object has no attribute 'template_set'
In InformForm class override __init__
def __init__(self, user, *args, **kwargs):
super(InformForm, self).__init__(*args, **kwargs)
self.fields['template'] = forms.ChoiceField(choices="make choice based on user")

Flask render_template clears all other forms

I have multiple forms with one submit button for each of them, all in one template. render_template returns only empty StringFields. How can I render without touching my other StringFields?
#app.route('/', methods=['GET','POST'])
def index():
msform = msForm(request.form)
synform = synForm(request.form)
if request.method == 'POST' and msform.validate() and msform.ms_submit.data:
processed_text = model.most_similar(positive=[msform.ms_3.data, msform.ms_2.data], negative=[msform.ms_1.data])
msform.ms_submit.label.text = processed_text[0][0]
return render_template('my-form.html', msform=msform, synform=synform)
elif request.method == 'POST' and synform.validate() and synform.syn_submit:
processed_text = model.most_similar([synform.syn_1.data])
return render_template('my-form.html', msform=msform, synform=synform)
return render_template('my-form.html', msform=msform, synform=synform)
class msForm(Form):
ms_1 = StringField(label='Eingabe_1', default = 'king', validators=[validators.DataRequired(message='This Field is required')])
ms_2 = StringField(label='Eingabe_2', default = 'man', validators=[validators.DataRequired(message='This Field is required')])
ms_3 = StringField(label='Eingabe_3', default = 'queen', validators=[validators.DataRequired(message='This Field is required')])
ms_submit = InlineSubmitField(label = '?')
class synForm(Form):
syn_1 = StringField(label='Eingabe', default = 'king', validators=[validators.DataRequired()])
syn_submit = InlineSubmitField('?')
I am assuming you have two separate <form>s on your HTML page, like this for brevity:
<form>{{ msform }}</form>
<form>{{ synform }}</form>
I believe you are saying this: upon completing and submitting one form, the values are lost from the other. Is that correct?
If so, that's the expected behavior, not from Flask, but from your browser. When you submit an HTML <form>, only the data from that form tag is sent to the server. Therefor, any data in other <form> tags is lost.
To submit all forms, render all the forms in the same <form> tag. Give each form a prefix to ensure the input names don't collide.
msform = msForm(request.form, prefix='ms')
<form method="post">
{{ msform }}
{{ synform }}
</form>

New to DJango need to fill form with initial data in get_data

Here is my html:
{% block my_dashboard_main %}
<form action="status/" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock %}
My urls.py:
urlpatterns = patterns('',
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'status/$', views.status),
url(r'thanks/$', views.thanks),
)
Here is my views.py:
STATUS_CHOICES = (
("GOOD", "Good"),
("BAD", "Bad"),
("COMPROMISED", "Compromised")
)
def thanks(request):
return render(request, "my_dashboard/ssa_panel/sent.html')
class SsaForm(forms.Form):
status = forms.ChoiceField(choices = STATUS_CHOICES, label="Status:")
def status(request):
print("STATUS CALLED method=",request.method)
if request.method == 'POST': # If the form has been submitted...
form = SsaForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
print("redirect to THANKS!")
return HttpResponseRedirect('thanks/') # Redirect after POST
else:
print("Requesting form\n")
form = SsaForm(initial = {"status", "Good"}) # An unbound form
return render(request, 'my_dashboard/ssa_panel/index.html', {
'form': form,
})
class IndexView(views.APIView):
# A very simple class-based view...
template_name = 'my_dashboard/ssa_panel/index.html'
def get_data(self, request, context, *args, **kwargs):
print("GET_DATA Called", context)
# Add data to the context here...
return context
The first time my page renders the I want the status to show up. It doesn't. Just the Submit button. After I submit once the "Status: [Good] <- combo box" is there. I want to go get the data for the for status in get_data and set it but I don't know how. do I set context['status']="Good" or something like that?
I'm obviously new to DJango and REST stuff.
You are trying to construct your initial value dictionary incorrectly using a comma (,) instead of a colon and also using the wrong choice key. Instead of
form = SsaForm(initial = {"status", "Good"})
try
form = SsaForm(initial = {"status": "GOOD"})

Categories

Resources