I have a User model and there is a password columns(PasswordType), amongst many other columns. When I try to do this
user = # get some user with some query
print(user.password)
# there is a password
user.password = 'something else'
db.session.is_modified(user)
# True
db.session.commit()
print(user.password)
# prints new password
user.password = None
db.session.is_modified(user)
# True
db.session.commit()
print(user.password)
# This correctly prints None
# Now the funny part,
user.password = 'something new again'
db.session.is_modified(user)
# False
db.session.commit()
print(user.password)
# Prints None again.
I've been at this for hours and can't figure out why this is happening.
The problem was with the PasswordType in SQLAlchemy-utils.
It works when I make a call to force_auto_coercion.
This may answer to your question: check this out: http://docs.sqlalchemy.org/en/rel_0_9/orm/session_api.html#sqlalchemy.orm.session.Session.is_modified
Instances present in the Session.dirty collection may report False when tested with this method. This is because the object may have received change events via attribute mutation, thus placing it in Session.dirty, but ultimately the state is the same as that loaded from the database, resulting in no net change here.
You may also wanna use this as for the state management utilities (in order to know which state your instance is in).
>>> from sqlalchemy.orm.util import object_state
>>> print object_state(instance)
Related
Very basically I have a model member and want to restrict searching members to allowed users. I use the following in the corresponding view
if request.user.has_perm('view_member'):
context['user_list'] = get_user(q)
Sadly this does not work even if
a) I give a user this permission via the admin interface
b) in my tests that look a bit like the following
def test_user_search_view(self):
self.client.login(username='testuser0', password='12345') # this is a user that was given the permission (see below)
response = self.client.post(reverse('library:search'), data={'q': "Müller"})
# Check our user is logged in
self.assertEqual(str(response.context['user']), 'testuser0') # Works
self.assertContains(response, "Max") # Fails, but Max should be there
For further tests I used the debugging mode of PyCharm to go into the test. In the console I then executed the following
>>> permission_view_user = Permission.objects.get(codename='view_member')
>>> test_user0.user_permissions.add(permission_view_user)
>>> test_user0.save() # For good luck
>>> user = get_object_or_404(User, pk=test_user0.pk)
>>> user.has_perm(permission_view_user)
False
I would expect true here.
I forgot the app name 🙈
It should be
if request.user.has_perm('appname.view_member'):
context['user_list'] = get_user(q)
I have the following simple pytest case, which tests a delete operation:
def test_delete_club(client, systemAdmin, operationalCountry1, club1):
rv = loginTo(client, '/admin/clubs/1', '+00000000000','testpassword')
decodedRv = rv.data.decode('utf-8')
assert '<td>testClub1</td>' in decodedRv
rv = client.get('/admin/delete_club/1', follow_redirects = True)
decodedRv = rv.data.decode('utf-8')
assert '<td>testClub1</td>' not in decodedRv
#ensure club1 does not exist in the database either
c = Club.query.get(1)
assert c is None
# make sure club roles are deleted along with the club
clubRoles = Role.query.filter(Role.club_id == club1.id).all()
assert len(clubRoles)
Basically, it hits the delete URL (/admin/delete_club/1) and after following redirects, it asserts that there is neither such a club nor any roles associated with that club in the database.
I am using TDD (test driven development). So I wrote the above test case before the relevant routing code. My route method looks like this:
#flaskApp.route('/admin/delete_club/<int:id>', methods=['GET'])
#login_required
def delete_club(id):
'''Deletes the club identified by the id'''
if not isCurrentUserSysAdmin():
#TODO better error handling
return 'you can not touch this'
clubToDelete = Club.query.get_or_404(id)
logging.debug('About to delete club: {}'.format(clubToDelete))
opCountryId = clubToDelete.operationalcountry_id
try:
db.session.delete(clubToDelete)
logging.debug('Deleted club: {}'.format(clubToDelete))
except:
flash('Error deleting club')
logging.error('Error deleting club. Club Details: {}'.format(clubToDelete))
return redirect(url_for('clubs', countryid = opCountryId))
Well, so far so good. Test case passed without a glitch. I was happy. I wanted to give it a go on the real web page. Then I noticed that, although the delete operation had succeeded, on the redirected page, the club I was trying to delete would still be present.
Then I found the BUG : I simply forgot to add db.session.commit() after deleting the club instance entity. That change fixed the web page.
However, I am still puzzled why my test case does not complain about it and how come it does not fail at all? Again, test case works without the commit statement.
Any ideas from more seasoned Flask/Python developers?
In short, db.session.delete register transaction into memory to execute. But without db.commit that wouldn't execute a query on the database. This method will change only the local representation of databaes. What I recommend you is to use context manager with session.begin().
I want to batch create users for admin. But, the truth is make_password is a time-consuming task. Then, if I returned the created user-password list util the new users are all created, it will let front user waiting for a long time. So, i would like to do somethings like code showed below. Then, I encountered a problem. Cause I can not figure out how to lock the user_id_list for creating, someone registered during the thread runtime will cause an Dulplicate Key Error error thing like that.
So, I am looking forward your good solutions.
def BatchCreateUser(self, request, context):
"""批量创建用户"""
num = request.get('num')
pwd = request.get('pwd')
pwd_length = request.get('pwd_length') or 10
latest_user = UserAuthModel.objects.latest('id') # retrieve the lastest registered user id
start_user_id = latest_user.id + 1 # the beginning user id for creating
end_user_id = latest_user.id + num # the end user id for creating
user_id_list = [i for i in range(start_user_id, end_user_id + 1)] # user id list for creating
raw_passwords = generate_cdkey(num, pwd_length, False) # generating passwords
Thread(target=batch_create_user, args=(user_id_list, raw_passwords)).start() # make a thread to perform this time-consuming task
user_password_list = list(map(list, zip(*[user_id_list, raw_passwords]))) # return the user id and password immediately without letting front user waiting so long
return {'results': user_password_list}
For my web app I have some variables being injected into the base/all templates with the following code:
#app.context_processor
def inject_into_base():
# If user is not logged in
if not current_user.is_authenticated:
# Set count to none
count = None
role = None
# Else Count is set to the number of unread messages
else:
count = db.session.query(message).filter(message.user_id_to == current_user.user_id).filter(message.status == False).count()
role = db.session.query(user_society_role.role_id).filter_by(user_id=current_user.user_id).order_by(user_society_role.role_id.desc()).first().role_id
return dict(count=count, role=role)
All is well until I hit a certain route that contains the line:
Survey.q9 = Survey.q9.split(',')
Which just simply splits the string into a list. It results in
sqlalchemy.exc.InterfaceError: <unprintable InterfaceError object>
If I hard code the two db.session.query's found in inject_into_base it will run just fine, and if I comment out the .split(', ') in the route it also works fine.
I have also found assigning anything to Survey.q9 breaks my app, and ONLY on this one certain route.
Any help is much appreciated, this is my first time asking a question on here so please go easy on me :)
Fixed my issue, turns out sqlalchemy's autoflush feature was breaking my app, all I had to do to fix this was add db = SQLAlchemy(app, session_options={"autoflush": False}) to my __init__.py
my first question here, perhaps a noob question but I really don't understand what I'm doing wrong, and I can't find any clue on the web.py documentation.
Is it possible to use a db select to validate a field?
What I'm doing:
I'm building a registration form, I'm having trouble with the username validation.
In every example I found, users are declared in a variable before the registration Class with code like this:
allowed = (
('jon','pass1'),
('tom','pass2')
)
and used in the validation like this:
form.Validator('Username already exists.', lambda x: x not in allowed)
Since I'm saving in db, I can change the allowed tuples with a db.select, but this mean the select is performed only once.
I want to check the users every time the POST is called, so I just replaced the "allowed" variable with a db.select this way:
form.Validator('Username already exists.', lambda x: x not in [o.usr for o in db.select('users',what='usr')])
If I test "x not in [o.usr..etc..etc..]" on the interpreter, this work..
>>> [o.usr for o in db.select('users',what='usr')]
0.0 (1): SELECT usr FROM users
[u'hhh', u'Fede', u'Vinz', u'Patro', u'Codino', u'Codino']
>>> x = "Fede"
>>> x not in [o.usr for o in db.select('users',what='usr')]
0.0 (2): SELECT usr FROM users
False
But when I run the code and I make a new registration with an existing username nothing happens.. as you can see the "Codino" username is been registered twice.
What I'm doing wrong?
..and more interesting: there is a smarter way to block the registration of an already used username?
Thanks,
Federico
I don't know if you already have an answer with this one since it's an old thread.
Here's what I did on checking if username already exist.
I create a validator such as these:
vuser_exist = form.Validator("Username already exist.", lambda u: u is None or model.get_user(u.username) is None)
register_form = form.Form(
form.Textbox("username", vuser_req, vuser_length, description="Username"),
form.Button("submit", type="submit", description="Register"),
validators = [vuser_exist],
)
In model.py
def get_user(user):
try:
return db.select('users', where='user=$user', vars=locals())[0]
except IndexError:
return None