Flask-SQLAlchemy changes don't persist - python

I'm making a web application, where user action can affect some bar graphs (seen in the gif below). Sometimes, changes aren't saved. When I reload the page, the changed bar graphs are shown (indicating that the user's action's were saved). When I reload the page again, sometimes the updated bar graphs and corresponding list are shown. Other times, they are not.
Here's the code for the view:
#app.route('/')
def home():
'''homepage for application'''
# redirect users who go to home page but aren't logged in
if not current_user.is_authenticated:
return redirect(url_for("landing"))
# reset the session
db.session.flush()
# get most recent entered weight
try:
last_weight = WeightEntry.query.filter_by(user_id = current_user.user_id).order_by(WeightEntry.date)[-1]
except IndexError:
return error("No weight recorded. Please contact support.")
return render_template("home.html",
today= Today(current_user.user_id),
foods = [food for food in FoodEntry.query.filter_by(user_id=current_user.user_id).all() if food.is_today() == True],
options = sorted(Food.query.filter(Food.user_id==current_user.user_id).all(), key=lambda x: x.name),
last_weight = last_weight)
I added the db.session.flush() in an attempt to solve the problem, but that didn't work.
The changes (logged foods) are stored here:
#app.route("/log_food", methods=["POST", "GET"])
#login_required
def log_food():
# add foods given by the user
if request.method == "POST":
for food in request.form.getlist("logged_food"):
try:
added_food = Food.query.filter_by(user_id=current_user.user_id, name=food).first()
x = FoodEntry(
food_id = added_food.food_id,
user_id = current_user.user_id)
db.session.add(x)
db.session.commit()
except:
return error("Unable to log food.")
return redirect(url_for("home"))
I'd appreciate any help I can get.
Thanks!

I fixed it by adding db.session.commit() to the page functions.
For example, for the homepage: I did:
#app.route('/')
def home():
'''homepage for application'''
db.session.commit()
...

Related

Name of a dictionary is not defined accessing through console

In my routes.py I set a variable to the converted dictionary generated from SQLAlchemy tuples right after the form validation statement.
When typing from routes import *
dict(Book.query.with_entities(Book.username, Book.choice).all()) in console i get the correct dictionary as wanted {'user1': 'choice1', 'user2': 'choice2'}
If I type the name of the variable dict_of_users assiged to this dictionary I get: NameError: name 'dict_of_users' is not defined
Why it does not recognise that variable since it is in the code?
The logic behind I want to achieve:
If the user select one choice from available in the list, that user and its choice are added as key and value in the dictionary, otherwise the dictionary is empty.
My routes.py:
#app.route("/booking", methods=['POST', 'GET'])
def booking():
session.permanent = True
app.permanent_session_lifetime = timedelta(seconds=5)
form = BookingForm()
if form.validate_on_submit():
book = Book(username=current_user.username, choice=form.book.data)
db.session.add(book)
db.session.commit()
flash('Your choice is registered', 'success')
dict_of_users = dict(Book.query.with_entities(Book.username, Book.choice).all())
return render_template('booking.html', title='Booking', form=form, dict_of_users=dict_of_users)
If it's only inside of the function, you can't access it outside of the function. Since the variable is only defined in the function, you get the NameError message. A fix is to define the variable in the global scope.
EDIT:
As a response to your comment:
if you want to access the dict_of_users variable, declare it outside of the function. Then the variable will contain the value of it's latest use in the global scope, and thus accesible outside of the function.
Something like this should do the trick:
dict_of_users = None
#app.route("/booking", methods=['POST', 'GET'])
def booking():
session.permanent = True
app.permanent_session_lifetime = timedelta(seconds=5)
form = BookingForm()
if form.validate_on_submit():
book = Book(username=current_user.username, choice=form.book.data)
db.session.add(book)
db.session.commit()
flash('Your choice is registered', 'success')
dict_of_users = dict(Book.query.with_entities(Book.username, Book.choice).all())
return render_template('booking.html', title='Booking', form=form, dict_of_users=dict_of_users)

Is there a way to return a value from function after redirect() in Flask?

