Custom User Authentication using DRF - python

Want to create a Web RESTful API with Django. For that I'm using Django REST Framework.
What are the necessary steps to get the authentication using a custom User model (subclassing AbstractBaseUser) exposing the endpoints to be used?

1. Create a Custom User Model
The documentation is good when it comes to Specifying a custom user model.
In our models.py, import AbstractBaseUser and BaseUserManager.
Then, create your class, for instances,
class Profiles(AbstractBaseUser):
userId = models.CharField(max_length=36, unique= True)
username = models.CharField(max_lenght=20)
password = models.CharField(max_lenght=256)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
...
Here it's very important to set USERNAME_FIELD and REQUIRED_FIELDS to define which fields are important when logging in.
Also in your custom user model class, you have to create two functions needed to deal with permissions, namely has_perm() and has_module_perms().
2. Create a Custom User Manager
Now the next step is to create a Custom User Manager, which is something recommended by Django.
So we create a class like
class ProfilesManager(BaseUserManager):
...
In that class, one needs to override two methods, namely create_user() and create_superuser(), to define what happens when a user is created and when a superuser is created, respectively.
Then, we need to tell Profiles class with an objects parameter where this ProfilesManager class is.
class Profiles(AbstractBaseUser):
userId = models.CharField(max_length=36, unique= True)
email = models.CharField(max_lenght=20)
password = models.CharField(max_lenght=256)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
...
USERNAME_FIELDS = 'userId'
REQUIRED_FIELDS = ['email']
objects = ProfilesManager()
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True
3. Set User Model
Go to your settings.py and add a AUTH_USER_MODEL property, specifying which user model Django should use.
AUTH_USER_MODEL = 'appName.Profiles'
4. Make migrations
To commit the changes made above one has to make the migrations by running
python manage.py makemigrations
5. Register User from REST API
As we have created the model, then we just need to create the serializers, views and urls.

Related

Override django user with AUTH_USER_MODEL

I have override the default User model in my app with AUTH_USER_MODEL.
Here is the user model I want to use in my app, which is tied closely to a legacy database:
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
name = models.CharField(max_length=128, blank=True)
email = models.EmailField(unique=True)
password = models.CharField(max_length=128)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
date_joined = models.DateTimeField(default=timezone.now)
normalized_email = models.EmailField(unique=True)
However, django complains that fields such as first_name, last_name, etc. are not provided. Is there a way to remove some of the existing fields in the User model? Or does specifying a custom user model only allow you to add additional fields on top of that? Is there a simple way to delete them, or do I basically have to add those fields (and ignore them) to our existing database in order to be able to use django with it?
AbstractUser is for when you want to add additional fields to Django's defaults. Use AbstractBaseUser if you don't want all those fields.

How can I distinguish a user is AminUser or normal User in the custom User model?

I want to create a custom User by inherit the AbstractUser:
https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#substituting-a-custom-user-model
But, there is a issue for me, when I use the permissions, there is a IsAdminUser permission.
If I have two custom User models, such as User model, and AminUser model (all of them inherit form AbstractUser). How can I distinguish a user is AminUser or normal User in the custom User model?
In your User Model define a Boolean field as isAdminUser. Now set this Boolean field to false for a normal user and True if the user is an admin. Depending upon the value of isAdminUser you can perform User Specific actions or Admin specific actions.For example:
class ClientUser(AbstractBaseUser):
username = models.CharField(max_length=20,unique=True)
name = models.CharField(max_length=20)
email = models.EmailField(max_length=254,null=True,blank=True)
company = models.ManyToManyField(ClientCompany,null=True,blank=True)
date_joined = models.DateTimeField(('date joined'), default=timezone.now)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)

In Django, is it possible for superusers to have different required fields than non-superusers?

I know that superusers and regular users are both just django's User objects, but how can I write a custom user class that requires some fields for plain users and doesn't require those fields for superusers?
No structure in the database is tricky. JSONFields for example may prove to be extremely hard to tame when the app grows.
I would go and try to make it "simple" - more maintainable (I imagine if you need to do stuff like that you may want to extend the model in the future). If this is a new project you can easily change the default user model. But that may or may not help you with your case.
You can always make two models:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
class Mortal(AbstractBaseUser):
is_superuser = False
username = models.CharField(max_length=256)
first_name = models.CharField(max_length=256)
last_name = models.CharField(max_length=256)
class Admin(AbstractBaseUser):
is_superuser = True
username = models.CharField(max_length=256)
and then make your own authentication backend:
class MyBackend(object):
"""
Danger! A backend to authenticate only via username
"""
def authenticate(self, username=None):
try:
return Mortal.objects.get(username=username)
except Mortal.DoesNotExist:
try:
return Admin.objects.get(username=username)
except Admin.DoesNotExist:
return None
You can have a profile class (say UserProfile) with foreign key to the user that is to be created only when user signs up using the website's registration form. That way, superuser which is created on admin site or through command line wouldn't need an extra profile instance attached to it.

