SQLAlchemy Check if Many-To-Many relationship exists - sqlalchemy

In my database there is a Many-To-Many relationship between users and projects. A user can be member of multiple projects and a project can have multiple members.
I want know if a given user is member of a given project.
I currently solve this by querying for both the project and the user and by then checking if the user is contained in the projects member list. Here is my (working) code:
def is_project_member(db: Session, project_id: int, user_id: int):
project = db.query(dms.Project).filter(dms.Project.id == project_id).first()
user = db.query(dms.User).filter(dms.User.id == user_id).first()
return user in project.members
Can this be done more efficiently? Might it possible to directly query the relationship table?
Here my database models:
project_user_table = Table(
"project_user_association",
Base.metadata,
Column("project_id", ForeignKey("projects.id")),
Column("user_id", ForeignKey("users.id"))
)
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
projects = relationship("Project",
secondary=project_user_table, back_populates="members")
class Project(Base):
__tablename__ = "projects"
id = Column(Integer, primary_key=True, index=True)
members = relationship("User", secondary=project_user_table,
back_populates="projects")

Related

How to use Sqlalchemy ORM to just get related ids not the full record of an association in a many-to-many?

Is there a way to just get the related category ids of a many-to-many association - without getting the full related category record?
Code being used currently is:
products = session.query(Product).join(association_table).filter(association_table.c.category_id == category)
for product in products:
print(product.product_id, [item.id for item in product.categories])
With the models:
association_table = Table(
"product_category_association_table",
Base.metadata,
Column("product_id", ForeignKey("product.id"), primary_key=True),
Column("category_id", ForeignKey("category.id"), primary_key=True),
)
class Product(Base):
__tablename__ = "product"
id = Column(Integer, primary_key=True)
categories = relationship(
"Category", secondary=association_table, back_populates="products"
)
product_id = Column(String(length=50), unique=True)
label = Column(String, nullable=False)
class Category(Base):
__tablename__ = "category"
id = Column(Integer, primary_key=True)
products = relationship(
"Product", secondary=association_table, back_populates="categories"
)
active = Column(Boolean, default=True)
label = Column(String, nullable=False)
This emits the initial SQL to get all products.
Then for each loop is does a query to the related category table (which is not needed as all that is wanted is the category ids).
Is this possible to achieve without the join over to the category table?

How to show different keyboards to different users in pyTelegramBotAPI using SQLAlchemy

I'm building telegram bot with SQLAlchemy and have the issue. I have class User (table users), that have relationship with class Role (table roles), accordingly, user can have different roles.
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import declarative_base, relationship, sessionmaker
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, index=True)
telegram_id = Column(String)
role_id = Column(Integer, ForeignKey("roles.id"))
role = relationship("Role", back_populates="users")
class Role(Base):
__tablename__ = 'roles'
id = Column(Integer, primary_key=True, index=True)
name = Column(String)
users = relationship("User", back_populates="role")
I want, to User with role "user" and with role "admin" have different keyboards when you call certain method. If there is the way, to do it with inheritance - it would be more prefeable

How do I add more than one inheritance relationship in SQLAlchemy?

So, I have a users table, and employees table, and a tenants table.
I'm using joined table inheritance.
class User(Base):
__tablename__ = 'usr_users'
usr_user_id = Column(Integer, primary_key=True)
usr_first_name = Column(Unicode(50))
usr_last_name = Column(Unicode(50))
tenant = relationship("Tenant", uselist=False, backref="User")
usr_type = Column(String(24))
__mapper_args__ = {
'polymorphic_identity':'user',
'polymorphic_on': usr_type
}
class Tenant(User):
"""
Application's user model.
"""
__tablename__ = 'ten_tenants'
ten_tenant_id = Column(Integer, ForeignKey('usr_users.usr_user_id'), primary_key=True)
__mapper_args__ = {
'polymorphic_identity': 'tenant'
}
class Employee(User):
__tablename__ = 'emp_employees'
emp_employee_id = Column(Integer, ForeignKey('usr_users.usr_user_id'), primary_key=True)
__mapper_args__ = {
'polymorphic_identity': 'employee'
}
I've got everything working when a user becomes an employee.
user = Employee()
session.add(user)
An entry in the user table, and a value in the "type" column of "employee".
But what if I have a user that is both a employee and a tenant?
What syntax do I use to pull the user, and then add a Tenant relationship so that the resulting user has both a employee relationship and a tenant relationship?

