[Django] #24804: META API: get_field method cache issue

15 views
Skip to first unread message

Django

unread,
May 15, 2015, 12:57:15 AM5/15/15
to django-...@googlegroups.com
#24804: META API: get_field method cache issue
-------------------------+-------------------------------------------------
Reporter: | Owner: nobody
intellisense |
Type: Bug | Status: new
Component: Core | Version: 1.8
(Cache system) | Keywords: meta, meta api, get_field,
Severity: Normal | FieldDoesNotExist, cache, internal cache
Triage Stage: | Has patch: 0
Unreviewed |
Easy pickings: 0 | UI/UX: 0
-------------------------+-------------------------------------------------
When '''Model._meta.get_field''' raises a
'''django.core.exceptions.FieldDoesNotExist''' exception for field name,
later on if we add the field with that name it stills throws exception
because of internal cache issue even the field has been added. Consider
below code to reproduce the issue:

{{{
from django.db.models import CharField
from django.core.exceptions import FieldDoesNotExist

class AutoCleanField(CharField):
def __init__(self, depends_on=None, *args, **kwargs):
self.depends_on = depends_on
super(AutoCleanField, self).__init__(*args, **kwargs)

def deconstruct(self):
name, path, args, kwargs = super(AutoCleanField,
self).deconstruct()
if self.depends_on:
kwargs['depends_on'] = self.depends_on
return name, path, args, kwargs

def contribute_to_class(self, cls, name, **kwargs):
super(AutoCleanField, self).contribute_to_class(cls, name,
**kwargs)
try:
field = cls._meta.get_field(self.depends_on)
except FieldDoesNotExist:
field = None

if not field:
raise TypeError('{0} `depends_on` field "{1}" does not exists
in model "{2}".'
.format(self.__class__.__name__,
self.depends_on, self.model.__name__))

class Test(models.Model):
auto = AutoCleanField(depends_on='name') # <--- intentionally setting
a field for param `depends_on` which does not exists
}}}

After the above code has been run, now add the '''name''' field in
'''Test''' model and re-run, The '''FieldDoesNotExist''' exception does
not go away:


{{{
class Test(models.Model):
# everything should works fine now, but didn't
name = models.CharField(max_length=255)
auto = AutoCleanField(depends_on='name')
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/24804>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
May 15, 2015, 2:44:03 AM5/15/15
to django-...@googlegroups.com
#24804: META API: get_field method cache issue
-------------------------------------+-------------------------------------
Reporter: intellisense | Owner: nobody
Type: Bug | Status: closed
Component: Core (Cache system) | Version: 1.8
Severity: Normal | Resolution: invalid
Keywords: meta, meta api, | Triage Stage:
get_field, FieldDoesNotExist, | Unreviewed
cache, internal cache |
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by charettes):

* status: new => closed
* needs_better_patch: => 0
* resolution: => invalid
* needs_tests: => 0
* needs_docs: => 0


Comment:

Hi intellisense,

This behavior might be a bit counter intuitive but it has nothing to do
with `get_field()` caching. It is instead related to how Python builds
classes internally.

When a model class is built its defined attributes such as fields are not
necessarily ordered the way they are defined; they are collected in an
non-ordered mapping (`dict`) and thus the declared ordering is lost.

In your example you're assuming the `auto` field will be added after the
`name` oen because the former is declared before. What's happening here is
that `auto` is ordered before `name` in `attrs` and thus its
`contribute_to_class`
[https://github.com/django/django/blob/dce004ab72e26103a86e98994de7f2cef9321f9b/django/db/models/base.py#L156-L158
is called before].

You shouldn't assume that `dict` will be ordered in any way. For example,
if you change the seed used to define a an order over unordered collection
(`python -R`) you should be able to make the following example print
either an empty tuple or a singleton containing the `name` field:

{{{#!python
from django.db import models

class Contributer(object):


def contribute_to_class(self, cls, name, **kwargs):

print(cls._meta.fields)

class Foo(models.Model):
name = models.CharField()
auto = Contributer()
}}}

I suggest you take a look at
[https://github.com/django/django/blob/dce004ab72e26103a86e98994de7f2cef9321f9b/django/contrib/contenttypes/fields.py#L20
how] the `GenericForeignKey` deals with a similar pattern.

--
Ticket URL: <https://code.djangoproject.com/ticket/24804#comment:1>

Reply all
Reply to author
Forward
0 new messages