Here I am sharing an example code of what i am trying to achieve.
my_bluprint = Blueprint('index',
__name__,
template_folder=template_dir)
#my_bluprint.route('/print', methods=['GET', 'POST'])
def my_function_2():
print('i was redirected')
if request.method == 'POST':
age = request.form.get('age')
print('AGE: ', age)
return render_template("form2.html")
#my_bluprint.route('/test', methods=['GET', 'POST'])
def my_function():
if request.method == 'POST':
print('my_function')
# do something
name = request.form.get('name')
print('my_name', name)
if name == 'root':
print('name is ', name)
return redirect(url_for("index.my_function_2"))
# age = request.form.get('age')
print('AGE: ',age) # I need input age from my_function_2 here
age_trans = my_module.ageMul(age)
print('TransAGE: ', age_trans)
return render_template("result.html", result=age_trans)
return render_template("form1.html")
I need to render different templates depending on the input from previous template.
On run time my program will use the input from one template to decide which template to redirect next. I tried rendering the templates the way i have used in my example code but I need inputs from previous templates for further processing combined with inputs of all the other templates which were rendered.
EDIT
i am able to redirect to other routes in my code. but i need input from the template i am using in my_function_2() in my_function() after the execution of my_function_2(). which means i need to come back to main route i-e /test with data from my_function_2. I need help in understanding how can I do that
Another Idea
is there a way to render different templates using conditions in same function lets say my_function(). If input from one html form makes the condition true render template A else render template B ? meanwhile retaining the inputs from first template rendered in my_function()? in this way i can have only one route rendering multiple templates ? but i want to keep data from all the templates previously rendered
what would be the good approach of doing this ?
EIDT 2
i tried this
#my_bluprint.route('/test', methods=['GET', 'POST'])
def my_function():
if request.method == 'POST':
#name = request.form.get('name')
session['name'] = request.form['name']
print('my_name', session['name'])
if session['name'] == 'root':
print('name is ', session['name'])
#return redirect(url_for("index.my_function_2"))
return render_template("form2.html")
age = request.form.get('age')
print('AGE ',age)
age_trans = my_module.ageMul(age)
print('TransAGE: ', age_trans)
print(session['name'])
return render_template("result.html", result=age_trans)
return render_template("form1.html")
First run goes smooth till return render_template("form2.html") after the form submitted here control goes back to if request.method == 'POST': and execute it from the start and gives me error here session['name'] = request.form['name'] but i want to the program control to always move forward with the values it got in first run of render_template("form1.html") until the page(url) is refreshed
You can pass it in query param in redirect or store it in session.

Flask: return render_template from outer def instead of #app.route's def

+NOTE: This is my very first question on StackOverflow, feedback is more than welcome.
I am working on a website with a lot of pages and I want to show some results from a query with a template when users use the search bar, but if users go to my page with the navbar I want to render another template.
#app.route("/python", methods=['GET', 'POST'])
def python():
language = 'python'
search(language)
return render_template("python/python.html")
def search (language):
if request.method == 'POST':
query = request.form['search_input']
print(query)
return render_template('results_render.html',
language = language, results = query)
After a bit of testing I summarized the behavior:
- 1. I don't know whether my results_render.html gets returned/rendered.
- 2. I know my python/python.html page gets rendered, but I don't know
whether this is overriding my results_render.html's render or not.
My expected behavior would be:
- 1. If a user goes to myhost/python the
return render_template("python/python.html") should run.
- 2. If a user searches for something in myhost/python the def search(languages): should return render_template('results_render.html',
language = language, results = query) whilst overriding return render_template("python/python.html")
OPTIONAL: If necessary routing the user to myhost/language/results (with language being a var) would be okay.
You need to return the value returned by search. You can use request.method to decide if that's needed, so it becomes:
#app.route("/python", methods=['GET', 'POST'])
def python():
language = 'python'
if request.method == 'POST':
return search(language)
return render_template("python/python.html")
As per your example, or even better:
#app.route("/python", methods=['GET', 'POST'])
def python():
language = 'python'
if request.method == 'POST':
query = request.form['search_input']
results = search(language, query)
return render_template(
'results_render.html',
language = language,
results = results
)
return render_template("python/python.html")
def search(language, query):
results = do_something_to_get_results(language, query)
return results

how can test u=g.user in Flask

