#34884: Half bug/half enhancement : inconsistent behavior of get_or_create()
regarding related attributes cache
-------------------------------------+-------------------------------------
     Reporter:  Laurent Lyaudet      |                    Owner:  nobody
         Type:  Uncategorized        |                   Status:  new
    Component:  Database layer       |                  Version:  dev
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:  ORM get_or_create    |             Triage Stage:
                                     |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------

Comment (by Laurent Lyaudet):

 I coded a monkey patch for my use case where it will not be soon to
 upgrade Django and in case some bikeshedding occurs that puts to trash
 coherency and efficiency in yet another open source software where some
 people against free software have influence.
 Here it is :

 `get_or_create_monkey_patch.py`
 {{{
 from django.db.models import QuerySet, ForeignKey


 original_get_or_create = QuerySet.get_or_create


 def patched_get_or_create(self, defaults=None, **kwargs):
      result, created = original_get_or_create(self, defaults=defaults,
 **kwargs)
      if defaults is not None:
          for key, value in defaults.items():
              if isinstance(result._meta.get_field(key), ForeignKey):
                  # isinstance handles OneToOneField also.
                  setattr(result, key, value)
      for key, value in kwargs.items():
          if isinstance(result._meta.get_field(key), ForeignKey):
              # isinstance handles OneToOneField also.
              setattr(result, key, value)
      return result, created


 QuerySet.get_or_create = patched_get_or_create
 }}}

 You can execute it at the end of the previous script + some test and
 obtain this:


 {{{
 In [15]: from django.db.models import QuerySet, ForeignKey

 In [16]: original_get_or_create = QuerySet.get_or_create

 In [17]: def patched_get_or_create(self, defaults=None, **kwargs):
     ...:      result, created = original_get_or_create(self,
 defaults=defaults, **kwargs)
     ...:      if defaults is not None:
     ...:          for key, value in defaults.items():
     ...:              if isinstance(result._meta.get_field(key),
 ForeignKey):
     ...:                  # isinstance handles OneToOneField also.
     ...:                  setattr(result, key, value)
     ...:      for key, value in kwargs.items():
     ...:          if isinstance(result._meta.get_field(key), ForeignKey):
     ...:              # isinstance handles OneToOneField also.
     ...:              setattr(result, key, value)
     ...:      return result, created
     ...:

 In [18]: QuerySet.get_or_create = patched_get_or_create

 In [19]: permission, created =
 Permission.objects.get_or_create(name="Test", content_type=content_type,
 codename="Test")

 In [20]: created
 Out[20]: False

 In [21]: connection.queries
 Out[21]:
 [{'sql': "SELECT t.oid, typarray FROM pg_type t JOIN pg_namespace ns ON
 typnamespace = ns.oid WHERE typname = 'hstore'",
   'time': '0.001'},
  {'sql': "SELECT typarray FROM pg_type WHERE typname = 'citext'",
   'time': '0.000'},
  {'sql': 'SELECT "django_content_type"."id",
 "django_content_type"."app_label", "django_content_type"."model" FROM
 "django_content_type" WHERE ("django_content_type"."app_label" = \'auth\'
 AND "django_content_type"."model" = \'permission\')',
   'time': '0.001'},
  {'sql': 'SELECT "auth_permission"."id", "auth_permission"."name",
 "auth_permission"."content_type_id", "auth_permission"."codename" FROM
 "auth_permission" WHERE ("auth_permission"."codename" = \'Test\' AND
 "auth_permission"."content_type_id" = 2 AND "auth_permission"."name" =
 \'Test\')',
   'time': '0.001'},
  {'sql': 'INSERT INTO "auth_permission" ("name", "content_type_id",
 "codename") VALUES (\'Test\', 2, \'Test\') RETURNING
 "auth_permission"."id"',
   'time': '0.001'},
  {'sql': 'SELECT "auth_permission"."id", "auth_permission"."name",
 "auth_permission"."content_type_id", "auth_permission"."codename" FROM
 "auth_permission" WHERE ("auth_permission"."codename" = \'Test\' AND
 "auth_permission"."content_type_id" = 2 AND "auth_permission"."name" =
 \'Test\')',
   'time': '0.001'},
  {'sql': 'SELECT "django_content_type"."id",
 "django_content_type"."app_label", "django_content_type"."model" FROM
 "django_content_type" WHERE "django_content_type"."id" = 2',
   'time': '0.000'},
  {'sql': 'SELECT "auth_permission"."id", "auth_permission"."name",
 "auth_permission"."content_type_id", "auth_permission"."codename" FROM
 "auth_permission" WHERE ("auth_permission"."codename" = \'Test\' AND
 "auth_permission"."content_type_id" = 2 AND "auth_permission"."name" =
 \'Test\')',
   'time': '0.001'}]

 In [22]: permission.content_type
 Out[22]: <ContentType: permission>

 In [23]: connection.queries
 Out[23]:
 [{'sql': "SELECT t.oid, typarray FROM pg_type t JOIN pg_namespace ns ON
 typnamespace = ns.oid WHERE typname = 'hstore'",
   'time': '0.001'},
  {'sql': "SELECT typarray FROM pg_type WHERE typname = 'citext'",
   'time': '0.000'},
  {'sql': 'SELECT "django_content_type"."id",
 "django_content_type"."app_label", "django_content_type"."model" FROM
 "django_content_type" WHERE ("django_content_type"."app_label" = \'auth\'
 AND "django_content_type"."model" = \'permission\')',
   'time': '0.001'},
  {'sql': 'SELECT "auth_permission"."id", "auth_permission"."name",
 "auth_permission"."content_type_id", "auth_permission"."codename" FROM
 "auth_permission" WHERE ("auth_permission"."codename" = \'Test\' AND
 "auth_permission"."content_type_id" = 2 AND "auth_permission"."name" =
 \'Test\')',
   'time': '0.001'},
  {'sql': 'INSERT INTO "auth_permission" ("name", "content_type_id",
 "codename") VALUES (\'Test\', 2, \'Test\') RETURNING
 "auth_permission"."id"',
   'time': '0.001'},
  {'sql': 'SELECT "auth_permission"."id", "auth_permission"."name",
 "auth_permission"."content_type_id", "auth_permission"."codename" FROM
 "auth_permission" WHERE ("auth_permission"."codename" = \'Test\' AND
 "auth_permission"."content_type_id" = 2 AND "auth_permission"."name" =
 \'Test\')',
   'time': '0.001'},
  {'sql': 'SELECT "django_content_type"."id",
 "django_content_type"."app_label", "django_content_type"."model" FROM
 "django_content_type" WHERE "django_content_type"."id" = 2',
   'time': '0.000'},
  {'sql': 'SELECT "auth_permission"."id", "auth_permission"."name",
 "auth_permission"."content_type_id", "auth_permission"."codename" FROM
 "auth_permission" WHERE ("auth_permission"."codename" = \'Test\' AND
 "auth_permission"."content_type_id" = 2 AND "auth_permission"."name" =
 \'Test\')',
   'time': '0.001'}]

 In [24]:

 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/34884#comment:1>
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 on the web visit 
https://groups.google.com/d/msgid/django-updates/0107018ae3708c06-a1da014b-afc0-469c-a29b-732a1c972bcf-000000%40eu-central-1.amazonses.com.

Reply via email to