I am wondering if I can add a requirment to the setup of my User document to check for a specific string. The idea is when a User Document is created with an email address, I want to make sure the email is from a college so it should end in ".edu"
example:
"john.doe#college.edu" is acceptable but "john.doe#gmail.com" is not
Here is my code:
class User(db.Document, UserMixin):
name = db.StringField(max_length=255, unique=True)
email = db.StringField(max_length=255, unique=True)
phone = db.StringField(max_length=255, unique=True)
password = db.StringField(max_length=255)
active = db.BooleanField(default=True)
confirmed_at = db.DateTimeField()
roles = db.ListField(db.ReferenceField(Role), default=[])
There is an optional regex argument you can define:
email = db.StringField(max_length=255, unique=True, regex=r'.*?boston\.edu$')
And, also, why not use a specific EmailField in this case:
email = db.EmailField(max_length=255, unique=True, regex=r'.*?boston\.edu$')
Related
The def clean_clean email isn't working. This function should do two things as shown by two ifs. The first checks if no changes have been made to the field and if no changes have been made validation doesn't start, this currently work. The second if should check if the email entered already exists and if so present a validation error message. As the second if doesn't if an exisitng email is entered this leads to SQL IntegrityError.
Model.py - table
class User(db.Model, TimestampMixin, UserMixin):
__tablename__ = 'user'
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), nullable=False, unique=True)
email = db.Column(db.String(80), nullable=False, unique=True)
password_hash = db.Column(db.String(128))
first_name = db.Column(db.String(20), nullable=False)
last_name = db.Column(db.String(20), nullable=False)
forms.py
class EditUserForm(FlaskForm):
first_name = StringField('First Name', validators=[DataRequired()])
last_name = StringField('Last Name', validators=[DataRequired()])
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
password2 = PasswordField(
'Repeat Password', validators=[DataRequired(), EqualTo('password')])
update = SubmitField('Update')
cancel = SubmitField('Cancel')
def clean_email(self):
cd = self.cleaned_data
email = cd(email=email.data)
# if email is not modified, so don't start validation flow
if self.instance.pk is not None and self.instance.email == email:
return cd
# check email is unique or not
if User.objects.filter(email=email).exists():
raise ValidationError("Email address already exists!".format(email))
return cd
SQL error when a existing email is entered
sqlite3.IntegrityError: UNIQUE constraint failed: user.email
In reference to this error listed:
sqlite3.IntegrityError: UNIQUE constraint failed: user.email
This means that you're trying to save a new entry into your 'user' table with an email that already exist. You must have the constraint in your models.py file that says something like
email = db.Column(db.String(120), unique=True, nullable=False)
If you want users to be able to save an email that's already being used by a different user, then you need to change this to...
email = db.Column(db.String(120), unique=False, nullable=False)
It's not clear to me what you're trying to do, but if you want to do a query to find if the email is in use by someone besides the current user, you could do something like this:
email_already_in_use_boolean = bool(db.session.query(User).filter(User.email==email.data).filter(User.id!=current_user_id).first())
I am using Flask as a Rest API for my WebApp.
In the frontend i use the User object quite often, which is why i need it from the backend to work with the user data.
My concern is, that the user object has an attribute password, which is obviously also sent to the frontend, when i make a request for a user object.
Should i define another class like UserPublic to send to the frontend and just strip out the password or is there a better way to do this with Flask, SQLAlchemy, Marshmallow?
I'm not sure if it's even a problem sending the password hash+salt to the frontend. I mean, i don't need it there, so why send it? Password check for login purposes is done in the backend anyway.
This is my User class:
class User(db.Model):
__tablename__ = 'user'
user_id = db.Column(db.Integer, primary_key=True,autoincrement=True, nullable=False)
public_id = db.Column(db.String, nullable=False)
fname = db.Column(db.String, nullable=False)
lname= db.Column(db.String, nullable=False)
bday = db.Column(db.Date, nullable=False)
street = db.Column(db.String, nullable=False)
zip = db.Column(db.String, nullable=False) #Zip used
city = db.Column(db.String, nullable=False)
country = db.Column(db.String, nullable=False, default='Germany')
password = db.Column(db.String, nullable=False)
admin = db.Column(db.Boolean, default=False)
email = db.Column(db.String, nullable=False)
iban = db.Column(db.String)
bic = db.Column(db.String)
gender = db.Column(db.CHAR, default='m', nullable=False)
created_by = db.Column(db.String)
updated_by = db.Column(db.String)
membership_status_id = db.Column(db.Integer, db.ForeignKey('membership_status.membership_status_id'))
member_since = db.Column(db.Date)
bookings = db.relationship('Booking', backref="User", lazy='select')
Marshmallow Schema:
class UserSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = User
include_fk = True
This is the endpoint to get a user object:
#app.route('/users/<public_id>', methods=['GET'])
#jwt_required
def get_user(public_id):
logger.info('Getting user with id: '+str(public_id))
current_user = User.query.filter_by(public_id=get_jwt_identity()).first()
if not current_user.admin:
return jsonify({'message' : 'Not privileged for this action'})
user = User.query.filter_by(public_id=public_id).first()
if not user:
return jsonify({'message' : 'No user found with id '+str(public_id)})
user_schema = UserSchema()
return user_schema.jsonify(user), 200
See doc about overriding generated fields.
Here's how to exclude the field from the auto-generated schema:
class UserSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = User
include_fk = True
exclude = ("password", )
# You may want to only exclude id on dump but keep it on load
# In this case, add it here by calling `auto_field` yourself
password = ma.auto_field(load_only=True)
I have the following user class,
class User(AbstractBaseUser, PermissionsMixin, Base):
email = models.EmailField(db_index=True, unique=True, max_length=255)
mobile = PhoneNumberField(null=True)
username = models.CharField(db_index=True, null=False, unique=True, max_length=255)
And I've the following room object,
class Room(Base):
name = models.CharField(db_index=True, unique=True, max_length=255)
status = models.CharField(default=RoomStatus.ACTIVE, max_length=255, null=True)
members = models.ManyToManyField(User)
last_activity = models.DateTimeField(default=timezone.now)
I'm trying to find if an user is present in the room as a member,
user = User.objects.get(id=kwargs.get('user_id'))
room = Room.objects.get(id=kwargs.get('room_id'))
This doesn't work,
member = room.members.filter(user=user)
How can I do this correctly
With a single query, you can filter on:
Room.objects.filter(id=kwargs['room_id'], members__id=kwargs['user_id']).exists()
Or if you need the Room and User anyway anyway:
room = Room.objects.get(id=kwargs['room_id'])
user = User.objects.get(id=kwargs.get('user_id'))
if user in room.members.all():
# ...
pass
You can also filter the room.members for id=user_id:
if room.members.filter(id=kwargs['user_id']).exists():
# ...
pass
I have created the following code to build a custom Django authentication model and I am attempting to create a superuser with the custom user manager but when I attempt to use the python manage.py createsuperuser command, I receive the following error: TypeError: create_superuser() got an unexpected keyword argument 'password'
UserHandler/models.py
# Custom Auth Manager
class AuthManager(BaseUserManager):
def create_superuser(self, supplied_email, supplied_password, supplied_first_name, supplied_last_name, supplied_language):
# Make this password great again (w/ salting and hashing)
hashed_salted_password = AbstractBaseUser.set_password(supplied_password)
superuser = AuthLookup()
superuser.email_address = supplied_email
superuser.password = hashed_salted_password
superuser.first_name = supplied_first_name
superuser.last_name = supplied_last_name
superuser.language = supplied_language
superuser.save()
# Abstract Auth Model
class AuthLookup(AbstractBaseUser, PermissionsMixin):
email_address = models.EmailField(unique=True)
password = models.CharField(max_length=100)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
organization_id = models.UUIDField(null=False, max_length=36, default=uuid.uuid4)
# Firebase tokens(Max length represents 5 device tokens at maximum length of 256 characters plus 4 commas.)
associated_devices = models.CharField(max_length=1284)
is_requester = models.BooleanField(null=False, default=0)
is_responder = models.BooleanField(null=False, default=0)
user_identifier = models.UUIDField(null=False, max_length=36, default=uuid.uuid4)
language = models.CharField(max_length=6, default='US_EN')
user_added = models.DateTimeField(default='1970-01-01 00:00:00')
object = AuthManager()
USERNAME_FIELD = 'email_address'
REQUIRED_FIELDS = ['first_name', 'last_name', 'language']
Settings.py
AUTH_USER_MODEL = 'UserHandler.AuthLookup'
Turns out that the arguments passed to the create_superuser functions must be named the same as the fields in the model.
The error is not present now. (Ive moved on to different errors unfortunately)
class AuthManager(BaseUserManager):
def create_superuser(self, email_address, password, first_name, last_name, language):
# Keep everyone honest here, make sure the email is valid
email_address = self.normalize_email(email_address)
# Make this password great again (w/ salting and hashing)
password = AbstractBaseUser.set_password(password)
superuser = AuthLookup()
superuser.email_address = email
superuser.password = password
superuser.first_name = first_name
superuser.last_name = last_name
superuser.language = language
superuser.save(using=self._db)
I'm working on an edit user page for my flask app, and I can't seem to figure out how to render the user's current roles like I do other variables, such as email.
Here's my models, form, and view:
#Flask-Principal Role Model
class Role(db.Model, RoleMixin):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
def __repr__(self):
return '<Role %r>' % (self.name)
#SQLALchemy User Model
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key = True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
first_name = db.Column(db.String(128))
last_name = db.Column(db.String(128))
business_name = db.Column(db.String(128))
active = db.Column(db.Boolean())
roles = db.relationship('Role', secondary=roles_users,
backref=db.backref('users', lazy='dynamic'))
#WTForms User Form
class UserForm(Form):
first_name = StringField('first name', validators= [Required()])
last_name = StringField('last name', validators= [Required()])
business_name = StringField('business name', validators= [Required()])
email = StringField('email', validators = [Required(), Email()])
active = BooleanField('active')
roles = MultiCheckboxField('roles', coerce=int)
#Edit User View
#app.route('/admin/users/<id>/edit/', methods = ['GET', 'POST'])
#roles_required('admin')
def edit_user(id):
user = User.query.filter_by(id = id).first()
editform = UserForm()
# This is how I've assigned choices for other MultiCheckboxField forms, but I haven't
# needed to populate the MultiCheckboxField from a user model before with role objects.
editform.roles.choices = [(x.id,x.name) for x in Role.query.all()]
if editform.validate_on_submit():
pass
editform.first_name.data = user.first_name
editform.last_name.data = user.last_name
editform.business_name.data = user.business_name
editform.email.data = user.email
editform.active.data = user.active
#The below doesn't show all the available roles, just the current roles assigned.
editform.roles.data = user.roles
return render_template("user_edit.html",
title = "Edit User",
user = user,
editform = editform)
So then, does anyone know how to have WTForms display all the Roles available, and have checked the ones which are currently in the user.roles list?
data for a multi-select field is a list of form_data after it has been coerced by your coerce callable - so what you provide needs to match what's available as the first entry in each tuple you provide to choices - in this case, an integer. That said, if you change your code to:
editform.roles.data = [role.id for role in user.roles]
you should see all the appropriate checkboxes checked.