Lets say I have flask Application Now I want to test u=g.user.be_friend(user) which make a user friend with eachother if they werene't friend befor But I don't know how I can test g.user and most importantly g.user.be_friend would you please help thanx ?
view.py:
user = Users.query.filter_by(name = name).first()
if user is None:
flash('User %s not found.' % name)
return redirect(url_for('layout.user'))
if user == g.user:
flash('You can\'t Friend yourself!')
return redirect(url_for('layout.user',page=1, sortby='normal'))
u = g.user.be_friend(user)
mytest_app.py:
def test_friend_main(self):
e1 = Users(name='admine1', email='admine2#gmail.com', age=25)
e2 = Users(name='teste2', email='teste2#gmail.com', age=27)
db.session.add_all([e1, e2])
db.session.commit()
with self.client:
self.assertTrue(g.user =='admine1')
e1.be_friend(e2)
response=self.client.get('/friend/admine1', follow_redirects=True)
self.assertTrue('You are now Friend with !',response.data)
You need flask-login module.
from flask.ext.login import current_user
#lm.user_loader # you should have same function
def load_user(user_id):
return User.get_user(user_id)
#flask_app.before_request
def before_request():
g.user = current_user # save current user in request context.
Sadly, but I didn't find any easy way to get current g value in test code. Only hard way with test_request_context. Anyway, you can test current_user, if it passed to template:
def test_something(self)
self.assertEqual(self.get_context_variable('current_user').name, name_should_be)
But at first, you should login:
response = self.client.post('/login', data=dict(name='admine1', email='admine2#gmail.com'))
User model can be improved: class User(UserMixin, db.Model):
Also, in test you shouldn't execute e1.be_friend(e2) directly. You should execute it from your production code.
I have implemented a unit test for the "followers" feature of the Flasky application that I feature in my book. Here is the test, you can probably take some ideas from it:
def test_follows(self):
u1 = User(email='john#example.com', password='cat')
u2 = User(email='susan#example.org', password='dog')
db.session.add(u1)
db.session.add(u2)
db.session.commit()
self.assertFalse(u1.is_following(u2))
self.assertFalse(u1.is_followed_by(u2))
timestamp_before = datetime.utcnow()
u1.follow(u2)
db.session.add(u1)
db.session.commit()
timestamp_after = datetime.utcnow()
self.assertTrue(u1.is_following(u2))
self.assertFalse(u1.is_followed_by(u2))
self.assertTrue(u2.is_followed_by(u1))
self.assertTrue(u1.followed.count() == 2)
self.assertTrue(u2.followers.count() == 2)
f = u1.followed.all()[-1]
self.assertTrue(f.followed == u2)
self.assertTrue(timestamp_before <= f.timestamp <= timestamp_after)
f = u2.followers.all()[-1]
self.assertTrue(f.follower == u1)
u1.unfollow(u2)
db.session.add(u1)
db.session.commit()
self.assertTrue(u1.followed.count() == 1)
self.assertTrue(u2.followers.count() == 1)
self.assertTrue(Follow.query.count() == 2)
u2.follow(u1)
db.session.add(u1)
db.session.add(u2)
db.session.commit()
db.session.delete(u2)
db.session.commit()
self.assertTrue(Follow.query.count() == 1)
When you look at this code, keep in mind that this application makes users followers of themselves, so for example, the follower count when a user just follows another user is two, not one.

Validating captcha in Flask

