I am building a Flask app and I use WTF forms.
My main page uses the MainForm which has a select field to choose a 'Tenant', two date fields and one Submit button which redirects to another page. The MainForm looks like:
class MainForm(Form):
tenant = SelectField('Tenant', choices=get_tenants())
start_date = DateField('From',
[validators.DataRequired('Please enter a valid date'),
DateRange(min=datetime(2013, 04, 01).date(), max=datetime.now().date())],
format='%Y-%m-%d', default=datetime.now().date())
end_date = DateField('To',
[validators.DataRequired('Please enter a valid date'),
DateRange(min=datetime(2013, 04, 01).date(), max=datetime.now().date())],
format='%Y-%m-%d', default=datetime.now().date())
room_stats = SubmitField('Room Stats')
In the new page /room-stats there is another form which has only one 'Back' button which redirects to the main page. It looks like:
class StatsForm(Form):
back = SubmitField('Back')
The view functions look like:
#main.route('/', methods=['GET', 'POST'])
#login_required
def index():
form = MainForm()
if form.validate_on_submit():
session['start_date'] = str(form.start_date.data)
session['end_date'] = str(form.end_date.data)
if current_user.is_administrator():
session['username'] = str(form.tenant.data)
else:
session['username'] = str(current_user.username)
if form.room_stats.data:
return redirect(url_for('main.room_stats'))
return render_template('index.html', form=form)
and
#main.route('/room-stats')
#login_required
def room_stats():
form = StatsForm()
[MORE_CODE_HERE]
if form.validate_on_submit():
if form.back.data:
return redirect(url_for(index))
return render_template('stats/room_stats.html', form=form)
Finally, the room_stats.html file has a form like:
<form action="/" method="post">
{{ form.hidden_tag() }}
{{ wtf.quick_form(form) }}
</form>
The app generally works fine but there is a problem about validation. Every time I click the Back button and I go back to my main form the Tenant field is highlighted in red and there a message
Not a valid choice
even though the field has a value and it is not blank.
I assume something is going wrong with the validation, but I cannot figure out what.
It is also strange that the Tenant field has no validators, so why is there this error message?
Related
I made a research here in Stack and my problem is the opposite of the majority, I saw some ways to make it appear, but my problem is that it's appearing when the user hits the "Register" button / Refresh the register page. So it's an annoying thing that appears wherever the user enter/refresh the page because the form is empty.
View.py
#unauthenticated_user
def register(request):
form_u = CreateUser(request.POST)
form_c = CreateClient(request.POST)
if request.method == 'POST':
form_u = CreateUser(request.POST)
form_c = CreateClient(request.POST)
if form_u.is_valid() and form_c.is_valid():
user = form_u.save()
group = Group.objects.get(name='func')
user.groups.add(group)
client = form_c.save(commit=False)
client.user = user
client.save()
return redirect('login')
else:
form_u = CreateUser()
form_c = CreateClient()
context = {'form_u': form_u, 'form_c': form_c}
return render(request, 'register.html', context)
HTML
<form method="POST" action="" id="ativa">
{% csrf_token %}
...
</form>
{{form_u.errors}}
{{form_c.errors}}
<div class="mt-4">
<div class="d-flex justify-content-center links">
Have an account ? Login
</div>
</div>
Print
P.S: The site is in portuguese, but I can share the form link in heroku
Your logic is opposite of what you want:
Initialize the forms with POST data regardless of whether the request is a POST or a GET request, which will result in the errors if there is no POST data.
Then you initialize empty forms when the form data is invalid.
Instead you'll want to pass POST data only if the request is a POST request, and you should initialize empty forms only if the request is not a POST request:
#unauthenticated_user
def register(request):
# If request is POST, validate forms and add objects.
if request.method == 'POST':
form_u = CreateUser(request.POST)
form_c = CreateClient(request.POST)
if form_u.is_valid() and form_c.is_valid():
user = form_u.save()
group = Group.objects.get(name='func')
user.groups.add(group)
client = form_c.save(commit=False)
client.user = user
client.save()
return redirect('login')
# We can remove the else statement here,
# because the function either redirects or resumes
# normal flow and renders the template
# with the form errors.
else:
# Only initialize empty forms when no POST request was made.
form_u = CreateUser()
form_c = CreateClient()
context = {'form_u': form_u, 'form_c': form_c}
return render(request, 'register.html', context)
For the following model and route, how can I get the page to display the existing data in the database field, in an editable box, with a ‘save changes’ button.
# MODEL
class Task(db.Model): #inherits from db>Model
__tablename__ = "Tasks"
id = db.Column(db.Integer, primary_key=True)
# datetime, need to work this out
Title = db.Column(db.String(4096))
Status = db.Column(db.String(4096))
Description = db.Column(db.String(4096))
Priority = db.Column(db.String(4096))
Assigned_To = db.Column(db.String(4096))
# ROUTE:
#app.route("/<int:task_id>/edit")
def _edit(task_id):
task = Task.query.get_or_404(task_id)
return render_template('update.html',task=task)
<—- update.html—->
<form method="POST" action="">
<fieldset>
{{ task.Title }}
{{ task.Description }}
</fieldset>
</form>
I would prefer not to define Forms if possible. I have an SQL Alchemy database configured and working.
The routes added to the end of this question are working fine - adding new tasks (rows) to the database and creating individual pages to view individual tasks (rows):
#app.route("/tasks", methods=['GET', 'POST'])
def new_post():
if request.method == "GET":
return render_template("tasks.html")
data = Task(
Title=request.form["title"],
Description=request.form["description"],
Status=request.form["status"],
Priority=request.form["priority"],
Assigned_To=request.form["assigned"],
)
db.session.add(data)
db.session.commit()
return redirect(url_for('index'))
#app.route("/<int:task_id>")
def qtsk(task_id):
task = Task.query.get_or_404(task_id)
return render_template('indtask.html',task=task)
I’m new to flask, I would greatly appreciate any help.
I was watching a Flask tutorial recently and this is how he deals with this situation:
#app.route("/post/<int:post_id>/update", methods=["GET", "POST"])
#login_required
def update_post(post_id):
post = Post.query.get_or_404(post_id)
if (post.author != current_user):
abort(403)
form = PostForm()
if form.validate_on_submit():
post.title = form.title.data
post.content = form.content.data
db.session.commit()
flash('Your post has been updated!', 'success')
return redirect(url_for('post', post_id=post_id))
elif request.method == 'GET':
form.title.data = post.title
form.content.data = post.content
return render_template('create_post.html', title="Update Post",
form=form, legend='Update Post')
What it does is it checks if the user is submitting the updated title or content using POST method. If the user is then just update the field and then commit it. Otherwise return the template with the title and content field in the form being filled with the current fields in the Post object.
Here's link for that tutorial (this part starts at 25:35): https://youtu.be/u0oDDZrDz9U
Hope it helps.
I was asking a simillar question.
I think you can update by using form. It doesnt have to be presented as a form to the user. You can write an html template that would not lokk like a form,but youneed a form to fetch data. Maybe you can use javascript and than you can skip the usage of form. I am on to remember javascrip an use it for my app.
I keep running into this strange issue that I can't seem to figure out a solution for. I cannot copy and show all of my code in it's entirety here, but I will try to outline the general structure of my flask app to present my issue.
(Let's ignore all of the content in the /static folder and my helper modules)
I have 3 main views, let's call them viewA, viewB, and index:
viewA.html
viewB.html
index.html
viewA and viewB both display two forms, but with different content (i.e. viewA displays form1 & form2, and viewB also displays form1 & form2).
A simplified version of my script code is as follows:
#imports
from flask import Flask, render_template, session, redirect, url_for, request
from flask_wtf import FlaskForm
#etc. etc.
app = Flask(__name__)
app.config['SECRET_KEY'] = 'blah blah blah'
manager = Manager(app)
bootstrap = Bootstrap(app)
moment = Moment(app)
class FormOne(FlaskForm):
sample_field = StringField('Sample Field:')
class FormTwo(FlaskForm):
other_field = StringField('Other Field:', validators=[Required()])
submit = SubmitField('Submit')
class UploadToA(FlaskForm):
content= StringField('Content to send to view A:', validators=[Required()])
submit = SubmitField('Submit')
class UploadToB(FlaskForm):
content= StringField('Content to send to view A:', validators=[Required()])
submit = SubmitField('Submit')
#app.route('/ViewA', methods=['GET', 'POST'])
def view_a():
"""
A lot of data manipulation
"""
form1 = FormOne()
form2 = FormTwo()
if request.method == 'GET':
"""
populate forms with content
"""
if request.method == 'POST':
if form2.validate_on_submit();
"""
clear session variables
"""
return redirect(url_for('index'), code=302)
return render_template('viewA.html', form1=form1, form2=form2)
#app.route('/ViewB', methods=['GET', 'POST'])
def view_b():
"""
A lot of data manipulation
"""
form1 = FormOne()
form2 = FormTwo()
if request.method == 'GET':
"""
populate forms with content
"""
if request.method == 'POST':
if form2.validate_on_submit();
"""
clear session variables
"""
return redirect(url_for('index'), code=302)
return render_template('viewB.html', form1=form1, form2=form2)
#app.route('/', methods=['GET', 'POST'])
def index():
"""
Some data manipulation
"""
formA = UploadToA()
formB = UploadToB()
if formA.validate_on_submit()':
"""
pull content from form A
create some session variables
"""
return redirect(url_for('view_a'))
if formB.validate_on_submit()':
"""
pull content from form B
create some session variables
"""
return redirect(url_for('view_b'))
return render_template('index.html', formA=formA, formB=formB)
if __name__ == '__main__':
manager.run()
Now the issue at hand I am having here is that for some strange reason when I'm in 'viewA.html' and I submit my form, I SHOULD be redirected back to 'index.html' but for some strange reason it redirects me to 'viewB.html'. Furthermore, the opposite also holds true: when i'm in 'viewB.html' and I submit my form, I SHOULD also be redirected back to 'index.html' but it redirects me to 'viewA.html'. Yet, if I am in either viewA or viewB, I have no issues of going back to the index view if I manually enter the url into my browser.
Any ideas as to why I might be running into this issue?
Thanks in advance :)
I have finally figured out the source of my problem. It turns out that in my 'viewA.html' template file, I had the following in my < form > tag:
<form class="form form-horizontal" method="post" role="form" action="{{url_for('index')}}">
And the problem all lies in that last part:
action="{{url_for('index')}}"
As a result, everytime I would submit form2 in viewA.html it would create a post request for my index page rather than a post request for the viewA.html page (which caused a redirect to the wrong view). Thus, by simply removing the action attribute (action="{{url_for('index')}}"), I was able to solve my problem!
Since the full code isn't here, I can't confirm this for sure, but what I think is happening is this:
You open form A
You submit form A
It sends a redirect to /index
It sends a redirect to /FormB
if formB.validate_on_submit():
return redirect(url_for('view_b'))
This is probably sending a redirect to View B. Try changing that last line to something like return something_else and seeing if it sends that after submitting form A.
I want users click on other users to visit their profile without editing their posts.
Here is my Profil view:
def Profil(request, username):
if not request.user.is_authenticated():
return render(request, 'blog/visitor.html')
else:
u = User.objects.get(username=username)
user = request.user
posts = Post.objects.filter(user=request.user)
context = {'user': user, 'user_url':u,'posts': posts}
return render(request, 'blog/profil.html', context)
And here my Index view
def IndexView(request):
if request.user.is_authenticated():
base_template_name = 'blog/base.html'
else:
base_template_name = 'blog/visitor.html'
posts = Post.objects.all()
return render(request, 'blog/index.html',
{'posts':posts, 'base_template_name':base_template_name})
( Is there any other solution apart this ? )
My urls.py
url(r'^(?P<username>\w+)/$', views.Profil, name = 'profil'),
and a link from index.html to profil.html looks like this:
{{post.user.username }}
the problem with this is when i click on the link it passes with the current user I'm logging in with and not the user that I click on it
LogIn with admin and want to see Ala's profile:
When I click it shows in the url this:
http://127.0.0.1:8000/blog/imedadmin/ which is supposed to be 'Ala' instead of 'imedadmin'.
In my opinion, you can send a variable to view in your render function, call it isHim. When logged in user is himself, set it equal to true and when it's not, set it to false. In you're view, check it with {{if isHim}} and then enable/disable edit buttons. I've used it and it answered.
You could do something like :
{% if request.user.id == post.user.id %}
# ur code {% endif %}
in the templates dir.
My app has a text box and a submission button. I am trying to create and redirect to dynamic URLs in my app, that are based off of what is typed in the text box. For example, the user enters in '1234', clicks submit, then is taken to 'website.com/results/1234'. The problem seems to be that the HTML for my button doesn't want to redirect the user to the new, dynamic URL. I am passing this to the HTML with Jinja.
Here is what I have.
The user starts on the home page, that is defined like this
#app.route("/home/", methods=["GET", "POST"])
def start():
return render_template("dashboard.html")
Dashboard.html has a text box and submission button (below). As you can see, the action of this button is to redirect to {{ results_page }}, where "results_page" comes from my Python function load_results (also below) and is passed to the HTML with render_template.
<div>
<form action="{{ results_page }}" class="form-inline" method="post">
<div class="form-group">
<label for="PubmedID">Pubmed ID(s)</label>
<input type="text" class="form-control" id="PubmedID" placeholder="18952863, 18269575" name="pmid" value="{{request.form.pmid}}">
</div>
<button type="submit" id= "myButton" class="btn btn-default" data-toggle="modal" data-target="#myModal">Submit</button>
</form>
</div>
The results page of my app uses the user input to look up some information and display it.
#app.route('/results/<query>', methods=["GET", "POST"])
def load_results(query):
form = pmidForm(secret_key='super secret key')
try:
if request.method == 'POST':
query = form.pmid.data #This is the user input
results_page = "website.com/results/"+query
return(query)
#do lots of stuff using the query
return render_template('results.html', form=form, results_page = results_page)
except Exception as e:
return(str(e))
If I run my app like this, the home page is fine, but when I click "Submit", the app doesn't take in the user input or do anything with it. Simply the home page refreshes.
I am new to web development, but since this code works fine if I hardcode the button to action = "website.com/results" in the HTML, AND do without the <query> in /results/<query> for the results page, I think that only a few adjustments should be needed to make my app dynamically redirect to and load pages correctly. Right now, I'm not sure what I'm missing. Please let me know where I'm going stray.
EDIT -
Now I have implemented a handle_form function that redirects to my dynamic URL. This function properly redirects but then I get a 404 error.
#app.route('/form/', methods=["POST"]) #handles form submission
def handle_form():
form = pmidForm(secret_key='super secret key')
if request.method == "POST":
query = request.form['pmid']
return redirect('/results/'+query)
I have also edited my form in the HTML action to go to /form/
<form action="website.com/form/" class="form-inline" method="post">
<div class="form-group">
<label for="PubmedID">Pubmed ID(s)</label>
<input type="text" class="form-control" id="PubmedID" placeholder="18952863, 18269575" name="pmid" value="{{request.form.pmid}}">
</div>
<button type="submit" id= "myButton" class="btn btn-default" data-toggle="modal" data-target="#myModal">Submit</button>
</form>
With this, my site will properly redirect to /results/<query>/ (e.g. /results/1234) but I get a 404 error. Here is how I have changed my load_results
#app.route('/results/<query>', methods=["GET"])
def load_results(query):
form = pmidForm(secret_key='super secret key')
try:
if request.method == 'GET':
query = form.pmid.data #THIS IS THE USER INPUT FROM THE FORM #referencing 'class pmidForm'
return query
.
.
#do stuff
I think I am very close but need to figure out why I am getting a 404 error. My guess is that I am using "GET" incorrectly. My form in the HTML uses method="post". Since this does not match with "GET", is there no way for my load_results(query) function to retrieve the contents of the form?
EDIT 2 -
Changed handle_form to redirect with url_for:
#app.route('/form/', methods=["POST"]) #handles form submission
def handle_form():
form = pmidForm(secret_key='super secret key')
if request.method == "POST":
query = request.form['pmid']
return redirect(url_for('.load_results', query=query))
And changed load_results to not return "query"
#app.route('/results/<query>', methods=["GET"])
def load_results(query):
form = pmidForm(secret_key='super secret key')
try:
if request.method == 'GET':
query = form.pmid.data # This shouldn't work??
.
.
# do stuff with the variable "query"
With this, it's still returning the 404 Error as before. Should I not be using if request.method == GET ?
Edit 3 -
Even a very simplified load_results will give me the 404 error, so I'm not sure what's up.
#app.route('/results/<query>', methods=["GET", "POST"])
def load_results(query):
q = query
return render_template('test.html', q=q)
EDIT - 3
It seems that the accepted solution IS the correct solution to this problem, however there is an issue with uwsgi in my server that is re-directing it to the wrong location :(
Your punctual problem is that /home route function also needs to put the results_page url on the templating context.
results_page = "website.com/results"
return render_template("dashboard.html", results_page=results_page)
Since the variable is undefined, flask is calling the original endpoint on the form submission.
Your larger problem is that your appraoch isn't going to get you a dynamic results url that looks like /results/1234.
Typical approaches are to redirect on the server side when you handle the post request; or to use JavaScript in the client to get the form data and change the browser location to /results/1234.
A simplified version of how to handle this with a server side redirect might look something like this. One route that handles the form submission and another that displays results. You simply redirect from one to the other to get the nice url.
#app.route('/form', methods=["POST"])
def handle_form():
query = form.pmid.data #This is the user input
return redirect(url_for('.load_results', query=query))
#app.route('/results/<query>') *removed the method spec to handle the redirect?
def load_results(query):
.
.
# do something with query