Failed to pass variable to macro arguments when using Bootstrap-Flask - flask

I was first using Flask bootstrap and it worked
{% import "bootstrap/wtf.html" as wtf %}
<form method="POST" action="{{url_for('edit_ad', id=ad.id)}}" enctype="multipart/form-data">
{{ form.hidden_tag() }}
{{ wtf.form_field(form.title) }}
{{ wtf.form_field(form.body) }}
{{ wtf.form_field(form.img_url) }}
{{ wtf.form_field(form.price) }}
<div>
<p>
<button class="" type="submit" value="Edit">Edit</button>
</p>
</div>
</form>
But I wanted to change it to Bootstrap Flask and use render_form. I'm having trouble with url_for
{% from 'bootstrap/form.html' import render_form %}
{{ render_form(form, action="{{url_for('edit_ad', id=ad.id)}}", method="POST", button_map={'edit_button': 'primary'}) }}
Here is my view:
#app.route('/edit_ad/<string:id>', methods=['GET', 'POST'])
#login_required
def edit_ad(id):
# more code
return render_template('edit_ad.html', form=form, ad=ad)
How should I pass id?
Thanks

You can pass the variable like this,
{% from 'bootstrap/form.html' import render_form %}
{{ render_form(form, action="/edit_ad/{{ ad.id }}", method="POST", button_map={'edit_button': 'primary'}) }}

You can't and don't need to use Jinja delimiters inside other delimiters (i.e. nest the curly brackets {{ {{...}} }}).
Just pass the url_for call to the action keyword directly. No curly brackets, no quotes:
{{ render_form(form, action=url_for('edit_ad', id=ad.id), method="POST", button_map={'edit_button': 'primary'}) }}

Related

How to save form instance created using sessionwizard class in django?

I want to save the created objects, but I can't get it to work. I can successfully fill out the form and submit it, but no data is saved in the database. Any suggestions as to what I'm doing incorrectly? I tried using form_data[0].save but it threw 'dict' object has no attribute 'save'
views
from django.shortcuts import render
from formtools.wizard.views import SessionWizardView
from django.core.files.storage import FileSystemStorage
from .forms import (
WithdrawForm1,
WithdrawForm2,
)
from django.conf import settings
import os
class WithdrawWizard(SessionWizardView):
template_name = 'withdraw.html'
form_list = [WithdrawForm1, WithdrawForm2]
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'media_root'))
def done(self, form_list, **kwargs):
form_data = [form.cleaned_data for form in form_list]
return render(self.request, 'done.html', {'data': form_data})
Template
{% load i18n %}
{% block head %}
{{ wizard.form.media }}
{% endblock %}
{% block content %}
<div class="row d-flex justify-content-center" style="height: 50vh;">
<div class="col-md-6">
<p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
<form action="" method="post" enctype=multipart/form-data>{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
</table>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">{% trans "first step" %}</button>
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% trans "prev step" %}</button>
{% endif %}
<input type="submit" value="{% trans 'submit' %}"/>
</form>
</div>
</div>
{% endblock %}
form_data is a list of dictionaries of form.cleaned_data. You need to save the form instances, not the cleaned data dictionaries.
Try this:
for form in form_list:
form.save()

FlaskForm validate_on_submit function always returns False

Here is my code:
# My Form
from flask_wtf import FlaskForm
from wtforms.validators import Length, InputRequired
from wtforms import StringField, SubmitField
class AddGradeForm(FlaskForm):
name = StringField('Grade', validators=[InputRequired(), Length(min=4, max=20)])
submit = SubmitField('Add')
# My route
#admin.route('/AddGrade', methods=['GET', 'POST']) # admin is my blueprint's name
def add_grade():
form = AddGradeForm()
if form.is_submitted():
return str(form.validate_on_submit()) # always returns False
...
flash('New grade has been added', 'success')
return redirect(url_for('grades'))
return render_template('add_grade.html', form=form)
# Template
<form method="POST" action="">
{{ form.hidden_tag() }}
<!-- Second row -->
<div class="row">
<div class="col-12">
<div class="form-group">
{{ form.name.label(class="form-control-label") }}
{% if form.name.errors %}
{{ form.name(class="form-control is-invalid") }}
<div class="invalid-feedback">
{% for error in form.name.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.name(class="form-control") }}
{% endif %}
</div>
</div>
</div>
<div class="input-group mb-35 " style="vertical-align: middle;">
{{ form.submit(class="btn btn-outline-primary btn-block") }}
</div>
</form>
On my local-server validate_on_submit() is working exactly the way I was expecting but In production-server validation_on_submit function always returns False(redirects to the form again with no validation error) I don't know why!?.
Please help me find out this error I have tried many attempts but I couln't found any solution .
I have Seen The same Error , This Worked For me :
Form
Use Form instead of FlaskForm
Route
Use parentheses () instead of brackets [] , when providing allowed methods
To check if form is submitted and validated use :
request.method == 'P0ST' and form.validate() instead of form.validate_on_submit() because we no longer using FlaskForm
Template
Remove form.hidden_tag and use form.csrf.token instead
Here is the code with the changes above :
# My Form
from wtforms import Form,StringField, SubmitField, validators
class AddGradeForm(Form): # replaced FlaskForm with Form
name = StringField('Grade',[validators.InputRequired(), validators.Length(min=4, max=20)])
submit = SubmitField('Add')
# My route
#admin.route('/AddGrade', methods=('GET', 'POST')) # change from [] to ()
def add_grade():
form = AddGradeForm()
if request.method == 'POST' and form.validate():
return str(form.validate_on_submit()) # now returns True
...
flash('New grade has been added', 'success')
return redirect(url_for('grades'))
return render_template('add_grade.html', form=form)
# Template
<form method="POST" action="">
{{ form.csrf_token }} <!-- from form.hidden_tag() to form.csrf_token -->
<div class="row">
<div class="col-12">
<div class="form-group">
{{ form.name.label(class="form-control-label") }}
{% if form.name.errors %}
{{ form.name(class="form-control is-invalid") }}
<div class="invalid-feedback">
{% for error in form.name.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.name(class="form-control") }}
{% endif %}
</div>
</div>
</div>
<div class="input-group mb-35 " style="vertical-align: middle;">
{{ form.submit(class="btn btn-outline-primary btn-block") }}
</div>
</form>

