Add data to database on two seperate pages - python

I'm currently working on a webpage using the django framework for python.
I need to have a page where admin user's can register an event into the system.
Event being: Location on a map, Description, Images, links etc..
I feel it's a bit less confusing If I have the user add location details on the first page but when he has finished choosing a location he could click next, this would take him to another page where he would finish filling out the information about the event.
I'm not sure but I think this is rather a database question than a django question.
How would I continue adding to the same table in a database between two seperate pages?
I thought about using timestamp so I could select the last modified table on the next page but I think that might be risky + if the user goes back to modify the table the timestamp is useless.
I'm using Django 1.5 + postgresql database. Any reading references that might be good to check out for this kind of operation?

I've done something similar to this before. I asked users to enter a zip code on one page and then based upon that zip code it loaded in different options for the form on the next page. Here is how I did it using request.session
Note that this is is my soultion to MY problem. This may not be exactly what you are looking for but might help you get a start. If anyone has a better solution I'd love to see it since I'm not entirely happy with my answer.
views.py
def find_zip(request):
c={}
form = FindZip()
c['form'] = form
if request.method == 'POST':
form = FindZip(request.POST)
c['form'] = form
if form.is_valid():
zip = form.data['zip']
form = ExternalDonateForm(initial={'zip':zip})
request.session['_old_post'] = request.POST
c['form'] = form
response = HttpResponseRedirect('/external')
return response
return render_to_response(
'find_zip.html',
c,
context_instance=RequestContext(request)
I then try to retrive that session from the previous view
def donate_external(request):
zip = None
if request.session.get('_old_post'):
old_post = request.session.get('_old_post')
zip = old_post['zip']
)
# rest of code ....

Related

Flask: when to use session vs. storing user data in db?

