I'm building setup and teardown for my pytest tests. I cannot drop tables because there are numerous dependiences between tables. Still, I need to drop all tables after each test.
#pytest.fixture()
def test_db():
model.Base.metadata.create_all(bind=test_engine)
db = TestSessionLocal()
yield db
model.Base.metadata.drop_all(bind=test_engine)
it throws me psycopg2.errors.DependentObjectsStillExist
I have also included cascades in my model but it does not work.
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True)
name = Column(String)
surname = Column(String)
position = Column(String)
skills = relationship("UserSkills", cascade="all, delete", passive_deletes=True)
manager = Column(Integer, ForeignKey("manager.id", ondelete='CASCADE'))
account_type = Column(Enum(AccountType), default=AccountType.USER)
created = Column(DateTime, default=datetime.datetime.utcnow)
How can I add cascade option to model.Base.metadata.drop_all(bind=test_engine) ?
Related
In models defined using declarative, I have the following structure:
class ItemBin(Base, BaseModelMixin, PostCommitTriggersModelMixin):
__tablename__ = "item_bins"
id = Column(INTEGER(11), nullable=False, unique=True)
account_id = Column(INTEGER(11), primary_key=True, nullable=False, index=True)
sku = Column(String(200), primary_key=True, nullable=False, server_default=text("''"))
warehouse_id = Column(INTEGER(11), primary_key=True, nullable=False, server_default=text("'0'"))
location_id = Column(INTEGER(11), primary_key=True, nullable=False, index=True, server_default=text("'0'"))
bin = relationship("Bin", uselist=False, primaryjoin="foreign(ItemBin.location_id) == Bin.id")
class Bin(Base):
__tablename__ = "bins"
id = Column(Integer, nullable=False, unique=True, autoincrement=True)
account_id = Column(Integer, primary_key=True, nullable=False)
warehouse = Column(String(100), primary_key=True, nullable=False, server_default=text("''"), default="")
location = Column(String(100), primary_key=True, nullable=False, server_default=text("''"), default="")
The lack of FKs is by design.
Our code allows for orphan ItemBins to exist, they get picked up later on and a default parent is set on them, and the change gets logged. The problem is that when we tried to add a Bin.item_bins relationship like:
class Bin(...):
...
item_bins = relationship(ItemBin, primaryjoin="foreign(ItemBin.location_id) == Bin.id", lazy="dynamic")
Then when the code does item_bin.bin.delete() the system tries to set item_bin.location_id = NULL and that fails with "Dependency rule tried to blank-out primary key column item_bins.location_id..."
Is it possible to define a relationship and not have it update its children when the parent is deleted? We're making do without the relationship, but we're having to write repetitive queries to refer to the objects' children, instead of doing Bin.item_bins.any() and similar.
I think I have a simple problem but I cannot understand where exactly I am making a mistake. My architecture look like that:
class CreditsTransaction(Base):
__tablename__ = 'credits_transaction'
id = Column(Integer, primary_key=True, index=True)
credit_amount = Column(Integer)
time_created = Column(DateTime(timezone=True), server_default=func.now())
user_id = Column(Integer, ForeignKey('user.id'))
# user = relationship('User', backref=backref("CreditsTransaction", cascade="all,delete"))
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, index=True)
credit_balance = Column(Integer)
username = Column(String)
email = Column(String)
password = Column(String)
time_created = Column(DateTime(timezone=True), server_default=func.now())
time_updated = Column(DateTime(timezone=True), onupdate=func.now())
# credits_transaction = relationship("CreditsTransaction", cascade="all,delete", backref="user")
In this code there are two commented lines, I tried all combinations, I think (according to tutorials) it should work with only second one uncommented. But with that I am getting error - ERROR: relation "user" does not exist at character 13. Googling that didn't help me much. I am sure that table user is created properly because above I can see Detected added table 'user'.
I tried changing the code in various ways but I cannot get it to work. (if I leave them commented, the api is working properly except deleting users that have some CreditsTransaction objects).
Here is the error when I try to delete this user - sqlalchemy.exc.IntegrityError: (psycopg2.errors.ForeignKeyViolation) update or delete on table "user" violates foreign key constraint "credits_transaction_user_id_fkey" on table "credits_transaction" and DETAIL: Key (id)=(3) is still referenced from table "credits_transaction"..
I know that I am looking for some SQLAlchemy version of Django's on_delete but I cannot implement it properly. Could someone please help me?
||||| EDIT: |||||
I think I got that with this code from SQLAlechemy docs:
class CreditsTransaction(Base):
__tablename__ = 'credits_transaction'
id = Column(Integer, primary_key=True, index=True)
credit_amount = Column(Integer)
time_created = Column(DateTime(timezone=True), server_default=func.now())
user_id = Column(Integer, ForeignKey('user.id', ondelete='CASCADE'))
user = relationship('User', back_populates="credits_transaction")
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, index=True)
credit_balance = Column(Integer)
username = Column(String)
email = Column(String)
password = Column(String)
time_created = Column(DateTime(timezone=True), server_default=func.now())
time_updated = Column(DateTime(timezone=True), onupdate=func.now())
credits_transaction = relationship("CreditsTransaction", back_populates="user", cascade="all,delete", passive_deletes=True)
But this one also results in this error - sqlalchemy.exc.IntegrityError: (psycopg2.errors.ForeignKeyViolation) update or delete on table "user" violates foreign key constraint "credits_transaction_user_id_fkey" on table "credits_transaction"
I'm trying to create a set of tables which can all have a Contactassigned to them.
class Contact(Base):
__tablename__ = "contact"
id = Column(Integer, primary_key=True)
name = Column(String, index=True, nullable=False, doc="Name of the contact.")
phone = Column(String, index=True, doc="Phone number of the contact.")
Contacts can be linked to from various other tables, and one record can have more than one contact in different fields.
class BusinessGroup(Base):
__tablename__ = "business_group"
id = Column(Integer, primary_key=True)
name = Column(String, index=True, nullable=False, doc="Name of the group.")
main_contact = Column(Integer, ForeignKey("contact.id"), doc="Main contact details for the group.")
second_contact = Column(Integer, ForeignKey("contact.id"), doc="Second contact details for the group.")
class Vendor(Base):
__tablename__ = "vendor"
id = Column(Integer, primary_key=True)
name = Column(String, index=True, nullable=False, doc="Name of the vendor.")
contact = Column(Integer, ForeignKey("contact.id"), doc="Main contact details for the vendor.")
This setup seems to work, but in flask-admin, no contact fields show up when creating a new item for either BusinessGroup or Vendor.
How can I make this design work? Or should I be modelling this kind of relationship in a different way entirely?
I ended up subclassing the Contact table so that I could have:
class MainContact(Contact):
__tablename__ = "main_contact"
id = Column(Integer, ForeignKey("contact.id"), primary_key=True)
business_group = relationship("BusinessGroup", back_populates="main_contact")
business_group_id = Column(Integer, ForeignKey("business_group.id"), nullable=False)
...
class SecondContact(Contact):
__tablename__ = "second_contact"
id = Column(Integer, ForeignKey("contact.id"), primary_key=True)
business_group = relationship("BusinessGroup", back_populates="second_contact")
business_group_id = Column(Integer, ForeignKey("business_group.id"), nullable=False)
...
class BusinessGroup(Base):
__tablename__ = "business_group"
id = Column(Integer, primary_key=True)
name = Column(String, index=True, nullable=False, doc="Name of the group.")
main_contact = relationship(
"MainContact", back_populates="business_group", uselist=False,
doc="Main contact details for the business_group."
)
second_contact = relationship(
"SecondContact", back_populates="business_group", uselist=False,
doc="Second contact details for the business_group."
)
As well as requiring subclassing so that we can have two different contacts referred to from the same model, the other important part was adding the foreign key relationships, so that the contacts show up in the Flask Admin panel.
I am new using sqlAlchemy and having problem creating new tables, specially when it comes around 2 foreign keys pointing to 1 table:
class Offers(db.Model):
__tablename__ = 'offers'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
contact_ign = db.Column(db.String(100))
conversion_rate = db.Column(db.Float)
stock = db.Column(db.Integer)
create_date = db.Column(db.DateTime(timezone=True), default=func.now())
currency_pair = db.relationship('CurrencyPairs', backref='pair', lazy='dynamic')
class CurrencyPairs(db.Model):
__tablename__ = 'currency_pairs'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
league = db.Column(db.String(100))
pair_id = db.Column(db.Integer, db.ForeignKey('offers.id'))
want = db.relationship('Currency', backref='want', lazy='dynamic')
have = db.relationship('Currency', backref='have', lazy='dynamic')
class Currency(db.Model):
__tablename__ = 'currency'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(100), nullable=False)
poe_trade = db.Column(db.Integer, nullable=False)
poe_official = db.Column(db.String(10), nullable=False)
tier = db.Column(db.Integer, nullable=False)
want_id = db.Column(db.Integer, db.ForeignKey('currency_pairs.id'))
have_id = db.Column(db.Integer, db.ForeignKey('currency_pairs.id'))
The error I am getting is:
sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'Mapper|CurrencyPairs|currency_pairs'. Original exception was: Could not determine join condition b
etween parent/child tables on relationship CurrencyPairs.want - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key refe
rence to the parent table
I have try different things but I get same result.
What am I doing wrong?
Thanks In advance.
I know this is an old question, but I had the same problem. I hope to help others with the answer.
This issue is addressed in the sqlalchemy documentation.
https://docs.sqlalchemy.org/en/13/orm/join_conditions.html#handling-multiple-join-paths
class Offers(db.Model):
__tablename__ = 'offers'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
contact_ign = db.Column(db.String(100))
conversion_rate = db.Column(db.Float)
stock = db.Column(db.Integer)
create_date = db.Column(db.DateTime(timezone=True), default=func.now())
currency_pair = db.relationship('CurrencyPairs', backref='pair', lazy='dynamic')
class CurrencyPairs(db.Model):
__tablename__ = 'currency_pairs'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
league = db.Column(db.String(100))
pair_id = db.Column(db.Integer, db.ForeignKey('offers.id'))
want_currency = relationship("Currency", foreign_keys='[Currency.want_id]', back_populates="want_currency_pairs")
have_currency = relationship("Currency", foreign_keys='[Currency.have_id]', back_populates="have_currency_pairs")
class Currency(db.Model):
__tablename__ = 'currency'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(100), nullable=False)
poe_trade = db.Column(db.Integer, nullable=False)
poe_official = db.Column(db.String(10), nullable=False)
tier = db.Column(db.Integer, nullable=False)
want_currency_pairs = relationship(CurrencyPairs, foreign_keys="[Currency.want_id]", back_populates="want_currency")
have_currency_pairs = relationship(CurrencyPairs, foreign_keys="[Currency.have_id]", back_populates="have_currency")
The way you wrote the code, sqlalchemy can't really understand which relationship to choose, because you have 2 of the same relationship. So you have to describe to sqlalchemy that there are 2 relationships to the same table.
I am new to SQLAlchemy, I am trying to build a practice project using SQLAlchemy. I have created the database containing tables with the following relationship. Now my questions are :
How to INSERT data into the tables as they are interdependent?
Does this form a loop in database design?
Is the looping database design, a bad practice? How to resolve this if its a bad practice?
Department.manager_ssn ==> Employee.SSN
and
Employee.department_id ==> Department.deptid
database relationship diagram
and following is the current version of code creating this exact database.
# Department table
class Departments(Base):
__tablename__ = "Departments"
# Attricutes
Dname= Column(String(15), nullable=False)
Dnumber= Column(Integer, primary_key=True)
Mgr_SSN= Column(Integer, ForeignKey('Employees.ssn'), nullable=False)
employees = relationship("Employees")
# Employee table
class Employees(Base):
__tablename__ = "Employees"
# Attributes
Fname = Column(String(30), nullable=False)
Minit = Column(String(15), nullable=False)
Lname = Column(String(15), nullable=False)
SSN = Column(Integer, primary_key=True)
Bdate = Column(Date, nullable=False)
Address = Column(String(15), nullable=False)
Sex = Column(String(1), default='F', nullable=False)
Salary = Column(Integer, nullable=False)
Dno = Column(Integer, ForeignKey('Departments.Dnumber'), nullable=False)
departments = relationship("Departments")
Please provide the solution in SQLAlchemy only and not in flask-sqlalchemy or flask-migrate and I am using Python 3.6.
You can avoid such circular reference design altogether by
Declaring the foreign key constraint on just one side of the relationship
Use a boolean flag to denote if the employee is a manager
class Department(Base):
__tablename__ = 'departments'
department_id = Column(Integer, primary_key=True)
employees = relationship('Employee', lazy='dynamic', back_populates='department')
class Employee(Base):
__tablename__ = 'employees'
employee_id = Column(Integer, primary_key=True)
is_manager = Column(Boolean, nullable=False, default=False)
department_id = Column(Integer, ForeignKey('departments.department_id'), nullable=False)
department = relationship('Department', back_populates='employees')
You can find the manager of the department using
department = session.query(Department).get(..)
department.employees.filter(Employee.is_manager == True).one_or_none()