On a website that I'm working on for my school, the user enters their school email and password, and if they have registered they log in. If not, a second part of the log in is revealed asking for a pen name and to confirm the password. Because of this, and my convoluted amateur Django programming, I have a list of errors named er. For instants, when the program tests whether the email is a school one, it might add to the er list "school email only". I am also using two form classes as well. The page uses ajax to call this function, which uses plain html instead of JSON because of the sites small size.
In the forms.py file I have:
class log_in(forms.Form):
username = forms.EmailField(error_messages= {'required': "Email Field is required,", 'invalid' : "Invalid Email Address."})
password = forms.CharField(help_text = 'Password Invalid')
class new_user(forms.Form):
username = forms.EmailField(error_messages = {'required': "Email Field is required,", 'invalid' : "Invalid Email Address."})
password = forms.CharField(required=True)
password2 = forms.CharField(required=True)
pen_name = forms.CharField(max_length=30, min_length=3, error_messages = {'required': "Pen Name is required", 'max_length': "Pen Name must be less than 30 characters", 'min_length': "Pen Name must be more than 3 characters"})
The problem is that I want to transfer the full error message that I specified in the error_message argument to the er list.
This is my views.py file
def user_log_in(request):
er = []
user_pass = log_in(request.POST)
if user_pass.is_valid(): # If it is valid at all
cleaned_info = user_pass.cleaned_data
email_bbn = cleaned_info['username'].split("#")
if 'bbns.org' in email_bbn: # Check if BBN email address
user_object = User.objects.filter(email = cleaned_info['username'])
if user_object.exists():
logged_in_user = auth.authenticate(username=cleaned_info['username'], password=cleaned_info['password'])
#add in is_active
if logged_in_user is not None: #If password is right
if user_object[0].get_profile().activated:
auth.login(request, logged_in_user)
return HttpResponseRedirect("")
else:
return HttpResponse("not_act")
else:
er.append("Incorrect Password")
else: # If new user
new_user_pass = new_user(request.POST)
if new_user_pass.is_valid():
cleaned_info_new = new_user_pass.cleaned_data
if cleaned_info_new['password'] == cleaned_info_new['password2']:
msg = "In order to activate your account at Knights of the Round Table, please click on this link:"
try:
send_mail('Activate', msg, 'michaelrgoldfine#gmail.com', [cleaned_info_new['username']], fail_silently=False)
new_user_object = User.objects.create_user(
username=cleaned_info_new['username'],
password=cleaned_info_new['password'],
email=cleaned_info_new['username']
)
new_user_profile = new_user_object.get_profile()
new_user_profile.pen_name = cleaned_info_new['pen_name']
new_user_profile.activated = False;
new_user_profile.save()
return HttpResponse("not_act")
except:
er.append("Error Sending Email")
else:
er.append('Passwords are not the same')
elif "TN" in request.POST: #If open but not filled in
print "TN"
er.append(new_user_pass.pen_name.error_messages)
else: # if new user field
print "n_usr"
return HttpResponse('n_usr')
else:
er.append("BBN email addresses only")
else:
for e in user_pass.errors:
er.append(e)
errors_template = Template("{% for e in errors %}<li>{{ e }}</li> {% endfor %}")
errors_html = errors_template.render(Context({'errors':er}))
return HttpResponse(errors_html)
I try to accsess the errors twice. Once, on the else you see right at the end with a for loop, and two elses up from that on elif 'TN'... The last one just returns the field thats invalid (so i get user_name or pen_name). The other one says that the form has no object pen_name or whatever I use it for.
It would be better to add errors to the actual form. Forms have an _errors dict attached to them that contain all the errors generated by the form. "Non-field errors" (errors that don't directly relate to a particular field or that relate to multiple fields) go in form._errors['__all__']. All field-specific errors go into the key of the field's name. So, errors for a foo field would go in form._errors['foo'].
Now, the list of errors for each item in the _errors dict is actually an ErrorList type, not a standard list. So, to add errors to the form you do:
from django.forms.util import ErrorList
form._errors.setdefault('foo', ErrorList()).append('Some error here')
Or, to add the error to non-field errors:
form._errors.setdefault('__all__', ErrorList()).append('Some error here')
Then, when your form renders, the errors will all fall naturally where they should, just like any normal validation error.
The array probably looks like error[i].field[i].error, so you're just calling the fieldname and not the error message. Call e.error in your Template() function.
Related
I'm working on a simple chat app that creates rooms for users to join. You enter your room name and it checks if the room exists already. If it does, you're redirected to the room. If not, it creates a new room, saves the room and redirects you there. The issue I'm having is in saving the new room that is created.
I keep getting a "DoesNotExist" error. "Room matching query does not exist."
Here is the code:
def check_view(request):
room_entity = request.POST['room_name']
username = request.POST['username']
if Room.objects.filter(name=room_entity).exists():
return redirect('/' + str(room_entity) + '/?username=' + str(username))
else:
new_room = Room.objects.create(name=room_entity)
new_room.save()
Room.save(new_room)
return redirect('/' + str(room_entity) + '/?username=' + str(username))
def room(request, room_info):
username = request.GET.get('username')
room_details = Room.objects.get(name=room_info)
return render(request, 'room.html', {
'username': username,
'room': room_info,
'room_details': room_details
})
room_details, created = Room.objects.get_or_create(name=room_info)
This will get the room if it exist or create it if its not found. The first variable in the returned tuple is the actual room object. The second is a bool to know if it was created(true) or if it already existed(false). Then you can create logic like:
room_details, created = Room.objects.get_or_create(name=room_info)
if created:
enter code here to initiate a new room
else:
enter code here to handle a room that already exists.
room_details = Room.objects.get(name=room_info)
If a Room object with name room_info does not exist in the database you will get this error.
Instead what you can do is:
try:
room = Room.objects.get(name=room_info)
except Room.DoesNotExist:
raise Http404("Given query not found....")
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.
Here is my code to check for already existing usernames :
def post(self):
have_error = False
username = self.request.get('username')
password = self.request.get('password')
verify = self.request.get('verify')
email = self.request.get('email')
params = dict(username= username,
email = email)
usernames = db.GqlQuery(' select * from Users where username = :1 ', username)
if not valid_username(username):
params['username_error'] = "Invalid username"
have_error = True
if usernames :
params['username_error'] = 'Username already exists'
have_error = True
With this code my form displays Username already exists for every username.
When the params['username_error'] = 'Username already exists' is changed to
params['username_error'] = usernames ,
the message displayed in the username_error field is :
<google.appengine.ext.db.GqlQuery object at 0x10a4e030>
I'd change your check to:
query = db.GqlQuery(' select * from Users where username = :1 ', username)
usernames = query.count(limit=2)
Now, the if usernames: guard will have the effect you desire.
The reason is simple: in your code, you build a query object, which you call usernames, but you never actually run that query -- you check the object for truthiness, which happens to hold (as does for almost all Python objects except empty containers and strings, None, zero numbers) but signifies nothing.
Here, I run the query in the simplest way -- just count how many items it would return (just-counting is a bit faster than actually fetching them:-) and with a very low limit (since we only care about counts of 0, which is correct, and 1, which means a duplicate).
Now usernames is a number counting how many other times that user name appears in the datastore (with 1 standing for "once or more":-) and the rest of your logic becomes accurate.
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")
class Member(models.Model):# member db table
userID = models.CharField(max_length=80,primary_key=True) #user id
password = models.CharField(max_length=32)# password
nickname = models.CharField(max_length=100)# user nickname
penalty = models.PositiveSmallIntegerField(max_length=10,default=0,null=True)
participation=models.ForeignKey('Room',default=None,blank=True,null=True)
def __unicode__(self):
return self.userID
def doJoin(request):
if request.is_ajax() and request.method == 'POST':
# check validation
userID = request.POST['userID']
userNickname = request.POST['nickname']
if (checkID(userID) == False) and (checkNickname(userNickname) == False) :
#save to the database
newUser = Member()
newUser.userID = userID
newUser.nickname = userNickname
newUser.password = request.POST['password']
print newUser.userID , newUser.nickname , newUser.password , newUser.penalty , newUser.participation
newUser.save() #<------------error line!!!!
return HttpResponse('true')
else:
return HttpResponse('false')
else:
HttpResponse('false')
line about 8
In function doJoin:
newUser.save() # <--- error... so sad...
What should I do? Help me please.
What's wrong in this source?
Do you have debugging turned off? If you're getting a 500 and you have debugging turned on, you'll get a stack trace with the exception.
What are checkID() and checkNickname() doing? If those are performing some sort of validation, you really should be doing that in a form class instead of in the view. I also wouldn't be pulling values directly out of the request.POST to populate your model. I would highly recommend retrieving those values from a form's cleaned_data dictionary.