I created a function called getData in effort to cut down 4 nested "if" statement inside my userInfo method. The result was devastating. I'm being humiliated by the fact that the page didn't proceed to my successful.html template. If I move everything inside getData method back to the userInfo function, everything return to normal. Is there a trick to making it work so I can restore my shame?
views.py
def userInfo (request):
# Set maximum to avoid default of 1000 forms.
UserFormSet = formset_factory (UserForm, formset = BaseUserFormSet, extra = 2, max_num = 5)
if request.method == 'POST':
formset = UserFormSet (request.POST)
if formset.is_valid ():
location = request.POST ['site']
data = formset.cleaned_data
getData (request, data, location) # ====> Created a function to cut down nested if statement
else:
formset = UserFormSet ()
...
def getData (request, data, location):
validUser = []
for form in data:
username = form.get ('user_name')
userID = form.get ('user_ID')
if username and userID:
n = register (username, userID, location)
if n.checkDataBase ():
validUser.append (username)
if validUser:
context = {'validUser': validUser}
return render (request, 'userform/success.html', context)
HTML
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Successfully Added</title>
</head>
<body>
<h1>User Information:</h1>
<ul>
{% for user in validUser %}
<li>{{ user }}</li>
{% endfor %}
</ul>
Add more users
</body>
</html>
Does it work if you change your getData() to:
if validUser:
context = {'validUser': validUser}
return request, 'userform/success.html', context
and your userInfo() to:
if formset.is_valid ():
location = request.POST ['site']
data = formset.cleaned_data
request, template, context = getData (request, data, location) # ====> Created a function to cut down nested if statement
return render (request, template, context)
try
return getData (request, data, location)
(add return statement).
Related
I have an Altair chart which I want to render in a Django Template. The chart is converted into a json object in the views. Here is the code
views.py
def home(request):
if request.method=='POST':
year = request.POST['year']
df, cus_dict = generate_df(year)
bar_obj = barGraphAltair(year, df)
bar_obj = json.loads(bar_obj.to_json())
print(bar_obj)
context = {'bar': bar_obj}
return render(request, 'index.html', context=context)
return render(request, 'index.html')
template
<div id='altair-viz'>
{% if bar %}
{{ bar|safe }}
{% endif %}
</div>
This just prints the json in the template. I know I have to use Vega to render the graph but I am not sure how to do that in jinja syntax
A temp solution
One way I got this to work, is by creating a different view and calling that view in the template as follows
views.py
def renderAltair(request):
df, cus_dict = generate_df('2017')
bar_obj = barGraphAltair('2017', df)
bar_obj = json.loads(bar_obj.to_json())
return JsonResponse(bar_obj)
template
<script>
vegaEmbed('#altair-viz', "{% url 'altair' %}")
</script>
This works, but as you can see from the original code, I get the year by submitting a form and passing that to the function for generating the graph. So I need the graph to be created in the home view
You can try this way.
def home(request):
if request.method=='POST':
year = request.POST['year']
context = {'year': year}
return render(request, 'index.html', context=context)
return render(request, 'index.html', {})
Not passing data in home view, will get that using json view.
template
<div id='altair-viz' data-url="{% url 'altair' %}?year={{year}}"></div>
<script>
var data_url = $('#altair-viz').attr('data-url');
vegaEmbed('#altair-viz', data_url)
</script>
and get data function
def renderAltair(request):
year = request.GET.get('year')
df, cus_dict = generate_df(year)
bar_obj = barGraphAltair(year, df)
bar_obj = json.loads(bar_obj.to_json())
return JsonResponse(bar_obj)
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".
My django form is invalid and so the .is_valid method never returns true. As a result, I am getting an "Expected HttpResponse but received None" type of error because my code never executes what is within the if-condition. I am wondering how to make my form valid. I am new to django so I am probably missing something obvious. Here is my code:
views.py
template_name1 = 'multiplication/detail.html'
template_name2 = 'multiplication/multiplied.html'
class myForm(forms.Form):
quantity1 = forms.IntegerField(required=False)
quantity2 = forms.IntegerField(required=False)
form = myForm()
def get(request):
return render(request,template_name1,{'form': form} )
def multiply_two_integers(x,y):
return x*y
def post(request):
if (form.is_valid()):
x = request.POST.get('quantity1')
y = request.POST.get('quantity2')
product = multiply_two_integers(x, y)
return render(request, template_name2, {'form': form, 'product':
product })
template_name1
<h1>Multiplication Function</h1>
<form action = "{% url 'multiplication:post' %}" method = "post">
{{ form.as_p }}
{% csrf_token %}
<input type = "submit" value ="Multiply">
<!--<button type="submit"> Multiply </button>-->
<h1>{{product}}</h1>
</form>
template_name2
<h1>{{product}}</h1>
urls/multiplication
from django.urls import path
from multiplication import views
app_name = 'multiplication'
urlpatterns = [
# /multiplication/
path('', views.get, name = 'get'),
path('multiplied', views.post, name='post')
]
This code is very strange. You seem to have a set of functional views, but are trying to randomly use some concepts from class-based views.
The reason why your form is not valid is because you never pass any data to it; an unbound form cannot be valid. You should not be instantiating the form outside of a view; you need to do it in the view, and when the request is a POST you should pass the POST data to it.
In function-based views you should not define separate functions for get and post. Combine them, as sown in the Django docs.
There is another point that you have missed about the error message; your reaction to it telling you that you have not returned a response if the form is invalid is to ask "why isn't it valid", but you should also do what it says and return a response in this case; the form will sometimes be actually invalid, and you should deal with this case.
Finally, to get the data from the form you should use form.cleaned_data, not request.POST.
def multiply_two_integers(x,y):
return x*y
def my_view(request):
if request.method == 'POST':
form = MyForm(request.POST)
if (form.is_valid()):
x = form.cleaned_data['quantity1']
y = form.cleaned_data['quantity2']
product = multiply_two_integers(x, y)
return render(request, template_name2, {'product': product })
else:
form = MyForm()
return render(request,template_name1,{'form': form} )
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()
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"})