Python - SqlAlchemy foreign key issues - python

So, I have been getting a lot of errors with SQLAlchemy from Flask and so far nothing I have found online has fixed it, only caused further errors to appear. My current model code is here:
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
nickname = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), unique=True, nullable=False)
posts = db.relationship('Post', back_populates='author', lazy='dynamic',
primaryjoin='User.id == Post.user_id')
created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow())
#property
def is_authenticated(self):
return True
#property
def is_active(self):
return True
#property
def is_anonymous(self):
return False
def get_id(self):
return str(self.id)
def __repr__(self):
return "<User %r>" % (self.nickname)
class Post(db.Model):
__tablename__ = 'post'
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return '<Post %r>' % (self.body)
My current error with this code is:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'post.user_id' could not find table 'user' with which to generate a foreign key to target column 'id'
I am at a complete loss as to what I'm doing wrong and why it is that no other solution has worked for me.

user.id needs to be users.id in your Post model:
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))

Related

sqlalchemy InvalidRequestError

I'm using flask-sqlalchemy, this is not the first relations that i've built, but for some reason it gives me an error when i start flask:
sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'mapped class User->users'. Original exception was: 'Table' object has no attribute 'sender_id'
These are two models that i'm trying to connect via ForeignKeys:
User:
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
about_me = db.Column(db.String(140))
last_seen = db.Column(db.DateTime, default=datetime.utcnow)
#relations
posts = db.relationship('Post', back_populates='author', lazy='dynamic')
messages_sent = db.relationship('Message', foreign_keys='messages.sender_id',
back_populates='author', lazy='dynamic')
messages_received = db.relationship('Message', foreign_keys='messages.recipient_id',
back_populates='recipient', lazy='dynamic')
last_message_read_time = db.Column(db.DateTime)
followed = db.relationship(
'User', secondary=followers,
primaryjoin=(followers.c.follower_id == id),
secondaryjoin=(followers.c.followed_id == id),
backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')
def __repr__(self):
return '<User {}>'.format(self.username)
And Messsage:
class Message(db.Model):
__tablename__ = 'messages'
id = db.Column(db.Integer, primary_key=True)
sender_id = db.Column(db.Integer, db.ForeignKey('users.id'))
recipient_id = db.Column(db.Integer, db.ForeignKey('users.id'))
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
#relations
author = db.relationship('User', back_populates='messages_sent', lazy='dynamic')
recipient = db.relationship('User', back_populates='messages_received', lazy='dynamic')
def __repr__(self):
return '<Message {}>'.format(self.body)
I've checked the database itself and it certainly has sender_id in the correct table, tried to change "foreign_keys" parameter to directly name of model Message.sender... Tried to change parameters of lazy on those relations. It still gives me same error.
This is subtle but messages in this case is a table so columns are referenced off of c, like messages.c.sender_id. To use the column of the model class (the mapped class) you would do Message.sender_id.
So...
messages_sent = db.relationship('Message', foreign_keys='messages.c.sender_id',
back_populates='author', lazy='dynamic')
# OR
messages_sent = db.relationship('Message', foreign_keys='Message.sender_id',
back_populates='author', lazy='dynamic')
There is some information here but it doesn't explain the table vs class situation: handling-multiple-join-paths I think I would just use ORM style references until you are more comfortable and then you could use table references if needed.

How to build 'many-to-many' rrelationship in SQLAlchemy

I'm looking to have a 'many-to-many' relationship between users and products. For this, I prepared another table product_user but it is not working well. I can't use it in secondary relationship.
Error:
NameError: name 'product_users' is not defined
This is code:
## Product model
class Product(db.Model):
__tablename__ = 'products'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
users = db.relationship("User", secondary=product_users, backref="users", lazy="dynamic")
def __repr__(self):
return '<Product %r>' % self.uid
## User model
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
products = db.relationship("Product", secondary=product_users, backref="products", lazy="dynamic")
def __repr__(self):
return '<User %r>' % self.uid
## Product User model
class ProductUser(db.Model):
__tablename__ = 'product_users'
id = db.Column(db.Integer, primary_key=True)
product_id = db.Column(db.Integer,db.ForeignKey('products.id'))
user_id = db.Column(db.Integer,db.ForeignKey('users.id'))
product = db.relationship("Product", backref='products', foreign_keys=[product_id])
user = db.relationship("User", backref='users', foreign_keys=[user_id])
def __repr__(self):
return '<ProductUser %r>'
Sorry I have typo table name.
from:
users = db.relationship("User", secondary=product_users, backref="users", lazy="dynamic")
products = db.relationship("Product", secondary=product_users, backref="products", lazy="dynamic")
to:
users = db.relationship("User", secondary='product_users', backref="users", lazy="dynamic")
products = db.relationship("Product", secondary='product_users', backref="products", lazy="dynamic")

SQLAlchemy operational error: no such table column user_id when trying to create new DB row

I am getting the below error when trying to add an ingredient on my Account page (which just includes the form AddToPantry - one field, ingredient_name).
sqlalchemy.exc.OperationalError
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) table ingredient has no column named user_id [SQL: 'INSERT INTO ingredient (ingredient_name, user_id) VALUES (?, ?)'] [parameters: ('bread', 1)] (Background on this error at: http://sqlalche.me/e/e3q8)
Here are my models:
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
recipes = db.relationship('Recipe', backref='user', lazy=True)
pantry_items = db.relationship('Ingredient', backref='user', lazy=True)
# meal_plans = db.relationship('Meal Plan', backref='user', lazy='dynamic')
def __repr__(self):
return f"User('{self.username}', '{self.email})"
class Recipe(db.Model):
id = db.Column(db.Integer, primary_key=True)
spoonacular_id = db.Column(db.Integer)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
def __repr__(self):
return f"Recipe('{self.spoonacular_id}')"
class Ingredient(db.Model):
id = db.Column(db.Integer, primary_key=True)
ingredient_name = db.Column(db.String(40), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
def __repr__(self):
return f"Recipe('{self.ingredient_name}')"
And this is my route for the account page, which is where the form is being loaded:
#app.route("/account", methods=['GET', 'POST'])
#login_required
def account():
form = AddToPantry()
if form.validate_on_submit():
ingredient = Ingredient(ingredient_name=form.ingredient_name.data, user=current_user)
db.session.add(ingredient)
db.session.commit()
return render_template('account.html', title='Account', form=form)
I had old pycache data. Once I deleted this folder, dropped my tables and recreated them the issue was resolved.

Many to Many relationship calling on Flask SQL Alchemy

I have the following badge (achievement) system database structure:
class Base(db.Model):
__abstract__ = True
id = db.Column(db.Integer, primary_key=True)
date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
date_modified = db.Column(db.DateTime, default=db.func.current_timestamp(),
onupdate=db.func.current_timestamp())
class User(UserMixin, Base):
__tablename__ = 'users'
username = db.Column(db.String(20), nullable=False, unique=True)
email = db.Column(db.String(50), nullable=False, unique=True)
password = db.Column(db.String(200), nullable=False)
name = db.Column(db.String(30), nullable=False)
badges = db.relationship('UserBadge', backref='ubadge',
lazy='dynamic')
class Badge(Base):
__tablename__ = 'badges'
name = db.Column(db.String(35), unique=True)
description = db.Column(db.String(300))
imgfile = db.Column(db.String(80))
badges = db.relationship('UserBadge', backref='badge',
lazy='dynamic')
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.name, self.description)
class UserBadge(Base):
__tablename__ = 'userbadges'
user_id = db.Column(db.Integer(), db.ForeignKey('users.id'))
badge_id = db.Column(db.Integer(), db.ForeignKey('badges.id'))
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.user_id, self.badge_id)
So i can return all the badges by a specific user, using:
ubadges = UserBadge.query.filter_by(user_id=user.id).all()
It returns:
[<Achievement: 1 - 1>]
But instead of 1 (user_id) and 1 (badge_id) i want to show the users.name and badges.name. How can i access those attributes?
In your UserBadge class, just use:
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.ubadge.name, self.badge.name)
It has both properties because you set them up using backref in the other classes.
PS: You might need to change the User backref to user, and then use self.user.name int he function above

