Django Custom User Admin Login

439 views
Skip to first unread message

vinaya...@iiitd.ac.in

unread,
Dec 19, 2013, 11:08:19 PM12/19/13
to django...@googlegroups.com
I'm creating a custom user by extending the AbstractBaseUser class. I have read the Django documentation and several tutorials online and I think that what I've done till now is very basic and should work.

The problem I'm facing is that even if I create a new DB from scratch and do a syncdb, the manage.py console **does not ask me for a admin username and password** (as it usually does). Hence, I cannot access /admin. 

The problem is partially (and wrongly) resolved my using AUTH_PROFILE_MODULE instead of AUTH_USER_MODEL. This however leads to 'Users' and 'MyUser' being showed as different objects on the admin page i.e. MyUser is not set as the default user. (obviously)

**models.py**

    class UserManager(BaseUserManager):
        def create_user(self, username, email, corp, password=None):
            if not (username or email or corp):
                raise ValueError('Users must have an username, email and corporation')
            user = self.model(username=username, email=UserManager.normalize_email(email), corp=corp, )
            user.set_password(password)
            user.is_active = True
            user.save(using=self._db)
            return user
    
        def create_superuser(self, username, email, corp, password):
            user = self.create_user(self, username, email, corp,
                                    password=password, )
            user.is_active = True
            user.is_admin = True
            user.is_staff = True
            user.is_superuser = True
            user.save(using=self._db)
            return user
    
    
    class MyUser(AbstractBaseUser, PermissionsMixin):
        username = models.CharField(max_length=254, unique=True, blank=False)
        first_name = models.CharField(max_length=30, blank=True)
        last_name = models.CharField(max_length=30, blank=True)
        email = models.EmailField(blank=True)
        corp = models.ForeignKey(Client, related_name="is_employee_of")
    
        is_active = models.BooleanField(default=True)
        is_admin = models.BooleanField(default=False)
        is_staff = models.BooleanField(default=False)
    
        def get_full_name(self):
            return self.first_name + " " + self.last_name
    
        def get_short_name(self):
            return self.first_name
    
        def __unicode__(self):
            return self.username + "@" + self.corp.shortName
    
        objects = UserManager()
    
        USERNAME_FIELD = 'username'
        REQUIRED_FIELDS = ['email', 'corp']


**admin.py**

    admin.site.register(Client)
    admin.site.register(Address)
    
    class UserCreationForm(forms.ModelForm):
        password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
        password2 = forms.CharField(label='Password Confirmation', widget=forms.PasswordInput)
    
        class Meta:
            model = MyUser
            fields = ('username', 'email', 'corp', 'first_name', 'last_name')
    
        def clean_password2(self):
            password1 = self.cleaned_data.get("password1")
            password2 = self.cleaned_data.get("password2")
    
            if password1 and password2 and password1 != password2:
                raise forms.ValidationError("Passwords don't match")
    
            return password2
    
        def save(self, commit=True):
            user = super(UserCreationForm, self).save(commit=False)
            user.set_password(self.cleaned_data["password1"])
    
            if commit:
                user.save()
            return user
    
    class UserChangeForm(forms.ModelForm):
        password = ReadOnlyPasswordHashField()
    
        class Meta:
            model = MyUser
    
        def clean_password(self):
            return self.initial["password"]
    
    class MyUserAdmin(UserAdmin):
        form = UserChangeForm
        add_form = UserCreationForm
    
        list_display = ('username', 'email', 'corp', 'first_name', 'last_name', 'is_admin', 'is_staff')
        list_filter = ('is_admin',)
        fieldsets = (
            (None, {'fields': ('username', 'email', 'password')}),
            ('Personal Info', {'fields': ('first_name', 'last_name', 'corp')}),
            ('Permissions', {'fields': ('is_admin', 'is_staff')}),
            ('Important Dates', {'fields': ('last_login',)}),
        )
    
        add_fieldsets = (
            (None, {
                'classes': ('wide',),
                'fields': ('username', 'email', 'password1', 'password2')}
            ),
        )
    
        search_fields = ('username',)
        ordering = ('username',)
        filter_horizontal = ()
    
    admin.site.register(MyUser, MyUserAdmin)

**settings.py**

    
    INSTALLED_APPS = (
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.sites',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'django.contrib.admin',
        'Profiles',
    )
    AUTHENTICATION_BACKENDS = (
            'django.contrib.auth.backends.ModelBackend',
    )
    AUTH_USER_MODEL = "Profiles.MyUser"

Russell Keith-Magee

unread,
Dec 20, 2013, 7:49:12 PM12/20/13
to Django Users
On Fri, Dec 20, 2013 at 12:08 PM, <vinaya...@iiitd.ac.in> wrote:
I'm creating a custom user by extending the AbstractBaseUser class. I have read the Django documentation and several tutorials online and I think that what I've done till now is very basic and should work.