How to get flask form field value into Jijnja2 URL in HTML

I am new to Flask. I want to pass flask_mongoengine.wtf field value to jijnja URL into HTML.
There is my code. I want to pass {{ form.from_time_filter }} form field value to the formtime='{{form.from_time_filter}}' How i can do it?
{% macro render_pagination(page, endpoint ) %}
<div class=pagination>
{%- for page in posts.iter_pages() %}
{% if page %}
{% if page != posts.page %}
{{ page }}
{% else %}
<strong>{{ page }}</strong>
{% endif %}
{% else %}
<span class=ellipsis>…</span>
{% endif %}
{%- endfor %}
</div>
{% endmacro %}
{{ render_pagination(posts, 'test_list') }}
<form method="POST">
{{ form.from_time_filter.label }} {{ form.from_time_filter }}
{{ form.to_time_filter.label }} {{ form.to_time_filter }}
{{ form.incon_filter.label }} {{ form.incon_filter }}
{{ form.wordtype_filter.label }} {{ form.wordtype_filter }}
<input type="submit" value="Filter">
</form>
I am not sure if this will help but I'll try to give an answer.
I am using a form to handle searches on my flask based website, the code for the form looks as follows:
<form action="{{ url_for('index') }}" class="form-inline" id="search-form" method="get" role="search">
<div class="form-group">
<input class="form-control" name="q" placeholder="Search" type="text" value="{% if search %}{{ search }}{% endif %}">
</div>
</form>
The python code that then handles the input looks as follows:
def get_entries(offset=0, per_page=5, table=""): # Used in Index
search_query = request.args.get('q')
page = request.args.get(get_page_parameter(), type=int, default=1)
if search_query:
entries = table.search(search_query).where(table.published == True).order_by(table.timestamp.desc()).paginate(page, per_page)
print(entries)
else:
entries = table.select().where(table.published == True).order_by(table.timestamp.desc()).paginate(page, per_page)
print(entries)
return entries[offset: offset + per_page]
When my search form is used it redirects to the /index page, the /index page then checks if there is a value with the name 'q' (this name is specified in the input tag using name="q") if there is, the URL will look like this:
when I search for "iframe".
And just in case you need it this is what my route for /index looks like:
#app.route('/') # For browsing and searching up blog entries
#app.route('/index')
def index():
table = Entry
pagination_entry = get_entries(table=table)
pagination = get_pagination_entries(table=table)
return render_template('index.html', pagination=pagination, pagination_entry=pagination_entry)

csrf_token missing and how to get details for specific id entered by user