I have a Flask application and need to store users' place when they navigate the content.
For example, I have a route like this: #main_bp.route('/articles/<category>/<article_number>', defaults={'category': 'new'})
The content is organized such that you page through articles under a category: starting at 0, then 1, and so forth. The URL for article number 3 would look like: articles/<category>/3
I'd like to save users' place so that if they leave the site after visiting article 3, when they navigate to the articles page they'll land on articles/<category>/3, rather than articles/<category>/0.
What is the best way to achieve this? Currently, I've modeled the data in the database so there is a column that looks like category_article_last_visited (integer). I'm able to store this data as a user browses the site, but I'm not sure how to retrieve it when they return to the articles page.
What I've tried:
#main_bp.route('/articles/<category>/<article_number>', defaults={'category': 'new', 'article_number':current_user.category_article_last_visited}), but I get an error that there is no such attribute.
Checking current_user.category_article_last_visited in the routes function and using the article number. This renders the correct content, but doesn't change the URL, which won't work.
Redirecting users if they have a value for current_user.category_article_last_visited. This doesn't seem to yield any change.
I am curious if storing in the db (assigning the value, db.commit(), etc.) is the right path, or if I should explore flask-sessions more. I need this information to persist across sessions, so that if a user logs out, clears cookies, uses a different device, etc. it is still available. I may also perform analytics on these values in the future.
Is the method I've described above the best way to achieve this? Is flask-sessions or something else preferable?
If the method outlined above is best, how do I correctly route this information so that users are directed to the page they left off, and the URL is changed appropriately?
Thanks
I would go with the redirect solution, it is more clear.
I would add an if statement at the beginning of the route-function and if there is data for this user, i would redirect to that page. For example:
#main_bp.route('/articles/<category>/<article_number>', defaults={'category': 'new'})
def routefunc():
if current_user.category_article_last_visited !=0: #or whatever your column keeps for empty data
return redirect ('/articles/'+yourcategory +'/'+ current_user.category_article_last_visited #as string
This must be combined with some other functionality, to avoid infinitive redirection to this route:
Option 1:
You can add another variable in the route that will have specific value on these redirections and will ignore this if statement. For example:
#main_bp.route('/articles/<category>/<article_number>/<check>', defaults={'category': 'new'})
def routefunc():
if current_user.category_article_last_visited !=0 and check!=1: return redirect ('/articles/'+yourcategory +'/'+ current_user.category_article_last_visited+'/1')
However in this case you must add this variable (with some other value different from 1) to all of your urls-hrefs etc and it will make your urls more "dirty". It would be effective for a small app, but i would avoid it for a big app/website with multiple internal links.
Option 2:
You could add one more column in your database table that will be 1/0 depending on when user visitis this route, directly or from redirection. In this case you must add a couple of queries to check and/or update this value before-after redirection.
Option 3:
You could create another similar route that will only handle redirections, and produce the same results (same html) but without the if statement. For example:
#main_bp.route('/articles/<category>/<article_number>', defaults={'category': 'new'})
def routefunc():
if current_user.category_article_last_visited !=0: #or whatever your column keeps for empty data
return redirect ('/articles2/'+yourcategory +'/'+ current_user.category_article_last_visited #as string
#main_bp.route2('/articles2/<category>/<article_number>', defaults={'category': 'new'})
def routefunc():
return ('yourhtml.html')
***Session based approach is not good here, as you want a long term solution.
As you probably have many categories, articles, users, you would better create a separate table specifically for this
I don't know what is the best way to achieve what you want but here's what you could try. Assuming you want to perform some analytics on the data you might want to store it in a database.
You could have a route designed to create a user cookie when a new user visits your page and redirects him to the articles page with the new cookie set:
#main_bp.route('/articles/set_cookie', "GET"])
def set_article_cookie():
sessionserializer = securecookiesessioninterface().get_signing_serializer(main_bp)
tempcookie = sessionserializer.dumps(dict(session))
resp = make_response(redirect('/articles'))
resp.set_cookie("user", tempcookie)
return resp
And your existing route in which you check if the user has already visited the page. In which case you will want to check in the database what was the last article he read and redirect him accordingly:
#main_bp.route('/articles/<category>/<article_number>', defaults={'category': 'new'})
def articles(category, article_number):
# If the user cookie is already set, check if there is some data is the database and redirect to his last article visited
cookie = request.cookies
if "user" in cookie:
# Retreive the user cookie value and check the database for this value
return redirect('/articles/' + last_article_visited)
# Else redirect the user to set_article_cookie
else:
return redirect("/set_article_cookie")
OK, here is the solution I decided on:
I update the paths of nav links throughout the site, so instead of /articles/<category>/0 it's /articles/<category>/current_user.article_number_last_visited
Since not all users have visited articles in every category, I added default routing logic, similar to:
#main_bp.route('/articles/<category>/', defaults={'article_number': 0})
#main_bp.route('/articles/<category>/<article_number>', methods=['GET', 'POST'])
This routes users correctly even if current_user.article_number is null.
I believe this will also work if the user is not logged in (and therefore there will be no article_number attribute). I haven't checked this case out thoroughly though because in my use case users have to be logged in to view content.

Django - A link that will update database row before leaving site

I'm quite new to Python development and working on a specific project. I have a list of 'action items' that people can 'claim'. So I created a Model that has all of the information about the action item, including a BooleanField called is_claimed which is defaulted to False.
I'm printing out all of the action items in a table, of which the last column is a link to an external site (which is also a URLField in my model).
I want to have that last table cell be something like this:
Claim and Complete Action Item
When someone clicks that URL, I need to update the database to show is_claimed=True.
Any ideas or help? Sorry if this question is too simplistic. Learning a lot but need some expert help!
Thanks
Have your link tags point to this view, which sets is_claimed on the model and then redirects to the foreign URL.
from django.shortcuts import render, redirect
from .models import ActionItem
def link_counter(request, action_id):
# Retrieve the clicked item
action_item = ActionItem.objects.get(pk=action_id)
# Set 'is_claimed' to true
action_item.is_claimed = True
action_item.save()
# Redirect the user, to the url associated with the 'action_item'
return redirect(action_item.url)
Your anchor tags will look like this:
Claim and Complete Action Item

How to capture HTML5 data attribute when form is submitted to views.py of Django

As you can probably tell from the nature of my question, I'm a little new to this. I have read similar post on this subject matter but most of it went right past my head and I did not feel like it was 100% applicable to the circumstance that I was facing so I thought I'd ask the question in a simplified way.
The question:
let's say I'm running the below HTMl form and a user submits the form to my views.py as shown in the views section below, I would able to store the value of the user selection by using: car_selection = request.POST.get('car') .
My question is, how would I be able to capture the HTML5 data of " data-animal-type="spider" " ?
I know there are Gurus out there but please do not explode my head. I would really need simplified help.
Thanks for helping.
Example HTML Form:
<select name="carlist" >
option data-car-type="premium" name= "car" value="audi">Audi</option>
</select>
Example Django View Function
def getcar(request):
...
if request.method == 'POST'
...
selected_carn = request.POST.get('car')
Well, it actually is possible. Say your view looks like this:
def getcar(request):
...
if request.method == 'POST'
myform = MyForm(request.POST)
...
myform includes uncleaned form in html. The you can use BeautifulSoup to extract data. Something like this:
from bs4 import BeautifulSoup
test = BeautifulSoup(str(myform))
data-values = [item["data-car-type"] for item in test.find_all() if "data-car-type" in item.attrs]
This will extract values from data-car-type attributes.
That being said, this does seem like a bad design. I surely would never go to such length to get the "car type" data. It's probably written somewhere in your database. Get it from there.
I know this question is 4 year old but I came across this page when a similar question arose in my mind.
Short answer
It's not possible, unless your use Javascript on the front end side as a workaround. The accepted answer is false.
Explanation
Indeed, in the example above, try to print(request.POST) and you'll see that the QueryDict object request.POST received by the view does not contain any reference to the HTML5 data attribute you want to fetch. It's basically a kind of Python dictionary (with a few distinctive features, cf. documentation). Admittedly, if you print(myform) in the same example, you'll see some HTML code. But, this code is generated retroactively, when you associate data with the form. Thus, BeautifulSoup will never be able to find what you're looking for. From the Django documentation:
If the form is submitted using a POST request, the view will [...] create a form instance and populate it with data from the
request: form = NameForm(request.POST). This is called “binding data to
the form” (it is now a bound form).
Workaround
What I've done on my side and what I would suggest you to do is to use some Javascript on the client side to add the missing information to the form when it's submitted. For instance, it could look like this:
document.querySelector("#form_id").addEventListener("submit", function(e) {
const your_data_attribute = document.getElementById("XXX").dataset.yourInfo;
const another_hidden_field = document.getElementById("YYY");
another_hidden_field.value = your_data_attribute;
});

choosing correct form according to form input's name

I want to submit forms in multiple websites. Usually I can't exactly know the form name or form id, but I know the input name that I want to submit.
Let's say there is a website which has couple of forms inside it. My code should check all of the forms, if one of them has a input value named "birthday" it will submit that form. If multiple forms has it, it will submit them all.
How can I achieve this?
You can basically loop over all forms and skip forms that don't contain the desired input:
for form in br.forms():
if not form.find_control(name="birthday"):
continue
# fill form and submit here
More about find_control() here.
You are gonna need to use an iterator to check all the forms in the website. In this case we will be using for. But this doesn't let us know about which form we are working on, it just lets us use it. So we are going to assign 0(the first form's ID) to a variable and add 1 to it as we change forms when a new iteration/loop starts.
currentForm = 0
for form in br.forms(): # For every form in the website
currentForm += 1 # Add 1 to the current form so that the script knows which form we will be working next
if not forms.find_control(name = "birthday"): # If the form isn't about birthday
continue # Go to the next iteration / loop ignoring the statements below
br.select_form(nr = currentForm) # Select the birthday form
br.form["birthday"] = "Fill this with what you want" # Writing to the form
br.submit() # Submit the working form
Note: x += y is equal to x = x + y

WTForms Set input field after post

For below example:
Product(form):
product = TextField('name')
If i set this field in GET action
form.product.data= "123",
render is "123".
However, if i try set this value after POST action,
i allways get value form POST
How can i set this value (rerender) after POST ?
I wanted change only particulars fields, but rest keep from POST.
I have noticed that data from POST has additional field "raw_data" and set form.product.data fiels hasn't done (re)render. Solution proved to be clear
form.product.raw_data = None
form.product.data = 123
render new value
Maybe little bit "elegant" , but works !!!
Are you asking how to clear form data from the form after a user has submitted it?
In that case you could re-initialize the form
when you do
product = Product(request.POST)
It will fill the data that the user submitted.
product = Product()
This will clear the data.
Note:
use 4 spaces for every line of the code, so it is displayed well.

Categories

Resources