mirror of
https://github.com/django/django.git
synced 2025-06-06 20:19:13 +00:00
Fixed #34744 -- Prevented recreation of migration for constraints with a dict_keys.
Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
This commit is contained in:
parent
dd45d5223b
commit
76c3e310dd
@ -14,6 +14,8 @@ from django.core.exceptions import FieldError
|
|||||||
from django.db import DEFAULT_DB_ALIAS, DatabaseError, connections
|
from django.db import DEFAULT_DB_ALIAS, DatabaseError, connections
|
||||||
from django.db.models.constants import LOOKUP_SEP
|
from django.db.models.constants import LOOKUP_SEP
|
||||||
from django.utils import tree
|
from django.utils import tree
|
||||||
|
from django.utils.functional import cached_property
|
||||||
|
from django.utils.hashable import make_hashable
|
||||||
|
|
||||||
logger = logging.getLogger("django.db.models")
|
logger = logging.getLogger("django.db.models")
|
||||||
|
|
||||||
@ -151,6 +153,27 @@ class Q(tree.Node):
|
|||||||
kwargs["_negated"] = True
|
kwargs["_negated"] = True
|
||||||
return path, args, kwargs
|
return path, args, kwargs
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def identity(self):
|
||||||
|
path, args, kwargs = self.deconstruct()
|
||||||
|
identity = [path, *kwargs.items()]
|
||||||
|
for child in args:
|
||||||
|
if isinstance(child, tuple):
|
||||||
|
arg, value = child
|
||||||
|
value = make_hashable(value)
|
||||||
|
identity.append((arg, value))
|
||||||
|
else:
|
||||||
|
identity.append(child)
|
||||||
|
return tuple(identity)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, Q):
|
||||||
|
return NotImplemented
|
||||||
|
return other.identity == self.identity
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.identity)
|
||||||
|
|
||||||
|
|
||||||
class DeferredAttribute:
|
class DeferredAttribute:
|
||||||
"""
|
"""
|
||||||
|
@ -2793,6 +2793,43 @@ class AutodetectorTests(BaseAutodetectorTests):
|
|||||||
["CreateModel", "AddField", "AddConstraint"],
|
["CreateModel", "AddField", "AddConstraint"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_add_constraints_with_dict_keys(self):
|
||||||
|
book_types = {"F": "Fantasy", "M": "Mystery"}
|
||||||
|
book_with_type = ModelState(
|
||||||
|
"testapp",
|
||||||
|
"Book",
|
||||||
|
[
|
||||||
|
("id", models.AutoField(primary_key=True)),
|
||||||
|
("type", models.CharField(max_length=1)),
|
||||||
|
],
|
||||||
|
{
|
||||||
|
"constraints": [
|
||||||
|
models.CheckConstraint(
|
||||||
|
check=models.Q(type__in=book_types.keys()),
|
||||||
|
name="book_type_check",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
book_with_resolved_type = ModelState(
|
||||||
|
"testapp",
|
||||||
|
"Book",
|
||||||
|
[
|
||||||
|
("id", models.AutoField(primary_key=True)),
|
||||||
|
("type", models.CharField(max_length=1)),
|
||||||
|
],
|
||||||
|
{
|
||||||
|
"constraints": [
|
||||||
|
models.CheckConstraint(
|
||||||
|
check=models.Q(("type__in", tuple(book_types))),
|
||||||
|
name="book_type_check",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
changes = self.get_changes([book_with_type], [book_with_resolved_type])
|
||||||
|
self.assertEqual(len(changes), 0)
|
||||||
|
|
||||||
def test_add_index_with_new_model(self):
|
def test_add_index_with_new_model(self):
|
||||||
book_with_index_title_and_pony = ModelState(
|
book_with_index_title_and_pony = ModelState(
|
||||||
"otherapp",
|
"otherapp",
|
||||||
|
@ -200,6 +200,44 @@ class QTests(SimpleTestCase):
|
|||||||
path, args, kwargs = q.deconstruct()
|
path, args, kwargs = q.deconstruct()
|
||||||
self.assertEqual(Q(*args, **kwargs), q)
|
self.assertEqual(Q(*args, **kwargs), q)
|
||||||
|
|
||||||
|
def test_equal(self):
|
||||||
|
self.assertEqual(Q(), Q())
|
||||||
|
self.assertEqual(
|
||||||
|
Q(("pk__in", (1, 2))),
|
||||||
|
Q(("pk__in", [1, 2])),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
Q(("pk__in", (1, 2))),
|
||||||
|
Q(pk__in=[1, 2]),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
Q(("pk__in", (1, 2))),
|
||||||
|
Q(("pk__in", {1: "first", 2: "second"}.keys())),
|
||||||
|
)
|
||||||
|
self.assertNotEqual(
|
||||||
|
Q(name__iexact=F("other_name")),
|
||||||
|
Q(name=Lower(F("other_name"))),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_hash(self):
|
||||||
|
self.assertEqual(hash(Q()), hash(Q()))
|
||||||
|
self.assertEqual(
|
||||||
|
hash(Q(("pk__in", (1, 2)))),
|
||||||
|
hash(Q(("pk__in", [1, 2]))),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
hash(Q(("pk__in", (1, 2)))),
|
||||||
|
hash(Q(pk__in=[1, 2])),
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
hash(Q(("pk__in", (1, 2)))),
|
||||||
|
hash(Q(("pk__in", {1: "first", 2: "second"}.keys()))),
|
||||||
|
)
|
||||||
|
self.assertNotEqual(
|
||||||
|
hash(Q(name__iexact=F("other_name"))),
|
||||||
|
hash(Q(name=Lower(F("other_name")))),
|
||||||
|
)
|
||||||
|
|
||||||
def test_flatten(self):
|
def test_flatten(self):
|
||||||
q = Q()
|
q = Q()
|
||||||
self.assertEqual(list(q.flatten()), [q])
|
self.assertEqual(list(q.flatten()), [q])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user