#36878: Migration's ModelState has varying type for unique_together and
index_together options causing autodetector crash
-------------------------------------+-------------------------------------
     Reporter:  Markus Holtermann    |                    Owner:  Markus
                                     |  Holtermann
         Type:  Bug                  |                   Status:  assigned
    Component:  Migrations           |                  Version:  dev
     Severity:  Normal               |               Resolution:
     Keywords:                       |             Triage Stage:  Accepted
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Comment (by Markus Holtermann):

 In order to understand where the incorrect setting or the values came
 from, I applied the following changes to the `state.py`:

 {{{#!diff
 diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py
 index 057651b1df..7220673142 100644
 --- a/django/db/migrations/state.py
 +++ b/django/db/migrations/state.py
 @@ -734,6 +734,32 @@ class StateApps(Apps):
              pass


 +class OptionsDict(dict):
 +    def __getitem__(self, key):
 +        val = super().__getitem__(key)
 +        if "together" in key:
 +            print(f"__getitem__({key!r}) = {val!r}")
 +        return val
 +
 +    def __setitem__(self, key, value):
 +        if "together" in key:
 +            import inspect, textwrap
 +
 +            print(f"__setitem__({key!r}, {value!r})")
 +            if not isinstance(value, set | tuple):
 +                calling_frame = inspect.stack(3)[1]
 +                code =
 textwrap.indent("".join(calling_frame.code_context), "    ")
 +                print(
 +                    f"  {calling_frame.filename}:{calling_frame.lineno}
 in {calling_frame.function}\n{code}"
 +                )
 +        return super().__setitem__(key, value)
 +
 +    def __delitem__(self, key):
 +        if "together" in key:
 +            print(f"__delitem__({key!r})")
 +        return super().__delitem__(key)
 +
 +
  class ModelState:
      """
      Represent a Django Model. Don't use the actual Model class as it's
 not
 @@ -751,7 +777,7 @@ class ModelState:
          self.app_label = app_label
          self.name = name
          self.fields = dict(fields)
 -        self.options = options or {}
 +        self.options = OptionsDict(options or {})
          self.options.setdefault("indexes", [])
          self.options.setdefault("constraints", [])
          self.bases = bases or (models.Model,)
 @@ -783,6 +809,37 @@ class ModelState:
                      "%r doesn't have one." % index
                  )

 +    def __getattr__(self, name):
 +        val = super().__getattr__(name)
 +        if (
 +            name == "options"
 +            and val
 +            and ("unique_together" in value or "index_together" in value)
 +        ):
 +            print(f"__getattr__({name!r}) = {val!r}")
 +        return val
 +
 +    def __setattr__(self, name, value):
 +        if name == "options" and (
 +            "unique_together" in value or "index_together" in value
 +        ):
 +            import inspect, textwrap
 +
 +            if (
 +                "unique_together" in value
 +                and not isinstance(value["unique_together"], set | tuple)
 +                or "index_together" in value
 +                and not isinstance(value["index_together"], set | tuple)
 +            ):
 +                print(f"__setattr__({name!r}, {value!r})")
 +                for i in range(1, 4):
 +                    calling_frame = inspect.stack(3)[i]
 +                    code =
 textwrap.indent("".join(calling_frame.code_context), "    ")
 +                    print(
 +                        f"
 {calling_frame.filename}:{calling_frame.lineno} in
 {calling_frame.function}\n{code}"
 +                    )
 +        return super().__setattr__(name, value)
 +
      @cached_property
      def name_lower(self):
          return self.name.lower()
 }}}
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36878#comment:2>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/0107019beb752dd9-a0ff4a78-89c3-4bdf-829a-72f012900c23-000000%40eu-central-1.amazonses.com.

Reply via email to