Flask with SQLAlchemy Many-to-many Relationship w/ Mixins

The problem I face is the following: in the following block of code you can see that I want to separate the id, created_on and updated_on attributes so that I follow the Don't Repeat Yourself (DRY) principle.
# Used to generalize a default database model with an ID
class IdentifierMixin(object):
id = db.Column(db.Integer, primary_key=True)
# Used to generalize timestamps on the default database model
class TimestampMixin(object):
created_on = db.Column(db.DateTime, server_default=db.func.now())
updated_on = db.Column(db.DateTime, server_default=db.func.now(),
onupdate=db.func.now())
# The many-to-many relationships
users_roles = db.Table('users_roles',
db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))
class User(declarative_base(), IdentifierMixin, TimestampMixin, UserMixin):
__tablename__ = 'user'
# Relationships
roles = db.relationship('Role', secondary=users_roles,
backref=db.backref('users', lazy='dynamic'))
uploads = db.relationship('Upload', backref='uploader', lazy='dynamic')
email = db.Column(db.String(100), nullable=False, unique=True)
username = db.Column(db.String(40), nullable=True, unique=True)
password = db.Column(db.String, nullable=False)
authenticated = db.Column(db.Boolean, default=False)
# True, as all users are active
def is_active(self):
return True
# Return the email address to satisfy Flask-Login's requirements
def get_id(self):
return self.email
# Return the authenticated parameter
def is_authenticated(self):
return self.authenticated
# False, anonymous users are not supported
def is_anonymous(self):
return False
def __repr__(self):
if self.username is not None:
return '<User %r>' % self.username
else:
return '<User %r>' % self.email
class Role(declarative_base(), IdentifierMixin, RoleMixin):
__tablename__ = 'role'
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
However, it seems that the id attribute (inherited from the IdentifierMixin mixin) cannot be found:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'users_roles.user_id' could not find table 'user' with which to generate a foreign key to target column 'id'
Can someone please explain what I am doing wrong? I would appriciate it immensely. With regards, Tim.

Categories

Resources