class Admin(models.Model):
username = models.CharField(primary_key=True, max_length=30)
password = models.CharField(max_length=255)
email = models.EmailField(unique=True)
created_on = models.DateTimeField(auto_now=True)
django_user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='admin')
class AdminAPIViewSet(viewsets.ModelViewSet):
queryset = Admin.objects.all()
serializer_class = AdminSerializer
permission_classes = [permissions.IsAdminUser]
def get_queryset(self):
if self.request.user.is_authenticated:
return Admin.objects.filter(username=self.request.user.admin.username)
else:
return []
def create(self, request, *args, **kwargs):
serializer = AdminSerializer(data=request.data)
if serializer.is_valid():
email = serializer.data['email']
username = serializer.data['email']
password = serializer.data['password']
with transaction.atomic():
django_user = User.objects.create_user(username, email, password)
admin = Admin.objects.create(**serializer.data, django_user=django_user)
#User.objects.filter(pk=1001).update(is_superuser=True, is_staff=True)
return Response(admin.pk)
return Response('/error')
class ClientFullAccessAPIViewSet(viewsets.ModelViewSet):
queryset = Client.objects.all()
serializer_class = ClientSerializer
permission_classes = [permissions.IsAdminUser]
def create(self, request, *args, **kwargs):
serializer = ClientSerializer(data=request.data)
if serializer.is_valid():
email = serializer.data['email']
username = serializer.data['email']
password = serializer.data['password']
with transaction.atomic():
django_user = User.objects.create_user(username, email, password)
client = Client.objects.create(**serializer.data, django_user=django_user)
return Response(client.username)
return Response('/error')
`Here am trying to make the admin see the all the clients and the client see his data only ,... but I couldn't find why the i cant see the all the list clients as an admin, I am keep getting not authorized to access this endpoint..
`
urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework import routers
import user_management.views
router = routers.DefaultRouter()
router.register(r'clients', user_management.views.ClientReadOnlyAPIViewSet)
router.register(r'clientslist', user_management.views.ClientFullAccessAPIViewSet)
router.register(r'admin', user_management.views.AdminAPIViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
path('api-auth/', include('rest_framework.urls')),
path('api/v1/', include(router.urls)),
#path('clients/', user_management.views.ClientAPIViewSet.as_view(), name="clients"),
]
`Here am trying to make the admin see the all the clients and the client see his data only ,... but I couldn't find why the i cant see the all the list clients as an admin, I am keep getting not authorized to access this endpoint..any help please?
The problem is that you defined your Admin model from a regular Django model so Django cannot find the user permissions associated to it.
You should inherit from a Django authentication user model (AbstractUser or AbstractBaseUser) as indicated in the documentation.
For instance, you can do:
from django.contrib.auth.models import AbstractUser
class CustomAdminUser(AbstractUser):
# Here you normally only want to
# define the fields not defined in
# the base model AbstractUser
pass
Then, to create your admin:
CustomAdminUser.objects.create_superuser(...)
Last but not least, two important things (mentioned in the above Django documentation's link):
Don’t forget to point the AUTH_USER_MODEL to your custom user model. Do this before creating any migrations or running manage.py migrate for the first time.
Register the model in the app’s admin.py.
Related
I am using the rest-auth module to enable user authentication on my web app. Though I am facing some difficulties in fetching details about the user. The Django-rest-framework return a key when I post my username and password, while that's enough for logging in I also want to fetch additional details like user.is_staff, user.username and user.email.
I tried to use Token serializer, but I am not sure if I am doing it right.
** settings.py **
REST_AUTH_SERIALIZERS = {
'TOKEN_SERIALIZER': '## How to define the path to my serializer ##',
}
** serializers.py **
from rest_framework import serializers
from lms.models.post_models import Post
from django.contrib.auth.models import User
from rest_auth.models import TokenModel
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'email')
class TokenSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = TokenModel
fields = ('key', 'user')
Please tell what piece is missing or if any piece is incorrect. Also, please help me figure out the part between ## ##.
Thank you!
I think you are doing it right, in your custom TokenSerializer you need to fetch the user somehow. If I look at the code of the LoginView, I see that you can use request object from context within serializer, so your TokenSerializer should be like:
# serializers.py
class TokenSerializer(serializers.ModelSerializer):
user = serializers.SerializerMethodField()
class Meta:
model = TokenModel
fields = ('key', 'user')
def get_user(self, instance):
request = self.context.get('request')
return UserSerializer(request.user).data
and then settings.py
REST_AUTH_SERIALIZERS = {
'TOKEN_SERIALIZER': 'project.serializers.TokenSerializer',
}
EDITED:
This might break your register view because if you look at the source code, at line 60 it uses the same serializer but doesn't pass the request object in the context of serializer. You can make it work by overriding this method
# views.py
from django.conf import settings
from rest_auth.registeraion.views import RegisterView
from allauth.account import app_settings as allauth_settings
from rest_auth.app_settings import (TokenSerializer,
JWTSerializer)
class CustomRegisterView(RegisterView):
def get_response_data(self, user):
if allauth_settings.EMAIL_VERIFICATION == \
allauth_settings.EmailVerificationMethod.MANDATORY:
return {"detail": _("Verification e-mail sent.")}
if getattr(settings, 'REST_USE_JWT', False):
data = {
'user': user,
'token': self.token
}
return JWTSerializer(data).data
else:
return TokenSerializer(user.auth_token, context={"request": self.request}).data
and then use this view for registration in your urls
# urls.py
from views import CustomRegisterView
urlpatterns = [
...,
url(r'^rest-auth/', include('rest_auth.urls')),
url(r'^rest-auth/registration/', CustomRegisterView.as_view())
]
Scenario:
I have two APIs currently. A RegisterAPI and a LoginAPI. The RegisterAPI registers a new user and a LoginAPI allows them to access their details only. I used JWT authentication as well.
I want to retrieve data from the database for a particular user who logs in using the Login API in DRF. My intention is to interact with the database but the problem is that I have two models in my serializer with a One-To-One relationship and I am confused about how to move forward with this issue. To get a clearer picture, view my code below:
models.py:
from django.db import models
from django.contrib.auth.models import User
class UserData(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
company = models.CharField(max_length=100, blank=True, null=True)
address = models.TextField()
views.py:
class RegisterAPI(APIView):
permission_classes = [AllowAny]
def post(self, request, format=None):
serializer = UserDataSerializer(data=request.data)
if serializer.is_valid():
content = {
'status': 'Thanks for registering'}
return Response(content, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class LoginAPI(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
user_id = request.user.id
queryset = User.objects.filter(user = request.user)
user_serializer = UserSerializer(queryset, many = True)
return Response(user_serializer.data)
serializers.py:
from rest_framework import serializers
from users.models import UserData
from django.contrib.auth.models import User
class UserDataSerializer(serializers.ModelSerializer):
class Meta:
model = UserData
fields = ('company', 'address')
class UserSerializer(serializers.ModelSerializer):
info = UserDataSerializer()
class Meta:
model = User
fields = ('username', 'email', 'password', 'info')
def create(self, validated_data):
p_data = validated_data['info']
password = validated_data['password', None]
user = User.objects.create(**validated_data)
if password is not None:
user.set_password(password)
user.save()
UserData.objects.create(user = user, **p_data)
return validated_data
urls.py:
from rest_framework.routers import DefaultRouter
from users.views import LoginAPI, RegisterAPI
router = DefaultRouter()
router.register('login_api', LoginAPI, basename = 'login_api')
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(router.urls)),
path('register_api/', RegisterAPI.as_view(), name = 'register_api'),
]
I want to return the user's Username, Email, Company and Address within the LoginAPI. These are from both the User and Profile models.
Example:
{'username' = 'Tim',
'email' = 'tim#company.com',
'company' = 'Riko',
'address' = 'Kenya'}
However, I am not able to retrieve the records from the User model and Profile model when accessing the Login API URL.
I am getting this error:
AttributeError at /login_api/
Got AttributeError when attempting to get a value for field `info` on serializer `UserSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.
Original exception text was: 'User' object has no attribute 'info'.**
Can anyone explain to me what went wrong?
DRF serializers subclasses Field (serializers.Field), so you can use the source argument to refer the attribute which should be used to resolve the value of the field (UserDataSerializer in this case):
class UserSerializer(serializers.ModelSerializer):
info = UserDataSerializer(source='userdata')
# ^^^^ Here
...
The error you're getting is pretty self-explanatory. You're passsing a User instance to the UserSerializer but you have a field defined as info. As the User instance does not have such attribute, it shows the error. So you need some way to tell the serializer the value of the field, using source to refer an existing attribite is one way of doing that.
I have an app that contains a model UserProfile()
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
city = models.CharField(max_length=50, default='')
phone = models.IntegerField(default='0')
image = models.ImageField(upload_to='profile_image', blank=True)
def __str__(self):
return self.user.username
connecting to a default user(User). I wanted to connect my user with a wishlist model
class WishList(models.Model):
toy_name_wish = models.ForeignKey(Toy, on_delete=models.CASCADE)
user_wish = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
def __str__(self):
return self.user_wish
And using generic view with def post(self, request): I created simple logic for a toy that will be shown in admin part as a user's wish item
class DetailToyView(TemplateView):
template_name = 'app/detail_toy.html'
#other defs
def post(self, request, toy_id):
toy = get_object_or_404(Toy, pk=toy_id)
user_profile = UserProfile()
wishlist = WishList()
try:
selected_toy = get_object_or_404(Toy, pk=toy_id)
except(KeyError, Toy.DoesNotExist):
return render(request, 'app/detail_toy.html', {'toy': toy})
else:
user_profile.user = self.request.user
user = user_profile.user
wishlist.toy_name_wish = toy
wishlist.user_wish = user
wishlist.save()
return HttpResponseRedirect(reverse('app:detail-category-toy', args=(toy.id,)))
If it's important here's my urls.py file
from django.urls import path
from django.conf.urls import url
from . import views
from django.contrib.auth import views as auth_views
app_name = 'app'
urlpatterns = [
path('', views.index, name='index'), # INDEX
path('personal-page/', views.personal_page, name='personal-page'),
# SIGN_IN, SIGN_OUT AND SIGN_UP
path('sign-in/', auth_views.login,
{'template_name': 'app/sign_in.html'},
name='sign-in'),
path('sign-out/', auth_views.logout,
{'next_page': '/'},
name='sign-out'),
path('sign-up/', views.sign_up, name='sign-up'),
# DETAIL_PAGES
#url(r'^book-detail/(?P<book>[0-9]+)/$', views.detail_book, name='book'),
url(r'^detail_category_toy/(?P<category_id>[0-9]+)/$', views.detail_category_toy, name='detail-category-toy'),
url(r'^detail-toy/(?P<toy_id>[0-9]+)/$', views.DetailToyView.as_view(), name='toy')]
So here is the problem when I click on the button I'm getting an error
ValueError at /detail-toy/2/
Cannot assign "<SimpleLazyObject: <User: admin>>": "WishList.user_wish" must be a "UserProfile" instance.
This means I cannot use user.username
So how do I get UserProfile instance instead of the basic User model?
P.S: Sorry for some stupid-called variables
Well, your immediate problem is that you are setting a User to the WishList object rather than a UserProfile. The line before the save should be:
wishlist.user_wish = user_profile
But really there are a lot of odd things going on here. A user can only have a single UserProfile, which sounds right, but in this view you always create a new one; if that user already has a profile, this will cause an error. And your WishList model is not really a list, but a single relationship between a profile and a toy.
What you actually need here is a many-to-many relationship between UserProfile and Toy, which is the wishlist:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
city = models.CharField(max_length=50, default='')
phone = models.IntegerField(default='0')
image = models.ImageField(upload_to='profile_image', blank=True)
wishlist = models.ManyToManyField('Toy')
(and you don't need the WishList model at all)
And in your view, use get_or_create to either get the existing profile or create a new one:
def post(self, request, toy_id):
toy = get_object_or_404(Toy, pk=toy_id)
user_profile = get_or_create(UserProfile, user=request.user)
user_profile.wishlist.add(toy)
return HttpResponseRedirect(reverse('app:detail-category-toy', args=(toy.id,)))
I am stuck with the JWT, can any one tell me how can I get user ID along with token.
My url.py :
from rest_framework_jwt.views import obtain_jwt_token
from scrumboard.views import UserDetail
from scrumboard import views
urlpatterns = [
url(r'^api-token-auth/', obtain_jwt_token),
url(r'^users/$', views.UserList.as_view()),
]
views.py:
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
serializers.py:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', 'password')
The problem is that when i go to api-token-auth/, its only giving me the username and password and not the id.
You can get the token, userid and username by following these steps:
Inside your app, create a utils.py file (at the same level where serializers.py is placed).
Content of utils.py:
from .serializers import UserSerializer #you have already created UserSerializer
def jwt_response_payload_handler(token, user=None, request=None):
user = UserSerializer(user, context={'request': request}).data
return {
'token': token,
'userid': user['id'],
'username':user['username']
}
In your settings.py, add
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER':
'app_name.utils.jwt_response_payload_handler', #app_name is name of the app which contains utils.py
}
Now when you hit the api (api-token-auth/) to get the token, it will respond with token, userid and username.
Example of response:
{
'token':'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImdhbmVzaG5lZ2lAZ21haWwuY29tIiwiZXhwIjoxNTI0NTAyNzIzLCJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImdhbmVzaG5lZ2kifQ.RxnYF1gwaYywSnnWC-ngrcZaw90lpD6Rq7jcbnEoZVk',
'userid':1,
'username':'ganeshnegi'
}
According to the django-rest-framework-jwt docs this view only returns the username and password (emphasis mine).
In your urls.py add [url(r'^api-token-auth/', obtain_jwt_token)] to enable obtaining a token via a POST included the user's username and password.
I wouldn't worry about not obtaining the id, you should be able to query the User table by username.
I am working on building a react native app with a django rest framework backend. I have created the registration ViewSet and have been able to successfully register a user from the front end using the following:
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSeralizer
I have created another ViewSet to verify the email and password of the user as follows:
class LoginViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = LoginSeralizer
#detail_route(['POST'])
def login_user(self,request, pk=None):
if request.method == "POST":
email = request.POST.get("email", False)
password = request.POST.get("password", False)
user = authenticate(email=email, password=password)
if user is not None:
if user.is_active:
return Response(user.data, status=status.HTTP_200_OK)
else:
return Response(user.error, status=status.HTTP_400_BAD_REQUEST)
else:
return Response(user.error, status=status.HTTP_400_BAD_REQUEST)
The detail_route method is not being called when I try to access the url from the front end. I get a bad response 400 error saying that the user has already been created. Am I on the right track? What changes do I make to the detail_route method so it would be called from the front end when the url is passed into the fetch method of the react native app?
The rest of the files in the django project are as follows:
The url.py:
from django.contrib import admin
from django.conf.urls import url, include
from rest_framework import routers
router = routers.DefaultRouter()
#makes sure that the API endpoints work
router.register(r'api/users', views.UserViewSet)
router.register(r'api/login', views.LoginViewSet)
admin.autodiscover()
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),]
The serializer.py:
class UserSeralizer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('username', 'email', 'password')
class LoginSeralizer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('email', 'password')
You have defined LoginViewSet/LoginSeralizer and UserViewSet/UserSeralizer practically the same. When you login you actually create user.
Read about authentication methods with REST framework:
http://www.django-rest-framework.org/api-guide/authentication/