Minimal test case: https://github.com/jdufresne/django-test-value-error
When running unit tests, the test runner does not correctly load all
necessary models. This behavior does not occur when running the actual
site; tests only.
I have created a minimal test case to illustrate the issue. My hunch is
the key points are:
* Do not use contrib.auth, instead create a fully custom user model
* Create an app "myauth" to house the custom user model
* Create an app "myapp" for other models
* Do not import the actual User model in any file
* Create a different model MyModel with a FK to settings.AUTH_USER_MODEL
* Create a model form for MyModel that inclues the FK as a field
* Create a unit test that imports the form
When running the unit tests, receive the following:
{{{
$ python manage.py test
Creating test database for alias 'default'...
E
======================================================================
ERROR: myapp.tests (unittest.loader.ModuleImportFailure)
----------------------------------------------------------------------
ImportError: Failed to import test module: myapp.tests
Traceback (most recent call last):
File "/usr/lib64/python2.7/unittest/loader.py", line 252, in _find_tests
module = self._get_module_from_name(name)
File "/usr/lib64/python2.7/unittest/loader.py", line 230, in
_get_module_from_name
__import__(name)
File "/home/jon/djtest/djtest/myapp/tests.py", line 2, in <module>
from myapp.forms import MyForm
File "/home/jon/djtest/djtest/myapp/forms.py", line 5, in <module>
class MyForm(forms.ModelForm):
File "/home/jon/djtest/venv/lib/python2.7/site-
packages/django/forms/models.py", line 282, in __new__
opts.help_texts, opts.error_messages)
File "/home/jon/djtest/venv/lib/python2.7/site-
packages/django/forms/models.py", line 201, in fields_for_model
formfield = f.formfield(**kwargs)
File "/home/jon/djtest/venv/lib/python2.7/site-
packages/django/db/models/fields/related.py", line 1260, in formfield
(self.name, self.rel.to))
ValueError: Cannot create form field for 'user' yet, because its related
model 'myauth.User' has not been loaded yet
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (errors=1)
Destroying test database for alias 'default'...
}}}
Key files:
settings.py
{{{
...
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'myapp',
'myauth',
)
...
AUTH_USER_MODEL = 'myauth.User'
}}}
myauth/models.py
{{{
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import AbstractBaseUser
class User(AbstractBaseUser):
first_name = models.CharField(max_length=255, db_index=True)
middle_name = models.CharField(max_length=64, blank=True)
last_name = models.CharField(max_length=255, db_index=True)
email = models.EmailField(max_length=255, unique=True)
is_active = models.BooleanField(default=True)
is_superuser = models.BooleanField(default=False)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'email'
}}}
myapp/models.py
{{{
from django.db import models
from django.conf import settings
class MyModel(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
}}}
myapp/forms.py
{{{
from django import forms
from myapp.models import MyModel
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['user']
}}}
myapp/tests.py
{{{
from django.test import TestCase
from myapp.forms import MyForm
class MyFormTestCase(TestCase):
def test_simple(self):
form = MyForm()
self.assertTrue(form)
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/22865>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* needs_better_patch: => 0
* needs_tests: => 0
* needs_docs: => 0
Comment:
To workaround the issue, one can add `from myauth.models import User` to
the top of `myapp/forms.py` or `myapp/models.py`. But this seems like it
should be unnecessary as both files make no direct reference to this model
and are trying to be `AUTH_USER_MODEL` agnostic.
--
Ticket URL: <https://code.djangoproject.com/ticket/22865#comment:1>
Comment (by timo):
I believe you need to have `django.contrib.auth` in `INSTALLED_APPS` in
order to use `from django.contrib.auth.models import AbstractBaseUser`.
--
Ticket URL: <https://code.djangoproject.com/ticket/22865#comment:2>
Comment (by jdufresne):
Replying to [comment:2 timo]:
> I believe you need to have `django.contrib.auth` in `INSTALLED_APPS` in
order to use `from django.contrib.auth.models import AbstractBaseUser`.
This appears to contradict actual behavior I've discovered from testing. I
purposely do not include `contrib.auth` in my actual application because I
don't want any auth models (groups, permissions, etc.). I stub the actual
`User.get_*` functions required by `contrib.admin` to avoid requiring
them.
This works 100% when running the actual site, but creates the above import
issue when running tests. The workaround provided above allows the User
model to load, and at that point, tests run 100% fine. So it seems, this
can work perfectly fine so long as `AUTH_USER_MODEL` is loaded ahead of
time at some point.
Adding `contrib.auth` to my `INSTALLED_APPS` would do nothing for me other
than creating a bunch of unused tables.
--
Ticket URL: <https://code.djangoproject.com/ticket/22865#comment:3>
Comment (by renatooliveira):
Hi, I just created a similar issue. Sorry, I didn't see this one
https://code.djangoproject.com/ticket/22866#ticket
--
Ticket URL: <https://code.djangoproject.com/ticket/22865#comment:4>
Comment (by jdufresne):
After analyzing #22866, it appears the discussion around the user model is
a red herring. Any model that has a FK using a string instead of the model
class can create the error if the model is never imported before the test.
--
Ticket URL: <https://code.djangoproject.com/ticket/22865#comment:5>
Comment (by timo):
I suspect this has been resolved the app loading refactor in 1.7, can you
test there?
--
Ticket URL: <https://code.djangoproject.com/ticket/22865#comment:6>
Comment (by renatooliveira):
Yeah, no errors when running on Django==1.7b4
--
Ticket URL: <https://code.djangoproject.com/ticket/22865#comment:7>
Comment (by aaugustin):
I can confirm that app-loading is the fix, that Django 1.7 will warn that
django.contrib.auth should be in INSTALLED_APPS, and that Django 1.9 will
require it.
--
Ticket URL: <https://code.djangoproject.com/ticket/22865#comment:8>
* status: new => closed
* resolution: => fixed
--
Ticket URL: <https://code.djangoproject.com/ticket/22865#comment:9>
Comment (by anonymous):
Replying to [comment:6 timo]:
> I suspect this has been resolved the app loading refactor in 1.7, can
you test there?
Confirmed. Fixed for me with 1.7. Thanks.
--
Ticket URL: <https://code.djangoproject.com/ticket/22865#comment:10>
Comment (by renatooliveira):
Repltying to [comment:8 aaugustin]
> that Django 1.7 will warn that django.contrib.auth should be in
INSTALLED_APPS, and that Django 1.9 will require it.
If you see ticket:22866 you'll see that this behavior isn't only in
{{{django.contrib.auth}}}
However it's fixed in 1.7. Are there plans to fix it in Django 1.6?
thanks!
--
Ticket URL: <https://code.djangoproject.com/ticket/22865#comment:11>
Comment (by aaugustin):
Unfortunately, the "fix" involves some major backwards-incompatibilities,
and thus cannot be backported to a future 1.6.x release.
--
Ticket URL: <https://code.djangoproject.com/ticket/22865#comment:12>
Comment (by renatooliveira):
Ok, thanks!
--
Ticket URL: <https://code.djangoproject.com/ticket/22865#comment:13>