#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.auth
inINSTALLED_APPS
in 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 User
to the top ofmyapp/forms.py
ormyapp/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_MODEL
agnostic.