sql alchemy: return all unique types of great great grandchildren

I have 6 tables. I am essentially trying to return all unique types of great great grandchildren
How do I return a list of all the unique types of sku_numbers in a FreightDomesticOrder?
Table Definitions:
class FreightOrderDomestic(db.Model):
"""
A shipment of products from a manufacturer to a fulfillment center
"""
__tablename__ = 'Freight_Order_Domestic'
id = db.Column(db.Integer, primary_key=True, nullable=False)
class Pallet(db.Model):
"""
An individual Pallet (full of individual cases)
"""
__tablename__ = 'Pallet'
id = db.Column(db.Integer, primary_key=True, nullable=False)
freight_order_fkey = db.ForeignKey("Freight_Order_Domestic.id")
freight_order_id = db.Column(db.Integer, freight_order_fkey, nullable=False)
class OuterCase(db.Model):
"""
An outer case (full of inner cases)
"""
__tablename__ = 'Outer_Case'
id = db.Column(db.Integer, primary_key=True, nullable=False)
pallet_fkey = db.ForeignKey("Pallet.id")
pallet_id = db.Column(db.Integer, pallet_fkey, nullable=False)
class InnerCase(db.Model):
"""
An individual case (full of individual items)
"""
__tablename__ = 'Inner_Case'
id = db.Column(db.Integer, primary_key=True, nullable=False)
outer_case_fkey = db.ForeignKey("Outer_Case.id")
outer_case_id = db.Column(db.Integer, outer_case_fkey, nullable=False)
class Each(db.Model):
"""
An individual item
"""
__tablename__ = 'Each'
id = db.Column(db.Integer, primary_key=True, nullable=False)
inner_case_fkey = db.ForeignKey("Inner_Case.id")
inner_case_id = db.Column(db.Integer, inner_case_fkey, nullable=False)
sku_fkey = db.ForeignKey("Sku.id")
sku_id = db.Column(db.Integer, sku_fkey, nullable=False)
class Sku(db.Model):
"""
The SKU of an product, the attributes it should have to determine pricing
"""
__tablename__ = 'Sku'
id = db.Column(db.Integer, primary_key=True, nullable=False)
sku_number = db.Column(db.String(255), nullable=False)
Here is what I am trying so far but I am stuck, I am also wondering how cheap I can make this:
SKUs = Session.query(Pallet, Outer_case, Inner_case, Each, Sku).filter(Pallet.id == Outer_case.pallet_id).filter(Outer_case.id == Inner_case.outer_case_id).filter(Inner_case.id == Each.inner_case_id).filter(Each.sku_id == sku.id).all()
My other idea was to loop through all Pallets and then Outer_cases and so on but that seems too expensive.
Edited post after table definitions:
Given your table definitions, this should work:
SKUs = session.query(Sku.sku_number)
.join(Each).join(InnerCase)
.join(OuterCase).join(Pallet)
.join(FreightOrderDomestic)
.filter(FreightOrderDomestic.id == myOrderNumber)
.group_by(Sku).all()
However, looking at your table definitions I have some other comments that will hopefully help:
You should setup relationships between the tables, so you can easily work with the different objects. Check out the sqlalchemy documentation on relationships here
I would suggest reading up on Database Normalization. This will help you understand some of the below points
You currently have Each setup so there will be duplicate items of the same type if they are assigned to different InnerCases. This is not a good database practice. You should setup this relationship as a many to many relationship, and you can read about that here. This will allow you to have a list of items, and each item can link to many different InnerCases
FreightDomesticOrder should have a column for order number. You don't want to use a key value as an order number
If you are going to handle international orders also, you should probably just create a type field for FreightOrderDomestic and rename it to FreightOrder
Original Post:
Can you provide the table definitions? This is very hard to answer accurately without seeing the relationships you have setup. Something like this could work, if you setup your tables like I would have given the description you gave, or it could not work because you didn't provide enough info:
SKUs = session.query(Sku.sku_number)
.join(Each).join(Inner_case)
.join(Outer_case).join(Pallet)
.join(Freight_order)
.filter(Freight_order.order_number == myOrderNumber)
.group_by(Sku).all()

