Setting up two different types of Users in Django 1.5/1.6
Asked Answered
V

3

11

Please note--this is an updated version of my original question on this subject, but deserves to be asked again with the change in how Django deals with users and authentication.

I'm working on a website with two very different kinds of users--let's call them Customers and Store Owners. Both register on the site, but have very different functionality. Customers simply have a single profile and can shop among the stores that they like. Store Owners have a single account but can have access to multiple stores, and each store can have multiple Store Owners.

The exact details of the models don't matter, but the two types of users would require very different fields. The models ideally would look something like this:

Customer
  email (username)
  password
  name
  address
  time_zone
  preferred_shipping
  favorite_stores (many-to-many field)
  ...

Store Owner
  email (username)
  password
  name
  balance
  stores_owned (many-to-many field on Stores)
  stores_managed (many-to-many field on Stores)
  ...

Originally, when Django had poor custom user support, I had a UserProfile class with some additional fields with a OneToOne on User, and then additional Customer and StoreOwner classes that were OneToOne on UserProfile. This didn't work very well.

Given the changes in Django 1.5/1.6, I'm trying to come up with the best way to structure this. Right now, I have the following:

class CustomerUser(AbstractBaseUser):
    ...

class StoreOwnerUser(AbstractBaseUser):
    ...

But because there would be two types of user, I can't set AUTH_USER_MODEL to only one of them.

What is the best way to structure this so that I can have two different types of users with different fields, without causing me any problems in user authentication, user creation, or the admin?

Also, how will I be able to tell from login alone whether this user is a CustomerUser or a StoreOwnerUser?

Vaasta answered 26/12, 2013 at 4:20 Comment(2)
You may want to create a base user, add a boolean flag which denotes whether or not a user is a customer or store owner, and attach another model which is the profile, using a one-to-one relationship.Oomph
It's been almost a year since this was asked, but I find myself in a similar situation. Question: what approach did you eventually take? Also, have you considered the case where a store-owner might want to register as a customer (using same email address)? How does your design cater for this case? Lastly, did you ever consider having separate login views for storeowners and customers (which can help easily distinguish the sign-on user type)?Amir
H
5

It seems like there are some common features and uncommon features to your user types. If there are common features in your user types that Django's default User model doesn't support out of the box, you should subclass it directly.

Adding in extra, uncommon features to your user types are best done not by subclassing but by using a profile. My rationale for this is because your authentication for these user types doesn't fundamentally change, but details about the user does depending on the type of user it is. To accomodate this, you create a separate model with these details and reference your User class as a OneToOne/ForeignKey relationship (depending on your design).

You can make modifications to your user creation process to identify what kind of user type it should be, and set its associated OneToOneField/ForeignKey (depending on your design) to the appropriate customer type model.

By doing it this way, you should only have one AUTH_USER_MODEL, and you should be able to handle details for your different customer types.

Halsey answered 26/12, 2013 at 4:29 Comment(2)
Thanks--this makes sense. But how would I know from a login whether the user logging in is a StoreOwner or Customer? Would I have in the base UserProfile class a storeowner_or_customer field, or something like that?Vaasta
Something like that. UserProfile would have to be available to User and it'd be something you'd set every time a User is created. I would suggest you have your two profile models to be your two user types (StoreOwner/Customer) and then you can add a "type" field to your User model which the two user type models reference via a OneToOne relationship. In your code, you first check the type (i.e., user_obj.type == "Customer" or something like that), and if true, you can access the related details directly user_obj.customer.address.Halsey
H
3

What is the best way to structure this so that I can have two different types of users with different fields, without causing me any problems in user authentication, user creation, or the admin?

You actually only have one type of user. Just that some users have specific properties set and others do not. Consider how django has "users" and "admins". They are the instances of the same model, but with different properties and permissions.

You should approach it similarly. Have one user model for your entire application. You can set properties/methods in your custom user class to identify what flags this user has set (which would determine the "type" of user there is).

Also, how will I be able to tell from login alone whether this user is a CustomerUser or a StoreOwnerUser?

You can use the user_passes_test decorator, which takes an argument that is a function name and will only process the view if the function returns a truth value.

Hark answered 26/12, 2013 at 4:38 Comment(2)
I like that idea a lot, but wouldn't that end up with me having a large body of unused fields in the model? Are there implications there, particularly for form generation?Vaasta
No, especially if you use reverse relationships of models correctly. For example, you don't need the stores_owned and stores_managed fields because this information you can fetch using the ORM. For example you could have a method in User that would return self.stores_set.count() for "stores managed"; you would have that method in the model is if you were to display it in the admin as part of that models list view, otherwise it would be a value that would be in a cache somewhere.Hark
S
1
  1. Create a BaseUser which extends Django's Abstract base User
  2. Create two Sub Classes Named CustomerUser and StoreOwnerUser which extends BaseUser

    from django.db import models
    from django.contrib.auth.models import AbstractUser
    
    class BaseUser(AbstractUser):
        # all the common fields go here, for example:
        email = models.EmailField(max_length=10,unique=True)
        name = models.CharField(max_length=120)
    
    class StoreOwnerUser(BaseUser):
        # All Store Owner specific attribute goes here
        balance = models.some_balance_field()
        stores_owned = models.some_stores_owned_field()
    
        class Meta:
        verbose_name = 'Store Owner'
    
    class CustomerUser(BaseUser):
        # All Customer specific attribute goes here
        customer_id = models.CharField(max_length=30, unique=True)
        address =  models.some_address
        time_zone = models.something...
        ...
    
        class Meta:
            verbose_name = 'Customer'
    
Skvorak answered 7/8, 2015 at 8:30 Comment(2)
What would be specified as AUTH_USER_MODEL? Also at the time of login: user = authenticate(username = username, password = password). From which table (CustomerUser or StoreOwnerUser) would the authenticate function check from?Sabrasabre
you can do this implementation #30496479Heritor

© 2022 - 2024 — McMap. All rights reserved.