How to setup tables/rows for flask app in Heroku postgreSQL database - flask-sqlalchemy

I am currently trying to set up my database tables/rows for a PostgreSQL/Flask app that I recently deployed to Heroku, but am unsure of how to go about it. Here are my tables in the models.py file:
class User (db.Model):
"""User in the system"""
__tablename__ = "users"
id = db.Column(
db.Integer,
primary_key=True
)
firstname = db.Column(
db.Text,
nullable=False
)
lastname = db.Column(
db.Text,
nullable=False
)
username = db.Column(
db.Text,
nullable=False,
unique=True
)
email = db.Column(
db.Text,
nullable=False,
unique=True
)
password = db.Column(
db.Text,
nullable=False
)
image = db.Column(
db.Text
)
state = db.Column(
db.Text,
nullable=False
)
vax_date = db.Column(
db.Text
)
covid_status = db.Column(
db.Text
)
review = db.relationship('Review', backref='users')
#classmethod
def signup(cls, firstname, lastname, username,
email, password, image, state, vax_date, covid_status):
"""Sign up user. Hashes password and adds user to system."""
hashed_pwd = bcrypt.generate_password_hash(password).decode('UTF-8')
user = User(
firstname=firstname,
lastname=lastname,
username=username,
email=email,
password=hashed_pwd,
image=image,
state=state,
vax_date=vax_date,
covid_status=covid_status)
db.session.add(user)
return user
#classmethod
def authenticate(cls, username, password):
user = cls.query.filter_by(username=username).first()
if user:
is_auth = bcrypt.check_password_hash(user.password, password)
if is_auth:
return user
return False
class Review (db.Model):
"""Reviews made by users for testing locations"""
__tablename__ = "reviews"
id = db.Column(
db.Integer,
primary_key=True
)
location = db.Column(
db.Text,
nullable=False
)
description = db.Column(
db.Text,
nullable=False
)
user_id = db.Column(
db.Integer,
db.ForeignKey('users.id', ondelete='CASCADE'),
nullable=False
)
user = db.relationship('User', backref="reviews", passive_deletes=True)
Right now when I try to create a user, I am able to create an account, but certain features do not work and I keep losing the user's session. I understand that this stems from the tables/rows not being created. How can I set this information up on my Heroku database?
UPDATE
after running heroku run python manage.py shell and db.create_all() I receive nothing. Here is a photo:
photo

Related

Flask SQLAlchemy have changes in parent database show up in child databases

I'm trying to set up my database such that any changes that occur in the parent database also shows up in the children databases as well. I have been looking at cascade and trying different variations but nothing is working.
class users(db.Model):
swipeNum = db.Column(db.BigInteger, nullable=False)#up to 18 digits safely
UID = db.Column(db.Integer, primary_key=True, nullable=False)
directoryID = db.Column(db.String(strlen), nullable=False)
firstName = db.Column(db.String(strlen), nullable=False)
lastName = db.Column(db.String(strlen), nullable=False)
email = db.Column(db.String(strlen), nullable=False)#maybe nullable?
userType = db.Column(db.Integer, nullable=False, default=0)
requests = db.relationship('requests', backref='requestor', lazy='dynamic', cascade='all, delete, delete-orphan')
class requests(db.Model):
requestID = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
reason = db.Column(db.String(strlen), nullable=False)#might need something more robust
# foreign
user = db.Column(db.Integer, db.ForeignKey('users.UID'))
And this is my test that I'm running. Only the last assert fails.
# Change UID of user 1234 to 4321
user = api.db.session.query(models.users).filter_by(UID=1234).first()
assert user.firstName == "Terrapin"
user.UID = 4321
api.db.session.commit()
api.db.session.close()
user2 = api.db.session.query(models.users).filter_by(UID=4321).first()
assert user2.UID == 4321
# Get lab permissions for 4321 and assert the same
user = api.db.session.query(models.requests).filter_by(user=4321).first()
assert user.user == 4321
Any help would be incredibly appreciated!

ORM query many to many to one, Flask sql-alchemy

