Flask & Python: Remember form data after POST - python

I'm trying to let the user create a "meeting" with several settings on a website.
User fills out a form
Form is send via POST
Website shows a summary page with the data entered by the user
User can go back and edit that data or press the "okay" button
Data from user is added to the database
I'm stuck at step 4.
session_create.html contains the form. I can submit the form and the data gets validated (wtforms). If the form data is validated, I display it on the session_summary.html page. The data is displayed there correctly.
The routes file:
#app.route('/create', methods=['GET','POST'])
#login_required
def create():
form = CreateSessionForm()
if form.validate_on_submit():
data = {}
data["title"] = form.title.data
data["description"] = form.description.data
data["date"] = form.date.data
data["time"] = form.time.data
data["sessiontype"] = form.sessiontype.data
data["host"] = current_user.username
if form.duration.data:
data["duration"] = form.duration.data
else:
data["duration"] = 0
return render_template('session_summary.html', title='Create a session', data=data)
return render_template('session_create.html', title='Create a session', form=form)
But my problem is that from here on I don't know how to access the data if the user confirms that the data entered was correct. Do I need to send all data in a hidden form to session_summary.html?
I'm sorry for not being more precise. I can't seem to wrap my head around it.

Related

How do I redirect to the same page in Flask, generated by a GET request, following a POST request?

I am creating a webapp with Python/Flask. I am using blueprints.
Say I search for a book and end up at the URL /search?book=somebook&author=someauthor via a GET request. On that page I have buttons for each result which will save that result to the user's saved books. This would be a POST request. However, I want to return the user to the same search page with the same URL params.
So the flow is:
User submits a search and ends up on /search?book=somebook&author=someauthor
User clicks subscribe on one of the results. A POST saves the book to the user's saved books.
User ends up on /search?book=somebook&author=someauthor again and the search result page is repopulated.
I think, incorrectly, I want something like this:
#search_bp.route('/search', methods=["GET", "POST"])
def search():
if request.method == "POST":
# save book to user's saved books
# somehow end up back on the same page from here
elif request.method == "GET":
# use request.args to populate page with results
return render_template("search.html", books=books)
In my mind I want to return redirect(url_for("search_bp.search")) but somehow get the request.args back into the URL. Is my only choice to hardcode the URL, i.e. concatenate a string url = "/search?book=" + request.args.get("book") + "&author=" + request.args.get("author") so that I can return redirect(url)?
You can pass values/variables to flask.url_for, example:
book = request.args.get('book')
author = request.args.get('author')
my_url = url_for('search_bp.search', book=book, author=author)
Additional values/parameters passed to url_for will be added to the URL as GET parameters, then you can do return redirect(my_url).
https://flask.palletsprojects.com/en/1.1.x/api/#flask.url_for

Django: User Reporting Some URL on Website

So i'm trying to build something, so that users would be able to report something on site. Here's the model,
class Report(models.Model):
reporting_url = models.URLField()
message = models.TextField()
I created Form for this Model including 'message' field only because 'reporting_url' is something it needs to populate by itself depending upon the specific page from where user has clicked "Report" button.
def report(request):
url_report = ???
if request.method == 'POST':
form = ReportForm(request.POST or None)
if form.is_valid():
new_form = form.save(commit=False)
new_form.reporting_url = url_report
new_form.save()
I was wondering How can I pass the specific url to 'reporting_url' field in form depending on the Page from where user has clicked "Report" button? (Much like s we see on social Networks).
Am I doing this correctly, Or is there a better way for doing this?
Please help me with this code. Thanks in Advance!
If there is a report button on that specific page then I believe you could write custom context processor.
More info: Django: get URL of current page, including parameters, in a template
https://docs.djangoproject.com/en/1.11/ref/templates/api/
Or maybe just write it directly in the views.py in your function and set
url_report = request.get_full_path()
I think you can use the form on the same page of the URL and use:
url_report = request.get_full_path()
in the view, to get the current URL.
Else if you want to create a separate view for the reporting form. You can use
url_report = request.META.get('HTTP_REFERER')
to get the previous or refering URL which led the user to that page.
request.META.get('HTTP_REFERER') will return None if it come from a different website.

Django and one-time links with data

