6

My small registration app gives and error when I try to validate the submited data by user and check if the entered e-mail exists.

here is my files:
forms:

from flask.ext.wtf import Form
from wtforms import TextField, BooleanField, PasswordField, TextAreaField, validators
from wtforms.validators import Required
from app import models

class RegisterForm(Form):
"""RegisterForm class needed for retrieving data from user"""
username = TextField('username', [validators.Length(min=3, max=50), validators.Required()])
email = TextField('email', [validators.Length(min=3, max=100), validators.Required()])
password = PasswordField('password', [validators.Required()])
age = TextField('age', [validators.Length(min=1, max=3), validators.Required()])
about_user = TextAreaField('about_user', [validators.Length(max=500)])
img_url = TextField('img_url')


def email_unique(self, email):
    if models.User.query.filter_by(email = email).first() != None:
        self.email.errors.append('This E-mail address is already in use. Please choose another one.') 
        return False

views:

#!flask/bin/python
from app import app, db, lm
from flask import render_template, url_for, flash, g, redirect, session, request
from flask.ext.login import login_user, logout_user, current_user, login_required
from forms import LoginForm, RegisterForm, EditForm
from models import User

@app.route('/register', methods = ['GET', 'POST'])
def register():
    form = RegisterForm()
    #makes the username unique
    u_unique =  form.username.data
    u_unique = User.unique_username(u_unique)

    #validates email adress and checks if it already exists or not 
    form.email_unique(form.email.data)

    if form.validate_on_submit():
        user = User(
            u_unique,
            form.password.data, 
            form.email.data, 
            form.age.data, 
            form.about_user.data,
            form.img_url.data)
        db.session.add(user)
        db.session.commit()
        flash('Thank you for your registration')
        flash('Your username is: ' + str(u_unique))
        return redirect(url_for('login'))
    else:
        for error in form.errors:
            flash(error)

    return render_template('register.html',
        title = 'Registeration',
        form = form)

The error is:

Traceback (most recent call last) File <br> "/home/maksad/Desktop/faskMonkey/flask/lib/python2.7/site-packages/flask/app.py", line 1701, in __call__ return self.wsgi_app(environ, start_response) 
File "/home/maksad/Desktop/faskMonkey/flask/lib/python2.7/site-packages/flask/app.py", line 1689, in wsgi_app response = self.make_response(self.handle_exception(e)) 
File "/home/maksad/Desktop/faskMonkey/flask/lib/python2.7/site-packages/flask/app.py", line 1687, in wsgi_app response = self.full_dispatch_request() 
File "/home/maksad/Desktop/faskMonkey/flask/lib/python2.7/site-packages/flask/app.py", line 1360, in full_dispatch_request rv = self.handle_user_exception(e) 
File "/home/maksad/Desktop/faskMonkey/flask/lib/python2.7/site-packages/flask/app.py", line 1358, in full_dispatch_request rv = self.dispatch_request() 
File "/home/maksad/Desktop/faskMonkey/flask/lib/python2.7/site-packages/flask/app.py", line 1344, in dispatch_request return self.view_functions[rule.endpoint](**req.view_args) 
File "/home/maksad/Desktop/faskMonkey/app/views.py", line 92, in register form.email_unique(form.email.data) 
File "/home/maksad/Desktop/faskMonkey/app/forms.py", line 26, in email_unique
 self.email.errors.append('This E-mail address is already in use. Please choose another one.')
 AttributeError: 'tuple' object has no attribute 'append'
davidism
  • 121,510
  • 29
  • 395
  • 339
Max
  • 2,425
  • 6
  • 35
  • 54

4 Answers4

14

The tuple objects cannot append. Instead, convert to a list using list(), and append, and then convert back, as such:

>>> obj1 = (6, 1, 2, 6, 3)
>>> obj2 = list(obj1) #Convert to list
>>> obj2.append(8)
>>> print obj2
[6, 1, 2, 6, 3, 8]
>>> obj1 = tuple(obj2) #Convert back to tuple
>>> print obj1
(6, 1, 2, 6, 3, 8)

Hope this helps!

Community
  • 1
  • 1
A.J. Uppal
  • 19,117
  • 6
  • 45
  • 76
  • Thank you very much. I tried a little different but using your method :) – Max Apr 06 '14 at 02:55
  • Glad to be of help :) – A.J. Uppal Apr 06 '14 at 03:05
  • 1
    The [wtforms documentation states](http://wtforms.simplecodes.com/docs/1.0.3/fields.html#wtforms.fields.Field.errors) that `errors` is a list: `errors: If validate encounters any errors, they will be inserted into this list.` This was in any case the source of my confusion. – Jon Aug 25 '14 at 14:29
  • 2
    the detail here is that Form.validate() seems to set errors to a list – ezdazuzena Jun 29 '15 at 15:09
  • @ezdazuzena Thanks for mentioning that. I was wondering why a change in code (not calling validate anymore) suddenly complained about `errors` being a tuple whereas it previously always was a list. – Tim May 04 '16 at 09:43
  • 1
    It seems that the type of `errors` varies depending on what server (middleware?) is being used. In the built-in Flask server, it's a tuple; when using gunicorn, it's a list. Coercing `errors` to a list and then appending to it (then setting `errors` to that list) will work no matter what server you are running. – Dan Tenenbaum Apr 24 '17 at 20:17
5

Just came across this myself. I think a better answer to your question is that you should validate the elements before you add errors to them. The validation process sets the error field to a list and if you change it before you validate fields it will be written over when you validate.

So override the validate method of your form, call the parent validate method then run your email_unique method in that.

Then you can remove the email_unique from the view since it will be part of your validate_on_submit

3

tuples are immutable types which means that you cannot splice and assign values to them. If you are going to be working with data types where you need to add values and remove values, use list instead:

>>> a = (1,2,3)
>>> a.append(2)
AttributeError: 'tuple' object has no attribute 'append'
>>> b = [1,2,3]
>>> b.append(2)
[1,2,3,2]
sshashank124
  • 31,495
  • 9
  • 67
  • 76
2

The answer is a bit deeper. "errors" is a tuple when Field class created.

class Field(object):
    """
    Field base class
    """
    errors = tuple()
    

But when method validate called, it convert it to list

def validate(self, form, extra_validators=tuple()):
    self.errors = list(self.process_errors)
    

So everything you need is just to call your email_unique function right after validate_on_submit, which in turns call validate function of the form and convert errors to list

@app.route('/register', methods = ['GET', 'POST'])
def register():
    form = RegisterForm()
    #makes the username unique
    u_unique =  form.username.data
    u_unique = User.unique_username(u_unique)

    #validates email adress and checks if it already exists or 

    if form.validate_on_submit():
        form.email_unique(form.email.data)
        user = User(
            u_unique,
            form.password.data, 
            form.email.data, 
            form.age.data, 
            form.about_user.data,
            form.img_url.data)
        db.session.add(user)
        db.session.commit()
        flash('Thank you for your registration')
        flash('Your username is: ' + str(u_unique))
        return redirect(url_for('login'))
    else:
        for error in form.errors:
            flash(error)

    return render_template('register.html',
        title = 'Registeration',
        form = form)
Logovskii Dmitrii
  • 2,629
  • 4
  • 27
  • 44