I experienced some obscure test failures after integrating Zinnia with my
application. I've tracked it down to the behavior of Proxy Models and I'm unable
to rationalize the documentation with the behavior I see. Here's what I've done
to understand the issue:
If I create an app with a Proxy Model for auth.User (using get_user_model):
# pm/models.py
# a stripped down copy of Zinnia's Author
from django.db import models
from django.contrib.auth import get_user_model
class MyUser(get_user_model()):
objects = get_user_model()._default_manager
def __str__(self):
return self.get_full_name() or self.get_username()
class Meta:
app_label = 'pm'
proxy = True
I see the following behavior in the shell:
$ ./manage.py shell
Python 2.7.4 (default, Apr 19 2013, 18:28:01)
[GCC 4.7.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.contrib.auth import get_user_model
>>> get_user_model()
<class 'django.contrib.auth.models.User'>
>>> get_user_model().objects.model
<class 'pm.models.MyUser'>
>>> get_user_model().objects.get(pk=1)
<MyUser: admin>
>>>
This seems to be contrary to the claim made in the documentation:
"QuerySets still return the model that was requested
There is no way to have Django return, say, a MyPerson object whenever you
query for Person objects. A queryset for Person objects will return those
types of objects. The whole point of proxy objects is that code relying on
the original Person will use those and your own code can use the extensions
you included (that no other code is relying on anyway). It is not a way to
replace the Person (or any other) model everywhere with something of your
own creation."
https://docs.djangoproject.com/en/dev/topics/db/models/#querysets-still-return-the-model-that-was-requestedIf I create a second app and model with a Proxy for auth.User:
# pm2/models.py
from django.db import models
from django.contrib.auth import get_user_model
class MyUser2(get_user_model()):
objects = get_user_model()._default_manager
def __str__(self):
return self.get_full_name() or self.get_username()
class Meta:
app_label = 'pm2'
proxy = True
Then I get an AssertionError with the following traceback:
$ ./manage.py shell --traceback
Traceback (most recent call last):
File "~/.virtualenvs/django_proxy_model_test/local/lib/python2.7/site-packages/django/core/management/base.py", line 222, in run_from_argv
self.execute(*args, **options.__dict__)
File "~/.virtualenvs/django_proxy_model_test/local/lib/python2.7/site-packages/django/core/management/base.py", line 255, in execute
output = self.handle(*args, **options)
File "~/.virtualenvs/django_proxy_model_test/local/lib/python2.7/site-packages/django/core/management/base.py", line 385, in handle
return self.handle_noargs(**options)
File "~/.virtualenvs/django_proxy_model_test/local/lib/python2.7/site-packages/django/core/management/commands/shell.py", line 54, in handle_noargs
get_models()
File "~/.virtualenvs/django_proxy_model_test/local/lib/python2.7/site-packages/django/db/models/loading.py", line 197, in get_models
self._populate()
File "~/.virtualenvs/django_proxy_model_test/local/lib/python2.7/site-packages/django/db/models/loading.py", line 72, in _populate
self.load_app(app_name, True)
File "~/.virtualenvs/django_proxy_model_test/local/lib/python2.7/site-packages/django/db/models/loading.py", line 96, in load_app
models = import_module('.models', app_name)
File "~/.virtualenvs/django_proxy_model_test/local/lib/python2.7/site-packages/django/utils/importlib.py", line 35, in import_module
__import__(name)
File "~/src/django_proxy_model_test/pm/models.py", line 5, in <module>
class MyUser(get_user_model()):
File "~/.virtualenvs/django_proxy_model_test/local/lib/python2.7/site-packages/django/db/models/base.py", line 134, in __new__
new_class._default_manager = new_class._default_manager._copy_to_model(new_class)
File "~/.virtualenvs/django_proxy_model_test/local/lib/python2.7/site-packages/django/db/models/manager.py", line 95, in _copy_to_model
assert issubclass(model, self.model)
AssertionError
After the first Proxy Model is registered, self.model points at the Proxy Model
instead of the original Model. The second Proxy Model is not a subclass of the
first.
Is there a contradiction between the behavior I observe and the documentation?
Or am I simply misunderstanding the documentation? Should it be possible for a
model to be proxied multiple times?
I appreciate any clarity that you can provide.
Aaron Spike