The problem I'm facing is that even if I create a new DB from scratch and do a syncdb, the manage.py console **does not ask me for a admin username and password** (as it usually does). Hence, I cannot access /admin. 

The problem is partially (and wrongly) resolved my using AUTH_PROFILE_MODULE instead of AUTH_USER_MODEL. This however leads to 'Users' and 'MyUser' being showed as different objects on the admin page i.e. MyUser is not set as the default user. (obviously)

This sounds like there's some sort of import problem happening.  

First off - if syncdb doesn't ask you for an admin password, you can do the same thing manually -  syncdb is just doing an implicit call to ./manage.py createsuperuser, so if you invoke that, you can create a user account.

The implicit call to createsuperuser happens because syncdb sends out a signal, and contrib.auth hooks onto that signal. If this isn't happening, it means that contrib.auth isn't being installed correctly. It's difficult to diagnose exactly what's happening without having all the code in front of me, but some possible candidates are:

 * An import error on one of your modules (e.g., Profiles) that is being swallowed
 * Multiple settings.py files on your PYTHONPATH, so the settings file you *think* is being used isn't actually the right one.
 
The capitalisation of the Profiles module name might also be causing problems; Python convention is generally that module names are lower case (yes, there are exceptions, but it's a generally true). it's possible you're getting tripped up by some internals in the app cache that are altering the capitalisation of the module, which in turn cause problems when you reference the capitalised module name.

If you try to run createsuperuser and it raises an error (or creates the wrong model), that might also help point at the problem.

Sorry I can't point you at a single obvious solution, but hopefully I've given you enough to get over this hump.

Yours,
Russ Magee %-)

vinaya...@iiitd.ac.in

unread,
Dec 21, 2013, 6:29:06 AM12/21/13
to django...@googlegroups.com
So I solved the problem. Even I don't know how. :/
I used this as a template and added my own models and requirements on top of it.

Slightly off topic but I needed this setup because I want to grant users object level permissions. django-guardian looked like a match but I'm having trouble making it work with custom user models. The developer of guardian has a warning for custom users.

Since the template I used to create my custom user had admin.site.unregister(Group) included in the admin.py file, guardian throws:
'MyUser' has no attribute 'groups' error. Allowing groups to register shows the same error. Do we need to implement custom groups when we use custom users? As of now, I don't need the group functionality - so it'd be great if there is a work around.

Here is my architecture:
|--- customuser
   |--- customauth
       |--- management
       |--- migrations
       |--- admin.py    // same as used in the initial post, slight additions
       |--- models.py  // same as used in the initial post, slight additions
       |--- views.py
   |--- customuser
       |--- setting.py
       |--- urls.py
   |--- client001
       |--- admin.py    // posted below
       |--- models.py  // posted below
       |--- views.py 

So, I have a separate app for each client (customer) that registers for my app. Each client can have multiple users with each user having permission to 'view' a 'stream' or not. The main app stores a list of all users and all clients.

customauth/models.py

class Address(models.Model):
    country = models.CharField(_('Country'), max_length=100, blank=True)
    *** ommited ***
    street_line3 = models.CharField(_('Address Line 3'), max_length=100, blank=True)

class Client(models.Model):
    cID = models.IntegerField(primary_key=True, blank=False)
    *** ommited ***
    address = models.ForeignKey(Address, related_name='located_at')

class MyUserManager(BaseUserManager):
    def create_user(self, email, username, password=None):
        *** ommited ***
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, username, password):
        *** ommited ***       
    user.is_admin = True

        user.is_superuser = True
        user.save(using=self._db)
        return user


class MyUser(AbstractBaseUser):
    email = models.EmailField(
        verbose_name='email address',
        max_length=255,
        unique=True,
        db_index=True,
    )
    username = models.CharField(max_length=254, unique=True, blank=False, db_index=True)
    *** ommited ***
    corp = models.ForeignKey(Client, related_name="employee_of", null=True)

    objects = MyUserManager()

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']


customauth/admin.py
admin.site.register(Address)
admin.site.register(Client)



class UserCreationForm(forms.ModelForm):
    password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
    password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)

    class Meta:
        model = ZenatixUser
        fields = ('email', 'username')


    def clean_password2(self):
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        user = super(UserCreationForm, self).save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user


class UserChangeForm(forms.ModelForm):
    password = ReadOnlyPasswordHashField()

    class Meta:
        model = ZenatixUser
        fields = ['username', 'corp', 'first_name', 'last_name', 'email', 'password', 'date_of_birth', 'is_active',
                  'is_admin']


    def clean_password(self):
        return self.initial["password"]