I am trying to display the result for the specific id entered by the user. I 'm not sure about the view.py file also. What changes should I need to make to get the desired result?
view.py file
def show(request):
if request.method == 'POST':
Num = allData.objects.only('emp_no')
data = request.POST.get('emp_no')
if data.is_valid():
for n in Num:
if n == data:
empid = data
emp = {'emp_no':data}
return render(request,'system/show.html',{'emp_no':data})
return(data.errors)
return HttpResponse("<h2>OOPS!! NO RECORD FOUND</h2>")
show.html
{% extends 'system/base.html' %}
{% load staticfiles %}
{% block body_block %}
<div class="container" "jumbotron">
<h2>Details</h2>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<label class="lb" for="emp_no" >Employee No.</label>
<input type="number" name="emp_no">
<button type="submit" class="btn btn-success">SUBMIT</button>
</form>
{% for allData in emp_no %}
{{ allData.GENDER_CHOICE}}
{{ allData.first_name }}
{{ allData.last_name }}
{{ allData.birth_day }}
{{ allData.hire_date }}
{{ allData.dept_no }}
{{ allData.dept_name }}
{{ allData.salary }}
{{ allData.from_date }}
{{ allData.to_date }}
{{ allData.titles }}
{% endfor %}
</div>
{% endblock %}
Welcome to SO!
From what you have there it looks like it would be worthwhile taking a look at the Django forms documentation.
The main thing that jumps out to me is that you are trying to reuse the same template both for the form and for the data display after the form is submitted. It might be easier to separate them.
If you do want to keep one template, then you don't want to show the form if there is data and vice versa - if there is no data, then you want to show the form. It would look something like this:
{% if emp_no %}
{% for allData in emp_no %}
{{ allData.GENDER_CHOICE}}
{{ allData.first_name }}
{{ allData.last_name }}
{{ allData.birth_day }}
{{ allData.hire_date }}
{{ allData.dept_no }}
{{ allData.dept_name }}
{{ allData.salary }}
{{ allData.from_date }}
{{ allData.to_date }}
{{ allData.titles }}
{% endfor %}
{% else %}
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<label class="lb" for="emp_no" >Employee No.</label>
<input type="number" name="emp_no">
<button type="submit" class="btn btn-success">SUBMIT</button>
</form>
{% endif %}
The title of your question also mentions csrf_token problems. You didn't give any details, but my guess is things are getting confused because you are loading the form even when you don't need it.
I will point out a few things before trying to post a solution.
Your code is not that readable because of the way you name variables. Try naming your variables in a way that tries to reflect what your code is doing.
I will suggest that you finish django tutorial if you haven't done that already. It will help you grab the core concepts of Django.
From what I understood you are trying to get an employee with a given employee number.
This is how I would do it.
In the views.py I would user render instead of HttpResponse.
def show(request):
context = {}
if request.method == 'POST':
# extract the emp no from the request
emp_no = request.POST.get('emp_no')
# get or create the employee with this emp_no
current_employee = Employee.objects.get_or_create(emp_no)
context['current_employee'] = current_employee
return render(request, 'show.html', context)
In the templates, you don't need a forloop. You are retrieving only one employee.
{% extends 'system/base.html' %}
{% load staticfiles %}
{% block body_block %}
<div class="container" "jumbotron">
<h2>Details</h2>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<label class="lb" for="emp_no" >Employee No.</label>
<input type="number" name="emp_no">
<button type="submit" class="btn btn-success">SUBMIT</button>
</form>
<div>
{{ current_employee.GENDER_CHOICE}}
{{ current_employee.first_name }}
{{ current_employee.last_name }}
{{ current_employee.birth_day }}
{{ current_employee.hire_date }}
{{ current_employee.dept_no }}
{{ current_employee.dept_name }}
{{ current_employee.salary }}
{{ current_employee.from_date }}
{{ current_employee.to_date }}
{{ current_employee.titles }}
</div>
</div>
{% endblock %}

django form wizard, does not go to second step

I am trying to perform the following using Django :
Wizard containing two formds
First form is a simple form containing some Ajax to compute automatically some fields
Second form is a user registration
The problem is the following :
The first form is displayed correctly and the Ajax within the page is working fine
The second form is never displayed after pushing on the button "submit"
The code is as follows :
urls.py
from forms import InsertPropertyForm1, InsertPropertyForm2
from views import InsertPropertyWizard
urlpatterns = patterns('',
url(r'^addProperty/$', InsertPropertyWizard.as_view([InsertPropertyForm1, InsertPropertyForm2]), name='addProperty'),
)
views.py
FORMS = [("property", InsertPropertyForm1),
("test", InsertPropertyForm2)
]
TEMPLATES = {'0': 'realEstate/addProperty.html',
'1' : 'realEstate/test.html',
}
class InsertPropertyWizard(SessionWizardView):
def get_template_names(self):
print ("next step !!!!! " + str(self.steps.current))
return [TEMPLATES[self.steps.current]]
def done(self, form_list, **kwargs):
print "Wizard done"
#do_something_with_the_form_data(form_list)
return HttpResponseRedirect('http://TO_FILL_IN')
realEstate/addProperty.html
{% extends 'realEstate/base.html' %}
{% load socialaccount %}
{% load i18n %}
{% block head %}
{{ wizard.form.media }}
{% endblock %}
{% block content %}
<h1> Insert an ad </h1>
<p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
<form class="form-horizontal" role="form" action="" method="post">{% csrf_token %}
<table>
{{ wizard.management_form }}
</table>
{{ form.non_field_errors }}
<fieldset>
<legend>Localisation</legend>
<div class="form-group">
{{ form.country.errors }}
<label class="col-lg-1" for="id_country">{{form.country.label}}</label>
<div class="col-lg-1">
{{ form.country }}
</div>
</div>
</fieldset>
</fieldset>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">{% trans "first step" %}
</button>
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{% trans "prev step" %}
</button>
{% endif %}
<input type="submit" value="{% trans "submit" %}"/>
</form>
{% endblock %}
Just thought it could help someone.
Problem was that one of the field was not defined in the form and I forgot to include it in the template.
The behaviour of the wizard was correct. It got stuc on the first page but did not display the error on the screen as the field was not displayed.
Got me crazy but it was my fault.
Cheers