5

I'm using Django 1.5 and I'm trying to make an application work with any custom user model. I've changed the app to use get_user_model everywhere and the app itself is not showing any problems so far.

The issue is that I want to be able to test the app as well, but I can't find a way to make ForeignKey model fields to test correctly using custom user models. When I run the test case attached below, I get this error:

ValueError: Cannot assign "<NewCustomUser: alice@bob.net>": "ModelWithForeign.user" must be a "User" instance.

This is the file I'm using for testing:

from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.tests.custom_user import CustomUser, CustomUserManager
from django.db import models
from django.test import TestCase
from django.test.utils import override_settings

class NewCustomUser(CustomUser):
    objects = CustomUserManager()
    class Meta:
        app_label = 'myapp'

class ModelWithForeign(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL)

@override_settings(
    AUTH_USER_MODEL = 'myapp.NewCustomUser'
)   
class MyTest(TestCase):
    user_info = {
        'email': 'alice@bob.net',
        'date_of_birth': '2013-03-12',
        'password': 'password1'
    }   

    def test_failing(self):
        u = get_user_model()(**self.user_info)
        m = ModelWithForeign(user=u)
        m.save()

I'm referencing the user model in the ForeignKey argument list as described here, but using get_user_model there doesn't change anything, as the user attribute is evaluated before the setting change takes place. Is there a way to make this ForeignKey play nice with testing when I'm using custom user models?

fcoelho
  • 311
  • 4
  • 8

1 Answers1

3

I asked about this on the Django mailing list as well but it seems that, at least currently, there is no way to change the settings.AUTH_USER_MODEL and have it work nicely with a ForeignKey.

So far, in order to test my app, I've created a runtests.py file from this answer:

import os, sys
from django.conf import settings

if len(sys.argv) >= 2:
    user_model = sys.argv[1]
else:
    user_model = 'auth.User'

settings.configure(
    ...
    AUTH_USER_MODEL=user_model,
    ...
)

...

And added a bash script to actually run the tests using different user models:

for i in "auth.User" "myapp.NewCustomUser"; do
    echo "Running with AUTH_USER_MODEL=$i"
    python runtests.py $i
    if [ $? -ne 0 ]; then
        break
    fi
done

The last bit is to use a function to actually retrieve the right user model info instead of just using a "static" variable:

def get_user_info():
    if settings.AUTH_USER_MODEL == 'auth.User':
        return {default user info}
    if settings.AUTH_USER_MODEL == 'myapp.NewCustomUser':
        return {my custom user info}
    raise NotImplementedError

I'm not claiming this to be a correct answer for the problem, but so far... It works.

Community
  • 1
  • 1
fcoelho
  • 311
  • 4
  • 8
  • Isn't this related to the fact that it's a site wide setting, and not an app-specific setting? You solution seems to make pretty good sense! – benjaoming Jun 06 '13 at 10:04
  • 1
    @benjaoming it's actually because the schema is not rebuilt between tests (and really shouldn't), which renders the swappable attribute useless in this case, since it defines which table actually exists. You can swap pretty much anything else, and the [docs](https://docs.djangoproject.com/en/dev/topics/testing/overview/#django.test.utils.override_settings) shows a few examples of site wide settings that can be overridden. – fcoelho Jun 09 '13 at 22:11