{{{
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.
* 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>