How to separate users (models) by admins and customers on Django - python

I would like to separate users of my Django app in two classes :
- Admin (users that use Django admin) - inherit from AbstractUser
- User (customers users) - inherit from AbstractBaseUser
I want to separate this two kinds of users because all fields of AbstractUser (is_staff, is_superuser, groups, permissions) are useless for my customer users and for permissions and group, I just want to implement something different. That why, I want to use AbstractBaseUser.
But for django admin users, AbstractUser class, it's just perfect and particularly with permissions feature.
class Admin(AbstractUser):
pass
class Customer(AbstractBaseUser):
pass
But now, is there a way to precise the User model used Admin for the django admin only?
And use the Customer model for the rest of my apps.
Did I have to implement this from scratch :
class MyUser(AbstractBaseUser):
username = models.CharField(max_length=30, unique=True)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
is_active = models.BooleanField(default=False)
class Admin(MyUser, PermissionsMixin):
is_staff = models.BooleanField(default=True)
class Customer(MyUser):
# specific fields
pass
With this implementation, if I set AUTH_USER_MODEL to User, permissions will not work because User has no permissions, is_superuser and is_staff fields.
And if a set it to Admin, I will not be able to authenticate Customers with django.contrib.auth.
So guys do you have a solution to this issue?

The way Django offers to you seems to be much more flexible and future-adapted.
You have a built-in User model, which you can override. Anyway, that model has permissions, groups, etc.
If you need different field sets for different kinds of users, you create a OneToOne profile models.
The separation point between your admins (actually, staff users) and regular customers is a User.is_staff attribute.
This way you gain a bunch of cool stuff (compared to two completely different user models):
Everything works out of the box: contrib.auth and contrib.admin modules.
Easy-customisable separation point: just override the admin_site.has_permission() and here you go.
You have the ability (but not obligation) to create users which are either customers and admins.
You can assign groups and permissions (different from your admins' ones) to your customers. Even you don't need it now, who knows.
As for drawbacks. The only one you've pointed out so far: your customers will be having (unused for now) permissions. Well, as they (as well as groups) are just separate tables, your customer data will have no performance of storage overhead.
That is to say, the overhead is negligeable compared to the benefits. I'd strongly recommend staying with Django's default User model and extending it if necessary.

Related

Django setting up database for different users

I have Django project for recording personal expenses and keeping personal budget.
I have created required models for the project and authorization using Django. However the idea is that each authentic user shall keep own expenses records, therefore needs likely separate database. I have researched Django documentation and it seems doest not provide answer to this. Probably there is no need to set up another database but to create unique model fields inherited from default admin user fields and store the data for each user in the single database.
Please advice correct approach for this task.
You can always extend the auth user model to include additional data. You can create a new model that has all the additional fields plus a one-to-one relationship with the django user model.
eg:
from django.db import models
from django.conf import settings
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
date_of_birth = models.DateField(blank=True, null=True)
...
Here, you should be able to use the default auth user fields plus the custom fields you need.

difference between ForeignKey and extending the User class/model in Django

Django: When extending User, better to use OneToOneField(User) or ForeignKey(User, unique=True)?
I went through this thread and found that ForeignKey(with unique=True) is better than OneToOneField, but what about extending the class itself, I.e. here is the example
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
# some additional fields
OR
class UserProfile(User):
# some additional fields
Difference between these two approaches and pros/cons and which one should I use?
EDIT:
I can use AbstractUser as well
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
phone_no = models.CharField(max_length=10, blank=True)
and then mentioned AUTH_USER_MODEL = 'myapp.User' in settings.py
main concern is, what approach should I use, extending the class or ForeignKey ?
Duplicates:
What's the difference between OneToOne and Subclassing a model in Django
Django Model Inheritance versus OneToOne field
MORE EDIT
Forget about ForeginKey or OneToOne, assume only one of these two exist, now compare that with extending/subclassing approach
First, it is good to know there currently are several options how to extend the Django user model. Each has its purpose (but there is some overlap as well). Django docs are a bit confusing as it seems from this there are two options, i.e. proxy or OneToOneField. However this relates to the existing user model, as further on in the docs is dealt with custom user models.
So in practice there are four (common) ways to deal with extending the user model:
Proxy model (no new databasefields, just to change user model behavior, i.e. new ordering, new methods, etc.).
OneToOneField (extra datafields needed within existing Djang user model).
Custom user model with AbstractBaseUser (extra datafields
needed, and specific requirements regarding authenticaton process,
e.g. using emailaddress als id token instead of username).
Custom user model with AbstractUser (extra datafields needed, no
change to authentication).
Implementing option 3 and 4 a fresh database migration is needed, so these are only suitable for new projects.
This is a good link for more detail on this. I think option 2 and 4 are closest as both only want to extend to add more datafields. Writer seems in favor of option 2, but when starting a new project option 4 seems easier to me. Somewhere in the comments writer mentions risk of not being able to upgrade to new Django versions for option 3 and 4. Seems far-fetched to me, but I can't tell really.
There is no better way to do, the thing is if you do extend AbstractUser you need to redefine some functions so it may be longer but you have more control on what you wanna do with your user.
Make a OneToOne field on django default user is faster and also allow you to add your own user custom fields but you can use directly User default field in your custom object, and your custom field on the user :
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User)
department = models.CharField(max_length=100)
You can do :
>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department
So it really depends on what you want to do. You can do your User for example if you want to take the mail adress as the identification token (it's a common exmaple but you can do much more things :p).
Here is a good explanation (I place it on user but you can read the whole page it's pretty interesting when you dive into User and authentication into Django).
Hope it help.
I am skeptical about the benefits of a unique FK verses one-to-one, you could achieve a similar thing in the admin by using fieldsets so I would prefer to have an explicit one-to-one field on the model, making the nature of the relation more obvious.
The duplicate questions you linked to aren't specific to the auth User model and discuss one-to-one vs model inheritance generally. Technically they are both the same (i.e. model inheritance uses a one-to-one field)
So ultimately the choice comes down to semantics: is your related model a 'subclass' of the other, or just a link to further related info?
In the case of auth User you would ask yourself then: are there some extra fields that should be present for all users (eg gender, facebook id etc)? or some fields you want to omit from the Django User model (eg to use unique email address as username)?
In this case the obvious choice is to extend AbstractUser. If you can't imagine specifying null=True on your user profile model you should consider extending AbstractUser.
On the other hand there may be some data that is more analogous to the old UserProfile model (have a look how things were in old versions of Django before extending AbstractUser was supported: https://docs.djangoproject.com/en/1.4/topics/auth/#storing-additional-information-about-users)
Perhaps for example you have different types of users who may or may not have certain extra sets of fields. In this case it may make sense to have a one-to-one link to one or more 'profile' models.

Django Model Inheritance versus OneToOne field

EDIT: Advantages and disadvantages of both methods.
SO,
I have three models: Person, Client, Member
Person is a base model, Client and Member are profiles for Person.
class Person(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(
verbose_name=_('email address'),
max_length=255,
unique=True,
)
class Client(User): #or maybe models.Model and explicit OneToField
first_name = models.CharField(verbose_name=_('first name'), max_length=30)
last_name = models.CharField(verbose_name=_('last name'), max_length=30)
class Member(User): #or maybe models.Model and explicit OneToField
description = models.CharField(verbose_name=_('first name'), max_length=255)
# other stuff
So, what I want?
In admin, when we add client or member, I want to fill field email (fields from base class) as if it was in derived class. For example, in admin list_display = ('email', 'first_name'). Not select boxes for user.
Person class can be instantiated separately and the "attached" to the created profiel. Like person=Person(email="test#gmail.com"), client = Client(person=person,first_name="test"...). Especially, this should work in forms. When user (person) is authenticated I want to give ability to fill in profile (client) form and attach person to this form.
One person can have both accounts. If I delete client/member, corresponding person should not be deleted.
3rd option actually is not necessary, but it is useful for me to know how to do it.
So, option 1 is perfectly solved by inheritance, Person is User, but this approach fails when option 2 is implemented. Since Person and Client are considered as one whole, I can't attach user, duplicate key error.
Option 2 is resolved by extending models.Model and appending person=models.OnetoOneField(Person,primary_key=True). However, admin is broken (1st option), because Client don't have fields like email.
So, what approach to take in order to solve above issues?
Are there simple ways?
If there are no simple ways, is there advanced way, like overriding metaclass, object descriptors or writing custom OneToOne field?
Any suggestions are welcomed.
Thank you.
You can use the post_delete signal for this.
So you would:
Register the post_delete signals on both the models
One delet, check if the other object exists, and delete.
There are lots of examples on the implementation of post_delete signals on StackOverflow, so i will leave that part out.

Django-Nonrel AbstractUser Permissions

I am working on a project and I have decided to use Google App Engine for hosting (Django-nonrel). The website will have multiple types of users (inheriting from AbstractUser), and I want to be able to create permissions to control what a user can see/do. Since the native Django permissions do not work on Nonrel, I tried using permission_backend_nonrel, however it only works if you use the standard User model.
I have spent lots of time searching for how others have gotten permissions to work on Nonrel and AbstractUser, but have not found anything. It seems like I should give up on getting permissions to work and just create fields within the user models to replicate permissions. For example, if I want only some users to have the ability to change their email address, then I could do:
accounts\models.py
class UserProfile(AbstractUser):
address = models.CharField(max_length=40)
can_change_email = models.BooleanField(default=True)
customers\models.py
class CustomerProfile(UserProfile):
company = models.BooleanField(max_length=40)
In this scenario I could set 'can_change_email' and control this behavior in the views for UserProfile.
I would prefer to use the built-in permission system, but running out of ideas. Any suggestions?
I'd say you might have better luck creating separate one-to-one models to signify the difference between your users. Django expects you to have a single user model.
Another option is to use the normal User model and create proxy models that reflect the changes you want to have between users.
The first way:
class CustomerProfile(models.Model):
user = models.OneToOneField(User)
The second way:
class CustomerProfile(User):
class Meta:
proxy = True

What's the proper way to use multiple AUTH_USER_MODEL in Django 1.5?

I want to use two different models for django.contrib.auth module. The first one is the default User model provided by Django which is completely suitable for admin access (groups, permissions etc.) but the other one is customer model which has a lot of different attributes (city, locale, address etc.) compared to default User model. These user groups must use different tables and mustn't have any relation.
I created a Customer model inherited from AbstractBaseUser and a middleware class called ChangeBaseUser like this:
class ChangeBaseUser(object):
def process_request(self, request):
match = resolve(request.path)
if match.app_name == "myapp":
settings.AUTH_USER_MODEL = 'myapp.Customer'
else:
settings.AUTH_USER_MODEL = 'auth.User'
It's working but I'm not sure whether this is the proper way to do it because in documentation there is a section (link) that implies the convenient way is to assign a static value for default user model.
If this is not the proper way, do you have any suggestions for having multiple user models per module basis?
If your requirement is to keep admin users and customers separate, I don't see anything wrong with having multiple user models. At this point, the customer model is like any model, except it is very similar to the user model and that is perfectly fine if that works for you. The only disadvantage is that you will have to possibly duplicate many helpers django gives you for the Django user model such as auth backend or sessions for users. If you are willing to do all that, this seems perfectly fine.
If you wish however to utilize many of the django helpers you might want to create a very basic user model which will serve as a base for both admins and customers:
class User(AbstractBaseUser):
# use this for auth and sessions
class Admin(models.Model):
user = models.OneToOneField(UserBase, related_name='admins')
# ... other admin-specific fields
class Customer(models.Model):
user = models.OneToOneField(UserBase, related_name='admins')
# ... other customer-specific fields
This will allow you to reuse many of the things Django provides out of the box however it will incur some additional db overhead since more joins will have to be calculated. But then you can cache things for customers so you can get some of the performance back.

Categories