How to declare a table class that contains multi-column primary key? - sqlalchemy

The columns of the primary key must be in specific order.
I see some code from document :
class User(Base):
__tablename__ = 'users'
id = Column(Integer)
__mapper_args__ = {
'primary_key':[id]
}
But it just does not work (I'm using mysql, and the id primary key will not be generated).
Any possible solutions?

In case columns are declared in the same order as they should be in the primary key:
class User(Base):
field1 = Column(Integer, primary_key=True)
field2 = Column(Integer, primary_key=True)
Otherwise declare it in __table_args__:
class User(Base):
field1 = Column(Integer)
field2 = Column(Integer)
__table_args__ = (
PrimaryKeyConstraint(field2, field1),
{},
)

Related

SQLAlchemy - how to make column unique per ForeignKey relationship?

In the Todo model below there is a unique constraint put on text column.
How can I narrow this constraint to validate uniqueness per "foreign-keyed" user only, not per all users as it is now?
I use SQLite.
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey
from sqlalchemy.orm import relationship
from database import Base
class Todo(Base):
__tablename__ = 'todos'
id = Column(Integer, primary_key=True, index=True)
text = Column(String, unique=True)
user_id = Column(Integer, ForeignKey('users.id'))
user = relationship("User", back_populates="todos")
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
hashed_password = Column(String)
is_active = Column(Boolean, default=True)
todos = relationship("Todo", back_populates="user")
If you use postgresql, you can use the Partial Index to implement this:
class Todo(Base):
__tablename__ = 'todos'
id = Column(Integer, primary_key=True, index=True)
text = Column(String) # ! removed the unique from here
user_id = Column(Integer, ForeignKey('users.id'))
user = relationship("User", back_populates="todos")
# ! added unique index with
__table_args__ = (
Index(
"todo_text_uc",
"text",
unique=True,
postgresql_where=(user_id != None),
# postgresql_where=(~user_id.is_(None)), # equivalent to the row above, but no linting warnings
),
)
For sqlite just replace postgresql_where with sqlite_where.

SQLAlchemy conditional many to many relationship with an additional column

This is the way that I usually use for m2m relationship implementation.
(Brought from docs.sqlalchemy.org)
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id'))
)
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child",
secondary=association_table,
backref="parents")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
Is there any way for using additional columns at the association_table table?
So it should be like
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id')),
Column('is_valid', Boolean, default=True) # Add the validation column
)
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child",
secondary=association_table,
backref="parents")
# How can I do implement this??
valid_children = relationship("Child",
secondary="and_(association_table.left_id == Parent.id, association_table.right_id == Child.id)"
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
I want to do query depends on is_valid column. How can I modify "secondary" attr in Parent table? Or should I fix the other part?
In this question, time_create column has the same value for all children. But in this case, I need a flag that makes able to retrieve whether this connection is still alive or not.
For example, if you implement a one-on-one chatting, there will be a chatting room consist of two-person, right?
And the table should be like as below:
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id')),
Column('is_left', Boolean, default=False) # Whether the user left or not
)
class Match(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
user = relationship("User",
secondary=association_table,
backref="matches")
# How can I do implement this??
exist_user = relationship("User",
secondary="and_(association_table.left_id == Parent.id, association_table.right_id == Child.id)"
class User(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
nickname = Column(String, unique=True)
How can I do for this?

sqlalchemy python update a primary key with foreign key restraints

I need to change a primary key in multiple tables that also have foreign key restraints. I was thinking to either do an update or to copy the entry with an updated primary key and delete the old entry. How can I accomplish either?
Below is a minimal working set up of my tables. P and S rely on the primary key of S and S relies on the primary key of H.
I need to update the a value of ever table for one entry. I am continually limited on changing the value because of the constraints. I would rather not remove the constraints so this can be a permanent solution for later use.
class H(Base):
__tablename__ = 'ch'
a = Column(String(255), nullable=False, primary_key=True)
other_columns = Column(String(255))
class S(Base):
__tablename__ = 'cs'
a = Column(String(255), ForeignKey('ch.a'), nullable=False, primary_key=True)
sn = Column(Integer, nullable=False, primary_key=True)
other_columns = Column(Date)
header = relationship("H", backref=backref('cs', order_by=a, cascade='delete, all, delete-orphan'), foreign_keys='S.a')
__table_args__ = (UniqueConstraint('a', 'sn', name='_a_sn_uc'),)
class P(Base):
__tablename__ = 'cs_p'
a = Column(String(255), nullable=False, primary_key=True)
sn = Column(Integer, nullable=False, primary_key=True)
ps = Column(Integer, nullable=False, primary_key=True)
other_columns = Column(String(255))
pa = relationship("S", backref=backref('cs_p', order_by=a, cascade='delete, all, delete-orphan'))
__table_args__ = (UniqueConstraint('a', 'sn', 'ps', name='_a_sn_ps_uc'),
ForeignKeyConstraint([a, sn], [S.a, S.sn]),)
class F(Base):
__tablename__ = 'cs_f'
a = Column(String(255), nullable=False, primary_key=True)
sn = Column(Integer, nullable=False, primary_key=True)
fs = Column(Integer, nullable=False, primary_key=True)
other_columns = Column(String(255))
fa = relationship("S", backref=backref('cs_f', order_by=a, cascade='delete, all, delete-orphan'))
__table_args__ = (UniqueConstraint('a', 'sn', 'fs', name='_a_sn_fs_uc'),
ForeignKeyConstraint([a, sn], [S.a, S.sn]),)

In SQLAlchemy, how do I create a unique pair?

class PostsSubscribe(Base):
__tablename__ = 'posts_subscribe'
id = Column(Integer, primary_key = True)
post_id = Column(Integer, ForeignKey('posts_posts.id'), nullable=False)
persona_id = Column(Integer, ForeignKey('personas_personas.id'), nullable=False)
UniqueConstraint('post_id', 'persona_id') #this doesn't work.
Base.metadata.create_all(engine)
This is my table so far. As you can see, I'm using the "Declorative" way of defining tables. I want to create a unique key , but my line doesn't work.
How do I create a unique pair?
UniqueConstraint should be not for a model class, but for its table. You can you __table_args__ to do that:
class PostsSubscribe(Base):
__tablename__ = 'posts_subscribe'
id = Column(Integer, primary_key = True)
post_id = Column(Integer, ForeignKey('posts_posts.id'), nullable=False)
persona_id = Column(Integer, ForeignKey('personas_personas.id'), nullable=False)
__table_args__ = (UniqueConstraint('post_id', 'persona_id', name='_person_post_uc'),
)

Many-to-many self-referential relationship in sqlalchemy

I'm trying to make a self-referential many-to-many relationship (it means that Line can have many parent lines and many child lines) in sqlalchemy like this:
Base = declarative_base()
class Association(Base):
__tablename__ = 'association'
prev_id = Column(Integer, ForeignKey('line.id'), primary_key=True)
next_id = Column(Integer, ForeignKey('line.id'), primary_key=True)
class Line(Base):
__tablename__ = 'line'
id = Column(Integer, primary_key = True)
text = Column(Text)
condition = Column(Text)
action = Column(Text)
next_lines = relationship(Association, backref="prev_lines")
class Root(Base):
__tablename__ = 'root'
name = Column(String, primary_key = True)
start_line_id = Column(Integer, ForeignKey('line.id'))
start_line = relationship('Line')
But I get the following error:
sqlalchemy.exc.ArgumentError: Could not determine join condition between parent/
child tables on relationship Line.next_lines. Specify a 'primaryjoin' expressio
n. If 'secondary' is present, 'secondaryjoin' is needed as well.
Do you know how I could remedy this?
You should just need:
prev_lines = relationship(
Association,
backref="next_lines",
primaryjoin=id==Association.prev_id)
Since this specifies the next_lines back reference there is no need to have a next_lines relationship.
You can also do this using the remote_side parameter to a relationship: http://www.sqlalchemy.org/trac/browser/examples/adjacency_list/adjacency_list.py