How to auto set the product category_id
to a default value when category is deleted? For example 1
to point to the first category.
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80))
content = db.Column(db.Text(), unique=True)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
atime = db.Column(db.DateTime())
def __init__(self, name, content, category_id):
self.name = name
self.content = content
self.category_id = category_id
self.atime = datetime.now()
def __repr__(self):
return '<Product %r>' % self.id
class Category(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80))
products = db.relationship('Product', backref='category', cascade="all, delete, delete-orphan")
def __init__(self, *args, **kwargs):
if len(kwargs) > 0:
self.name = kwargs['name']
def __repr__(self):
return '<Category %r>' % self.name
I don't want to use cascade to delete them!
Yes, you can define a column with a default value of 0 as a Foreign Key. However, for the constraint to work, you would need to have a row in the source table with a value of 0 as well. Example: CREATE TABLE dbo.
ON DELETE SET DEFAULT SQL Server sets the rows in the child table to their default values if the corresponding rows in the parent table are deleted. To execute this action, the foreign key columns must have default definitions. Note that a nullable column has a default value of NULL if no default value specified.
A foreign key with cascade delete means that if a record in the parent table is deleted, then the corresponding records in the child table will automatically be deleted. This is called a cascade delete in SQLite. A foreign key with a cascade delete can only be defined in a CREATE TABLE statement.
There are two things that need to be performed in orchestration here:
I think the cleanest way would be to set the category_id
to a NULL
value when its Category is deleted: SET NULL
is one of the possible Referential Actions of the ON DELETE
clause, which you can add to your ForeignKey definition:
category_id = db.Column(db.Integer, db.ForeignKey('category.id', ondelete='SET NULL'))
In the same way you can use an option SET DEFAULT
, but in this case you need to also configure the default value for the column category_id
: category_id = Column(..., server_default=1)
. Note that implementations of these differ between different RDBMS.
As to the cascade
option: You should basically remove the cascade="all, delete, delete-orphan"
from your products
relationship definition. In fact, you want to ensure that delete, delete-orphan
are not there.
Having said that, you really need to test your code to cover different scenarios, as two different deletions of the Category
object might produce different results depending on your RDBMS and SA configuration:
# scenario-1: delete in session: SA might set category_id of all chilren Products to None
c1 = session.query(Category).get(1)
session.delete(c1)
session.commit()
# scenario-2: delete without loading an object into the session: SA will perform no additional logic
session.query(Category).filter(Category.id == 2).delete()
session.commit()
Hope all this points you in the right direction. As usual, enabled SQL logging in your test code using echo=True
or just by configuring logging
module, and you will see what SA is doing to your database. Other changes which which you did not see in the SQL were done by RDBMS itself given your Referential Action.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With