My User model has a relationship to the Address model. I've specified that the relationship should cascade the delete operation. However, when I query and delete a user, I get an error that the address row is still referenced. How do I delete the user and the addresses?
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
addresses = db.relationship('Address', cascade='all,delete', backref='user')
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey(User.id))
db.session.query(User).filter(User.my_id==1).delete()
IntegrityError: (IntegrityError) update or delete on table "user" violates foreign key constraint "addresses_user_id_fkey" on table "address"
DETAIL: Key (my_id)=(1) is still referenced from table "address".
'DELETE FROM "user" WHERE "user".id = %(id_1)s' {'id_1': 1}
You have the following...
db.session.query(User).filter(User.my_id==1).delete()
Note that after "filter", you are still returned a Query object. Therefore, when you call delete(), you are calling delete() on the Query object (not the User object). This means you are doing a bulk delete (albeit probably with just a single row being deleted)
The documentation for the Query.delete() method that you are using says...
The method does not offer in-Python cascading of relationships - it is
assumed that ON DELETE CASCADE/SET NULL/etc. is configured for any
foreign key references which require it, otherwise the database may
emit an integrity violation if foreign key references are being
enforced.
As it says, running delete in this manner will ignore the Python cascade rules that you've set up. You probably wanted to do something like..
user = db.session.query(User).filter(User.my_id==1).first()
db.session.delete(user)
Otherwise, you may wish to look at setting up the cascade for your database as well.
Related
I'm attempting to check if the key of a particular entity (as defined with an ORM class) already exists in a database.
Note that it needs to work regardless of the names of the (key) attributes. It needs to work generically across all ORM models.
Here's the basic structure of what I need:
# User class definition using ORM
class Employee(Base):
__tablename__ = 'User'
USER_ID = Column(String(50), primary_key=True)
... # OTHER FIELDS EXIST HERE, NOT PART OF PRIMARY KEY
# We have a particular ORM instance
# We need to check if its PK exists in table
# Other fields may have any values
user_instance = User(USER_ID = 1)
# We have a session with DB
session = Session()
# ===
# Now, how do we check if User with USER_ID = 1 (dynamically - we might have different data)
# ===
I am aware of sqlalchemy.inspect but I'm not really sure how it would be applied in this case.
Any help is greatly appreciated.
This is my model:
class Subscriber(models.Model):
...
tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, null=True)
...
This is the generated SQL, according to sqlmigrate (and to manual inspection of the database):
ALTER TABLE `myapp_subscriber` ADD CONSTRAINT `myapp_subscriber_tenant_id_b52815ee_fk_myapp_tenant_id` FOREIGN KEY (`tenant_id`) REFERENCES `myapp_tenant` (`id`);
I was expecting something like this:
CREATE TABLE child (
id INT,
parent_id INT,
INDEX par_ind (parent_id),
FOREIGN KEY (parent_id)
REFERENCES parent(id)
ON DELETE CASCADE
) ENGINE=INNODB;
With the ON DELETE CASCADE.
MySql (MariaDB actually) complains when I delete:
SQL Error (1451): Cannot delete or update a parent row: a foreign key constraint fails
Which makes sense since there is no ON DELETE CASCADE clause.
Why is Django 2.1.5 not honoring the ON DELETE CASCADE clause?
From the docs:
on_delete doesn’t create a SQL constraint in the database. Support for
database-level cascade options may be implemented later
It will perform the cascade in Django itself, so if you delete a Tenant object using Django delete() your Subscriber object will also be deleted. But not if you do it in SQL.
Given a simple declarative based class;
class Entity(db.Model):
__tablename__ = 'brand'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False)
And the next script
entity = Entity()
entity.name = 'random name'
db.session.add(entity)
db.session.commit()
# Just by accessing the property name of the created object a
# SELECT statement is sent to the database.
print entity.name
When I enable echo mode in SQLAlchemy, I can see in the terminal the INSERT statement and an extra SELECT just when I access a property (column) of the model (table row).
If I don't access to any property, the query is not created.
What is the reason for that behavior? In this basic example, We already have the value of the name property assigned to the object. So, Why is needed an extra query? It to secure an up to date value, or something like that?
By default, SQLAlchemy expires objects in the session when you commit. This is controlled via the expire_on_commit parameter.
The reasoning behind this is that the row behind the instance could have been modified outside of the transaction, so if you are not careful you could run into data races, but if you know what you are doing you can safely turn it off.
I have a table where there is a User table and a Follow table. The follow table has two foreign keys which refer to the user's id who follows and the user who's being followed id.
If a user was to delete their account, I would like all the records in the following table to be deleted along with the User record. The way I thought to do this was by using onupdate='CASCASE', ondelete='CASCASE' like so:
follower = db.Column(db.Integer, db.ForeignKey('accounts.id'), onupdate='CASCADE', ondelete='CASCADE')
following = db.Column(db.Integer, db.ForeignKey('accounts.id'), onupdate='CASCADE', ondelete='CASCADE')
I try to update my database (using Flask-Migrate/Alembic) however, I receive the error:
sqlalchemy.exc.ArgumentError: Unknown arguments passed to Column: ['ondelete']
So it appears onupdate works fine but not ondelete.
Why do I have this issue and how can I solve it? Thanks.
onupdate and ondelete are parameters for the ForeignKey constructor, not the Column constructor. See http://docs.sqlalchemy.org/en/rel_0_9/core/constraints.html#sqlalchemy.schema.ForeignKey.
The Column constructor does have an onupdate parameter which is why it seemed to work, but what you are looking for is the ForeignKey onupdate and ondelete parameters.
It should look like this:
follower = db.Column(db.Integer, db.ForeignKey('accounts.id', onupdate='CASCADE', ondelete='CASCADE'))
following = ...
I have a large number of .create() calls that rely on a ForeignKey in another table (Users). However, there is no point in the code where I actually create users.
Is there a way for there to be a Users entry created for each foreign key is specified on another table in SQLAlchemy?
For example:
class Rr(db.Model):
__tablename__ = 'rr'
id = db.Column(db.Integer, primary_key=True)
submitter = db.Column(db.String(50), db.ForeignKey('user.username'))
class User(db.Model):
__tablename__ = 'user'
username = db.Column(db.String, primary_key=True)
so If I call Rr(id, submitter=John) is there a way for a John entry to be created in the user table if it does not already exist?
I understand that I can create a wrapper around the .create() method such that it checks the submitter and creates one if it doesn't exist but this seems excess as there are a large number of models that want Users to be automatically created.
I can't think of any orm or sql implementation that does what you ask but there is something that effectively accomplishes what you seek to do described in this SO answer: Does SQLAlchemy have an equivalent of Django's get_or_create?
basically get the User from the db if it exists, if it doesn't create it.
The only down side to this method is that you would need to do 2 queries instead of one but I don't think there is a way to do what you seek in one query