how can test u=g.user in Flask - python

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.

Related

GET and POST requests in testing/ Django

Guy, I need your help. Since I'm newbie and I'm making my first app in Django I came to an issue while testing my app. There is problem with testing views, and since tested manually everything works fine problem occurs while testing automatition.
I think the issue may be related to POST / GET requests because I made things in way that, most things are based on GET, even those which change things in DB. POST request is reserved only to forms. When I start tests, every action that is made seems lacking-effect like those GET requests don't work.
Ok here comes the code:
Views:
#login_required
def new(request):
if request.method == "POST":
player = Player.objects.get(name=request.user)
hosted = Game(host=player.nick)
form = CreateGame(instance=hosted, data=request.POST)
if form.is_valid():
form.save()
return redirect("home", name="new_room")
#login_required
def delete_room(request, id):
game = Game.objects.get(id=id)
player = Player.objects.get(name=request.user)
if player.nick == game.host and game.how_many_players_ready == 0:
if not game.is_played:
game.delete()
return redirect("home", name="game_deleted")
else:
return redirect('detail', id=game.id)
else:
return redirect('detail', id=game.id)
Tests:
class TestViews(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(username='testuser', password='12345')
self.user2 = User.objects.create_user(username='testuser2', password='12345')
usr = self.user
self.client.login(username='testuser', password='12345')
self.player = Player.objects.create(name=usr, parent = usr.username, nick = 'nie')
def test_create_many_rooms(self):
#this one works -> POST
self.new_room=reverse('new_room')
self.client.post(self.new_room, {'name' :'mariaczi', 'host':'mario'})
self.client.post(self.new_room, {'name': 'mariaczi2','host':'mario'})
self.client.post(self.new_room, {'name': 'mariaczi3', 'host': 'mario'})
suma = Game.objects.all().count()
self.assertEquals(suma,3)
def test_host_delete_empty_room(self):
#this one not
game = Game.objects.create(name='empty', host='testuser', is_played=False, max_players=4)
self.delete_room = reverse('delete_gam', args=[game.id])
self.client.get(self.delete_room, follow=True)
suma = Game.objects.all().count()
self.assertEquals(suma,0)
OUTCOME: ======================================================================
FAIL: test_host_delete_empty_room (game.tests.test_views.TestViews)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/dottore/PycharmProjects/Nowa_gra/Nova_gra/game/tests/test_views.py", line 53, in test_host_delete_empty_room
self.assertEquals(suma,0)
AssertionError: 1 != 0
Models:
class Game(models.Model):
name = models.CharField(max_length=150)
host = models.CharField(max_length=10)
is_played = models.BooleanField(default = False)
max_players = models.IntegerField(default=4)
who_is_ready = models.ManyToManyField(Player, related_name="guys_ready", blank=True)
who_is_playing = models.ManyToManyField(Player, related_name="guys_playing", blank=True)
turn = models.IntegerField(default=1)
turn_of_player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name='czyja_tura', blank=True, null=True)
#property
def how_many_players_ready(self):
return self.who_is_ready.count()
#property
def how_many_players_playing(self):
return self.who_is_playing.count()
#property
def players_ready(self):
return list(self.who_is_ready.all())
#property
def players_playing(self):
return list(self.who_is_playing.all())
#property
def players_playing_str(self):
res = [i.nick for i in self.who_is_playing.all()]
return res
#property
def first_player(self):
return self.players_playing[0]
#property
def second_player(self):
return self.players_playing[1]
#property
def third_player(self):
return self.players_playing[2]
#property
def forth_player(self):
return self.players_playing[3]
#property
def next_player(self):
x = self.players_playing.index(self.turn_of_player)
nast= x+1
if nast > (self.how_many_players_playing - 1):
self.turn +=1
self.save()
return self.players_playing[0]
else:
return self.players_playing[x+1]
def __str__(self):
return self.name
Where is the TRICK? Shall I always handle things with database changes other way then generating GET?
There is no trick here... I think the Game object is not getting delete, your delete_room has many if-else's. Common debugging technique is to put a print statement in each if else condition to see which path it's taking.
My suspicion is on first part of this if condition,
if player.nick == game.host this will result in "nie" == "testuser"
which are not equal. But find it yourself using print statements.

Flask Multiprocessing

I am trying to develop a small app which will basically plot some graphs based on set of data user provided or requested. The process_order function should be run concurrent different set of data. For some reason I get only the first graph not the second and successive one. I have opened a different browser session with different user got the same result.
Example I tried to open a browser one from IE and another one from Chrome with logged in as user1 with set of 5 inputs - in IE (Expected 5 graphs) and as user2 with set of 20 input - in Chrome (Expected 20 Graphs) I was expecting 2 browser window with 5 graphs from one and 20 graphs from second window:
I am new to python and flask apologies if its something have already asked/answered in different questions
app = Flask(__name__)
plot = None
my_session = None
session_id = None
#app.route('/')
def home():
if not session.get('logged_in'):
return render_template('login.html')
else:
return render_template('template.html')
#app.route('/login', methods=['POST'])
def do_admin_login():
if request.form['password'] == 'password' and request.form['username'] in 'admin' :
session['logged_in'] = True
my_session = Bot(request.form['username'])
my_session.session_id = request.form['username']
session['user'] = request.form['username']
return render_template('template.html')
else:
flash('wrong password!')
return home()
#app.route("/ask", methods=['POST','GET'])
def ask():
string = str(request.form['message'])
res = my_session.process(string)
return jsonify({'status':'OK','answer':bot_res})
class Bot():
#Define user level
def __init__(self,session_id):
self.return_string = ""
self.session_id = session_id
def process(self, string):
self.session_id = session['user']
self.string = string
self.return_string = self.process_order() # This method I would like to execute as concurrent at the same time for multiple users/session with different set ot data
return self.return_string
def process_order(self):
self.plot = Plot()
self.plot.global_url = range(5)
self.i = 0
self.inputs = [] #tuples
self.number = range(5)
self.url_values = zip(self.number,self.inputs)
self.pool = Pool()
self.func = partial(self.plot.do_plot,self.var1)
self.pool.map(self.func,self.url_values)
self.pool.close()
self.pool.join()
process_confirmation = "Your request is complete"
return process_confirmation
class Plot():
def __init__(self):
pass
def do_plot(self, var1):
self.number, self.url = plot_values
self.var1 = var1
fig = plt.figure(self.number)
self.line = str(self.url[1])
self.res = urllib.request.urlopen(self.line)
plt.imshow(plt.imread(self.res))
self.file_name = "%03d.png"%(self.number,)
plt.savefig(self.file_name)
plt.close()
if __name__ == "__main__":
session_id = None
app.secret_key = os.urandom(12)
my_session = Bot(session_id)
app.run(host='0.0.0.0', port=80)
If the problem you are having is that individual requests take a long time, then your problem might stem from using flasks builtin server.
You can try deploying your app with gevent:
http://flask.pocoo.org/docs/1.0/deploying/wsgi-standalone/#gevent

Flask-SQLAlchemy changes don't persist

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()
...

SimpleListFilter called twice and finally ?e=1

I have an admin page in django 1.4.3 [final]. We use CF cards in a lot of hardware, each card connects to a VPN. I have a function which determines whether the card is online or not depending on its last feedback written in database.
models.py:
class Card(models.Model):
...
last_feedback = models.DateTimeField(_('Last feedback'), null=True, blank=True)
...
def online_status(self):
nowtime = calendar.timegm(timezone.now().utctimetuple())
seen = calendar.timegm(self.last_feedback.utctimetuple())
diff = nowtime-seen
if (self.state == 1) and (diff < 300):
return '<div style="width:100%%; height:100%%; background-color:green; color:white;">online & production</div>'
elif (diff < 300):
return '<div style="width:100%%; height:100%%; background-color:orange;">online</div>'
else:
return "offline"
online_status.allow_tags = True
admin.py:
class OnlineFilter(admin.SimpleListFilter):
title = _("Online")
parameter_name = "online"
def lookups(self, request, model_admin):
return (("yes", "Yes"),
("no", "No"),
)
def queryset(self, request, queryset):
out = self.filter(request, queryset)
f = open("/tmp/list", "w")
f.write(str(out))
f.close()
return out
def filter(self, request, queryset):
if not self.value():
return queryset
else:
out = []
if self.value() == 'yes':
for i in queryset:
try:
if i.online_status() != "offline":
out.append(i)
except:
pass
elif self.value() == 'no':
for i in queryset:
try:
if i.online_status() == "offline":
out.append(i)
except:
pass
return out
class CardAdmin(admin.ModelAdmin):
list_filter = [ ... , OnlineFilter ]
and everytime I try to set the online filter, the file /tmp/list gets full with the right set of cards and then filled with the default list - as if the filter gets called twice. And the URL in my browser says ?e=1 instead of ?online=yes. If the filter is not set, it is called only once - giving only one set of cards in the file.
BTW: the OnlineFilter.filter method is out of the queryset method just so that I know what goes out of my code, in final release I would have put it in only one method ...
is this a bug? or a feature? Am I doing something wrong? If so, what?
I wouldn't be surprised if django calls the queryset function twice - I seem to remember noticing that once before. Not sure why though. Regarding why the filter is not working, django expects a QuerySet to be returned, not a list. You might be able to construct a query to determine online status, but given the complexity I suspect it will prove to be beyond the powers to the django ORM and you'd need to revert to raw SQL. The alternative would be to create a list of primary keys, the simply filter the queryset by those, so:
def filter(self, request, queryset):
if not self.value():
return queryset
else:
pks = []
if self.value() == 'yes':
for i in queryset:
try:
if i.online_status() != "offline":
pks.append(i.pk)
except:
pass
elif self.value() == 'no':
for i in queryset:
try:
if i.online_status() == "offline":
pks.append(i.pk)
except:
pass
out = queryset.filter(pk__in=pks)
return out

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