-
-
Notifications
You must be signed in to change notification settings - Fork 675
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Data Integrity: Raise error on attempt to delete an object required via a Relationship #533
Comments
The default behavior of class Contact(SQLModel, table=True):
...
invoicing_contact_of: List["Client"] = Relationship(back_populates="invoicing_contact", sa_relationship_kwargs={"lazy": "subquery", "passive_deletes": "all"}) And now you get an error when you try to delete a sqlalchemy.exc.IntegrityError: (psycopg2.errors.ForeignKeyViolation) update or delete on table "contact" violates foreign key constraint "client_invoicing_contact_id_fkey" on table "client"
DETAIL: Key (id)=(1) is still referenced from table "client". |
@meirdev Thanks for the suggestion, it was quite difficult to find documentation on this. However, I am not getting the desired behavior. I have modified the model as follows: class Contact(SQLModel, table=True):
"""An entry in the address book."""
id: Optional[int] = Field(default=None, primary_key=True)
first_name: Optional[str]
last_name: Optional[str]
company: Optional[str]
email: Optional[str]
address_id: Optional[int] = Field(default=None, foreign_key="address.id")
address: Optional[Address] = Relationship(
back_populates="contacts", sa_relationship_kwargs={"lazy": "subquery"}
)
invoicing_contact_of: List["Client"] = Relationship(
back_populates="invoicing_contact",
sa_relationship_kwargs={"lazy": "subquery", "passive_deletes": "all"}
) Now if I understand correctly, this function is supposed to raise an def delete_by_id(self, entity_type: Type[sqlmodel.SQLModel], entity_id: int):
"""Deletes the entity of the given type with the given id from the database"""
logger.debug(f"deleting {entity_type} with id={entity_id}")
with self.create_session() as session:
session.exec(
sqlmodel.delete(entity_type).where(entity_type.id == entity_id)
)
session.commit() It doesn't, the deletion proceeds and the respective |
Works for me: from typing import Optional, List
from sqlmodel import (
Field,
Relationship,
SQLModel,
create_engine,
Session,
delete,
)
class Contact(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
invoicing_contact_of: List["Client"] = Relationship(
back_populates="invoicing_contact",
sa_relationship_kwargs={"lazy": "subquery", "passive_deletes": "all"},
)
class Client(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
invoicing_contact_id: int = Field(default=None, foreign_key="contact.id")
invoicing_contact: Contact = Relationship(
back_populates="invoicing_contact_of",
sa_relationship_kwargs={"lazy": "subquery"},
)
engine = create_engine("postgresql://postgres:postgrespw@localhost:55000", echo=True)
SQLModel.metadata.drop_all(engine)
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
contact = Contact()
client = Client(invoicing_contact=contact)
session.add(client)
session.commit()
session.refresh(client)
session.exec(delete(Contact).where(Contact.id == 1))
session.commit() |
Strange. Will run the minimal example to investigate. But does it matter that you are using PostgreSQL and I am using SQLite? |
Indeed! in SQLite the foreign key constraints are disabled by default (https://www.sqlite.org/foreignkeys.html, 2), you have to enable them manually. the simplest way is to listen to the from sqlalchemy import event
engine = create_engine("sqlite:///")
event.listen(engine, "connect", lambda c, _: c.execute("PRAGMA foreign_keys = ON")) |
@meirdev Now the If I try to delete a 2023-01-20 10:08:07.075 | ERROR | core.intent_result:log_message_if_any:44 - (sqlite3.IntegrityError) FOREIGN KEY constraint failed
[SQL: DELETE FROM client WHERE client.id = ?]
[parameters: (1,)]
(Background on this error at: https://sqlalche.me/e/14/gkpj)
NoneType: None |
I think the error came from something else, in my sample code if I try to delete session.exec(delete(Client).where(Client.id == 1))
session.commit() |
@meirdev I can verify that our minimal examples are working fine. However, in the context of the entire data model, this is not working. Basic idea of the data model: Trying to delete a
and so does trying to delete a It seems that by adding just this to the
... I have made every object with a path to an existing The "FOREIGN KEY contraint failed" message is also unhelpful because it doesn't tell us any details. Is there a way to get more info? |
First Check
Commit to Help
Example Code
Description
(As far as I know the documentation does not handle data integrity topics - please point me to the chapter if I am wrong.)
Consider these two model classes
Contact
andClient
. To keep the integrity of the data model, I need the following behavior:An exception is raised if there is an attempt to delete a
Contact
that is still the invoicing contact of an existingClient
.Does SQLModel support this, perhaps via SQLAlchemy?
Operating System
macOS
Operating System Details
No response
SQLModel Version
0.0.8
Python Version
3.10
Additional Context
No response
The text was updated successfully, but these errors were encountered: