Skip to content
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

Composite constraints #1146

Draft
wants to merge 31 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ca4c4a2
Add support for composite unique constraint
atkei Apr 8, 2024
07f8b94
Fix code format
atkei Apr 12, 2024
e4a5209
Add tests
atkei Apr 18, 2024
dc539bf
Merge branch 'master' into composite-unique-constraint-2
atkei Oct 10, 2024
012c608
Add comments
atkei Oct 19, 2024
c7ce718
Merge branch 'master' into composite-constraints
dantownsend Jan 24, 2025
fb02cd5
`add_constraints` method
dantownsend Jan 24, 2025
074e273
change module name from `contraint` to `constraints`
dantownsend Jan 24, 2025
a953b99
refactor -> `add_unique_constraint`
dantownsend Jan 25, 2025
f62205f
update docs
dantownsend Jan 25, 2025
4e1b8e5
add check constraints
dantownsend Jan 25, 2025
e92aca2
make sure `CheckConstraint` works in migrations
dantownsend Jan 25, 2025
3f5d6ea
fix typo in check condition ddl statement
dantownsend Jan 25, 2025
acd341d
remove unused import
dantownsend Jan 25, 2025
5e7e201
improve docstring
dantownsend Jan 25, 2025
fae32e4
refactor to use `constraints` list on `Table`
dantownsend Jan 26, 2025
f369b81
update the docs
dantownsend Jan 26, 2025
0c6d0cf
improve logic for getting `Table.constraints`
dantownsend Jan 26, 2025
400046f
try lambda
dantownsend Jan 30, 2025
7bc9a2e
strings
dantownsend Jan 31, 2025
388e89f
Revert "strings"
dantownsend Jan 31, 2025
83dad23
added `querystring_for_constraint`
dantownsend Jan 31, 2025
dc56104
stop using `constraints` list
dantownsend Feb 1, 2025
b3039d5
update `_table_str`
dantownsend Feb 1, 2025
0963b58
update tests
dantownsend Feb 1, 2025
cc107b9
update tests
dantownsend Feb 1, 2025
423c40c
fix linter errors
dantownsend Feb 1, 2025
7995249
update docstrings
dantownsend Feb 1, 2025
1b9e178
Update constraints.rst
dantownsend Feb 1, 2025
b712fb1
add docs for using constraints with mixins
dantownsend Feb 1, 2025
f49edf0
make sure dates can be used in check constraints
dantownsend Feb 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions docs/src/piccolo/schema/constraints.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
===========
Constraints
===========

Unique constraints
==================

Single column
-------------

Unique constraints can be added to a single column using the ``unique=True``
argument of ``Column``:

.. code-block:: python

class Band(Table):
name = Varchar(unique=True)

Multi-column
------------

Use the ``add_unique_constraint`` method to add a multi-column constraint to a
``Table``:

.. currentmodule:: piccolo.table

.. automethod:: Table.add_unique_constraint

-------------------------------------------------------------------------------

Check constraints
=================

.. automethod:: Table.add_check_constraint
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dantownsend For now, constraints are droped by simply deleting (or commenting out) the constraints from the table definition. I'm fine with that, but it should be indicated in the docs so the user knows how to use that. Another option is to create a drop_unique_constraint classmethod that will drop the constraints. As you wish, both options suit me.

1 change: 1 addition & 0 deletions docs/src/piccolo/schema/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The schema is how you define your database tables, columns and relationships.

./defining
./column_types
./constraints
./m2m
./one_to_one
./advanced
73 changes: 70 additions & 3 deletions piccolo/apps/migrations/auto/diffable_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@

from piccolo.apps.migrations.auto.operations import (
AddColumn,
AddConstraint,
AlterColumn,
DropColumn,
DropConstraint,
)
from piccolo.apps.migrations.auto.serialisation import (
deserialise_params,
serialise_params,
)
from piccolo.columns.base import Column
from piccolo.constraints import Constraint
from piccolo.table import Table, create_table_class


Expand Down Expand Up @@ -62,6 +65,8 @@ class TableDelta:
add_columns: t.List[AddColumn] = field(default_factory=list)
drop_columns: t.List[DropColumn] = field(default_factory=list)
alter_columns: t.List[AlterColumn] = field(default_factory=list)
add_constraints: t.List[AddConstraint] = field(default_factory=list)
drop_constraints: t.List[DropConstraint] = field(default_factory=list)

def __eq__(self, value: TableDelta) -> bool: # type: ignore
"""
Expand Down Expand Up @@ -92,6 +97,19 @@ def __eq__(self, value) -> bool:
return False


@dataclass
class ConstraintComparison:
constraint: Constraint

def __hash__(self) -> int:
return self.constraint.__hash__()

def __eq__(self, value) -> bool:
if isinstance(value, ConstraintComparison):
return self.constraint._meta.name == value.constraint._meta.name
return False


@dataclass
class DiffableTable:
"""
Expand All @@ -103,6 +121,7 @@ class DiffableTable:
tablename: str
schema: t.Optional[str] = None
columns: t.List[Column] = field(default_factory=list)
constraints: t.List[Constraint] = field(default_factory=list)
previous_class_name: t.Optional[str] = None

def __post_init__(self) -> None:
Expand Down Expand Up @@ -196,10 +215,54 @@ def __sub__(self, value: DiffableTable) -> TableDelta:
)
)

add_constraints = [
AddConstraint(
table_class_name=self.class_name,
constraint_name=i.constraint._meta.name,
constraint_class_name=i.constraint.__class__.__name__,
constraint_class=i.constraint.__class__,
params=i.constraint._meta.params,
schema=self.schema,
)
for i in sorted(
{
ConstraintComparison(constraint=constraint)
for constraint in self.constraints
}
- {
ConstraintComparison(constraint=constraint)
for constraint in value.constraints
},
key=lambda x: x.constraint._meta.name,
)
]

drop_constraints = [
DropConstraint(
table_class_name=self.class_name,
constraint_name=i.constraint._meta.name,
tablename=value.tablename,
schema=self.schema,
)
for i in sorted(
{
ConstraintComparison(constraint=constraint)
for constraint in value.constraints
}
- {
ConstraintComparison(constraint=constraint)
for constraint in self.constraints
},
key=lambda x: x.constraint._meta.name,
)
]

return TableDelta(
add_columns=add_columns,
drop_columns=drop_columns,
alter_columns=alter_columns,
add_constraints=add_constraints,
drop_constraints=drop_constraints,
)

def __hash__(self) -> int:
Expand All @@ -225,10 +288,14 @@ def to_table_class(self) -> t.Type[Table]:
"""
Converts the DiffableTable into a Table subclass.
"""
class_members: t.Dict[str, t.Any] = {}
for column in self.columns:
class_members[column._meta.name] = column
for constraint in self.constraints:
class_members[constraint._meta.name] = constraint

return create_table_class(
class_name=self.class_name,
class_kwargs={"tablename": self.tablename, "schema": self.schema},
class_members={
column._meta.name: column for column in self.columns
},
class_members=class_members,
)
Loading
Loading