Opened 11 years ago

Closed 11 years ago

Last modified 11 years ago

#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 Jon Dufresne, 11 years ago

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.

comment:2 by Tim Graham, 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.

in reply to:  2 comment:3 by Jon Dufresne, 11 years ago

Replying to 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.

comment:4 by Renato Oliveira, 11 years ago

Hi, I just created a similar issue. Sorry, I didn't see this one

https://code.djangoproject.com/ticket/22866#ticket

comment:5 by Jon Dufresne, 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.

Last edited 11 years ago by Jon Dufresne (previous) (diff)

comment:6 by Tim Graham, 11 years ago

I suspect this has been resolved the app loading refactor in 1.7, can you test there?

comment:7 by Renato Oliveira, 11 years ago

Yeah, no errors when running on Django==1.7b4

comment:8 by Aymeric Augustin, 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 Tim Graham, 11 years ago

Resolution: fixed
Status: newclosed

in reply to:  6 comment:10 by anonymous, 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 Renato Oliveira, 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 Aymeric Augustin, 11 years ago

Unfortunately, the "fix" involves some major backwards-incompatibilities, and thus cannot be backported to a future 1.6.x release.

comment:13 by Renato Oliveira, 11 years ago

Ok, thanks!

Note: See TracTickets for help on using tickets.
Back to Top