Multiple User Types In Django

I am new to Django and trying to create an App with two User Types (Freelancers and Customers). I understand how to create a User profile Class and it works well for me:
class UserProfile(models.Model):
user = models.OneToOneField(User)
description = models.CharField(max_length=100, default='')
country = models.CharField(max_length=100, default='')
website = models.URLField(default='')
phone = models.IntegerField(default=0)
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile = UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender=User)
This works well for me on a one user type user. But now I am building an app with 2 types of users (freelancers and customers), what is the best approach to get this done. Both users will have different view and info. Should I:
Create 2 different apps, and repeat the normal registeration and login for each.
If I do the above, hope the freelancers when logged in won't access customers view.
How do I add user type to the user profile if I decide to use one app and model for it.
Please I need a step by step beginner approach, or a link to relevant source.
Thanks.
You could try this:
class UserProfile(models.Model):
user = models.ForeignKey(User)
#define general fields
class Freelancer(models.Model):
profile = models.ForeignKey(UserProfile)
#freelancer specific fields
class Meta:
db_table = 'freelancer'
class Customers(models.Model):
profile = models.ForeignKey(UserProfile)
#customer specific fields
class Meta:
db_table = 'customer'
You can then have as many Users as you want from the UserProfile.
You should need just use Groups Django mechanism - you need to create two groups freelancer and let say common and check whether user is in first or second group - then show him appropriate view
To check whether user is in group you can use
User.objects.filter(pk=userId, groups__name='freelancer').exists()
You Could Try extending the Default Django Auth User like this
Create an App with Account or Whatever name you like , then in models.py write like below
class User(AbstractUser):
is_head = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_public = models.BooleanField(default=False)
Add Auth Extended Model in Settings.py
AUTH_USER_MODEL = 'accounts.User'
Migrate your Account app and you are all set with Your User Extended Model.

ValueError: Lookup failed for model referenced by field

I have made Custom User model in my Django project. Here it is:
class CustomUser(User):
avatar = models.ImageField(upload_to='avatars')
about_myself = models.TextField(max_length=300)
USERNAME_FIELD = 'username'
def __str__(self):
return self.username
def is_author(self):
return 'blog.change_post' and 'blog.add_post' in self.get_all_permissions()
And after it, I changed all Foreign Keys of user to new CustomUser model. It works OK. But I make one new migration and django cause error, when I want to migrate it:
ValueError: Lookup failed for model referenced by field blog.Comment.author: main.CustomUser
My blog.Comment model:
class Comment(models.Model):
content = models.TextField()
author = models.ForeignKey(CustomUser)
date_create = models.DateTimeField(auto_now_add=True)
post = models.ForeignKey(Post)
What should I do?
Thanks!
Judging from the code you posted, you might be might be better served by extending the user model rather than replacing it. This pattern is usually called a profile model and works via a one-to-one relationship with User.
Profiles provides application specific fields and behaviors, while allowing User to go about it's usual business unchanged. It doesn't require you to muck around with rewriting auth or even necessarily change your foreign keys.
Here's an example of your code written as a profile:
class Profile(models.Model):
# Link to user :
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to='avatars')
about_myself = models.TextField(max_length=300)
def __str__(self):
return self.user.username
def is_author(self):
return 'blog.change_post' and 'blog.add_post' in self.user.get_all_permissions()
Comment model:
class Comment(models.Model):
content = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL)
date_create = models.DateTimeField(auto_now_add=True)
post = models.ForeignKey(Post)
# How to access the profile:
def check_author(self):
self.author.profile.is_author()
You'll also want to add a signal to create a new profile when a user is registered:
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_profile_for_new_user(sender, created, instance, **kwargs):
if created:
profile = Profile(user=instance)
profile.save()
Django docs on extending users.
If a profile approach doesn't work for you, try inheriting from AbstractUser or AbstractBaseUser instead of User. The abstract models provide the same basic functionality as User and are the preferred technique for recent Django versions.
There are a handful of additional steps however, check out the docs on creating custom users for a run down.

Categories