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")
Related
I have defined two model with relation between them as below:
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(40), nullable=False, unique=False)
db.relationship('User', backref='role', lazy='dynamic')
def __init__(self, name):
self.name = name
def __repr__(self):
return f'<Role id={self.id} name={self.name}>'
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(40), unique=True)
password = db.Column(db.String(40), unique=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
def __init__(self, username, password, role_id):
self.username = username
self.password = password
self.role_id = role_id
def __repr__(self):
return f'<User id={self.id} username={self.username} password={self.password}>'
Then inside shell I created entries as below:
> admin_role = Role('Admin')
> db.session.add(admin_role)
> db.session.commit()
> admin_user = User('adminusername', 'adminpassword',admin_role.id)
> db.session.add(admin_user)
> db.session.commit()
When I try to query model I get perfect result:
>>> admin_role = Role.query.first()
>>> admin_user = User.query.first()
>>> print(admin_role)
<Role id=1 name=Admin>
>>> print(admin_user)
<User id=1 username=adminusername password=adminpassword>
But when I try to access relation
print(admin_role.users)
print(admin_user.role)
I get errors Role object has no attribute users and User object has no attribute role respectively.
Typo? You have to assign db.relationship() instance to a variable.
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(40), nullable=False, unique=False)
- db.relationship('User', backref='role', lazy='dynamic')
+ users = db.relationship('User', backref='role', lazy='dynamic')
I have trouble linking two tables with 'Posts' and 'Comments' so comments are only displayed on particular post, where they have been created.
With linking post and user I used current_user.id to make link between tables, but using ForeignKey gaves me always error:
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship Post.post_rel - there are no foreign keys linking these tables
Below is my code:
class Post(db.Model):
__tablename__ = 'post'
id = db.Column(Integer, primary_key=True)
title = db.Column(String(50))
subtitle = db.Column(String(50))
author = db.Column(String(20))
date_posted = db.Column(DateTime)
content = db.Column(Text)
post_rel = relationship('Post', back_populates='comment_rel', foreign_keys='[Comment.post_id]')
def get_comments(self):
return Comments.query.filter_by(post_id=post.id).order_by(Comments.timestamp.desc())
def __repr__(self):
return '<Post %r>' % (self.body)
class Comment(db.Model):
__tablename__ = 'comment'
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.String(140))
author = db.Column(db.String(32))
timestamp = db.Column(db.DateTime(), default=datetime.utcnow, index=True)
post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
comment_rel = relationship('Comment', uselist=False, back_populates='post_rel')
def __init__(self, text, author, timestamp):
""""""
self.text = text
self.author = author
self.timestamp = timestamp
def __repr__(self):
return '<Post %r>' % (self.body)
def show(self):
return self.author + '\n' + self.text
In your relationship, you have to change the name of the tables.
post_rel = relationship('Comment', back_populates='comment_rel',
foreign_keys='[Comment.post_id]')
comment_rel = relationship('Post', uselist=False,
back_populates='post_rel')
I have corrected your code:
BaseModel = declarative_base()
class Post(BaseModel):
__tablename__ = 'post'
id = Column(Integer, primary_key=True)
title = Column(String(50))
subtitle = Column(String(50))
author = Column(String(20))
post_rel = relationship('Comment', back_populates='comment_rel', foreign_keys='[Comment.post_id]')
class Comment(BaseModel):
__tablename__ = 'comment'
id = Column(Integer, primary_key=True)
text = Column(String(140))
author = Column(String(32))
comment_rel = relationship('Post', uselist=False, back_populates='post_rel')
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
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'))
I do have these 2 calsses als DB models trying to buidl a 1 to many relation:
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), index=True) #, unique=True)
firstname = db.Column(db.String(50))
lastname = db.Column(db.String(50))
bt_ids = db.relationship("BT", order_by="BT.id", backref="user")
class BT(db.Model):
__tablename__ = 'bt'
id = db.Column(db.Integer, primary_key=True)
bt_id = db.Column(db.String(255), unique=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
user = db.relationship("User", backref=db.backref('bt', order_by=id))
But I guess I do not really understand the way of setting up the relationships:
ArgumentError: Error creating backref 'user' on relationship 'User.bt_ids': property of that name exists on mapper 'Mapper|BT|bt'
Any ideas?
EDIT
I actually wanted to achive something like the example of SQLAlchemy
class User(Base):
__tablename__ = 'users'
id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
name = Column(String(50))
fullname = Column(String(50))
password = Column(String(12))
**addresses = relationship("Address", order_by="Address.id", backref="user")**
def __repr__(self):
return "<User(name='%s', fullname='%s', password='%s')>" % (
self.name, self.fullname, self.password)
class Address(Base):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
email_address = Column(String, nullable=False)
user_id = Column(Integer, ForeignKey('users.id'))
**user = relationship("User", backref=backref('addresses', order_by=id))**
def __repr__(self):
return "<Address(email_address='%s')>" % self.email_address
There is already a FK relation between User and BT through user_id property so you can't create another relation named user in BT.
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), index=True) #, unique=True)
firstname = db.Column(db.String(50))
lastname = db.Column(db.String(50))
bts = db.relationship("BT", order_by="BT.id", backref="user")
class BT(db.Model):
__tablename__ = 'bt'
id = db.Column(db.Integer, primary_key=True)
bt_id = db.Column(db.String(255), unique=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
Now I got it...
This was wrong:
user = db.relationship("User", backref=db.backref('bt_ids', order_by=id))
Now it works!