I think I have a simple case here but I'm not finding good examples of the implementation ( or probably failing to understand).
After the user (not logged in) types his username to a form, Django would generate a unique URL based of this data (encoded in URL?) for the user that can be accessed once and within 5 minutes. Based on that URL (after clicking it) the data (username) would be decoded and ready for use in this one-time view.
Simple scenario if needed: user nimda fills the form and then is redirected (for example) to a view that shows the generated URL. Then nimda clicks the generated URL and a view is shown with the data he or she typed into the form
If you don't need that url you could save data to the session and send the user to a specific url.
The view connected to the url generates content depending on the (anonymous) users session. The user can see the content as long as you sessions last or you implement a time stamp an check this before delivering content.
If you need the url:
Build a model connected with the sessions with url and a time stamp.
Configure the urls.py for the url-model like
url(r'^dataUrl/(?P[0-9]+)/$', PostDelete.as_view()),
Assign the user session and the entered data (saved to the session) with
the url-model.
When delivering the content check for the random-url-part, and the timestamp and deliver the date (or not ;) )
You can access the session in a cvb's like this:
class YourClassName(TemplateView):
template_name = ""
def get_context_data(self, **kwargs):
context = super(YourClassName , self).get_context_data(**kwargs)
DataYouNeed = self.request.session["SessionVariableOfTheUser"]
userDAta = self.request.user #if this is usefull `
or in a createView:
class URLCreate(CreateView):
model = randomUrl
template_name = "entryCreate.html"
success_url = "../xyz/"
form_class = UrlCreateForm
# if you like to change the success-url
def get_success_url(self):
#print dir(self.object.instance)
#print self.object.instance.id
url = "../bringMeTo/%s" % self.object.instance.id
return url
def form_valid(self,form):
form.instance.user = self.request.user
self.request.session["formData"]= form.instance
return super(URLCreate, self).form_valid(form)
pass
This is not a ready solution! Just an inspiration for a start.

How to route form errors to another view using Flask, FlaskWTF

I'm trying to create two views using Flask. The first view show_entries displays a list of entries in a table. It also includes a form to create new entries.
The form gets submitted to a second view new_entry which accepts the POST method and is responsible for adding the new entry to the table using SQLAlchemy. new_entry then redirects back to show_entries.
My problem is that form.errors are not routed to show_entries, so the user never sees them. I'm not sure of the best way to go about this, or if I'm even on the right track with the way I've divided up the views.
Here's what I currently have:
def show_entries():
entryForm = EntryForm()
entries = g.user.entries
return render_template('show_entries.html',
entries=entries,
entryForm=entryForm)
def new_entry():
form = EntryForm()
if form.validate_on_submit():
newEntry = Entry(g.user, form.time.data)
db_session.add(newEntry)
db_session.commit()
flash('New entry was succesfully posted')
return redirect(url_for('show_entries'))
The normal pattern is to have /show_entries as a listing page with new_entry as the form. When you do a GET request to new_entry you get the form, then POST to it to add the entry. That way if there's an error you can just show it next to the form - all the data is available. If you split the views as you have then you'll need some way of moving the error data (and form data) from the new_entry view to the show_entries view.
Something more like (untested):
def show_entries():
entries = g.user.entries
return render_template('show_entries.html',
entries=entries)
def new_entry():
form = EntryForm()
if form.validate_on_submit():
newEntry = Entry(g.user, form.time.data)
db_session.add(newEntry)
db_session.commit()
flash('New entry was successfully posted')
return redirect(url_for('show_entries'))
return render_template('show_new_entry_form.html',
entryForm=form)

Unit testing Flask function (with logged in user)

I'm new to Python and Flask but I'm gradually getting to grips with it. I've got so far into building an app and I'm now thinking I should start some unit testing. I really can't get my head around it though. I've read various docs, posts and examples but I can't seem to transfer this to my own code. I'm hoping if someone can show me how to write a test for one of my functions then things will fall into place. It's very tempting at this stage to ignore it and press on building my app.
#app.route('/user/<nickname>/create_bike', methods = ['GET', 'POST'] )
#login_required
def create_bike(nickname):
user = User.query.filter_by(nickname = nickname).first()
bikes = user.bikes.all()
bikecount = user.bikes.count()
form = CreateBike()
if form.validate_on_submit():
if bikecount < user.bike_allowance:
# let user create a bike
newbike = Bikes(bikename = form.bikename.data,
user_id = g.user.id, shared = SHARED )
db.session.add(newbike)
db.session.commit()
flash('Your new bike has been created')
return redirect(url_for('create_bike', nickname = nickname))
else:
flash("You have too many bikes")
return render_template('create_bike.html',
user = user,
form = form
)
UPDATE - Here's my working test
def test_create_bike(self):
u = User(nickname = 'john', email = 'john#example.com', account_type = "tester")
db.session.add(u)
db.session.commit()
# login user
with self.app as c:
with c.session_transaction() as sess:
sess['user_id'] = int(u.get_id())
# http://pythonhosted.org/Flask-Login/#fresh-logins
sess['_fresh'] = True
rv = c.post('/user/john/create_bike', data = dict(
bikename = 'your new bike',
user_id = sess['user_id'],
shared = 1
), follow_redirects = True)
assert 'Your new bike has been created' in rv.data
Your test is likely failing because that view requires a logged in user, and since you arent passing in any session data, you are being redirected to the login page (the data argument to .post is form data, available in request.form in your view). Before your assertion you can see what the response is to help you along the way:
print rv.status_code
print rv.location #None if not a redirect
There's some documentation around sessions in tests here, and if you're using Flask-Login like it looks you are, this answer shows you how to set the session up so you get a logged in user
You're on the right track.
Checking if the html page contains some piece of data that should be there is one the most common scenarios in testing web apps. You just need to repeat for all the other pages that you create. You can also add tests to see that creating a user follows all the validation rules that you've setup or that the username field is unique etc.

Categories