This may be totally stupid thing to ask but I have such a requirement in my model where atleast either category
or parent_category
is not null
My model looks like
class BudgetCategories(db.Model):
__tablename__ = 'budget_categories'
uuid = Column('uuid', GUID(), default=uuid.uuid4, primary_key=True,
unique=True)
budget_id = Column(GUID(), ForeignKey('budgets.uuid'), nullable=False)
budget = relationship('Budget', backref='budgetCategories')
category = Column('category', sa.types.String, nullable=True)
parent_category = Column('parent_category', sa.types.String, nullable=True)
amount = Column('amount', Numeric(10, 2), nullable=False)
recurring = Column('recurring', sa.types.Boolean,
nullable=False)
created_on = Column('created_on', sa.types.DateTime(timezone=True),
nullable=False)
How can I specify that. I don't even know what to try
Any pointers appreciated
I am using PostgreSQL
as the backend database
Columns are nullable by default The default value of SQLAlchemy nullable is False unless it's a primary key. A foreign key is also nullable by default.
The sqlalchemy backref is one of the type keywords and it passed as the separate argument parameters which has to be used in the ORM mapping objects. It mainly includes the event listener on the configuration attributes with both directions of the user datas through explicitly handling the database relationships.
unique – When True, indicates that this column contains a unique constraint, or if index is True as well, indicates that the Index should be created with the unique flag. To specify multiple columns in the constraint/index or to specify an explicit name, use the UniqueConstraint or Index constructs explicitly.
A foreign key in SQL is a table-level construct that constrains one or more columns in that table to only allow values that are present in a different set of columns, typically but not always located on a different table.
I needed XOR behavior in my SQLalchemy models. I come up with the following definition (backend used: PostgreSQL):
from sqlalchemy.schema import (
CheckConstraint
)
class ScheduledNotebook(Base):
__table_args__ = (
(CheckConstraint('(uuid::text IS NULL) <> (notebook_path IS NULL)', name='uuid_xor_notebook_path')),
)
id = Column(Integer, primary_key=True)
notebook_path = Column(String, nullable=True, unique=True)
uuid = Column(UUID(as_uuid=True), primary_key=True, unique=True, nullable=True)
and following alembic migration (note: autogenerate won't detect it - you have to add it manually):
def upgrade():
op.create_check_constraint(
'uuid_xor_notebook_path',
table_name='scheduled_notebooks',
schema='metadata',
condition='(uuid::text IS NULL) <> (notebook_path IS NULL)'
)
def downgrade():
op.drop_constraint('uuid_xor_notebook_path')
and it works like a charm:
- only notebook_path - OK
datalake=# INSERT INTO scheduled_notebooks (schedule,enabled,owner, notebook_path) VALUES ('{"kind":"hourly"}',true,'akos', '/a/b/c/d/e.ipynb');
INSERT 0 1
- only uuid - OK
datalake=# INSERT INTO scheduled_notebooks (schedule,enabled,owner, uuid) VALUES ('{"kind":"hourly"}',true,'akos', '7792bd5f-5819-45bf-8902-8cf43102434d');
INSERT 0 1
- both uuid and notebook_path - FAILS as desired
datalake=# INSERT INTO scheduled_notebooks (schedule,enabled,owner, uuid, notebook_path) VALUES ('{"kind":"hourly"}',true,'akos', '7792bd5f-5819-45bf-8902-8cf43102434f', '/a/b/c/d');
ERROR: new row for relation "scheduled_notebooks" violates check constraint "uuid_xor_notebook_path"
DETAIL: Failing row contains (567, /a/b/c/d, {"kind": "hourly"}, t, akos, null, null, null, 7792bd5f-5819-45bf-8902-8cf43102434f).
- neither uuid nor notebook_path - FAILS as desired
datalake=# INSERT INTO scheduled_notebooks (schedule,enabled,owner) VALUES ('{"kind":"hourly"}',true,'akos');
ERROR: new row for relation "scheduled_notebooks" violates check constraint "uuid_xor_notebook_path"
DETAIL: Failing row contains (568, null, {"kind": "hourly"}, t, akos, null, null, null, null).
I am not 100% sure about the PostgreSQL
syntax, but following addition to your BudgetCategories
model should do the trick using CheckConstraint
:
class BudgetCategories(Base):
__tablename__ = 'budget_categories'
# ...
# @note: new
__table_args__ = (
CheckConstraint('NOT(category IS NULL AND parent_category IS NULL)'),
)
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