I'm creating a Django API with DRF. I have a custom User Model like this :
models.py :
from typing import Type, List
from uuid import uuid4
from django.db import models
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.base_user import AbstractBaseUser
from django.utils.translation import ugettext_lazy as _
from django_extensions.db.models import TimeStampedModel
from main.apps.user.managers import UserManager
def to_profile_picture(instance: "User", filename: str) -> str:
return f"profile_pictures/{instance.email}/{uuid4()}_{filename}"
class User(AbstractBaseUser, TimeStampedModel, PermissionsMixin):
email = models.EmailField(_("Email Address"), unique=True)
first_name = models.CharField(
_("First Name"), max_length=128, blank=True, null=True
)
last_name = models.CharField(_("Last Name"), max_length=128, blank=True, null=True)
display_name = models.CharField(_("Display Name"), max_length=128)
is_active = models.BooleanField(_("Active"), default=True)
is_staff = models.BooleanField(_("Is staff"), default=False)
profile_picture = models.ImageField(
upload_to=to_profile_picture, null=True, blank=True
)
objects: Type[UserManager] = UserManager()
USERNAME_FIELD: str = "email"
REQUIRED_FIELDS: List[str] = []
class Meta:
verbose_name: str = _("User")
verbose_name_plural: str = _("Users")
#property
def get_full_name(self) -> str:
return f"{self.first_name} {self.last_name}"
managers.py :
from django.contrib.auth.base_user import BaseUserManager
class UserManager(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: str, password: str = None, **extra_fields):
extra_fields.setdefault("is_superuser", False)
extra_fields.setdefault("is_staff", False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault("is_superuser", True)
extra_fields.setdefault("is_staff", 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)
viewsets.py :
from typing import Type
from rest_framework import viewsets, status
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.decorators import action
from main.apps.user.models import User
from main.apps.user.serializers import UserSerializer
class UsersViewset(viewsets.ModelViewSet):
def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
if self.action == "create":
permission_classes = [AllowAny]
else:
permission_classes = [IsAuthenticated]
return [permission() for permission in permission_classes]
queryset = User.objects.all()
serializer_class: Type[UserSerializer] = UserSerializer
def create(self, request, *args, **kwargs):
serializer: UserSerializer = UserSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = User.objects.create_user(
serializer.validated_data.pop("email"),
serializer.validated_data.pop("password"),
**serializer.validated_data,
)
return Response(status=status.HTTP_201_CREATED, data=UserSerializer(user).data)
#action(detail=False)
def me(self, request: Request):
return Response(
status=status.HTTP_200_OK, data=UserSerializer(request.user).data
)
serializers.py :
from typing import Type, Tuple, Dict, Any
from rest_framework import serializers
from main.apps.user.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model: Type[User] = User
fields: Tuple[str] = (
"id",
"email",
"password",
"display_name",
"first_name",
"last_name",
"profile_picture",
)
extra_kwargs: Dict[str, Any] = {"password": {"write_only": True}}
My issue here is that when I go to the route I defined for the UserViewset and trigger the list action, it returns the full URL with the http://domain/ in front of the file path, which is actually exactly what I want. But on the /me route or the create route, only the path is returned, without the domain.
I'm thinking it might have something to do with the context that is not passed to the serializer in custom routes, but might be in the default ModelViewset routes.
If anyone knows how I can replicate the list action behavior on my other routes, I'll be glad !
Okay, found out that it was what I thought, I just needed to pass the request as context to my serializer.
Change the method me in file viewsets.py from that :
#action(detail=False)
def me(self, request: Request):
return Response(
status=status.HTTP_200_OK, data=UserSerializer(request.user).data
)
to this :
#action(detail=False)
def me(self, request: Request):
return Response(
status=status.HTTP_200_OK,
data=UserSerializer(request.user, context={"request": request}).data,
)
Related
I am new to django need a bit of help in testing a particular section of the user model.
Heres the error showing while running the test
These are my codes
models.py
from django.utils.translation import ugettext_lazy as _
from django.db import models
from django.utils.crypto import get_random_string
from django.db import models
from django.contrib.auth.models import(
BaseUserManager,
AbstractBaseUser,
PermissionsMixin,
)
def generate_vid():
"""Generates a vid for the users"""
not_unique = True
while not_unique:
vid = get_random_string(10, 'abcdefg0123456789')
if not User.objects.filter(v_id = vid).exists():
not_unique=False
return vid
class UserManager(BaseUserManager):
"""Model for user manager"""
def create_user(self, username, password=None, **params):
"""Create and return a user"""
user = self.model(username=username, **params)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, password, **params):
"""Create and return a user"""
params.setdefault('is_staff',True)
params.setdefault('is_superuser',True)
params.setdefault('is_active',True)
if params.get('is_staff') is not True:
raise ValueError(_('Superuser must have is_staff=True.'))
if params.get('is_superuser') is not True:
raise ValueError(_('Superuser must have is_superuser=True.'))
return self.create_user(username, password, **params)
class User(AbstractBaseUser, PermissionsMixin):
"""Models for user"""
v_id = models.CharField(
max_length=10,
default=generate_vid,
primary_key = True,
)
username = models.CharField(max_length=20, unique=True)
email = models.EmailField(blank=True, unique = True)
# parent_id = models.ForeignKey('User', on_delete=models.SET_DEFAULT, default=0)
usertype = models.CharField(max_length=1, choices=[('f', 'family'), ('v', 'veteran')])
REQUIRED_FIELDS = []
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
USERNAME_FIELD = 'username'
objects = UserManager()
def __str__(self):
return self.username
views.py
from base.models import (
User,)
from .serializers import (
UserSerializer,)
from rest_framework import viewsets
class UserViewset(viewsets.ModelViewSet):
"""Views for user model"""
serializer_class = UserSerializer
queryset = User.objects.all()
Serializer
from django.contrib.auth import get_user_model
from base.models import (
User,
)
from rest_framework import serializers
class UserSerializer(serializers.Serializer):
"""Serializer for our models"""
class Meta:
model = get_user_model()
fields = ['username', 'password']
extra_kwargs = {'password': {'write_only': True, 'min_length': 5}}
def create(self, validated_data):
return get_user_model().objects.create_user(**validated_data)
urls.py
"""
URL mappings for the user API
"""
from rest_framework import routers
from django.urls import path, include
from . import views
router = routers.DefaultRouter()
router.register(r'user', views.UserViewset)
app_name='user'
urlpatterns = [
path('', include(router.urls)),
]
The test which I created to check the model manager(i.e. creating a user using User.objects.create() works fine ...
But when i tried creating an api and test the user api , using APIClient the post wasn't working..
test code
from django.urls import reverse
from django.test import TestCase
from rest_framework.test import APIClient
from rest_framework import status
CREATE_USER_URL = reverse('user:user-list')
class PublicUserTests(TestCase):
"""Tests for user API"""
def setUp(self):
self.client = APIClient()
def test_create_user_success(self):
"""Test creating a user is succesful"""
payload = {
'username': 'testusername',
'password': 'testpas123',
}
res = self.client.post(CREATE_USER_URL, payload)
# self.assertEqual(res, status.HTTP_201_CREATED)
self.assertEqual(res.status_code, status.HTTP_201_CREATED)
Kindly help.
Your serializer should be a ModelSerializer instead of a basic serializer.
Only the ModelSerializer will add fields based on the Meta attributes based on the model.
https://www.django-rest-framework.org/api-guide/serializers/
I'm newbie with django. I want to create an login, signup API so I find a solution on Internet. But it's not user my own User model, it use django.contrib.auth.models import AbstractUser. I don't need some field of AbtractUser so I give it =none.
Here is my models code:
from django.db import models
from django.contrib.postgres.fields import ArrayField
from django.contrib.postgres.fields import JSONField
from django.contrib.auth.models import AbstractUser
# Create your models here.
# class User(models.Model):
# name = models.CharField(null=False,blank=False, max_length=512)
# username = models.CharField(null=False,blank=False, max_length=512, unique=True)
# email = models.EmailField(null=False,blank=False, max_length=512, unique=True)
# password = models.CharField(null=False,blank=False, max_length=512)
# status = models.CharField(null=True,blank=True, max_length=512)
# role = models.IntegerField(null=False, blank=False, default=1)
# USERNAME_FIELD = 'username'
# REQUIRED_FIELDS = []
# def __str__(self):
# return self.username
class User(AbstractUser):
last_login = None
is_staff = None
is_superuser = None
first_name = None
last_name = None
name = models.CharField(null=False,blank=False, max_length=512)
username = models.CharField(null=False,blank=False, max_length=512, unique=True)
email = models.EmailField(null=False,blank=False, max_length=512, unique=True)
status = models.CharField(null=True,blank=True, max_length=512)
role = models.IntegerField(null=False, blank=False, default=1)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []
def __str__(self):
return self.username
class Category(models.Model):
title = models.TextField(null=False, blank=False)
description = models.TextField(null=False, blank=False)
class Question(models.Model):
title = models.TextField(null=False, blank=False)
description = models.TextField(null=False, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
choices = models.JSONField(null=False, blank=False)
answer = models.TextField(null=False,blank=False)
level = models.IntegerField(null=True, blank=True)
But I can't login to my Django Admin with my Superuser. I create new superuser but I got an error TypeError: User() got an unexpected keyword argument 'is_staff'
My command to create superuser: python manage.py createsuperuser
I don't understand why my model can affect to my app superuser. Anyone can explain for me and give me some solution.
Here is my code in serializer:
from django.contrib.postgres import fields
from rest_framework import serializers
from app.models import User, Question, Category
class RegistrationSerializer(serializers.ModelSerializer):
email = serializers.EmailField(max_length=50, min_length=6)
username = serializers.CharField(max_length=50, min_length=6)
password = serializers.CharField(max_length=150, write_only=True)
class Meta:
model = User
fields = ['id', 'name','email', 'username', 'password', 'role']
def validate(self, args):
email = args.get('email', None)
username = args.get('username', None)
if User.objects.filter(email=email).exists():
raise serializers.ValidationError({'email': ('Email already exists')})
if User.objects.filter(username=username).exists():
raise serializers.ValidationError({'username': ('Username already exists')})
return super().validate(args)
# def create(self, validated_data):
# return User.objects.create_user(**validated_data)
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ['id', 'name', 'username', 'email', 'role']
class CategorySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Category
fields = ['id', 'title', 'description']
class QuestionSerializer(serializers.ModelSerializer):
# category = CategorySerializer(read_only=False)
class Meta:
model = Question
fields = ['id', 'description', 'category', 'choices', 'answer', 'level']
Here is my code in views:
from django.db.models import query
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework import generics
from rest_framework import viewsets
from rest_framework import serializers
from rest_framework import permissions
import uuid
from app.models import User, Category, Question
from app.serializers import UserSerializer, QuestionSerializer, CategorySerializer, RegistrationSerializer
from django.shortcuts import get_object_or_404
# Create your views here.
class RegistrationAPIView(generics.GenericAPIView):
serializer_class = RegistrationSerializer
permission_classes = (permissions.IsAuthenticated,)
def post(self, request):
serializer = self.get_serializer(data = request.data)
if(serializer.is_valid()):
serializer.save()
return Response({
"RequestId": str(uuid.uuid4()),
"Message": "User created successfully",
"User": serializer.data}, status=status.HTTP_201_CREATED
)
return Response({"Errors": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
class ListUsersAPIView(APIView):
permission_classes = (permissions.IsAuthenticated,)
def get(self, request):
listUser = User.objects.all()
info = UserSerializer(listUser, many = True)
return Response(data = info.data, status = status.HTTP_200_OK)
# def post(self, request, format=None):
# serializer = UserSerializer(data=request.data)
# if serializer.is_valid():
# serializer.save()
# return Response(serializer.data, status=status.HTTP_201_CREATED)
# return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class DetailUsersAPIView(APIView):
permission_classes = (permissions.IsAuthenticated,)
def get(self,request, id):
try:
user = get_object_or_404(User, pk=id)
data = UserSerializer(user).data
return Response(data, status = status.HTTP_200_OK)
except:
return Response("Error",status = status.HTTP_404_NOT_FOUND)
def patch(self, request, id):
user = get_object_or_404(Users, pk=id)
serializer = UserSerializer(user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class CategoryViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = CategorySerializer
queryset = Category.objects.all()
class QuestionViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = QuestionSerializer
queryset = Question.objects.all()
Thanks very much.
You will need to define your own UserManager [Django-doc] to register the user without making use of is_staff, is_superuser, etc. This manager is used to create user objects. Since you have set fields like is_staff, is_superuser, etc. to None, these are no longer model fields.
Therefore you should implement your own user manager that will no longer try to set these fields in the user model. So this means it looks like:
from django.contrib.auth.models import UserManager
class CustomUserManager(UserManager):
def create_user(self, username, email=None, password=None, **extra_fields):
return self._create_user(username, email, password, **extra_fields)
def create_superuser(self, username, email=None, password=None, **extra_fields):
return self._create_user(username, email, password, **extra_fields)
This UserManager will not add is_staff and/or is_superuser to the fields that are used. You thus assign the CutomUserManager as objects manager to the user model:
class User(AbstractUser):
# ⋮
objects = CustomUserModel()
I am creating a cross-platform application in which the users are - Admin, Instructor, and Student. And for registering a new user privilege is only for admin users, so an admin user can add another admin, instructor, and student. I am trying to make nested serializers by which a UserProfile (AdminProfile, InstructorProfile, or StudentProfile) when created also creates User. I am new to Django and DRF. How can this be done?
serializers.py
from rest_framework import serializers
from .models import User, ProfileAdmin
# # This will have GET method for seeing the users according to the userType.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'password', 'user_type']
extra_kwargs = {'password': {'write_only': True}}
class UserProfileSerializer(serializers.ModelSerializer):
user_account = UserSerializer(many=False)
class Meta:
model = ProfileAdmin
fields = ['email', 'first_name', 'last_name', 'address', 'telNum', 'aliasMailID', 'MSteamsID', 'user_account']
def create(self, validated_data):
account_data = validated_data.pop('user_account')
user = ProfileAdmin.objects.create(**validated_data)
User.objects.create(**account_data)
return user
def update(self, instance, validated_data):
account_data = validated_data.pop('user_account')
user_account = instance.user_account
instance.email = validated_data.get('email', instance.email)
instance.first_name = validated_data.get('first_name', instance.first_name)
instance.last_name = validated_data.get('last_name', instance.last_name)
instance.address = validated_data.get('address', instance.address)
instance.telNum = validated_data.get('telNum', instance.telNum)
instance.aliasMailID = validated_data.get('aliasMailID', instance.aliasMailID)
instance.MSteamsID = validated_data.get('MSteamsID', instance.MSteamsID)
instance.save()
user_account.username = account_data.get('username', user_account.username)
user_account.user_type = account_data.get('user_type', user_account.user_type)
return instance
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser, PermissionsMixin, UserManager, AbstractBaseUser
from django.conf import settings
from phonenumber_field.modelfields import PhoneNumberField
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.utils.translation import gettext_lazy as _
class CustomAccountManager(UserManager):
def _create_user(self, username, password, user_type, **extra_fields):
if not username:
raise ValueError(_('The Username must be set'))
if not password:
raise ValueError('The password must be set')
if not user_type:
raise ValueError('The user type must be set')
username = self.model.normalize_username(username)
user = self.model(username=username, user_type=user_type, **extra_fields)
user.set_password(password)
user.save()
return user
def create_user(self, username, password, user_type, **extra_fields):
if user_type == 'STUDENT' or user_type == 'INSTRUCTOR':
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(username, password, user_type, **extra_fields)
def create_superuser(self, username, password, user_type, **extra_fields):
# if is_userType == 'Admin'
if user_type == 'ADMIN':
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(username, password, user_type, **extra_fields)
class User(AbstractBaseUser, PermissionsMixin):
USER_TYPE_CHOICES = (
('ADMIN', 'admin'),
('INSTRUCTOR', 'instructor'),
('STUDENT', 'student'),
)
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user can log into this admin site.'),
)
user_type = models.CharField(max_length=40, choices=USER_TYPE_CHOICES)
username = models.CharField(max_length=30, unique=True)
password = models.CharField(_('password'), max_length=128)
objects = CustomAccountManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['password', 'user_type']
class ProfileAdmin(User, models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
email = models.EmailField(verbose_name="email", max_length=85, unique=True)
aliasMailID = models.EmailField(verbose_name="aliasMailID", max_length=85, unique=True, blank=False)
address = models.TextField()
MSteamsID = models.EmailField(verbose_name="MSteamsID", max_length=85, unique=True, blank=False)
telNum = PhoneNumberField(null=False, blank=False, unique=True
views.py
class RegisterUserAPI(generics.GenericAPIView):
serializer_class = UserProfileSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
return Response({
"user": UserProfileSerializer(user, context=self.get_serializer_context()).data,
})
urls.py
from django.conf.urls import url, include
from django.urls import path
from .views import RegisterUserAPI
urlpatterns = [
path('users/', RegisterUserAPI.as_view(), name="userRegister"),
]
When I run it, I have access to http://localhost:8000/api/users and can see the form to fill the required field, but as soon as I hit POST I get a TypeError:
TypeError at /api/users/
User() got an unexpected keyword argument 'user'
Request Method: POST
Request URL: http://127.0.0.1:8000/api/users/
Django Version: 3.0
Exception Type: TypeError
Exception Value:
User() got an unexpected keyword argument 'user'
Exception Location: C:\PyCharm\MobileLearningApplication\venv\lib\site-packages\django\db\models\base.py in __init__, line 500
Python Executable: C:\PyCharm\MobileLearningApplication\venv\Scripts\python.exe
Python Version: 3.8.9
Python Path:
['C:\\PyCharm\\MobileLearningApplication\\MobileLearningApplication',
'C:\\Python\\python38.zip',
'C:\\Python\\DLLs',
'C:\\Python\\lib',
'C:\\Python',
'C:\\PyCharm\\MobileLearningApplication\\venv',
'C:\\PyCharm\\MobileLearningApplication\\venv\\lib\\site-packages']
Server time: Sat, 9 Oct 2021 09:37:44 +0000
Git Repo
I'm a beginner in the Django. I tried to implement JWT inside Python but when I add a new user I get an error, which is as follows.
django.db.utils.OperationalError: table sampleapp_userprofile has no column named email
I have created the email field and also put the default value in the email field, and I still get this error .. I have provided the git repository and given some code below, please help.
models.py
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
# Create your models here.
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,
)
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 UserProfile(AbstractBaseUser):
email = models.EmailField(verbose_name="email",max_length=60, unique=True,default='')
username = models.CharField(max_length=30,default='Null')
password = models.CharField(verbose_name='password',max_length=16)
last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
objects = MyAccountManager()
USERNAME_FIELD = 'username'
EmailField = 'email'
REQUIRED_FIELDS = ['username']
def __str__(self):
return self.email
views.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status, generics
from rest_framework_simplejwt.views import TokenObtainPairView
from .serializers import *
from rest_framework.decorators import api_view
# Create your views here.
class LoginView(TokenObtainPairView):
"""
Login View with jWt token authentication
"""
serializer_class = MyTokenObtainPairSerializer
class registrationView(APIView):
def post(self,request,format=None):
if request.method == 'POST':
serializer = RegistrationSerializer(data=request.data)
data = {}
if serializer.is_valid():
account = serializer.save()
data['response'] = 'successfully registered new user.'
else:
data = serializer.errors
return Response(data)
serializers.py
from rest_framework import serializers, status
from django.contrib.auth.models import User
from django.contrib.auth import authenticate
from .models import UserProfile
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
print('in')
user = authenticate(username=attrs['username'], password=attrs['password'])
if user is not None:
if user.is_active:
data = super().validate(attrs)
refresh = self.get_token(self.user)
refresh['username'] = self.user.username
try:
obj = UserProfile.objects.get(user=self.user)
refresh['employeeRole'] = obj.employeeRole
data["refresh"] = str(refresh)
data["access"] = str(refresh.access_token)
data["employee_id"] = self.user.id
data['user_name']= self.user.username
data["employeeRole"] = obj.employeeRole
data['first_name']= self.user.first_name
data['last_name']= self.user.last_name
except Exception as e:
raise serializers.ValidationError('Something Wrong!')
return data
else:
raise serializers.ValidationError('Account is Blocked')
else:
raise serializers.ValidationError('Incorrect userid/email and password combination!')
class RegistrationSerializer(serializers.ModelSerializer):
email = serializers.EmailField(style={'input_type': 'email'})
username = serializers.CharField(min_length=1)
class Meta:
model = UserProfile
fields = ['email','username', 'password']
extra_kwargs = {
'password': {'write_only': True,'min_length':8},
}
def save(self):
account = UserProfile(
email=self.validated_data['email'],
username=self.validated_data['username'],
password=self.validated_data['password']
)
account.save()
return account
I'm experiencing a problem with Django users and the Django REST Framework.
In my settings I have defined a custom user model:
AUTH_USER_MODEL = 'api.User'
In my sandbox.api package I have defined this model in the models.py file:
from django.db import models
from django.core.exceptions import ValidationError
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import UserManager
from django.contrib.auth.models import PermissionsMixin
from django.utils.translation import ugettext_lazy as _
from django.core import validators
from django.core.mail import send_mail
from django.utils import timezone
from django.core.exceptions import NON_FIELD_ERRORS
from random import randrange
class User(AbstractBaseUser, PermissionsMixin):
"""
Custom user class
"""
username = models.CharField(_('username'), max_length=20, unique=True,
help_text=_('Required. 30 characters or fewer. Letters and digits only.'),
validators=[
validators.RegexValidator(r'^[a-z0-9A-Z]{1,20}$', _('Enter a valid username.'), 'invalid')
])
email = models.EmailField(_('email address'), max_length=60, blank=False, null=False, unique=True)
is_staff = models.BooleanField(_('staff status'), default=False,
help_text=_('Designates whether the user can log into this admin '
'site.'))
is_active = models.BooleanField(_('active'), default=True,
help_text=_('Designates whether this user should be treated as '
'active. Unselect this instead of deleting accounts.'))
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
def get_full_name(self):
return self.username
def get_short_name(self):
return self.username
def email_user(self, subject, message, from_email=None, **kwargs):
send_mail(subject, message, from_email, [self.email], **kwargs)
def clean(self):
errors = {}
id = self.id
if not id:
id = 0
n = len(list(User.objects.raw('SELECT id FROM auth_user WHERE LOWER(username) = LOWER(%s) AND id != %s', [self.username, id])))
if n > 0:
errors['username'] = 'User with this username already exists.'
n = len(list(User.objects.raw('SELECT id FROM auth_user WHERE LOWER(email) = LOWER(%s) AND id != %s', [self.email, id])))
if n > 0:
errors['email'] = 'User with this e-mail address already exists.'
if len(errors) > 0:
raise ValidationError(errors)
def save(self, *args, **kwargs):
self.username = self.username.lower()
self.email = self.email.lower()
super(User, self).save(*args, **kwargs)
def __unicode__(self):
return u'%s' % self.username
class Meta:
db_table = 'auth_user'
verbose_name = _('user')
verbose_name_plural = _('users')
abstract = False
And in my sandbox.api package I have defined a serializer in the serializers.py file:
class UserSerializer(ExtSerializer):
"""
User serializer
"""
name = serializers.SerializerMethodField('get_name')
description = serializers.SerializerMethodField('get_description')
url = serializers.SerializerMethodField('get_url')
location = serializers.SerializerMethodField('get_location')
facebook_id = serializers.SerializerMethodField('get_facebook_id')
twitter_id = serializers.SerializerMethodField('get_twitter_id')
path = serializers.SerializerMethodField('get_path')
feed = serializers.HyperlinkedIdentityField(lookup_field='username', view_name='user-feed-collection')
timeline = serializers.HyperlinkedIdentityField(lookup_field='username', view_name='user-timeline-collection')
followers = serializers.HyperlinkedIdentityField(lookup_field='username', view_name='user-follower-collection')
following = serializers.HyperlinkedIdentityField(lookup_field='username', view_name='user-following-collection')
posts_count = serializers.SerializerMethodField('get_posts_count')
followers_count = serializers.SerializerMethodField('get_followers_count')
following_count = serializers.SerializerMethodField('get_following_count')
def get_path(self, user):
request = self.context['view'].request
return reverse_lazy('user-singleton', kwargs={
"username": user.username
}, request=request, format=FORMAT_SUFFIX)
def get_facebook_id(self, user):
return user.profile.facebook_id
def get_twitter_id(self, user):
return user.profile.twitter_id
def get_posts_count(self, user):
return Post.objects.filter(author__pk=user.pk).count()
def get_following_count(self, user):
return Follower.objects.filter(follower=user.pk).count()
def get_followers_count(self, user):
return Follower.objects.filter(following=user.pk).count()
def get_name(self, user):
return user.profile.name
def get_description(self, user):
return user.profile.description
def get_url(self, user):
return user.profile.url
def get_location(self, user):
return user.profile.location
class Meta:
model = User
fields = ('path', 'id', 'username', 'name', 'description', 'url', 'location', 'is_active',
'facebook_id', 'twitter_id', 'email',
'feed', 'timeline', 'following', 'followers',
'posts_count', 'following_count', 'followers_count')
I'm 100% that the URL bindings are correct for the API. When I try to GET a list of users I get the following error however:
Manager isn't available; User has been swapped for 'api.User'
Does anybody have any idea about what's going on here? I'm using Django 1.7.1 on Python 2.7.6.
Thanks in advance for any help!
Kind regards,
K.
Instead of referring to User directly, you should reference the user
model using django.contrib.auth.get_user_model(). This method will
return the currently active User model – the custom User model if one
is specified, or User otherwise.
Kinda late but nonetheless...