class MyUserAdmin(UserAdmin):
    form = UserChangeForm
    add_form = UserCreationForm
    list_display = ('username', 'corp', 'email', 'is_admin')
    list_filter = ('corp',)
    fieldsets = (
        (None, {'fields': ('username', 'email', 'corp', 'password')}),
        ('Personal info', {'fields': ('date_of_birth', 'first_name', 'last_name')}),
        ('Permissions', {'fields': ('is_admin', 'is_active')}),

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

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

admin.site.register(ZenatixUser, MyUserAdmin)
#admin.site.unregister(Group)

client001/models.py
class Stream(models.Model):
    id = models.AutoField(primary_key=True)
    uuid = models.CharField(unique=True, max_length=36, db_index=True)
    metadata = hstore.DictionaryField(db_index=False, default={'key':'value'})
    objects = hstore.HStoreManager()

    class Meta:
        permissions = (
            ('read_stream', 'Read Stream'),
        )

class ClientInfo(models.Model):
    corp = models.ForeignKey(Client, blank=False, db_index=True, related_name='+')
    def save(self, *args, **kwargs):
        if ClientInfo.objects.all().count()>0:
            print ClientInfo.objects.all()
            raise ValueError("Only one corp can be registered per client")
        else:
            super(ClientInfo, self).save(*args, **kwargs)
 client001/admin.py
admin.site.register(ClientInfo)

class StreamAdmin(GuardedModelAdmin):
    prepopulated_fields = {"uuid": ("metadata",)}
    list_display = ('uuid', 'metadata')
    search_fields = ('uuid', 'metadata')
    #ordering = ('-created_at',)
    #date_hierarchy = 'created_at'

admin.site.register(Stream, StreamAdmin)

vinaya...@iiitd.ac.in

unread,
Dec 21, 2013, 6:50:15 AM12/21/13
to django...@googlegroups.com, lukaszb...@gmail.com
Also, from initial tests it appears that django-guardian is not setting permissions with custom user. I wrote a small manage.py script to test that:

from django.core.management.base import BaseCommand, CommandError
from iiitd.models import *
from guardian.shortcuts import assign_perm, remove_perm

class Command(BaseCommand):
    def handle(self, *args, **options):
        user = MyUser.objects.filter(username='abc')[0]
        stream = Stream.objects.filter(uuid='001')[0]
        assign_perm('read_stream', user, stream)
        print user.has_perm('read_stream', stream)
        remove_perm('read_stream', user, stream)
        print user.has_perm('read_stream', stream)

It prints True in both cases.

However, the guardian_userobjectpermission table is updating correctly when I add or delete permissions. Confused. o.O

Russell Keith-Magee

unread,
Dec 21, 2013, 9:13:50 PM12/21/13
to Django Users
On Sat, Dec 21, 2013 at 7:29 PM, <vinaya...@iiitd.ac.in> wrote:
So I solved the problem. Even I don't know how. :/
I used this as a template and added my own models and requirements on top of it.

Slightly off topic but I needed this setup because I want to grant users object level permissions. django-guardian looked like a match but I'm having trouble making it work with custom user models. The developer of guardian has a warning for custom users.

Since the template I used to create my custom user had admin.site.unregister(Group) included in the admin.py file, guardian throws:
'MyUser' has no attribute 'groups' error. Allowing groups to register shows the same error. Do we need to implement custom groups when we use custom users? As of now, I don't need the group functionality - so it'd be great if there is a work around.

As indicated in the custom user docs -- no, you don't *have* to implement groups. However, if you're using any functionality that depends on groups, you will need to at least provide a null implementation. If you're using an app that integrates with object-level permissions, then it sounds like you're going to need to provide at least *some* interface.

Beyond that, I can't be much help - I don't know anything about django-guardian, and can't comment on whether it's been upgraded to support custom Users (there are plenty of apps out there that haven't been). It sounds like you need to take this problem up with the maintainers of django-guardian.

Yours,
Russ Magee %-)

Alexander Myasnov

unread,
Mar 20, 2014, 11:54:35 AM3/20/14
to django...@googlegroups.com, lukaszb...@gmail.com


суббота, 21 декабря 2013 г., 15:50:15 UTC+4 пользователь vinaya...@iiitd.ac.in написал:
Hello, have you solved this bug?

Now I have the same behavior with django-1.6.2 and django-guardian-1.2.

Alexander Myasnov

unread,
Mar 21, 2014, 6:17:37 AM3/21/14
to django...@googlegroups.com, lukaszb...@gmail.com
Sorry, it was my bug: wrong has_perm() declaration in custom user model.
Everything works fine.

четверг, 20 марта 2014 г., 19:54:35 UTC+4 пользователь Alexander Myasnov написал:
Reply all
Reply to author
Forward
0 new messages