Django ManyToMany error: “Cannot resolve keyword XXX into a field”
This week I was doing some refactoring, and started getting the following exception in Django's admin site. This was under Django 1.1.1, and Python 2.6.4.
Traceback: File "/usr/lib/pymodules/python2.6/django/core/handlers/base.py" in get_response 92. response = callback(request, *callback_args, **callback_kwargs) File "/usr/lib/pymodules/python2.6/django/contrib/admin/sites.py" in root 490. return self.model_page(request, *url.split('/', 2)) File "/usr/lib/pymodules/python2.6/django/views/decorators/cache.py" in _wrapped_view_func 44. response = view_func(request, *args, **kwargs) File "/usr/lib/pymodules/python2.6/django/contrib/admin/sites.py" in model_page 509. return admin_obj(request, rest_of_url) File "/usr/lib/pymodules/python2.6/django/contrib/admin/options.py" in __call__ 1098. return self.change_view(request, unquote(url)) File "/usr/lib/pymodules/python2.6/django/db/transaction.py" in _commit_on_success 240. res = func(*args, **kw) File "/usr/lib/pymodules/python2.6/django/contrib/admin/options.py" in change_view 840. form = ModelForm(instance=obj) File "/home/chase/bullhorn/branches/powerfill/django/powerfill/search/admin.py" in __init__ 113. super(CompanyAdminForm, self).__init__(*args, **kwargs) File "/usr/lib/pymodules/python2.6/django/forms/models.py" in __init__ 222. object_data = model_to_dict(instance, opts.fields, opts.exclude) File "/usr/lib/pymodules/python2.6/django/forms/models.py" in model_to_dict 140. data[f.name] = [obj.pk for obj in f.value_from_object(instance)] File "/usr/lib/pymodules/python2.6/django/db/models/fields/related.py" in value_from_object 964. return getattr(obj, self.attname).all() File "/usr/lib/pymodules/python2.6/django/db/models/manager.py" in all 105. return self.get_query_set() File "/usr/lib/pymodules/python2.6/django/db/models/fields/related.py" in get_query_set 424. return superclass.get_query_set(self)._next_is_sticky().filter(**(self.core_filters)) File "/usr/lib/pymodules/python2.6/django/db/models/query.py" in filter 498. return self._filter_or_exclude(False, *args, **kwargs) File "/usr/lib/pymodules/python2.6/django/db/models/query.py" in _filter_or_exclude 516. clone.query.add_q(Q(*args, **kwargs)) File "/usr/lib/pymodules/python2.6/django/db/models/sql/query.py" in add_q 1675. can_reuse=used_aliases) File "/usr/lib/pymodules/python2.6/django/db/models/sql/query.py" in add_filter 1569. negate=negate, process_extras=process_extras) File "/usr/lib/pymodules/python2.6/django/db/models/sql/query.py" in setup_joins 1737. "Choices are: %s" % (name, ", ".join(names))) Exception Type: FieldError at /admin/search/company/2273/ Exception Value: Cannot resolve keyword 'company' into field.
Having made quite a number of changes before noticing this, it took some time to track down. It turned out that it started happening when I moved an import. Eventually, I found Django Ticket #1796. While that ticket is marked as "Fixed", it does not actually appear to be fixed in all cases.
The problem is deep in the Django stack, and involves class loading at the Python level as well. It's in a piece of the Django code that is only executed for ManyToMany relationships. Read the ticket if you're interested in the details.
In my case, the ManyToMany relationship in question was a field on a Company model which referenced a Django User model:
class Company(models.Model): ... connection_users = models.ManyToManyField( User, symmetrical=False, blank=True, null=True )
I also have a UserProfile model which extends the base User model:
class UserProfile(models.Model): user = models.ForeignKey(User, unique=True) company = models.ForeignKey(Company) phone = PhoneNumberField()
Essentially, the django.contrib.auth.models.User model is necessarily loaded first, then my related UserProfile model is loaded before the Company model due to Python class loading behavior. When Company finally loads, the Django bug kicks in and it confuses the company field on UserProfile with a symmetric version of the ManyToMany relationship connection_users on Company.
After much fiddling, the fix was to force the class loading order to load Company before UserProfile. A good spot to do this is at the top of the models module.
from company import Company, UserProfile