How to get a new object after form submittig? - forms

I'm trying flask nad there is a simple task to submit a form.
Page is showing a one picture and a form, if form was submitted correctly, picture should be changed, if not - be the same.
I can't understand the mechanism of how to show only one object on a page and get another after form submitting.
Tried to use iterator over the list of images is folder "static", but my implementation was not working correctly.
Please provide me a feedback how to do it in rigth way?
Now I have the simple view:
#app.route("/", methods=["GET", "POST"])
def start_view():
picture = None
form = InputForm(csrf_enabled=False)
if form.validate_on_submit():
picture = form.picture.data
form.picture.data = ""
return render_template('04-2.html', form=form, picture=picture)
class InputForm(Form):
picture = StringField('What is on a picture?', validators[DataRequired()])
submit = SubmitField('Submit')
And a simple template:
<body>
<form method="POST">
{{ form.picture.label }} {{ form.picture }}
{{ form.submit() }}
</form>
{% if form.errors %}
<span style="color: red">{{ form.error }}</span>
{% endif %}
</body>
Thank you!

Your form doesnt contain any pictures. It has a StringField and a SubmitField. If you want to see any image you need to have an <img> tag in your HTML pointing to the image location in your server
your view should look like:
from Flask import session
# in order to use sessions you have to use a secret key for your app
app.secret_key = 'some secret key'
#app.route("/", methods=["GET", "POST"])
def start_view():
img_list = ['filename1', 'filename2', 'filename3']
# if this is not the first form submission
if session.has_key('current'):
# if we reach the end of the list show the first image again
if int(session['current']) == len(img_list) - 1:
session['current'] = 0
# move to next image
else:
session['current'] = int(session['current']) + 1
else:
session['current'] = 0
picture = 'first_image_filename' # this should be the img on load
form = InputForm(csrf_enabled=False)
if form.validate_on_submit():
picture = img_list[int(session['current'])] # the filename of the next image
form.picture.data = ""
return render_template('04-2.html', form=form, picture=picture)
so template should look like:
<body>
<form method="POST">
{{ form.picture.label }} {{ form.picture }}
<img src="{{url_for('static', filename='img/' + picture)}}"
{{ form.submit() }}
</form>
{% if form.errors %}
<span style="color: red">{{ form.error }}</span>
{% endif %}
</body>

Related

NoReverseMatch error from Python Tutorial