I have three flask-sqlalchemy-models. Books - unique entries by admin:
class Book(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
book_name = db.Column(db.String(20), unique=True, nullable=False)
def __repr__(self):
return self.book_name
Bookscomp - entries by users, related to above:
class Bookscomp(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
book = db.Column(db.Integer, db.ForeignKey('book.id'))
Company - user, related to above:
class Company(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(100), nullable=False)
books = db.relationship('Bookscomp', secondary=companybook, lazy='dynamic',
backref=db.backref('company'))
companybook = db.Table('companybook',
db.Column('companyid', db.Integer, db.ForeignKey('company.id'), primary_key=True),
db.Column('bookid', db.Integer, db.ForeignKey('bookscomp.id'), primary_key=True),
)
Problem: I am trying to get book_name from Book model, through Company + Bookscomp.
So a company has many books and each book has reference to general book info.
Tried like this:
company = OrganizatCompanyion.query.filter_by(id=comp.id).first()
books = company.books.all()
for item in books:
print(item.book.book_name)
#AttributeError: 'int' object has no attribute
print(item.book)
#Gives book id from book model, but I need name
Why I cannot get book_name in the above code snippet directly? And how would it be best to achieve this?
You haven't defined a relationship between Book and Bookcomp so when you ask for item.book-- it's getting the book = db.Column(db.Integer... value. Maybe tweak your Bookscomp model to be something like:
class Bookscomp(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
book_id = db.Column(db.Integer, db.ForeignKey('book.id'))
book = db.relationship('Book')

Edit many-to-many relationship in Flask-SQLAlchemy withg WTForms

I struggle handling a many-to-many relationship (here: users and groups) in a Flask form. My database structure (simplified) looks as follow:
association_user_group = db.Table(
'association_user_group',
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('group_id', db.Integer, db.ForeignKey('group.id'))
)
class Group(db.Model):
__tablename__ = 'group'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(32))
users = db.relationship(
'User',
secondary=association_user_group,
backref=db.backref('groups', lazy='dynamic'))
class User(UserMixin, db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
#property
def group_ids(self):
return [u.id for u in self.groups]
To handle a form that allows an admin to edit the users, I have:
class MultiCheckboxField(SelectMultipleField):
widget = widgets.ListWidget(prefix_label=False)
option_widget = widgets.CheckboxInput()
class UserEditForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password')
groups = MultiCheckboxField('Groups', coerce=int)
submit = SubmitField('Apply changes')
and the following route:
#bp.route('/user_edit/<int:id>', methods=['GET', 'POST'])
#login_required
#admin_required
def user_edit(id):
user = User.query.get(id)
if request.method == 'GET':
form = UserEditForm(obj=user)
form.groups.data = [grp.id for grp in user.groups]
else:
form = UserEditForm(request.form)
form.groups.choices = [(grp.id, grp.name) for grp in Group.query.all()]
if form.validate_on_submit():
#form.populate_obj(user) <-- does not work
user.username = form.data.username
if form.password.data != '':
user.set_password(form.password.data)
# how do I update the 'groups'?
db.session.add(user)
db.session.commit()
return "Data={}".format(form.data)
The database and form works, but I am unable to copy the groups form content back to the database. Ideally, I'd be able to 'populate back' the form content to the User object, but this fails because of the groups (and I think it might also fail with the password field that is empty if no change is requested).
I then tried to delete all groups from user and re-add the ones I want, but did not figure out a reasonable way to achieve this. I am able to add an group membership with user.groups.append(grp) where grp is the corresponding database object. I also am able to remove a group membership in the same way, but given the group ids this would mean looping through all group ids, retrieving the group object, and then using this object to invoke the remove method, which seems overly complicated.
Overall, I suspect that I attempt to implements all this in a far too awkward way...

SQLAlchemy Find a user with a single query (probably join necessary)

I have three models which have relationships.
Firstly, the participant model.
class Participant(UserMixin, db.Model):
__tablename__ = 'participants'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), index=True)
team_id = db.Column(db.Integer, db.ForeignKey('teams.id'))
# Relationships
team = db.relationship("Team", back_populates="members")
Secondly, the event model.
class Event(db.Model):
__tablename__ = 'events'
id = db.Column(db.Integer, primary_key=True)
Thirdly, the team model.
class Team(db.Model):
__tablename__ = 'teams'
id = db.Column(db.Integer, primary_key=True)
event_id = db.Column(db.Integer, db.ForeignKey('events.id'))
# Relationships
members = db.relationship('Participant', back_populates="team")
Several participants are allowed to have the same email address if their team is not connected to the same event.
I am looking for a query which checks if there is a participant with a given email address who is connected to a team, which is connected to the same event. I know the event.id and the email address in advance.
Pseudo code
def check(EVENTID, EMAIL):
if db.session.query(Team, Event, Participant). \
filter(Team.event_id == EVENTID). \
filter(Participant.team_id == Team.id). \
filter(Participant.email == EMAIL).first():
return true
I think it can be done with one single query using joins, but I couldn't figure it out. Please help!

nested sqlalchemy filter with parent and son

With the following scheme:
class User(Base):
id = Column(Integer, primary_key=True)
name = Column(String)
class Photo(Base):
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey(User.id), nullable=False)
user = relationship(User)
class Tag(Base):
id = Column(Integer, primary_key=True)
tag_name = Column(String)
tag_version = Column(Integer)
photo_id = Column(Integer, ForeignKey(Photo.id), nullable=False)
photo = relationship(Photo)
How do I create an SQLAlchemy query to get all the photos of a specific user, that don't have a specific tag and version.
As in "all the photos of the user with id "1234" that don't have a "cat" of version "2" tagged in them".
Also interesting would be "all the users who have at least one photo without a specific tag"
I'm using postgreSQL btw.
Here is a complete example that sets up relationships, creates some sample data, then performs your two queries.
Setup:
from datetime import datetime
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey, not_
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
engine = create_engine('sqlite:///', echo=True)
Session = sessionmaker(bind=engine)
session = Session()
Base = declarative_base(bind=engine)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
class Photo(Base):
__tablename__ = 'photo'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
user_id = Column(Integer, ForeignKey(User.id), nullable=False)
user = relationship(User, backref='photos')
class Tag(Base):
__tablename__ = 'tag'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
photo_id = Column(Integer, ForeignKey(Photo.id), nullable=False)
photo = relationship(Photo, backref='tags')
Base.metadata.create_all()
session.add(User(name='davidism', photos=[
Photo(name='sun', tags=[Tag(name='bright'), Tag(name='day')]),
Photo(name='moon', tags=[Tag(name='bright'), Tag(name='night')])
]))
session.add(User(name='eran', photos=[
Photo(name='party', tags=[Tag(name='people'), Tag(name='night')]),
Photo(name='cat')
]))
session.commit()
Query all photos with no tags at all:
no_tags = session.query(Photo).outerjoin(Photo.tags).filter(not_(Photo.tags.any())).all()
print 'no tags: ', len(no_tags)
Query all photos without the tag 'night':
not_night = session.query(Photo).outerjoin(Photo.tags).filter(not_(Photo.tags.any(Tag.name == 'night'))).all()
print 'not night: ', len(not_night)
Assuming existance of backrefs Tag.photo = relationship(Photo, backref='tags') and
Photo.user = relationship(User, backref="photos") both can be done using any construct. This might not generate the most optimal SQL SELECT statement, but it is a very clean sqlalchemy.
Part-1: "all the photos of the user with id "1234" that don't have a "cat" of version "2" tagged in them"
def get_user_photos_without_tag(user_id, tag_name, tag_version):
qry = (session.query(Photo)
.filter(~Photo.tags.any(and_(
Tag.tag_name == tag_name,
Tag.tag_version == tag_version))
)
.filter(Photo.user_id == user_id)
)
return qry.all()
photos = get_user_photos_without_tag(1234, 'cat', 2)
Part-2: "all the users who have at least one photo without a specific tag"
def get_user_with_photos_without_tag(tag_name, tag_version):
qry = (session.query(User)
.filter(User.photos.any(
~Photo.tags.any(and_(
Tag.tag_name == tag_name,
Tag.tag_version == tag_version))
))
)
return qry.all()
res = get_user_with_photos_without_tag('cat', 2)