customize models in Django RestFramework using AbstractBaseUser, BaseUserManager - python

i wanna customize models. so i used AbstractBaseUser, BaseUserManager
i use **extra_field to add 'point' attribute.
i enterd http://127.0.0.1:8000/account/signup/
and write my data
but the errors occured
enter image description here
what's wrong with my code?
i'm a beginner, plz help me
# serializers.py
from .models import User
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
def create(self, validated_data):
user = User.objects.create_user(
email = validated_data['email'],
nickname = validated_data['nickname'],
name = validated_data['name'],
password = validated_data['password'],
point = validated_data['point']
)
return user
class Meta:
model = User
fields = ['name', 'nickname', 'email', 'password']
# models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class UserManager(BaseUserManager):
def create_user(self, email, nickname, name, password=None, **point):
if not email:
raise ValueError('must have user email')
if not nickname:
raise ValueError('must have user nickname')
if not name:
raise ValueError('must have user name')
user = self.model(
email = self.normalize_email(email),
nickname = nickname,
name = name,
point = point
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, nickname, name, password=None):
user = self.create_user(
email,
password = password,
nickname = nickname,
name = name
)
user.is_admin = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
id = models.AutoField(primary_key=True)
email = models.EmailField(default='', max_length=100, null=False, blank=False, unique=True)
nickname = models.CharField(default='', max_length=100, null=False, blank=False, unique=False)
name = models.CharField(default='', max_length=100, null=False, blank=False)
point = models.IntegerField(default=500, max_length=10000000)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['email', 'name', 'nickname', 'password']
def __str__(self):
return self.name
# views.py
from django.shortcuts import render
from .serializers import UserSerializer
from .models import User
from rest_framework import generics
# 회원가입
class UserCreate(generics.CreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
# urls.py
from django.urls import path, include
from . import views
from rest_framework import urls
urlpatterns =[
path('signup/', views.UserCreate.as_view()),
path('api-auth/', include('rest_framework.urls')),
]
i wanna customize models. so i used AbstractBaseUser, BaseUserManager
i use **extra_field to add 'point' attribute.
i enterd http://127.0.0.1:8000/account/signup/
and write my data
but the errors occured
what's wrong with my code?
i'm a beginner, plz help me

When you unpack values using ** then you should access the values like this:
def create_user(self, email, nickname, name, password=None, **kwargs):
if not email:
raise ValueError('must have user email')
if not nickname:
raise ValueError('must have user nickname')
if not name:
raise ValueError('must have user name')
user = self.model(
email = self.normalize_email(email),
nickname = nickname,
name = name,
point = kwargs['point']
)
user.set_password(password)
user.save(using=self._db)
return user
Check how I get the value of point. The thing is that your definition of point is { point: 500 }. So is a dict. You need to get the value of point from that dict.

Related

Unknown field(s) (username) specified for Account. Check fields/fieldsets/exclude attributes of class AccountAdmin

Ive been spending hours on figuring out how to fix this error however I cannot figure it out? I have specified the different fields.
This occurs when im in the admin panel and i try to add a new account - SNI works fine. I dont understand. Am i missing something? I have set the user field too
Model
from asyncio import FastChildWatcher
import email
from pyexpat import model
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class userCouncil(BaseUserManager):
def create_user(self, userID, password=None):
if not email:
raise ValueError("Email is required")
user = self.model(userID = self.normalize_email(userID))
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, userID, password):
user = self.model(userID = self.normalize_email(userID))
user.set_password(password)
user.is_staff = True
user.is_admin = True
user.save(using=self._db)
return user
class sni(models.Model):
SNI = models.CharField(max_length=10, primary_key=True)
# USERNAME_FIELD = 'SNI'
def __str__(self):
return self.SNI
class Account(AbstractBaseUser):
userID = models.EmailField(max_length=80, unique=True)
name = models.CharField(max_length=100)
dateOfBirth = models.DateField(max_length=8, null=True)
homeAddress = models.CharField(max_length=100, null=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
sni = models.OneToOneField(sni, on_delete=models.CASCADE, null=True, blank=True)
USERNAME_FIELD = 'userID'
objects = userCouncil()
def __str__(self):
return self.userID
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True
Admin
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import Account, sni
class AccountAdmin(UserAdmin):
ordering = ['userID']
list_display = ('userID', 'is_staff', 'is_admin')
search_fields = ['userID']
readonly_fields = ('id','userID')
filter_horizontal = ()
list_filter = ()
fieldsets = ()
admin.site.register(sni)
admin.site.register(Account, AccountAdmin)

Manager isn't available; 'auth.User' has been swapped for 'diabetes.UserSignupModel'

I have made a custom user model using AbstractBaseUser, BaseUserManager and a signup form using that model. but whenever I try to load the signup form it gives me an error. my app name is diabetes. I have added the app is installed app in settings.py also added the AUTH_USER_MODEL='diabetes.UserSignupModel'. i can manually add data in the database.
models.py
from django.db import models
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class SignUpManager(BaseUserManager):
def create_user(self, email, username, age, name, password=None):
if not email:
raise ValueError("insert user")
if not username:
raise ValueError("insert username")
if not name:
raise ValueError("insert name")
if not age:
raise ValueError("insert age")
user = self.model(
email=self.normalize_email(email),
username=username,
age=age,
name=name,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, username, age, name, password):
user = self.create_user(
email=self.normalize_email(email),
username=username,
password=password,
age=age,
name=name,
)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class UserSignupModel(AbstractBaseUser):
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
age = models.CharField(max_length=15)
name = models.CharField(max_length=15)
username = models.CharField(max_length=15, unique=True)
date_joined = models.DateTimeField(verbose_name="date joined", auto_now_add=True)
last_login = models.DateTimeField(verbose_name="last login", auto_now=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
USERNAME_FIELD = "username"
REQUIRED_FIELDS = ["email", "name", "age"]
objects = SignUpManager()
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True
#migrate --run-syncdb in case no work
admin.py
from django.contrib import admin
# Register your models here.
from diabetes.models import UserSignupModel
admin.site.register(UserSignupModel)
forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from diabetes.models import UserSignupModel
class UserSignupForm(UserCreationForm):
email=forms.EmailField(max_length=50,help_text="add a valid email address")
name=forms.CharField(max_length=50,help_text="add a valid name")
age=forms.IntegerField()
class meta:
model=UserSignupModel
fields=("email","username","name","age","password1","password2")
In UserSignupForm, it's class Meta: not class meta: and make sure when referring to your user model, you are using settings.AUTH_USER_MODEL.
from django.conf import settings
class UserSignupForm(UserCreationForm):
email=forms.EmailField(max_length=50,help_text="add a valid email address")
name=forms.CharField(max_length=50,help_text="add a valid name")
age=forms.IntegerField()
class Meta:
model=settings.AUTH_USER_MODEL
fields=("email","username","name","age","password1","password2")

Custom user model - 'Group' object has no attribute 'user_set'

I am fairly new to Django and set up a custom user model with token authentication. Now I am trying to add the possibility of adding users to user groups from the backend in order to specify permissions. However, I dont seem to be able to get past this error. I am using Python 3.7 and Django 2.2.6. Appreciate your help!
The error:
File "/Users/.../Dev/.../account/models.py", line 114, in save_m2m
self.instance.user_set.set(self.cleaned_data['users'])
AttributeError: 'Group' object has no attribute 'user_set'
my models.py:
from django.db import models
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
from django import forms
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.contrib.auth.models import Group
class MyAccountManager(BaseUserManager):
def create_user(self, email, username, password=None):
if not email:
raise ValueError("Users must have an email address")
if not username:
raise ValueError("Users must have a username")
user = self.model(
email=self.normalize_email(email),
username=username,
)
group = Group.objects.all()
user.groups.add(group)
# group.user_set.add(user)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, username, password):
user = self.create_user(
email=self.normalize_email(email),
password=password,
username=username,
)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name='email', max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
date_joined = models.DateTimeField(
verbose_name='date joined', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
plan_choices = [("P", "Pro"), ("B", "Basic"),
("F", "Free"), ("S", "Staff"), ("A", "Admin")]
plan = models.CharField(choices=plan_choices, default="F", max_length=1)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = MyAccountManager()
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
# if new user is registered to the server, a new token is generated
Token.objects.create(user=instance)
User = get_user_model()
# Create ModelForm based on the Group model.
# class GroupAdminForm(forms.ModelForm):
# class Meta:
# model = Group
# exclude = []
# Add the users field.
# users = forms.ModelMultipleChoiceField(
# queryset=User.objects.all(),
# required=False,
# widget=FilteredSelectMultiple('users', False)
# )
# def __init__(self, *args, **kwargs):
# super(GroupAdminForm, self).__init__(*args, **kwargs)
# if self.instance.pk:
# self.fields['users'].initial = self.instance.user_set.all()
# def save_m2m(self):
# self.instance.user_set.set(self.cleaned_data['users'])
# def save(self, *args, **kwargs):
# instance = super(GroupAdminForm, self).save()
# self.save_m2m()
# return instance
my admin.py:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from account.models import Account
from django.contrib.auth.models import Group
# from .models import GroupAdminForm
class AccountAdmin(UserAdmin):
list_display = ('email', 'username', 'date_joined',
'last_login', 'is_admin', 'is_staff', 'plan')
search_fields = ('email', 'username')
readonly_fields = ('date_joined', 'last_login')
filter_horizontal = ()
list_filter = ()
fieldsets = ()
admin.site.register(Account, AccountAdmin)
# Unregister the original Group admin.
# admin.site.unregister(Group)
# class GroupAdmin(admin.ModelAdmin):
# form = GroupAdminForm
# filter_horizontal = ['permissions']
# admin.site.register(Group, GroupAdmin)
UPDATE
I adjusted the code to no longer overwrite the Group class, and instead extend PermissionsMixin in my Account class. This allowed me to see permission groups and specific groups on a per user basis, however when these are applied, there seems to be no effect at all. I still have the same access as before. What am I missing?
group = Group.objects.all() is a queryset. user_set can be accessed from a Group instance.
Also, add the possibility of adding users to user groups from the backend - you don't need to do that. Once the user is created, you can always add the user to a particular group by:
groups = Group.objects.filter(...) # get groups you want to add the user to
user.groups.add(*groups)

Passwords are not hashed with Django as well as some missing columns

I'm not sure why my passwords are not hashed when creating a new custom user (email as username) using Django Rest Framework
This is what I see in postgres. Not sure why my admin/staff/active columns aren't showing up either even after migrating
models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class UserManager(BaseUserManager):
def create_user(self, email, password=None, first_name=None, last_name=None, is_active=True, is_staff=False, is_admin=False):
if not email or not first_name or not last_name:
raise ValueError('Email is required')
if not password:
raise ValueError('Password is required')
if not first_name:
raise ValueError('First name is required')
if not last_name:
raise ValueError('Last name is required')
user_object = self.model(
email=self.normalize_email(email),
first_name=first_name,
last_name=last_name,
active=is_active,
staff=is_staff,
admin=is_admin,
)
user_object.set_password(password) # change password
user_object.save(self._db)
return user_object
def create_staff_user(self, email, first_name, last_name, password=None):
staff_user_object = self.create_user(email, first_name, last_name, password, is_staff=True)
return staff_user_object
def create_superuser(self, email, first_name, last_name, password=None):
super_user_object = self.create_user(email, first_name, last_name, password, is_staff=True, is_admin=True)
return super_user_object
class User(AbstractBaseUser):
email = models.EmailField(unique=True, max_length=255)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
date_joined = models.DateTimeField(auto_now_add=True) # joined timestamp
is_active = models.BooleanField(default=True) # Can login?
is_staff = models.BooleanField(default=False) # staff user, non super user
is_admin = models.BooleanField(default=False) # super user?
objects = UserManager()
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = [] # email and passwords are required by default
settings.py
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', # Using Bcrypt to store passwords
views.py
class UserList(generics.ListCreateAPIView):
""" List all Users, or create a new User. """
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
""" Retrieve, update or delete a User instance. """
queryset = User.objects.all()
serializer_class = UserSerializer
]
urls.py
urlpatterns = [
path('accounts/', views.UserList.as_view()),
path('accounts/<int:pk>/', views.UserDetail.as_view()),
]
serializer.py
from rest_framework import serializers
from .models import User
from properties.models import Property
class UserSerializer(serializers.ModelSerializer):
properties = serializers.PrimaryKeyRelatedField(many=True, queryset=Property.objects.all())
class Meta:
model = User
fields = '__all__'
The DRF serializer calls the default create() method of the model and it won't call the .set_password() method. So, You have to call the method explicitly in your create() method of UserSerializer
class UserSerializer(serializers.ModelSerializer):
# other code
def create(self, validated_data):
password = validated_data.pop('password', None)
user_instance = super().create(validated_data)
if password:
user_instance.set_password(password)
user_instance.save()
return user_instance

DjangoRestFramework - How to update fields in model

I am using Django user model and also extended with my own profile model for some other data.When i want to update user data which is in Profile Model It doesn't get updated because all the user id,username,email,password reside in user model while the fields which are needed to get updated are in profile model.I have used this approach, all it does is takes inputs and displays the response but does not show any change in User Data when viewing it as a whole.
models.py
class Profile(models.Model):
user = models.OneToOneField(User,related_name='profile',on_delete=models.CASCADE)
location = models.CharField(max_length=30,blank=True)
friends_count = models.PositiveIntegerField(default=0)
profile_pic = models.FileField(upload_to='profile_pics/',blank=True,null=True)
def natural_key(self):
return (self.user.username,)
views.py
class UserCreateAPIViewSet(viewsets.ModelViewSet,mixins.UpdateModelMixin):
""""A View which handles Creating and Updating User Profile"""
serializer_class = UserProfileCreateSerializer
queryset = User.objects.all()
authentication_classes = (TokenAuthentication,)
permission_classes = (permissions.UpdateOwnProfile,)
filter_backends = (filters.SearchFilter,)
search_fields = ('username','email',)
class UserUpdateAPI(generics.GenericAPIView,mixins.UpdateModelMixin):
"""Update User Profile Data"""
permission_classes = (permissions.UpdateOwnProfile,)
authentication_classes = (TokenAuthentication,)
queryset = Profile.objects.all()
serializer_class = ProfileUpdateSerializer
def put(self,request,*args,**kwargs):
return self.partial_update(request,*args,**kwargs)
urls.py
url(r'^user-update/(?P<pk>\d+)/$',views.UserUpdateAPI.as_view(),name="user-update"),
router = DefaultRouter()
router.register(r'profile',views.UserCreateAPIViewSet)
serializers.py
class UserProfileCreateSerializer(serializers.ModelSerializer):
""""A serializer for user data request"""
location = serializers.CharField(source='profile.location')
friends_count = serializers.IntegerField(source='profile.friends_count',read_only=True)
profile_pic = serializers.FileField(source='profile.profile_pic',allow_empty_file=True,allow_null=True)
class Meta:
model = User
fields = (
'pk',
'username',
'email',
'password',
'location',
'friends_count',
'profile_pic',
)
extra_kwargs = {
'password':{'write_only':True},
'friends_count':{'read_only':True},
}
def create(self, validated_data):
""""Create and return a new User"""
user = User(
email = validated_data['email'],
username = validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
return user
class ProfileUpdateSerializer(serializers.ModelSerializer):
"""A serializer for updating user data"""
class Meta:
model = Profile
fields = ('location','profile_pic')
Looks like you want to extend from the AbstractBaseUser (see code below), using this as your UserProfileCreateSerializer:
class UserProfileCreateSerializer(serializers.ModelSerializer):
""""A serializer for user data request"""
location = serializers.CharField(source='profile.location')
friends_count = serializers.IntegerField(source='profile.friends_count',read_only=True)
profile_pic = serializers.FileField(source='profile.profile_pic',allow_empty_file=True,allow_null=True)
class Meta:
model = Profile
fields = (
'pk',
'username',
'email',
'password',
'location',
'friends_count',
'profile_pic',
)
extra_kwargs = {
'password':{'write_only':True},
'friends_count':{'read_only':True},
}
def create(self, validated_data):
""""Create and return a new User"""
user = Profile(
email = validated_data['email'],
username = validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
return user
class ProfileUpdateSerializer(serializers.ModelSerializer):
"""A serializer for updating user data"""
class Meta:
model = Profile
fields = ('location','profile_pic')
Then extend from the AbstractBaseUser in models.py:
from __future__ import unicode_literals
from django.db import models
from django.core.mail import send_mail
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.base_user import AbstractBaseUser
from django.utils.translation import ugettext_lazy as _
from .managers import ProfileManager
class Profile(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
username = models.CharField(_('first name'), max_length=30, blank=True)
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
date_joined = models.DateTimeField(_('date joined'), auto_now_add=True)
is_active = models.BooleanField(_('active'), default=True)
location = models.CharField(max_length=30,blank=True)
friends_count = models.PositiveIntegerField(default=0)
profile_pic = models.FileField(upload_to='profile_pics/',blank=True,null=True)
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def get_full_name(self):
'''
Returns the first_name plus the last_name, with a space in between.
'''
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
'''
Returns the short name for the user.
'''
return self.first_name
def natural_key(self):
return (self.username,)
def email_user(self, subject, message, from_email=None, **kwargs):
'''
Sends an email to this User.
'''
send_mail(subject, message, from_email, [self.email], **kwargs)
And then create a file called managers.py in the same directory as models.py:
from django.contrib.auth.base_user import BaseUserManager
class ProfileManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
"""
Creates and saves a User with the given email and password.
"""
if not email:
raise ValueError('The given email must be set')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email, password=None, **extra_fields):
extra_fields.setdefault('is_superuser', False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(email, password, **extra_fields)
To wrap all this up you need to add this to your settings.py file:
AUTH_USER_MODEL = 'app.Profile'
And also don't forget to re-run all database migrations before using $ python manage.py runserver.

Categories