#22865 closed Bug (fixed)
Testing framework: ValueError: Cannot create form field for 'user' yet, because its related model 'myauth.User' has not been loaded yet
| Reported by: | Jon Dufresne | Owned by: | nobody |
|---|---|---|---|
| Component: | Testing framework | Version: | 1.6 |
| Severity: | Normal | Keywords: | |
| Cc: | Triage Stage: | Unreviewed | |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
All tested with Django 1.6.5
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)
Change History (13)
comment:1 by , 11 years ago
follow-up: 3 comment:2 by , 11 years ago
I believe you need to have django.contrib.auth in INSTALLED_APPS in order to use from django.contrib.auth.models import AbstractBaseUser.
comment:3 by , 11 years ago
Replying to timo:
I believe you need to have
django.contrib.authinINSTALLED_APPSin order to usefrom 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.
comment:5 by , 11 years ago
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 related model is never imported before the test.
follow-up: 10 comment:6 by , 11 years ago
I suspect this has been resolved the app loading refactor in 1.7, can you test there?
comment:8 by , 11 years ago
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.
comment:9 by , 11 years ago
| Resolution: | → fixed |
|---|---|
| Status: | new → closed |
comment:10 by , 11 years ago
Replying to 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.
comment:11 by , 11 years ago
Repltying to 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!
comment:12 by , 11 years ago
Unfortunately, the "fix" involves some major backwards-incompatibilities, and thus cannot be backported to a future 1.6.x release.
To workaround the issue, one can add
from myauth.models import Userto the top ofmyapp/forms.pyormyapp/models.py. But this seems like it should be unnecessary as both files make no direct reference to this model and are trying to beAUTH_USER_MODELagnostic.