Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom User Model error

I'm trying to set up my custom user model in Django. The reason is that I want to use email as the username, and remove the username field entirely. I've run into a error, that I just can't figure out.

Manager isn't available; User has been swapped for 'app.MyUser'
Exception Location: .../django/db/models/manager.py in __get__, line 256
Python Version: 2.7.3

Python Path:    
[...project specific files,
'/usr/lib/python2.7',
'/usr/lib/python2.7/plat-linux2',
'/usr/lib/python2.7/lib-tk',
'/usr/lib/python2.7/lib-old',
'/usr/lib/python2.7/lib-dynload',
'/usr/local/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages/PIL',
'/usr/lib/python2.7/dist-packages/gtk-2.0',
'/usr/lib/pymodules/python2.7',
'/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode']

I've googled like crazy, but haven't found too many pages about this error message. I have found some pages, with suggestions on how to solve it, but none of the suggestions have worked for me.

My code: I've set the custom user model. I have declared the custom user model AUTH_USER_MODEL = 'app.MyUser' in settings.py. I have also set up a custom UserManager:

class MyUserManager(BaseUserManager):

    def create_user(self, email, password=None):
        """
        Creates and saves a User with the given email. Note that none of the optional fields gets values in the creation. These fields will have to be filled out later on.
        """

        if not email:
            raise ValueError('Users must have an email address')

        user = self.model(email=MyUserManager.normalize_email(email))

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password=None):
        """
        Creates and saves a superuser with the the above mentioned attributes

        """

        user = self.create_user(email, password=password)
        user.is_admin = True
        user.save(using=self._db)
        return user


class MyUser(AbstractBaseUser, PermissionsMixin):
    """
    Custom made User model. No username, instead email is used as unique field and index
    """

    Genders = (('M', 'Man'), ('K', 'Woman'))    
    FirstName = models.CharField(max_length=30)
    LastName = models.CharField(max_length=40)
    Gender = models.CharField(max_length=2, choices=Genders, default='K')
    email = models.EmailField(verbose_name='email address', max_length=255, unique=True, db_index=True,)
    twitter = models.CharField(max_length=30)
    is_admin = models.BooleanField(default=False)


    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    def get_full_name(self):
        # The user is identified by their email address
        return self.email

    def get_short_name(self):
        # The user is identified by their email address
        return self.email

    def __unicode__(self):
        return self.email

    objects = MyUserManager()

I've tried to declare to different types of UserAdmins, none of which is making any difference,the first one I tried was;

class MyUserAdmin(UserAdmin):
    # The forms to add and change user instances
    #form = UserChangeForm
    #add_form = FrontpageRegistrationForm

    list_display = ('email', 'FirstName', 'LastName', 'Gender', 'twitter')
    list_filter = ()

    add_fieldsets = ((None, {'classes': ('wide',),'fields': ('email', 'password1', 'password2')}),)

    search_fields = ('email',)
    ordering = ('email',)
    filter_horizontal = ()

admin.site.register(MyUser, MyUserAdmin)

I've commented out the two attributes add_form and form because they raised some form errors I wanted to get back to at a later point.

The second UserAdmin was made, after reading about a possible fix here. This didn't help the situation though;

class MyUserAdmin(admin.ModelAdmin):
    # The forms to add and change user instances
    #form = UserChangeForm
    add_form = FrontpageRegistrationForm
    add_fieldsets = ((None, {'classes': ('wide',),'fields': ('email', 'password1', 'password2')}),)

    def get_fieldsets(self, request, obj=None):
        if not obj:
            return self.add_fieldsets
        return super(MyUserAdmin, self).get_fieldsets(request, obj)

    def get_form(self, request, obj=None, **kwargs):
        defaults = {}
        if obj is None:
            defaults.update({'form': self.add_form,'fields': admin.util.flatten_fieldsets(self.add_fieldsets),})

    defaults.update(kwargs)
    return super(MyUserAdmin, self).get_form(request, obj, **defaults)

I've also tried deleting all tables in the db with no luck.

I would be eternally greatful to anyone who even looks at the problem. And if any one were to solve this, I would try my best to talk my wife into naming our firstborn after the Avatar that gave me a solution so that I could go on living my life.

EDIT: I tried setting the AUTH_USER_MODELto mainfolder.app.MyUserI'm sure the "mainfolder" is on the pythonpath. init.py in the app should be correct. The new settings.py gave the following server error; auth.user: AUTH_USER_MODEL is not of the form 'app_label.app_name'.admin.logentry: 'user' has a relation with model smartflightsearch.SFSdb.MyUser, which has either not been installed or is abstract.registration.registrationprofile: 'user' has a relation with model, which has either not been installed or is abstract. A new clue I don't know how to interpret..

like image 782
Rookie Avatar asked May 05 '13 14:05

Rookie


2 Answers

TL;DR: Use the code from the Solution part at the end of the following answer.

Longer explanation: You see, as of Django 1.5, it's not enough to subclass Django's UserAdmin to be able to interact with swappable user models: you need to override respective forms as well.

If you jump to django.contrib.auth.admin source, you'll see that the UserAdmin's form and add_form have these values:

# django/contrib/auth/admin.py
class UserAdmin(admin.ModelAdmin):
    ...
    form = UserChangeForm
    add_form = UserCreationForm 

Which point us to forms in django.contrib.auth.forms that do not respect swappable user models:

# django/contrib/auth/forms.py
class UserCreationForm(forms.ModelForm):
    ...
    class Meta:
        model = User  # non-swappable User model here.

class UserChangeForm(forms.ModelForm):
    ...
    class Meta:
        model = User  # non-swappable User model here.

Solution: So, you should follow a great already existing answer (don't forget to vote it up!) which boils down to this:

from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm, UserChangeForm

class MyUserChangeForm(UserChangeForm):
    class Meta:
        model = get_user_model()

class MyUserCreationForm(UserCreationForm):
    class Meta:
        model = get_user_model()

class MyUserAdmin(UserAdmin):
    form = MyUserChangeForm
    add_form = MyUserCreationForm

admin.site.register(MyUser, MyUserAdmin)

Hopefully, this would be fixed in the future releases of Django (here's the corresponding ticket in the bug tracker).

like image 165
Ivan Kharlamov Avatar answered Nov 04 '22 17:11

Ivan Kharlamov


When you said you set AUTH_USER_MODEL = 'app.MyUser' I'm assuming your app where is located the MyUser class, have a structure, perharps, like this:

inside the app/ dir: init.py and models.py and stuff..

so inside the models.py you have the MyUser and inside the init.py:

from models import MyUser

like image 25
Douglas Miranda Avatar answered Nov 04 '22 18:11

Douglas Miranda