Having this models with ManyToMany relation in FastAPI app. I need to populate sent_log table with data from POST request:
{
"user_id": 1
"article_id": 2
}
models.py
sent_log = Table(
"sent_log",
Base.metadata,
Column("user_id", ForeignKey("users.id"), primary_key=True),
Column("article_id", ForeignKey("articles.id"), primary_key=True),
Column('sent_time', DateTime(), server_default=text('NOW()')),
)
class User(Base):
"""User model. Has Many2Many relationship with Article."""
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
telegram_id = Column(Integer, unique=True, index=True)
username = Column(String(50))
pet_name = Column(String(50))
language_code = Column(String(5))
# date_join = Column(DateTime(), server_default=text('NOW()')),
sent_articles = relationship(
"Article", secondary=sent_log, back_populates="sent_to_user"
)
class Article(Base):
"""Article model. Has Many2Many relationship with User."""
__tablename__ = "articles"
id = Column(Integer, primary_key=True, index=True)
text = Column(String(1024))
image_url = Column(String(500))
language_code = Column(String(255), index=True)
sent_to_user = relationship(
"User", secondary=sent_log, back_populates="sent_articles"
)
How to make it in crud.py?
Related
Given this polymorphic model
class OrganizationBase(Base):
__tablename__ = "x_organization_base"
__mapper_args__ = {
"polymorphic_identity": "base",
"polymorphic_on": "model_type",
}
model_type = db.Column(db.String(), nullable=False)
id = Column(Integer(), primary_key=True)
cont'd
class UmbrellaOrganization(OrganizationBase):
__tablename__ = "x_umbrella_organization"
__mapper_args__ = {"polymorphic_identity": "umbrella"}
id = db.Column(Integer, db.ForeignKey(OrganizationBase.id), primary_key=True)
umbrella_accounting_id = db.Column(db.String(255), nullable=False, unique=True)
properties = db.relationship(
"UmbrellaOrganizationProperty",
lazy="joined",
backref=backref("umbrella_organization", uselist=False),
)
class Organization(OrganizationBase):
__tablename__ = "x_organization"
__mapper_args__ = {"polymorphic_identity": "organization"}
id = db.Column(Integer, db.ForeignKey(OrganizationBase.id), primary_key=True)
umbrella_accounting_id = db.Column(
db.String(255),
db.ForeignKey(UmbrellaOrganization.umbrella_accounting_id),
nullable=False,
index=True,
)
and this eagerly loaded relationship
class UmbrellaOrganizationProperty(Base):
__tablename__ = "x_umbrella_organization_property"
id = Column(Integer(), primary_key=True)
umbrella_organization_id = db.Column(
Integer, db.ForeignKey(UmbrellaOrganization.id), nullable=False, index=True
)
type = db.Column(db.String(), nullable=False)
this query will produce invalid SQL:
query = (
db.session.query(
Organization,
UmbrellaOrganization,
)
.join(
UmbrellaOrganization,
UmbrellaOrganization.umbrella_accounting_id == Organization.umbrella_accounting_id,
)
)
y = query.limit(5)
Specically, there main query will be duplicated with the same alias 'anon_1' occuring twice:
ProgrammingError: (psycopg2.errors.DuplicateAlias) table name "anon_1" specified more than once
This only happens with limit() applied.
It appears that the polymorphism mapper wants to join the (eagerly loaded) UmbrellaOrganziationProperty to both the UmbrellaOrganization and OrganizationBase, even though it does not belong there. Without changing the model, the only way I have found to prevent this is telling it to not load OrganizationProperty eagerly, by adding this query option:
.options(lazyload(UmbrellaOrganization.properties))
This is potentially problematic because client code may expect the properties in the results. What else can I do?
In my pet project I need to send some article to user and log this action with ManyToMany relationship.
So, any article can be sent to any user, but only once. After sending we put data to 'sent_log' table with what article_id sent to which user_id.
How do I get (with SQLAlchemy ORM) all articles that is not sent to given user_id?
This code doesn't work for me.
def get_user_articles(db: Session, user_telegram_id: int):
"""Get user articles matching language code and user_telegram_id."""
return (
db.query(models.Article)
.select_from(join(left=models.Article, right=models.User))
.filter(models.Article.language_code == models.User.language_code)
.filter(models.User.telegram_id == user_telegram_id)
.all()
)
Maybe I've built bad DB structure, guide me than.
My models:
sent_log = Table('sent_log', Base.metadata,
Column('user_id', ForeignKey('users.id'), primary_key=True),
Column('article_id', ForeignKey('articles.id'), primary_key=True)
)
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
telegram_id = Column(Integer, unique=True, index=True)
username = Column(String(50))
pet_name = Column(String(50))
language_code = Column(String(5))
sent_articles = relationship("Article", secondary=sent_log, back_populates="sent_to_user")
class Article(Base):
__tablename__ = "articles"
id = Column(Integer, primary_key=True, index=True)
text = Column(String(1024))
image_url = Column(String(500))
language_code = Column(String(255), index=True)
sent_to_user = relationship("User", secondary=sent_log, back_populates="sent_articles")
I have these two models:
class User(db.Model, UserMixin):
__tablename__ = 'users'
__table_args__ = (
PrimaryKeyConstraint('id',),
)
id = db.Column(db.Integer, primarky_key=True)
username = db.Column(db.String, unique=True, nullable=False)
email = db.Column(db.String, unique=True, nullable=False)
password = db.Column(db.String, nullable=False)
class Review(db.Model):
__tablename__ = 'reviews'
__table_args__ = (
PrimaryKeyConstraint('id', ),
)
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.Text, nullable=False)
I wanted to create a relationship between these two tables so that when a user writes a review it should go to Review model and it has a relation with user_id in Users model. I have tried lots of answers but they are returning different kinds of errors I am confused, can anybody help?
You can use db.relationship to create a one-to-many relationship between users and reviews.
class User(db.Model, UserMixin):
__tablename__ = 'users'
__table_args__ = (
PrimaryKeyConstraint('id',),
)
id = db.Column(db.Integer, primarky_key=True)
username = db.Column(db.String, unique=True, nullable=False)
email = db.Column(db.String, unique=True, nullable=False)
password = db.Column(db.String, nullable=False)
reviews = db.relationship('Review', backref='user', cascade='all, delete, delete-orphan')
class Review(db.Model):
__tablename__ = 'reviews'
__table_args__ = (
PrimaryKeyConstraint('id', ),
)
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
This would allow for user.reviews and review.user
Try this:
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
user = db.relationship(User, backref='user_reviews', lazy=True)
And read the documentation.
I am trying to integrate with a user-group-role table structure where a user can belong to many groups and have multiple roles on each group.
I found a similar question to this, however it does not allow for multiple roles: Many-to-many declarative SQLAlchemy definition for users, groups, and roles
I have the following table structure and would like to be able to access the roles in the following sort of manner: user.groups[0].roles
class Role(Base):
__tablename__ = 'roles'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(Unicode(16), unique=True)
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(Unicode(16), unique=True)
class Group(Base):
__tablename__ = 'groups'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(Unicode(16), unique=True)
class UserGroup(Base):
__tablename__ = 'user_group_role'
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey('users.id', ondelete='CASCADE'), nullable=False)
group_id = Column(Integer, ForeignKey('groups.id', ondelete='CASCADE'), nullable=False)
role_id = Column(Integer, ForeignKey('roles.id', ondelete='CASCADE'), nullable=False)
This is an example from Turbogears' default full-stack quickstart.
from sqlalchemy import Table, ForeignKey, Column
from sqlalchemy.types import Unicode, Integer, DateTime
from sqlalchemy.orm import relation, synonym
from .model import DeclarativeBase, metadata, DBSession
# This is the association table for the many-to-many relationship between
# groups and permissions.
group_permission_table = Table('tg_group_permission', metadata,
Column('group_id', Integer,
ForeignKey('tg_group.group_id',
onupdate="CASCADE",
ondelete="CASCADE"),
primary_key=True),
Column('permission_id', Integer,
ForeignKey('tg_permission.permission_id',
onupdate="CASCADE",
ondelete="CASCADE"),
primary_key=True))
# This is the association table for the many-to-many relationship between
# groups and members - this is, the memberships.
user_group_table = Table('tg_user_group', metadata,
Column('user_id', Integer,
ForeignKey('tg_user.user_id',
onupdate="CASCADE",
ondelete="CASCADE"),
primary_key=True),
Column('group_id', Integer,
ForeignKey('tg_group.group_id',
onupdate="CASCADE",
ondelete="CASCADE"),
primary_key=True))
class Group(DeclarativeBase):
__tablename__ = 'tg_group'
group_id = Column(Integer, autoincrement=True, primary_key=True)
group_name = Column(Unicode(16), unique=True, nullable=False)
users = relation('User', secondary=user_group_table, backref='groups')
class User(DeclarativeBase):
__tablename__ = 'tg_user'
user_id = Column(Integer, autoincrement=True, primary_key=True)
user_name = Column(Unicode(16), unique=True, nullable=False)
email_address = Column(Unicode(255), unique=True, nullable=False)
display_name = Column(Unicode(255))
class Permission(DeclarativeBase):
__tablename__ = 'tg_permission'
permission_id = Column(Integer, autoincrement=True, primary_key=True)
permission_name = Column(Unicode(63), unique=True, nullable=False)
description = Column(Unicode(255))
groups = relation(Group, secondary=group_permission_table,
backref='permissions')
Here is my setup:
class User(Base):
__tablename__ = 'users'
user_id = Column(Integer, primary_key=True)
class Object(Base):
__tablename__ = 'objects'
object_id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('User.user_id'))
class ObjectHistory(Base):
__tablename__ = 'object_histories'
history_id = Column(Integer, primary_key=True)
object_id = Column(Integer, primary_key=True)
class Action(Base):
__tablename__ = 'actions'
action_id = Column(Integer, primary_key=True)
class ActionDetail(Base):
__tablename__ = 'action_details'
detail_id = Column(Integer, primary_key=True)
history_id = Column(Integer, ForeignKey('ObjectHistory.id')
object = relationship('Object', secondary='object_histories',
primaryjoin='ActionDetail.history_id==ObjectHistory.history_id',
secondaryjoin='Object.object_id==ObjectHistory.object_id',
backref='action_details')
user = relationship('User', ????, backref='action_details')
How can I create the user relationship on ActionDetail? Is it possible? I've tried something similar to the object relationship above it, but to no avail.