SQLAlchemy: How can I define a relationship as the union of two other relationships?

How can I implement a self-referential many-to-many relationship that is effectively the union of two other relationships?
The relationship should return all FacebookFriendship models that exist between a user and other users in the network. A user may have a FacebookFriendship that points to another existing user, but due to FB API outages, privacy controls, etc, the mirror FBFriendship might not exist for the existing user to this user.
# This class is necessary for python-social-auth
# A UserSocialAuth model only exists for users who are in the network
class UserSocialAuth(_AppSession, Base, SQLAlchemyUserMixin):
"""Social Auth association model"""
__tablename__ = 'social_auth_usersocialauth'
__table_args__ = (UniqueConstraint('provider', 'uid'),)
id = Column(Integer, primary_key=True)
provider = Column(String(32))
uid = Column(String(UID_LENGTH))
extra_data = Column(JSONType())
user_id = Column(
Integer, ForeignKey(User.id), nullable=False, index=True)
user = relationship(
User,
backref=backref('social_auth', lazy='dynamic')
)
This relationship finds FacebookFriendship models that point from this user to any existing user.
facebook_friendships = relationship(
FacebookFriendship,
primaryjoin=and_(
user_id == FacebookFriendship.user_id,
provider == 'facebook'
),
secondary=FacebookFriendship.__table__,
secondaryjoin=uid == FacebookFriendship.fb_uid_friend,
foreign_keys=[provider, user_id, uid],
viewonly=True,
uselist=True,
lazy='dynamic',
)
This relationship finds FacebookFriendship models that point to this user.
other_facebook_friendships = relationship(
FacebookFriendship,
primaryjoin=and_(
uid == FacebookFriendship.fb_uid_friend,
provider == 'facebook'
),
foreign_keys=[provider, uid],
viewonly=True,
uselist=True,
lazy='dynamic',
)
I was able to express the union query using the hybrid_property decorator, but this prevents usage of comparators like any() or from using association proxies, at least from what I can tell.
# Can I rewrite this using relationship()?
#hybrid_property
def all_facebook_friendships(self):
return self.facebook_friendships.union(
self.other_facebook_friendships).correlate(
FacebookFriendship)
# FBFriendship models are created for every friend that a user has,
# regardless of whether they're in the network or not.
class FacebookFriendship(Base):
__tablename__ = u'user_fb_friend'
user_id = Column(Integer, sa.ForeignKey(User.id), primary_key=True)
user = relationship(
User, backref=backref('facebook_friendships', lazy='dynamic'),
primaryjoin=User.id == user_id)
fb_uid_friend = Column(sa.String(length=255), primary_key=True)
In the end, I'd like to query this relationship like any other InstrumentedAttribute:
UserSocialAuth.query.filter(UserSocialAuth.all_facebook_friendships.any()).all()
and define an association_proxy on the User model:
User.all_facebook_friends = association_proxy('all_facebook_friendships', 'user')
Sorry for the length of this question, but I've trialed & errored to no avail for days now.
Related:
How can I achieve a self-referencing many-to-many relationship on the SQLAlchemy ORM back referencing to the same attribute?
How to create relationship many to many in SQLAlchemy (python, flask) for model User to itself
Using zzzeek's solution linked above, I created a self-referential M2M relationship by using a select statement as the "secondary" argument to relationship().
friendship_union = select([
FacebookFriendship.dater_id,
cast(FacebookFriendship.fb_uid_friend, Integer()).label(
'fb_uid_friend')
]).union(
select([
cast(FacebookFriendship.fb_uid_friend, Integer()),
FacebookFriendship.dater_id]
)
).alias()
cls.all_fb_friendships = relationship(
UserSocialAuth,
secondary=friendship_union,
primaryjoin=UserSocialAuth.user_id == friendship_union.c.dater_id,
secondaryjoin=and_(
UserSocialAuth.provider == 'facebook',
cast(UserSocialAuth.uid, Integer() ) == friendship_union.c.fb_uid_friend,
),
viewonly=True
)