I am trying to create a form to edit a user account.
This code is for a user that already exists.
As you can see, I have put in a return statement before the try/catch.
This will generate the expected integrity exception when a username already exists.
However if I comment that out and let it go into the try/catch block, no exception is generated, it simply redirects to the HTTPFound with no error.
the call to flush does nothing.
if form.validate():
user.username = form.username.data
if form.password.data != "":
user.password = encrypt(form.password.data)
user.is_active = form.is_active.data
user.email = form.email.data
user.groups = form.groups.data
# if i return here i will get integrity error as expected
#return HTTPFound(location=request.route_url('user_details', id=user.id))
with transaction.manager as tx:
try:
request.sqldb.flush()
# no exception is generated here
return HTTPFound(location=request.route_url('user_details', id=user.id))
except IntegrityError as e:
tx.abort()
if 'UNIQUE constraint failed: users.username' in str(e):
form.username.errors.append('Username already exists')
else:
errors.append('Integrity Error')
edit
here is the code that works
the transaction manager part was not even required:
user.username = form.username.data
if form.password.data != "":
user.password = encrypt(form.password.data)
user.is_active = form.is_active.data
user.email = form.email.data
user.groups = form.groups.data
try:
request.sqldb.flush()
return HTTPFound(location=request.route_url('user_details', id=user.id))
except IntegrityError as e:
if '(IntegrityError) duplicate key value' in str(e):
errors.append('Username already exists')
else:
errors.append('Integrity Error')
request.sqldb.rollback()
You need to fetch the model, and do changes to the model objects within the transaction manager, otherwise the session's flush() has a limited scope. The SQLAlchemy is smart enough to try to flush only that subset of changes that occur within the context manager. Thus:
with transaction.manager as tx:
try:
user = .... # get the user
user.username = form.username.data
if form.password.data != "":
user.password = encrypt(form.password.data)
user.is_active = form.is_active.data
user.email = form.email.data
user.groups = form.groups.data
request.sqldb.flush()
# no exception is generated here
return HTTPFound(location=request.route_url('user_details', id=user.id))
except IntegrityError as e:
tx.abort()
if 'UNIQUE constraint failed: users.username' in str(e):
form.username.errors.append('Username already exists')
else:
errors.append('Integrity Error')
My solution is based on global exception handling.
http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/pylons/exceptions.html
from pyramid.view as view
import sqlalchemy.exc
#view.view_config(context=sqlalchemy.exc.IntegrityError, renderer='json')
def integrity_error(exc, request):
request.response.status_code = 400
return {
'error': exc.orig.args[1]
}
or
#view.exception_view_config(sqlalchemy.exc.IntegrityError, renderer='json')
def integrity_error(exc, request):
request.response.status_code = 400
return {
'error': exc.orig.args[1]
}
The differernce is described at http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/views.html
In this way, I can get a RESTful Api from Pyramid. It is rough, but useful in fast prototyping, and can save lots of code in routes.
Related
I'm using fladk and flask-sqlalchemy for this project. In my register endpoint, I check to see if the email in the POST data already exists in the database.The db.session.query method returns None but later, when I try an insert the record a sqlalchemy.exc.IntegrityError is thrown. In this example below, the POST data already exists in the database so I would expect the server to respond with: User already exists.
#auth_ep.route('/register', methods=['POST'])
def register():
if request.method != 'POST':
return abort(405)
data = request.get_json()
print('Post ata:', data)
# Check if post data is valid
if (
not data
or not data.get('email')
or not data.get('password')
or not data.get('name')
or not data.get('phone')
or not data.get('dateOfBirth')
):
return {'message': 'Missing data'}, 400
# Check if user already exists
try:
user = db.session.query(User).filter_by(email=data['email']).first()
print('user is none? ', user is None)
# Create user
user = User(
data['name'],
data['email'],
data['password'],
data['phone'],
datetime.datetime.strptime(data['dateOfBirth'], '%Y-%m-%d')
)
# save user to db
db.session.add(user)
db.session.commit()
return {'message': 'User created'}, 201
except sqlalchemy.exc.IntegrityError:
print('User already exists', sqlalchemy.IntegrityError')
return {'message', 'User already exists'}, 400
Database contents
sqlite> SELECT * FROM user WHERE 1=1;
1|$2a$10$AuTLEo/2qlE0lZ7/zpfnUeRiM7Nw0bp/9AMOrz64NSJpfsNK.I1ly|Admin|admin#site.com|1234567890|2000-01-01 00:00:00.000000||||2022-04-04 13:47:16.971327|2022-04-04 13:47:16.971334
OUTPUT:
127.0.0.1 - - [04/Apr/2022 10:59:42] "OPTIONS /auth/register HTTP/1.1" 200 -
{'email': 'admin#site.com', 'password': '$2a$10$6UFc5wQjEXlNGFp6b56w6.kvPLqNUp7H89mTUGIa6cp588Jsj/1Bi', 'name': 'Admin', 'phone': '1234567890', 'dateOfBirth': '2000-01-01'}
user is none? True
User already exists. sqlalchemy.IntegrityError
This question might appear to be a duplicate and/or too boring. I have already read this, this, this, this and this questions & answers. But couldn't find solution that fits to my problem.
I'm new to Django framework. To learn it I want to create simple blog. When user clicks Register button (after filling required fields), following error thrown:
ValueError at /user/register/ The view user.views.register didn't
return an HttpResponse object. It returned None instead.
views.py
def register(request):
"""
Registers new user.
"""
if request.POST:
if request.method == 'POST':
personal_info = UserFormModel(request.POST)
if personal_info.is_valid():
email = personal_info.cleaned_data['email']
username = personal_info.cleaned_data['username']
if User.objects.filter(email=email).exists():
return HttpResponse('email error')
elif User.objects.filter(username=username).exists():
return HttpResponse('username error')
else:
return HttpResponse('saved')
else:
personal_info = UserFormModel()
return render_to_response('user/registration.html',
{
'title': 'Registration',
'username_error': 'Sorry, someone already has that username.',
'personal_info': personal_info,
},
context_instance=RequestContext(request))
If necessary I can share any files content.
Any helpful comment or answer would be appreciated.
In line
if personal_info.is_valid():
if personal info is not valid, it will return None.(return nothing)
add an else condition, because you are not handling a case.
if personal_info.is_valid():
# code here
else:
return HttpResponse('personal info provided not valid')
One of the better ways to handle situations like this from not occurring is to keep a dictionary for status message and result initialised at the start of the function, and return only at a single place instead of returning at multiple places, and storing result in result_dict.
result_dict = {'status': False}
and always return at the end of the function
return HttpResponse(json.dumps(result_dict))
This way you will never miss a case in returning values.
So Final code should look like
def register(request):
"""
Registers new user.
"""
result_dict = {'status': False}
if request.POST:
if request.method == 'POST':
personal_info = UserFormModel(request.POST)
if personal_info.is_valid():
email = personal_info.cleaned_data['email']
username = personal_info.cleaned_data['username']
if User.objects.filter(email=email).exists():
result_dict['message'] = 'email error'
elif User.objects.filter(username=username).exists():
result_dict['message'] = 'username error'
else:
result_dict['message'] = 'saved'
result_dict['status'] = True
else:
result_dict['message'] = 'personal info provided not valid'
else:
personal_info = UserFormModel()
return render_to_response('user/registration.html',
{
'title': 'Registration',
'username_error': 'Sorry, someone already has that username.',
'personal_info': personal_info,
},
context_instance=RequestContext(request))
return HttpResponse(json.dumps(result_dict))
Issue: Create a method that verifies if username exists when Creating an Account, deny account if username exists, and advises the user to create a new username and repeat the process should need be.
I have some idea of how to do this based on some STACKS questions (1, 2) that I have read on here. Such as something like:
Send the username to the server.
Check for the existence of the
username in the Database.
Respond to the client with true or false
depending on the presence of the username.
Based on the response, send
the user a client side alert!
I am uncertain how to properly execute the process in the Sign Up Page (also known as the Create an Account page) using Pyramid and SQLAlchemy. Since I am newbie, I want to make sure I am creating code that is fast, efficient and with smart design. I want to ensure I am staying within best practices.
Right now in the User database, Username is UNIQUE; this causes the system to crash when a user tries to create a username that exists in the db. My code is missing something as there is a traceback that indicates DETAIL: Key (username)=(baseball) already exists. Any help or suggestions is truly appreciated! If I have a poor method, suggestions of a better method is highly welcomed!
Software: Python 2.7, Pyramid 1.5.7, SQLAlchemy 1.0.9
views.py
(code: to the create a user page and save new user)
#view_config(route_name='create_user', request_method='GET', renderer='templates/create_account.jinja2')
def user_form_view(request):
return {}
#view_config(route_name='save_new_user')
def save_new_user(request):
with transaction.manager:
username = request.params['username']
check_username = api.retrieve_user(username) #retrieves one_user
#check_users = api.retrieve_users() #this retrieves ALL the users
taken = False
for user in check_username: #prints out all user info
if username == user.username:
taken = True
break
if taken:
username = request.params['username']
password = request.params['password']
firstname = request.params['firstname']
lastname = request.params['lastname']
email = request.params['email']
new_user = api.create_user(username, password, firstname, lastname, email)
new_account = api.update_group_add_user('Registered User', new_user)
transaction.commit()
return HTTPSeeOther(location=request.route_url('login'))
Traceback:
IntegrityError: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurring prematurely) (psycopg2.IntegrityError) duplicate key value violates unique constraint "users_username_key"
DETAIL: Key (username)=(baseball) already exists.
[SQL: 'INSERT INTO users (username, firstname, lastname, email, password, institution, created_on) VALUES (%(username)s, %(firstname)s, %(lastname)s, %(email)s, %(password)s, %(institution)s, %(created_on)s) RETURNING users.id'] [parameters: {'username': u'baseball', 'firstname': u'jlo', 'lastname': u'lo', 'institution': None, 'created_on': datetime.datetime(2015, 11, 24, 22, 27, 20, 286260), 'password': '78d8045d684abd2eece923758f3cd781489df3a48e1278982466017f', 'email': u'j'}]
Updates based on suggestion below
Question:
Where should I create the function validate_registration -- outside of the registration_view function? Should this be a Boolean statement? Is this the best method? Where would transaction.commit() exist?
View Code with GET and POST:
def validate_registration_form(request):
with transaction.manager:
username = request.params['username']
check_username = api.retrieve_user(username)
password = request.params['password']
firstname = request.params['firstname']
lastname = request.params['lastname']
email = request.params['email']
if check_username is not None:
return False
else:
return True
#view_config(route_name='registration', renderer='templates/create_account.jinja2')
#view_config(route_name='save_registration', renderer='templates/create_account.jinja2')
def registration_view(request):
if request.method == 'GET':
return {} # render the empty form
elif request.method == 'POST':
if validate_registration_form(request): #save new_user and redirect
new_user = api.create_user(username, password, firstname, lastname, email)
new_account = api.update_group_add_user('Registered User', new_user)
transaction.commit()
raise HTTPSeeOther(location=request.route_url('login'))
else:
# form is not valid, re-render the form
# with the data user entered and an error message
return {
'error_message': 'username already taken',
'username': request.POST.get('username', ''),
'password': request.POST.get('password', ''),
'firstname': request.POST.get('firstname', ''),
'lastname': request.POST.get('lastname', ''),
'email': request.POST.get('email', '')
}
form:
<form action="/save_registration" method="POST">
<div class="form-group">
<dl>
<dt><label for = 'username'> Username: <em>single word--no spaces</em> </label></dt>
#more ....
Well, the classic approach to server-side form validation on submit is something like this (in pseudocode):
#view_config(route_name='registration', renderer='my_rego_form.jinja2') # handles both GET and POST
def rego_form(request):
if request.metod == 'GET':
return {} # render the empty form
elif request.method == 'POST':
if validate_rego_form(request):
# create a new user and redirect elsewhere
create_user(request)
raise HTTPFound('/post_registration_page')
else:
# form is not valid, re-render the form
# with the data user entered and an error message
return {
'error_message': 'Error creating user',
'username': request.POST.get('username', '')
'email': request.POST.get('email', '')
'phone_num': request.POST.get('phone_num', '')
}
else:
# some other HTTP method, not sure how to deal
So, basically, the form needs to be able to re-render itself with the data submitted by the client.
The validation method itself can be trivial and just check for the user with the given email in the db. Or, as you tried to do, instead of pre-validation you can try to just create a record and handle the exception if one occurs.
I am using Flask+Python and to check if a username (and email) is already taken or not i am using this logic:
#app.route('/register', methods=['GET', 'POST'])
def register():
form = SignupForm()
if form.validate_on_submit():
user = Users.query.filter_by(username=form.username.data).first()
email = Users.query.filter_by(email=form.email.data).first()
if form.username.data in user:
error = 'Username already taken. Choose another'
elif form.email.data in email:
error = 'Email already registered. Login or register with another Email'
else:
user = Users(
form.username.data,
form.password.data,
#form.confirm.data ,
form.email.data,
1,
# form.cityaddress.data,
# form.countryaddress.data,
#form.accept_tos.data,
)
db.session.add(user)
db.session.commit()
return redirect(url_for('index'))
But its giving error like object has no attribute 'username'
I know my logic for fetching data from db is not correct. I have little knowledge of SQLalchemy.
Could you suggest me How can i fetch Username (and Email) column value from table Users and then check them if there are same as form.username.data ?
Your queries look fine, the return value from first() will be an instance of your User object, or None if there were no results:
u = Users.query.filter_by(username=form.username.data).first()
if u is not None:
print u.username
print u.email
So given that, here's what your logic could look like:
user_by_name = Users.query.filter_by(username=form.username.data).first()
user_by_email = Users.query.filter_by(email=form.email.data).first()
if user_by_name:
error = 'Username already taken. Choose another'
elif user_by_email:
error = 'Email already registered. Login or register with another Email'
else:
#Unique user and email
You could also do it in one query:
existing = Users.query.filter((Users.username == form.username.data) | (Users.email == form.email.data)).all()
if existing:
error = 'User or email taken'
Note the use of filter rather than filter_by - you cant use the bitwise operators in filter_by. Here's a quick working example
Your error confuses me. That said, your code looks okayish, except for the test. I use this then:
user = Users.query.filter_by(username=form.username.data).first()
...
if user is not None:
error("user already found")
I'm trying to test the content of messages while processing ModelForms with Django. I've got the following View (assume there's a Thing model with a required name field):
#login_required
def update(request, thing_id):
thing = Thing.objects.get(id=thing_id) # assume this works
if request.method == "POST":
form = ThingModelForm(request.POST, instance=thing)
if form.is_valid():
form.save()
messages.success(request, "Success!")
return redirect("/wherever")
else:
messages.error(request, "Oops!")
else:
form = ThingModelForm(instance=thing)
args = ("myapp/update.html", {"form": form})
kwargs = {"context_instance": RequestContext(request)}
return render_to_response(*args, **kwargs)
Now, I've got two unit tests. The first tests valid data, while the second tests invalid data. (Note that client login happens during setUp):
def test_update_account(self):
url = reverse('update', args=[1]) # assume that's a valid id
resp = self.client.post(url, {"name": "foo"})
self.assertEqual(resp.status_code, 302)
m = resp.cookies.get('messages', '')
self.assertTrue("Success!" in m.output())
And now to test invalid data:
def test_update_account_failure(self):
url = reverse('update', args=[1]) # assume that's a valid id
resp = self.client.post(url, {"name": ""}) # name is required
self.assertEqual(resp.status_code, 200)
# This works:
self.assertTrue("Oops!" in resp.content)
# This fails:
m = resp.cookies.get('messages', '')
self.assertTrue("Oops!" in m.output())
Why would accessing the message's content through the cookie work in one instance but fail in another?
Two things you could check:
When you create the request self.client.post(url, {"name": ""}) is a Thing instance returned here: thing = Thing.objects.get(id=thing_id) If not it will not reach the line of code where you set your error message: messages.error(request, "Oops!") as Thing.objects.get will throw an error.
If there are no results that match the query, get() will raise a
DoesNotExist exception.
If the first thing does return a Thing instance, you could check whether a redirect return redirect("/wherever") after setting the error message messages.error(request, "Oops!") does change anything.