Django Tutorial part 4 I am getting a NoReverseMatch at /polls/1/
NoReverseMatch at /polls/1/
Reverse for 'polls.index' not found. 'polls.index' is not a valid view
In template /var/www/html/django_app/polls/templates/polls/detail.html, error at line 5
Reverse for 'polls.index' not found. 'polls.index' is not a valid view function or pattern name...
I have looked up and researched this question on here for a while and am hitting a wall. There are many references to the problem but none that match the problem I am seeing and the state of my current code.
urls.py
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
detail.py
<h1>{{ question.question_text }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'polls.vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }</label><br>
{% endfor %}
<input type="submit" value="Vote">
</form>
views.py
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
return Question.objects.order_by('-pub_date')[:5]
class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
# Redisplay the question voting form.
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
# Always return and HttpResponseRedirect after successfully dealin
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
I can clearly see what it saying the problem is but I can't seem to determine what is causing it.
Any extra eyes would help and be greatly appreciated!

Flask-WTF, unexpected output using macro use of {{ field |safe}} (HTML escaping)

When the input is
When then output is
But when I change the input to
My output changes to
Please some body explain me why this is happening?
What's the role of {{ field |safe}}?
My App.py code -
class ContactForm(FlaskForm):
name = StringField("Name Of Student",validators = [InputRequired(message
= 'Name is missing'),Length(min=5,max=10,message="wrong")])
email = StringField("email",[validators.Email("Please enter your email
address.")])
#app.route('/form', methods = ['GET','POST'])
def index():
form = ContactForm()
if form.validate_on_submit():
return render_template('macro_output.html',form=form)
return render_template('main_form.html',form=form)
macro_form.html
{% macro render_output(field) %}
<p>
{{ field.label}}
{{ field |safe}}
</p>
{% endmacro %}
macro_output.html
{% from "macro_form.html" import render_field,
render_ErrorMessage,render_output %}
<html>
<body>
{{ render_output(form.name)}}
{{ render_output(form.name.data)}}
{{ render_output(form.email)}}
{{ render_output(form.email.data)}}
</body>
</html>
The jijna2 filter safe treats strings as, well, safe and does not do any pre-formatting by automatically escaping any characters that would otherwise be interpreted as code (in this case HTML code).
So if a variable, say, form.name.data = <html> then invoking {{ form.name.data | safe }} will embed an html tag (<html>) into your HTML, and I expect that the HTML browser parser simply ignores it as an error (you can check the rendered page source). But invoking {{ form.name.data }} jinja2 will escape the '<>' characters only 'html' will go into the HTML and therefore be rendered as text.
I would strongly advise against the use of the safe filter for user input, since an arbitrary user could inject code into your page, e.g. <script> ... </script>

Dynamic routing from form data

I am new to flask an am having an issue with creating a dynamic url from form data. Specifically from the value of a SelectField of WTFORMS. My code is as follows
My form looks like this
from flask_wtf import Form
from wtforms import SelectField
from wtforms.fields.html5 import DateField
class SelectEventForm(Form):
sports = SelectField(u'Select Sport')
start_after_date = DateField('Starts After Date')
start_before_date = DateField('Starts Before Date')
My controller has the following code
#app.route('/event', methods=['GET', 'POST'])
def event():
form = SelectEventForm(request.form)
sports = betfair_client.call_rest_api('listEventTypes/', {"filter": {}})
form.sports.choices = []
for sport in sports:
for key in sport:
form.sports.choices.append((key, sport[key]))
return render_template('events.html', form=form)
#app.route('/event/<sports>', methods=['GET', 'POST'])
def event_select(sports):
#print request.form
#print request.form.get('sports')
return render_template('events_two.html')
The form in html is as follows
<form class="form-style-7" action="{{ url_for('event_select', sports=form.sports.sport) }}" method="post">
<ul>
<li name="sport">
{{ form.sports.label}} {{ form.sports }}
</li>
<li>
{{ form.start_after_date.label }} {{ form.start_after_date }}
</li>
<li>
{{ form.start_before_date.label }} {{ form.start_before_date }}
</li>
<li>
<input type="submit" value="Next">
</li>
</ul>
</form>
What I would like to do, is se the value from the SelectField to generate the url. Have been stuck on this for some time. The form itself and the drop down list display everything correctly
Thanks
I'd change your form to post back to it's generating page, and then deal with the logic from there. So change
{{ url_for('event_select', sports=form.sports.sport) }}
to just:
{{ url_for('event') }}
then adjust your event method to be something like:
from flask import redirect
#app.route('/event', methods=['GET', 'POST'])
def event():
form = SelectEventForm(request.form)
if form.validate_on_submit():
chosen_sport = form.sports.data
return redirect(url_for('event_select', sports=chosen_sport))
sports = betfair_client.call_rest_api('listEventTypes/', {"filter": {}})
form.sports.choices = []
for sport in sports:
for key in sport:
form.sports.choices.append((key, sport[key]))
return render_template('events.html', form=form)
All we're doing is grabbing the from if it's submitted, then grabbing the chosen sport from the form element, and then using that to redirect the user to the event_select method with the chosen sport as the argument.

Site-wide form with Symfony2?

I have simple form at all site pages: username, password, [Sign In]
I tried to make with with simple HTML, but I get
The CSRF token is invalid. Please try to resubmit the form
Idea to make form in each action seems bad. What is the good practice of doing site-wide forms?
You have to do it this way:
First, I guess you have some base template file like layout.html.twig and all other pages extend it. Eg:
// Resources/views/layout.html.twig
<doc ... bla blah>
<title>My site</title>
...(js, css)...
<body>
<div id="top">
{% render url("site_wide_form") %}
</div>
{% block content %}
{% endblock content %}
</body>
You need controller that will handle your form:
//Controller/SitewideController.php
/**
* #Route("/some/url/here", name="site_wide_form")
* #Template("yourbudle:folder:site_wide_form.html.twig")
*/
public function someAction()
{
..... your code for form, process submission etc ...
return ["form"=>$form->createView()] ;
}
and template file:
// site_wide_form.html.twig
<form action="{{ path("site_wide_form") }}" method="post">
{{ form_widget(form) }}
</form>
That's it. Read this to understand render tag: http://symfony.com/doc/2.2/book/templating.html#embedding-controllers
The CSRF token is missing. In your template (I assume you use Twig). You can tell Symfony2 to render all the remaining form elements using:
{{ form_rest(form) }}
Or you can render just the CSRF token:
{{ form_row(form._token) }}

symfony2 form twig input value

I am doing a form with symfony2 and twig, form who get infos from BDD.
I want to customize render in function of some informations.
If my data chanson is empty, I want to show input to set it.
If my data is not empty I want to show a paragraphe who shows data, and a link for modify the value and show the input.
I try something like that :
{% if form_widget(session.chanson).attrvalue!='' %}
<p>{{form_widget(session.chanson).attrvalue}} <a>modify</a></p>
{% else %}
<p>{{ form_label(session.chanson,"Chanson : ") }}
{{ form_errors(session.chanson) }}
{{ form_widget(session.chanson) }}</p>
{% endif %}
It's not working. I try with value instead of attrvalue, it's not working neither.
Here is what symfony say : Item "attrvalue" for "<input type="text" id="form_chanson" name="form[chanson]" required="required" value="La Rage" />" does not exist in CDUserBundle:Prof:edit_session.html.twig at line 19
Did someone know the issue ?
Thank you,
You could check if the app.session.chanson variable is empty instead using:
{% if app.session.chanson %}
<p>{{ app.session.chanson }} modify</p>
{% else %}
<p>{{ form_label(app.session.chanson,"Chanson : ") }}
{{ form_errors(app.session.chanson) }}
{{ form_widget(app.session.chanson) }}</p>
{% endif %}
You then need to plug the action you want on the modify link.
Also note that if your object chanson is stored in a session, the right way to access it in your twig template is by using the app.session object.