I am creating a captcha just for an exercise. The creation of the captcha images seem fine. But every time I try validating the captcha challenge entered by the user, the validation is done against the next captcha. I am stuck at how to go with this.
Function for creating captcha images- captcha.py
import random
import Image
import ImageFont
import ImageDraw
import ImageFilter
import JpegImagePlugin
import PngImagePlugin
def gen_captcha(text, fnt, fnt_sz, file_name, fmt='JPEG'):
fgcolor = random.randint(0,0xff0000)
bgcolor = fgcolor ^ 0xffffff
font = ImageFont.truetype(fnt,fnt_sz)
dim = font.getsize(text)
im = Image.new('RGB', (dim[0]+5,dim[1]+5), bgcolor)
d = ImageDraw.Draw(im)
x, y = im.size
r = random.randint
for num in range(100):
d.rectangle((r(0,x),r(0,y),r(0,x),r(0,y)),fill=r(0,0xffff00))
d.text((3,3), text, font=font, fill=fgcolor)
im = im.filter(ImageFilter.EDGE_ENHANCE_MORE)
im.save(file_name)
signup function from views.py
#app.route('/signup', methods = ['GET', 'POST'])
def signup():
if g.user is not None and g.user.is_authenticated():
return redirect(url_for('index'))
words = open('app/corncob_caps.txt').readlines()
captcha_word = words[random.randint(1,len(words))]
captcha_filename = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(10)) + '.jpg'
captcha.gen_captcha(captcha_word.strip(), 'app/os.ttf', 25, 'app/static/' + captcha_filename + '')
form = SignUpForm(captcha_word)
if form.validate_on_submit() == False:
return render_template('signup.html', form = form, filename = captcha_filename)
else:
user = User(form.email.data, form.password.data)
db.session.add(user)
db.session.commit()
flash('You have successfully signed up.')
flash('You may login now.')
return redirect(url_for('login'))
return render_template('signup.html', form = form, filename = captcha_filename)
I am passing the captcha_word to my form class. The form class is:
class SignUpForm(Form):
email = EmailField('Email Address', validators = [email()])
password = PasswordField('Password', validators = [Required('Please enter a valid password between 8 and 30 characters.'), Length(min = 8, max = 30)])
captcha = TextField('Captcha', validators = [Required('You must enter the challenge captcha.')])
submit = SubmitField('Create Account')
captcha_word = ''
def __init__(self, word, *args, **kwargs):
Form.__init__(self, *args, **kwargs)
self.get_word(word)
def get_word(self, word):
self.captcha_word = word
def validate(self):
if not Form.validate(self):
return False
elif self.captcha_word != self.captcha.data.upper():
print self.captcha_word
print self.captcha.data.upper()
self.captcha.errors.append("Wrong captcha!")
return False
user = self.get_user()
if user:
self.email.errors.append("That email is already taken.")
return False
else:
return True
def get_user(self):
return User.query.filter_by(email = self.email.data.lower()).first()
I inserted the two print statements inside to see why the comparison was coming wrong. The first print showed the next captcha whereas the print self.captcha.data.upper() displayed the user entered data.
I am not sure, but it seems the signup route is being called twice. But I don't know how to fix this. Any ideas?
If you need to use a captcha, you can use the feature that's already built into Flask-WTF and save yourself reinventing the wheel.
If you do want to reinvent the wheel, then the main problem you're having is that you're recreating the captcha when the user submits the form, you have no way to remember and refer to the old value.
So this is how it's working at the moment:
User goes to sign in, you generate a captcha, then because they haven't submitted a form, it shows the sign in form including the captcha picture.
User fills in the form and hits the submit button- this loads the signup view again,creates a new random captcha, then goes down the form submitted
logic path, so when you compare the user captcha data to the current captcha data, it doesn't match.
So you're missing persistence, the captcha you generate the first time round doesn't get held anywhere, so when the user submits you've got no way to refer back to it. So you need to store that captcha word somewhere. You could simply just store that captcha word in the user's session and use that to validate against when you need to, or perhaps sign it with itsdangerous and store it in the form itself as a hidden field?
Code Example:
This just takes your code and adjusts it a little to store the value in the session-- not tested, and can definitely been improved, but should work:
#app.route('/signup', methods = ['GET', 'POST'])
def signup():
if g.user is not None and g.user.is_authenticated():
return redirect(url_for('index'))
if request.method == 'post':
captcha_word = session["captcha"]
else:
words = open('app/corncob_caps.txt').readlines()
captcha_word = words[random.randint(1,len(words))]
session["captcha"] = captcha_word
captcha_filename = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(10)) + '.jpg'
captcha.gen_captcha(captcha_word.strip(), 'app/os.ttf', 25, 'app/static/' + captcha_filename + '')
form = SignUpForm(captcha_word)
if form.validate_on_submit() == False:
return render_template('signup.html', form = form, filename = captcha_filename)
else:
user = User(form.email.data, form.password.data)
db.session.add(user)
db.session.commit()
flash('You have successfully signed up.')
flash('You may login now.')
return redirect(url_for('login'))
return render_template('signup.html', form = form, filename = captcha_filename)

Categories