Skip to content
Projeler
Gruplar
Parçacıklar
Yardım
Yükleniyor...
Oturum aç / Kaydol
Gezinmeyi değiştir
D
django
Proje
Proje
Ayrıntılar
Etkinlik
Cycle Analytics
Depo (repository)
Depo (repository)
Dosyalar
Kayıtlar (commit)
Dallar (branch)
Etiketler
Katkıda bulunanlar
Grafik
Karşılaştır
Grafikler
Konular (issue)
0
Konular (issue)
0
Liste
Pano
Etiketler
Kilometre Taşları
Birleştirme (merge) Talepleri
0
Birleştirme (merge) Talepleri
0
CI / CD
CI / CD
İş akışları (pipeline)
İşler
Zamanlamalar
Grafikler
Paketler
Paketler
Wiki
Wiki
Parçacıklar
Parçacıklar
Üyeler
Üyeler
Collapse sidebar
Close sidebar
Etkinlik
Grafik
Grafikler
Yeni bir konu (issue) oluştur
İşler
Kayıtlar (commit)
Konu (issue) Panoları
Kenar çubuğunu aç
Batuhan Osman TASKAYA
django
Commits
b69f8eb0
Kaydet (Commit)
b69f8eb0
authored
Ara 27, 2018
tarafından
Paveł Tyślacki
Kaydeden (comit)
Tim Graham
Ock 12, 2019
Dosyalara gözat
Seçenekler
Dosyalara Gözat
İndir
Eposta Yamaları
Sade Fark
Fixed #30062 -- Added support for unique conditional constraints.
üst
1e837c4b
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
291 additions
and
26 deletions
+291
-26
schema.py
django/db/backends/base/schema.py
+38
-12
schema.py
django/db/backends/sqlite3/schema.py
+9
-2
base.py
django/db/models/base.py
+2
-0
constraints.py
django/db/models/constraints.py
+28
-6
constraints.txt
docs/ref/models/constraints.txt
+14
-0
models.py
tests/constraints/models.py
+7
-1
tests.py
tests/constraints/tests.py
+62
-5
test_operations.py
tests/migrations/test_operations.py
+131
-0
No files found.
django/db/backends/base/schema.py
Dosyayı görüntüle @
b69f8eb0
...
...
@@ -80,6 +80,7 @@ class BaseDatabaseSchemaEditor:
sql_delete_fk
=
sql_delete_constraint
sql_create_index
=
"CREATE INDEX
%(name)
s ON
%(table)
s (
%(columns)
s)
%(extra)
s
%(condition)
s"
sql_create_unique_index
=
"CREATE UNIQUE INDEX
%(name)
s ON
%(table)
s (
%(columns)
s)
%(condition)
s"
sql_delete_index
=
"DROP INDEX
%(name)
s"
sql_create_pk
=
"ALTER TABLE
%(table)
s ADD CONSTRAINT
%(name)
s PRIMARY KEY (
%(columns)
s)"
...
...
@@ -296,7 +297,7 @@ class BaseDatabaseSchemaEditor:
# Make the table
sql
=
self
.
sql_create_table
%
{
"table"
:
self
.
quote_name
(
model
.
_meta
.
db_table
),
"definition"
:
", "
.
join
(
(
*
column_sqls
,
*
constraints
)
),
"definition"
:
", "
.
join
(
constraint
for
constraint
in
(
*
column_sqls
,
*
constraints
)
if
constraint
),
}
if
model
.
_meta
.
db_tablespace
:
tablespace_sql
=
self
.
connection
.
ops
.
tablespace_sql
(
model
.
_meta
.
db_tablespace
)
...
...
@@ -339,11 +340,15 @@ class BaseDatabaseSchemaEditor:
def
add_constraint
(
self
,
model
,
constraint
):
"""Add a check constraint to a model."""
self
.
execute
(
constraint
.
create_sql
(
model
,
self
))
sql
=
constraint
.
create_sql
(
model
,
self
)
if
sql
:
self
.
execute
(
sql
)
def
remove_constraint
(
self
,
model
,
constraint
):
"""Remove a check constraint from a model."""
self
.
execute
(
constraint
.
remove_sql
(
model
,
self
))
sql
=
constraint
.
remove_sql
(
model
,
self
)
if
sql
:
self
.
execute
(
sql
)
def
alter_unique_together
(
self
,
model
,
old_unique_together
,
new_unique_together
):
"""
...
...
@@ -1008,7 +1013,14 @@ class BaseDatabaseSchemaEditor:
def
_delete_fk_sql
(
self
,
model
,
name
):
return
self
.
_delete_constraint_sql
(
self
.
sql_delete_fk
,
model
,
name
)
def
_unique_sql
(
self
,
fields
,
name
):
def
_unique_sql
(
self
,
model
,
fields
,
name
,
condition
=
None
):
if
condition
:
# Databases support conditional unique constraints via a unique
# index.
sql
=
self
.
_create_unique_sql
(
model
,
fields
,
name
=
name
,
condition
=
condition
)
if
sql
:
self
.
deferred_sql
.
append
(
sql
)
return
None
constraint
=
self
.
sql_unique_constraint
%
{
'columns'
:
', '
.
join
(
map
(
self
.
quote_name
,
fields
)),
}
...
...
@@ -1017,7 +1029,7 @@ class BaseDatabaseSchemaEditor:
'constraint'
:
constraint
,
}
def
_create_unique_sql
(
self
,
model
,
columns
,
name
=
None
):
def
_create_unique_sql
(
self
,
model
,
columns
,
name
=
None
,
condition
=
None
):
def
create_unique_name
(
*
args
,
**
kwargs
):
return
self
.
quote_name
(
self
.
_create_index_name
(
*
args
,
**
kwargs
))
...
...
@@ -1027,14 +1039,28 @@ class BaseDatabaseSchemaEditor:
else
:
name
=
self
.
quote_name
(
name
)
columns
=
Columns
(
table
,
columns
,
self
.
quote_name
)
return
Statement
(
self
.
sql_create_unique
,
table
=
table
,
name
=
name
,
columns
=
columns
,
)
if
condition
:
return
Statement
(
self
.
sql_create_unique_index
,
table
=
table
,
name
=
name
,
columns
=
columns
,
condition
=
' WHERE '
+
condition
,
)
if
self
.
connection
.
features
.
supports_partial_indexes
else
None
else
:
return
Statement
(
self
.
sql_create_unique
,
table
=
table
,
name
=
name
,
columns
=
columns
,
)
def
_delete_unique_sql
(
self
,
model
,
name
):
def
_delete_unique_sql
(
self
,
model
,
name
,
condition
=
None
):
if
condition
:
return
(
self
.
_delete_constraint_sql
(
self
.
sql_delete_index
,
model
,
name
)
if
self
.
connection
.
features
.
supports_partial_indexes
else
None
)
return
self
.
_delete_constraint_sql
(
self
.
sql_delete_unique
,
model
,
name
)
def
_check_sql
(
self
,
name
,
check
):
...
...
django/db/backends/sqlite3/schema.py
Dosyayı görüntüle @
b69f8eb0
...
...
@@ -4,6 +4,7 @@ from decimal import Decimal
from
django.apps.registry
import
Apps
from
django.db.backends.base.schema
import
BaseDatabaseSchemaEditor
from
django.db.backends.ddl_references
import
Statement
from
django.db.models
import
UniqueConstraint
from
django.db.transaction
import
atomic
from
django.db.utils
import
NotSupportedError
...
...
@@ -398,7 +399,13 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
self
.
delete_model
(
old_field
.
remote_field
.
through
)
def
add_constraint
(
self
,
model
,
constraint
):
self
.
_remake_table
(
model
)
if
isinstance
(
constraint
,
UniqueConstraint
)
and
constraint
.
condition
:
super
()
.
add_constraint
(
model
,
constraint
)
else
:
self
.
_remake_table
(
model
)
def
remove_constraint
(
self
,
model
,
constraint
):
self
.
_remake_table
(
model
)
if
isinstance
(
constraint
,
UniqueConstraint
)
and
constraint
.
condition
:
super
()
.
remove_constraint
(
model
,
constraint
)
else
:
self
.
_remake_table
(
model
)
django/db/models/base.py
Dosyayı görüntüle @
b69f8eb0
...
...
@@ -1009,6 +1009,8 @@ class Model(metaclass=ModelBase):
for
model_class
,
model_constraints
in
constraints
:
for
constraint
in
model_constraints
:
if
(
isinstance
(
constraint
,
UniqueConstraint
)
and
# Partial unique constraints can't be validated.
constraint
.
condition
is
None
and
not
any
(
name
in
exclude
for
name
in
constraint
.
fields
)):
unique_checks
.
append
((
model_class
,
constraint
.
fields
))
...
...
django/db/models/constraints.py
Dosyayı görüntüle @
b69f8eb0
from
django.db.models.query_utils
import
Q
from
django.db.models.sql.query
import
Query
__all__
=
[
'CheckConstraint'
,
'UniqueConstraint'
]
...
...
@@ -66,34 +67,55 @@ class CheckConstraint(BaseConstraint):
class
UniqueConstraint
(
BaseConstraint
):
def
__init__
(
self
,
*
,
fields
,
name
):
def
__init__
(
self
,
*
,
fields
,
name
,
condition
=
None
):
if
not
fields
:
raise
ValueError
(
'At least one field is required to define a unique constraint.'
)
if
not
isinstance
(
condition
,
(
type
(
None
),
Q
)):
raise
ValueError
(
'UniqueConstraint.condition must be a Q instance.'
)
self
.
fields
=
tuple
(
fields
)
self
.
condition
=
condition
super
()
.
__init__
(
name
)
def
_get_condition_sql
(
self
,
model
,
schema_editor
):
if
self
.
condition
is
None
:
return
None
query
=
Query
(
model
=
model
)
where
=
query
.
build_where
(
self
.
condition
)
compiler
=
query
.
get_compiler
(
connection
=
schema_editor
.
connection
)
sql
,
params
=
where
.
as_sql
(
compiler
,
schema_editor
.
connection
)
return
sql
%
tuple
(
schema_editor
.
quote_value
(
p
)
for
p
in
params
)
def
constraint_sql
(
self
,
model
,
schema_editor
):
fields
=
[
model
.
_meta
.
get_field
(
field_name
)
.
column
for
field_name
in
self
.
fields
]
return
schema_editor
.
_unique_sql
(
fields
,
self
.
name
)
condition
=
self
.
_get_condition_sql
(
model
,
schema_editor
)
return
schema_editor
.
_unique_sql
(
model
,
fields
,
self
.
name
,
condition
=
condition
)
def
create_sql
(
self
,
model
,
schema_editor
):
fields
=
[
model
.
_meta
.
get_field
(
field_name
)
.
column
for
field_name
in
self
.
fields
]
return
schema_editor
.
_create_unique_sql
(
model
,
fields
,
self
.
name
)
condition
=
self
.
_get_condition_sql
(
model
,
schema_editor
)
return
schema_editor
.
_create_unique_sql
(
model
,
fields
,
self
.
name
,
condition
=
condition
)
def
remove_sql
(
self
,
model
,
schema_editor
):
return
schema_editor
.
_delete_unique_sql
(
model
,
self
.
name
)
condition
=
self
.
_get_condition_sql
(
model
,
schema_editor
)
return
schema_editor
.
_delete_unique_sql
(
model
,
self
.
name
,
condition
=
condition
)
def
__repr__
(
self
):
return
'<
%
s: fields=
%
r name=
%
r>'
%
(
self
.
__class__
.
__name__
,
self
.
fields
,
self
.
name
)
return
'<
%
s: fields=
%
r name=
%
r
%
s>'
%
(
self
.
__class__
.
__name__
,
self
.
fields
,
self
.
name
,
''
if
self
.
condition
is
None
else
' condition=
%
s'
%
self
.
condition
,
)
def
__eq__
(
self
,
other
):
return
(
isinstance
(
other
,
UniqueConstraint
)
and
self
.
name
==
other
.
name
and
self
.
fields
==
other
.
fields
self
.
fields
==
other
.
fields
and
self
.
condition
==
other
.
condition
)
def
deconstruct
(
self
):
path
,
args
,
kwargs
=
super
()
.
deconstruct
()
kwargs
[
'fields'
]
=
self
.
fields
if
self
.
condition
:
kwargs
[
'condition'
]
=
self
.
condition
return
path
,
args
,
kwargs
docs/ref/models/constraints.txt
Dosyayı görüntüle @
b69f8eb0
...
...
@@ -69,3 +69,17 @@ date.
.. attribute:: UniqueConstraint.name
The name of the constraint.
``condition``
-------------
.. attribute:: UniqueConstraint.condition
A :class:`Q` object that specifies the condition you want the constraint to
enforce.
For example, ``UniqueConstraint(fields=['user'], condition=Q(status='DRAFT')``
ensures that each user only has one draft.
These conditions have the same database restrictions as
:attr:`Index.condition`.
tests/constraints/models.py
Dosyayı görüntüle @
b69f8eb0
...
...
@@ -3,6 +3,7 @@ from django.db import models
class
Product
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
255
)
color
=
models
.
CharField
(
max_length
=
32
,
null
=
True
)
price
=
models
.
IntegerField
(
null
=
True
)
discounted_price
=
models
.
IntegerField
(
null
=
True
)
...
...
@@ -12,5 +13,10 @@ class Product(models.Model):
check
=
models
.
Q
(
price__gt
=
models
.
F
(
'discounted_price'
)),
name
=
'price_gt_discounted_price'
,
),
models
.
UniqueConstraint
(
fields
=
[
'name'
],
name
=
'unique_name'
),
models
.
UniqueConstraint
(
fields
=
[
'name'
,
'color'
],
name
=
'name_color_uniq'
),
models
.
UniqueConstraint
(
fields
=
[
'name'
],
name
=
'name_without_color_uniq'
,
condition
=
models
.
Q
(
color__isnull
=
True
),
),
]
tests/constraints/tests.py
Dosyayı görüntüle @
b69f8eb0
...
...
@@ -83,7 +83,10 @@ class CheckConstraintTests(TestCase):
class
UniqueConstraintTests
(
TestCase
):
@classmethod
def
setUpTestData
(
cls
):
cls
.
p1
=
Product
.
objects
.
create
(
name
=
'p1'
)
cls
.
p1
,
cls
.
p2
=
Product
.
objects
.
bulk_create
([
Product
(
name
=
'p1'
,
color
=
'red'
),
Product
(
name
=
'p2'
),
])
def
test_eq
(
self
):
self
.
assertEqual
(
...
...
@@ -100,6 +103,29 @@ class UniqueConstraintTests(TestCase):
)
self
.
assertNotEqual
(
models
.
UniqueConstraint
(
fields
=
[
'foo'
,
'bar'
],
name
=
'unique'
),
1
)
def
test_eq_with_condition
(
self
):
self
.
assertEqual
(
models
.
UniqueConstraint
(
fields
=
[
'foo'
,
'bar'
],
name
=
'unique'
,
condition
=
models
.
Q
(
foo
=
models
.
F
(
'bar'
))
),
models
.
UniqueConstraint
(
fields
=
[
'foo'
,
'bar'
],
name
=
'unique'
,
condition
=
models
.
Q
(
foo
=
models
.
F
(
'bar'
))),
)
self
.
assertNotEqual
(
models
.
UniqueConstraint
(
fields
=
[
'foo'
,
'bar'
],
name
=
'unique'
,
condition
=
models
.
Q
(
foo
=
models
.
F
(
'bar'
))
),
models
.
UniqueConstraint
(
fields
=
[
'foo'
,
'bar'
],
name
=
'unique'
,
condition
=
models
.
Q
(
foo
=
models
.
F
(
'baz'
))
),
)
def
test_repr
(
self
):
fields
=
[
'foo'
,
'bar'
]
name
=
'unique_fields'
...
...
@@ -109,6 +135,18 @@ class UniqueConstraintTests(TestCase):
"<UniqueConstraint: fields=('foo', 'bar') name='unique_fields'>"
,
)
def
test_repr_with_condition
(
self
):
constraint
=
models
.
UniqueConstraint
(
fields
=
[
'foo'
,
'bar'
],
name
=
'unique_fields'
,
condition
=
models
.
Q
(
foo
=
models
.
F
(
'bar'
)),
)
self
.
assertEqual
(
repr
(
constraint
),
"<UniqueConstraint: fields=('foo', 'bar') name='unique_fields' "
"condition=(AND: ('foo', F(bar)))>"
,
)
def
test_deconstruction
(
self
):
fields
=
[
'foo'
,
'bar'
]
name
=
'unique_fields'
...
...
@@ -118,15 +156,34 @@ class UniqueConstraintTests(TestCase):
self
.
assertEqual
(
args
,
())
self
.
assertEqual
(
kwargs
,
{
'fields'
:
tuple
(
fields
),
'name'
:
name
})
def
test_deconstruction_with_condition
(
self
):
fields
=
[
'foo'
,
'bar'
]
name
=
'unique_fields'
condition
=
models
.
Q
(
foo
=
models
.
F
(
'bar'
))
constraint
=
models
.
UniqueConstraint
(
fields
=
fields
,
name
=
name
,
condition
=
condition
)
path
,
args
,
kwargs
=
constraint
.
deconstruct
()
self
.
assertEqual
(
path
,
'django.db.models.UniqueConstraint'
)
self
.
assertEqual
(
args
,
())
self
.
assertEqual
(
kwargs
,
{
'fields'
:
tuple
(
fields
),
'name'
:
name
,
'condition'
:
condition
})
def
test_database_constraint
(
self
):
with
self
.
assertRaises
(
IntegrityError
):
Product
.
objects
.
create
(
name
=
self
.
p1
.
name
)
Product
.
objects
.
create
(
name
=
self
.
p1
.
name
,
color
=
self
.
p1
.
color
)
def
test_model_validation
(
self
):
with
self
.
assertRaisesMessage
(
ValidationError
,
'Product with this Name already exists.'
):
Product
(
name
=
self
.
p1
.
name
)
.
validate_unique
()
with
self
.
assertRaisesMessage
(
ValidationError
,
'Product with this Name and Color already exists.'
):
Product
(
name
=
self
.
p1
.
name
,
color
=
self
.
p1
.
color
)
.
validate_unique
()
def
test_model_validation_with_condition
(
self
):
"""Partial unique constraints are ignored by Model.validate_unique()."""
Product
(
name
=
self
.
p1
.
name
,
color
=
'blue'
)
.
validate_unique
()
Product
(
name
=
self
.
p2
.
name
)
.
validate_unique
()
def
test_name
(
self
):
constraints
=
get_constraints
(
Product
.
_meta
.
db_table
)
expected_name
=
'
unique_name
'
expected_name
=
'
name_color_uniq
'
self
.
assertIn
(
expected_name
,
constraints
)
def
test_condition_must_be_q
(
self
):
with
self
.
assertRaisesMessage
(
ValueError
,
'UniqueConstraint.condition must be a Q instance.'
):
models
.
UniqueConstraint
(
name
=
'uniq'
,
fields
=
[
'name'
],
condition
=
'invalid'
)
tests/migrations/test_operations.py
Dosyayı görüntüle @
b69f8eb0
...
...
@@ -501,6 +501,51 @@ class OperationTests(OperationTestBase):
self
.
assertEqual
(
definition
[
1
],
[])
self
.
assertEqual
(
definition
[
2
][
'options'
][
'constraints'
],
[
check_constraint
])
def
test_create_model_with_partial_unique_constraint
(
self
):
partial_unique_constraint
=
models
.
UniqueConstraint
(
fields
=
[
'pink'
],
condition
=
models
.
Q
(
weight__gt
=
5
),
name
=
'test_constraint_pony_pink_for_weight_gt_5_uniq'
,
)
operation
=
migrations
.
CreateModel
(
'Pony'
,
[
(
'id'
,
models
.
AutoField
(
primary_key
=
True
)),
(
'pink'
,
models
.
IntegerField
(
default
=
3
)),
(
'weight'
,
models
.
FloatField
()),
],
options
=
{
'constraints'
:
[
partial_unique_constraint
]},
)
# Test the state alteration
project_state
=
ProjectState
()
new_state
=
project_state
.
clone
()
operation
.
state_forwards
(
'test_crmo'
,
new_state
)
self
.
assertEqual
(
len
(
new_state
.
models
[
'test_crmo'
,
'pony'
]
.
options
[
'constraints'
]),
1
)
# Test database alteration
self
.
assertTableNotExists
(
'test_crmo_pony'
)
with
connection
.
schema_editor
()
as
editor
:
operation
.
database_forwards
(
'test_crmo'
,
editor
,
project_state
,
new_state
)
self
.
assertTableExists
(
'test_crmo_pony'
)
# Test constraint works
Pony
=
new_state
.
apps
.
get_model
(
'test_crmo'
,
'Pony'
)
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
4.0
)
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
4.0
)
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
6.0
)
if
connection
.
features
.
supports_partial_indexes
:
with
self
.
assertRaises
(
IntegrityError
):
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
7.0
)
else
:
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
7.0
)
# Test reversal
with
connection
.
schema_editor
()
as
editor
:
operation
.
database_backwards
(
'test_crmo'
,
editor
,
new_state
,
project_state
)
self
.
assertTableNotExists
(
'test_crmo_pony'
)
# Test deconstruction
definition
=
operation
.
deconstruct
()
self
.
assertEqual
(
definition
[
0
],
'CreateModel'
)
self
.
assertEqual
(
definition
[
1
],
[])
self
.
assertEqual
(
definition
[
2
][
'options'
][
'constraints'
],
[
partial_unique_constraint
])
def
test_create_model_managers
(
self
):
"""
The managers on a model are set.
...
...
@@ -1854,6 +1899,92 @@ class OperationTests(OperationTestBase):
self
.
assertEqual
(
definition
[
1
],
[])
self
.
assertEqual
(
definition
[
2
],
{
'model_name'
:
"Pony"
,
'name'
:
"test_remove_constraint_pony_pink_gt_2"
})
def
test_add_partial_unique_constraint
(
self
):
project_state
=
self
.
set_up_test_model
(
'test_addpartialuniqueconstraint'
)
partial_unique_constraint
=
models
.
UniqueConstraint
(
fields
=
[
'pink'
],
condition
=
models
.
Q
(
weight__gt
=
5
),
name
=
'test_constraint_pony_pink_for_weight_gt_5_uniq'
,
)
operation
=
migrations
.
AddConstraint
(
'Pony'
,
partial_unique_constraint
)
self
.
assertEqual
(
operation
.
describe
(),
'Create constraint test_constraint_pony_pink_for_weight_gt_5_uniq '
'on model Pony'
)
# Test the state alteration
new_state
=
project_state
.
clone
()
operation
.
state_forwards
(
'test_addpartialuniqueconstraint'
,
new_state
)
self
.
assertEqual
(
len
(
new_state
.
models
[
'test_addpartialuniqueconstraint'
,
'pony'
]
.
options
[
'constraints'
]),
1
)
Pony
=
new_state
.
apps
.
get_model
(
'test_addpartialuniqueconstraint'
,
'Pony'
)
self
.
assertEqual
(
len
(
Pony
.
_meta
.
constraints
),
1
)
# Test the database alteration
with
connection
.
schema_editor
()
as
editor
:
operation
.
database_forwards
(
'test_addpartialuniqueconstraint'
,
editor
,
project_state
,
new_state
)
# Test constraint works
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
4.0
)
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
4.0
)
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
6.0
)
if
connection
.
features
.
supports_partial_indexes
:
with
self
.
assertRaises
(
IntegrityError
),
transaction
.
atomic
():
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
7.0
)
else
:
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
7.0
)
# Test reversal
with
connection
.
schema_editor
()
as
editor
:
operation
.
database_backwards
(
'test_addpartialuniqueconstraint'
,
editor
,
new_state
,
project_state
)
# Test constraint doesn't work
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
7.0
)
# Test deconstruction
definition
=
operation
.
deconstruct
()
self
.
assertEqual
(
definition
[
0
],
'AddConstraint'
)
self
.
assertEqual
(
definition
[
1
],
[])
self
.
assertEqual
(
definition
[
2
],
{
'model_name'
:
'Pony'
,
'constraint'
:
partial_unique_constraint
})
def
test_remove_partial_unique_constraint
(
self
):
project_state
=
self
.
set_up_test_model
(
'test_removepartialuniqueconstraint'
,
constraints
=
[
models
.
UniqueConstraint
(
fields
=
[
'pink'
],
condition
=
models
.
Q
(
weight__gt
=
5
),
name
=
'test_constraint_pony_pink_for_weight_gt_5_uniq'
,
),
])
gt_operation
=
migrations
.
RemoveConstraint
(
'Pony'
,
'test_constraint_pony_pink_for_weight_gt_5_uniq'
)
self
.
assertEqual
(
gt_operation
.
describe
(),
'Remove constraint test_constraint_pony_pink_for_weight_gt_5_uniq from model Pony'
)
# Test state alteration
new_state
=
project_state
.
clone
()
gt_operation
.
state_forwards
(
'test_removepartialuniqueconstraint'
,
new_state
)
self
.
assertEqual
(
len
(
new_state
.
models
[
'test_removepartialuniqueconstraint'
,
'pony'
]
.
options
[
'constraints'
]),
0
)
Pony
=
new_state
.
apps
.
get_model
(
'test_removepartialuniqueconstraint'
,
'Pony'
)
self
.
assertEqual
(
len
(
Pony
.
_meta
.
constraints
),
0
)
# Test database alteration
with
connection
.
schema_editor
()
as
editor
:
gt_operation
.
database_forwards
(
'test_removepartialuniqueconstraint'
,
editor
,
project_state
,
new_state
)
# Test constraint doesn't work
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
4.0
)
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
4.0
)
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
6.0
)
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
7.0
)
.
delete
()
# Test reversal
with
connection
.
schema_editor
()
as
editor
:
gt_operation
.
database_backwards
(
'test_removepartialuniqueconstraint'
,
editor
,
new_state
,
project_state
)
# Test constraint works
if
connection
.
features
.
supports_partial_indexes
:
with
self
.
assertRaises
(
IntegrityError
),
transaction
.
atomic
():
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
7.0
)
else
:
Pony
.
objects
.
create
(
pink
=
1
,
weight
=
7.0
)
# Test deconstruction
definition
=
gt_operation
.
deconstruct
()
self
.
assertEqual
(
definition
[
0
],
'RemoveConstraint'
)
self
.
assertEqual
(
definition
[
1
],
[])
self
.
assertEqual
(
definition
[
2
],
{
'model_name'
:
'Pony'
,
'name'
:
'test_constraint_pony_pink_for_weight_gt_5_uniq'
,
})
def
test_alter_model_options
(
self
):
"""
Tests the AlterModelOptions operation.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment