Create URL for detailview using DRF Modelviewset - django

i'm using DRF modelviewset to create an api for users model. Can anyone explain or help how can I pass a url in react-native for the detailview of user. Right now i'm getting the detailview in this manner 127.0.0.1:8000/users/users/2/. But everytime i don't want to pass the id in the url.
models.py
class User(AbstractUser):
"""This Class is used to extend the in-build user model """
ROLE_CHOICES = (('CREATOR','CREATOR'),('MODERATOR','MODERATOR'),('USERS','USERS'))
GENDER_CHOICES = (('MALE','MALE'),('FEMALE',"FEMALE"),('OTHER','OTHER'))
date_of_birth = models.DateField(verbose_name='Date of Birth', null=True)
profile_image = models.ImageField(upload_to='media/profile_images', verbose_name='Profile Image', default='media/profile_images/default.webp', blank=True)
bio = models.TextField(verbose_name='Bio')
role = models.CharField(max_length=10, verbose_name='Role', choices=ROLE_CHOICES, default='USERS')
gender = models.CharField(max_length=6, verbose_name='Gender', choices=GENDER_CHOICES)
serializers.py
class UserSerializer(serializers.ModelSerializer):
following = serializers.SerializerMethodField(read_only=True)
followers = serializers.SerializerMethodField(read_only=True)
class Meta:
model = User
fields = ('first_name','last_name','username','password','email','date_of_birth',
'profile_image','bio','role','gender', 'following','followers')
extra_kwargs = {'is_active':{'write_only':True},
'password':{'write_only':True}}
def create(self, validated_data):
logger.info('Information Stored!')
return User.objects.create_user(**validated_data)
def update(self, *args, **kwargs):
user = super().update( *args, **kwargs)
p = user.password
user.set_password(p)
user.save()
return user
views.py
class UserAPI(viewsets.ModelViewSet):
serializer_class = UserSerializer
# permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
def get_queryset(self):
users = User.objects.all()
return users
urls.py
router = DefaultRouter()
router.register('users', views.UserAPI, basename='users'),
router.register('following', views.FollowingAPI, basename='following'),
urlpatterns = router.urls
How can i solve this. Need your help please. Thank you

You can make use of #action decorator.
Give this a try:
class UserAPI(viewsets.ModelViewSet):
...
#action(detail=False)
def profile(self, request):
serializer = UserSerializer(request.user)
return Response(serializer.data)
Now go to 127.0.0.1:8000/users/profile/, you should see the current authenticated user's data.

this may be helpful.
You can use user = request.user.id , by in this way you can get current login user.

Related

I created an extra table extra table in one to one relation with User table. how to show phone field in User registration

I am trying to create a simple API to get a user register.
I am using the default User table for authentication purpose, created another table called "phone" with one to one relation with User.
I am trying to add "phone" field just above the password. (I hope the image attached is visible).
**
Serializer.py
class UserRegisterSerializer(serializers.ModelSerializer):
class Meta:
model = UserDetailsModel
fields = ('phone', 'user')
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(max_length=68, min_length=6, write_only=True)
class Meta:
model = User
fields = ('username','first_name', 'last_name','email','password')
read_only_fields = ('id',)
**
models.py<<
**
class UserDetailsModel(models.Model):
phone = models.IntegerField()
balance = models.DecimalField(max_digits=10, decimal_places=2, default=0)
user = models.OneToOneField(get_user_model(),primary_key='email' , on_delete=models.CASCADE)
def __str__(self):
return str(self.user)
**
views.py
**
class RegisterView(generics.GenericAPIView):
serializer_class = RegisterSerializer
def post(self, request):
user = request.data
serializer = self.serializer_class(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
user_data = serializer.data
return Response(user_data,status=status.HTTP_201_CREATED)
class DetailsRegisterView(generics.GenericAPIView):
serializer_class = UserRegisterSerializer
def post(self, request):
user = request.data
serializer = self.serializer_class(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
user_data = serializer.data
return Response(user_data,status=status.HTTP_201_CREATED)
**
urls
**
urlpatterns = [
path('',RegisterView.as_view()),
path('details', DetailsRegisterView.as_view())
]
**
You probably can use source in a serializer with a FK
class RegisterSerializer(...)
...
phone = serializers.CharField(..., source='userdetails.phone')
see also : the doc
I have some doubt in create case, in a update case this code work fine.
see also : How to serialize a relation OneToOne in Django with Rest Framework?
and an other way to resolve your issue : nested serializer
Updated code:
serializers>
from django.contrib.auth.models import User
from django.http import JsonResponse
from rest_framework import serializers
from .models import UserDetailsModel
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username','first_name', 'last_name','email','password')
read_only_fields = ('id',)
class UserRegisterSerializer(serializers.ModelSerializer):
user = RegisterSerializer(required=True)
class Meta:
model = UserDetailsModel
fields = ('phone','user')
def create(self, validated_data):
user_data = validated_data.pop('user')
user = RegisterSerializer.create(RegisterSerializer(), validated_data=user_data)
data, created = UserDetailsModel.objects.update_or_create(user=user,
phone=validated_data.pop('phone'))
return data
class DetailView(serializers.ModelSerializer):
user = RegisterSerializer(read_only=True)
class Meta:
model = UserDetailsModel
fields = ('user','phone')
Remaining code stays the same.

Django: How to add user(author of the POST request) before save in serializers?

I'm tried to add author of the request before saving it in serializers.py
And got error:
Cannot assign "<django.contrib.auth.models.AnonymousUser object at 0x0000029169C48040>": "Category_product.posted_user" must be a "User" instance.
models.py
class Category_product(models.Model):
category_name = models.CharField(max_length=200, unique=True)
posted_user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
def __str__(self):
return self.category_name
views.py
class Category_productDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Category_product.objects.all()
serializer_class = Category_productSerializer
serializers.py
class Category_productSerializer(serializers.ModelSerializer):
post_user = serializers.ReadOnlyField(
source='posted_user.username')
class Meta:
model = Category_product
fields = ['id', 'category_name','post_user']
def validate(self, data):
data['posted_user'] = self.context['request'].user
return data
Have you tried overriding the user in your view?
Assuming you have Authentacion set up, because if not everyone could get in and it won't be a user, it would be Anonymous User instance.
If that's not the case, you can try it out in the view with perform create:
class Category_productDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Category_product.objects.all()
serializer_class = Category_productSerializer
def get_user(self):
user = self.request.user
return user
def perform_create(self, serializer):
"""
Set the sender to the logged in user.
"""
serializer.save(posted_user=self.get_user())

How to retrieve all items created by a user using the django rest framework

I am trying to get only courses belonging to a particular user below I have the model, serializer and view I am using to try and achieve this. If I delete the entire get_queryset function from the view the api returns the appropriate user and every course created by every user. If get_queryset remains, the api always returns user not found and still gives every course that exists. Can anyone point me to how I can achieve my desired result.
view:
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsProfileOwnerOrReadOnly]
# queryset = User.objects.all()
serializer_class = UserSerializer
def get_queryset(self):
user = self.request.user
queryset = User.objects.all()
if user is not None:
queryset = queryset.filter(courses__owner_id=user.id)
return queryset
serializer
class UserSerializer(serializers.ModelSerializer):
courses = serializers.PrimaryKeyRelatedField(
many=True, queryset=Course.objects.all())
class Meta:
model = User
fields = ['id', 'username', 'courses']
Model
class Course (models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
pub_date = models.DateField(default=date.today)
owner = models.ForeignKey('auth.User', related_name='courses', on_delete=models.CASCADE)
def __str__(self):
return self.title
You need to filter objects by user
class CreatePostsView(viewsets.ModelViewSet):
model = Post
serializer_class = PostsSerializer
def get_queryset(self):
user = self.request.user
return Post.objects.filter(owner=user)
class CoursesByOwnerView(RetrieveModelMixin, GenericViewSet):
serializer_class = YourModelSerializer
authentication_classes =[TokenAuthentication,]
permission_classes = [IsAuthenticated,]
def list(self, request, *args, **kwargs):
course_taker = self.request.user
courses = YourModel.objects.filter(owner=course_taker).values('your_model_fields')
return Response(courses)
Given your answer in the comments:
Either you use self.request.user given by the authentication middleware. In this case, it will only work for authenticated users, and you can't see courses for another User.
Either you use the endpoint users/<int:pk>/ you mentioned. In this case, you can fetch the user with:
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsProfileOwnerOrReadOnly]
serializer_class = UserSerializer
def get_queryset(self):
return UserDetail.objects.filter(pk=self.kwargs["pk"])
See this thread if you need another example: Django 2.0 url parameters in get_queryset
EDIT: In both cases, change your UserSerializer with:
class UserSerializer(serializers.ModelSerializer):
courses = serializers.PrimaryKeyRelatedField(
many=True, read_only=True)
class Meta:
model = User
fields = ['id', 'username', 'courses']

How to edit user permission in Django Rest Framework

I am following the tutorial of django Rest Framework. I want to add user-based permission so that only authenticated user can view each user's detail information.
Objective : Anyone can view the UserList, but only owner can view its UserDetail.
models.py
class Meeting(models.Model):
created = models.DateTimeField(auto_now_add=True)
sinceWhen = models.DateTimeField(null=True)
tilWhen = models.DateTimeField(null=True)
owner = models.ForeignKey('auth.User', related_name='meetings', on_delete=models.CASCADE)
#highlighted = models.TextField()
def save(self, *args, **kwargs):
super(Meeting, self).save(*args, **kwargs)
class Meta:
ordering = ('created',)
views.py
from django.contrib.auth.models import User
# User is not created inside models.py
class UserList(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserListSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,)
# I added IsOwnerOrReadOnly to make it work, but this is the part where it causes error!
serializers.py
class UserSerializer(serializers.ModelSerializer):
meetings = serializers.PrimaryKeyRelatedField(many=True, queryset=Meeting.objects.all())
#owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = User
fields = ('id', 'username', 'meetings',)
class UserListSerializer(serializers.ModelSerializer):
#meetings = serializers.PrimaryKeyRelatedField(many=True, queryset=Meeting.objects.all())
class Meta:
model = User
fields = ('username',)
permissions.py
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# Any permissions are only allowed to the owner of the meeting
return obj.owner == request.user
I overrode IsOwnerOrReadOnly so that only user can view the details of his/her user detail.
And add this to permission_class in views.py.
Then I got this error :
File "/home/tony/env/lib/python3.6/site-packages/rest_framework/views.py" in check_object_permissions
345. if not permission.has_object_permission(request, self, obj):
File "/home/tony/swpp_hw1/meetings/permissions.py" in has_object_permission
15. return obj.owner == request.user
Exception Type: AttributeError at /users/1/
Exception Value: 'User' object has no attribute 'owner'
I tried to add User class in models.py, but again it causes error...
How can solve this issue?
Try to change it as:
return obj == request.user
as object is user you are trying to access and request.user is current authenticated user.

django rest api - adding users to keyword model

i want to implement a feature in my rest api that users can add specific keywords for a news feed.
so if the users make a post request with a keyword within, the user object will be added on the predefined keyword (predefined in the database).
I have tried it with this code, but always if i try to simulate the post request with postman and i have this problem:
the keyword will be added but not the provided json data, its just a empty string and the post request returns also an empty keyword...
I hope you are able to help me and maybe you could give me an advice how to just allow the static keywords which are already defined and allow user only have a keyword once (no double keywords with same value)
Made with this headers:
[{"key":"Content-Type","value":"application/json","description":""}]
[{"key":"Authorization","value":"Token xxxxxxx","description":""}]
Body:
{
"name": "keyword1"
}
Authorization works, so the user added to the empty keyword
I am very new to django and i am doing this project to improve my skills, so please be lenient to me :) So it could be that its completly wrong, please give me some advices to solve my problem
These are the snippets for the implementation:
models.py
class Keywords(models.Model):
name = models.CharField(max_length=200)
user = models.ManyToManyField(User)
def __str__(self):
return self.name
serializers.py
class KeywordSerializer(serializers.Serializer):
class Meta:
model = Keywords
fields = ('id', 'name', 'user')
def create(self, validated_data):
keyword = Keywords(**validated_data)
keyword.save()
keyword.user.add(self.context['request'].user)
return keyword
views.py
class KeywordAPI(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated, ]
serializer_class = KeywordSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
keyword = serializer.save()
return Response({
"name": KeywordSerializer(keyword, context=self.get_serializer_context()).data,
})
Try this snippet:
class KeywordSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Keywords
fields = '__all__'
def create(self, validated_data):
user = validated_data.pop('user')
kw = Keywords.objects.create(**validated_data)
kw.user.add(user)
kw.save()
return kw
and views:
from rest_framework import viewsets, permissions
class KeywordAPI(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated, ]
serializer_class = KeywordSerializer
queryset = Keywords.objects.all()
The input payload be as
{
"name":"kw1"
}
NOTE
Here I used ModelSerializer class, because it's very handy for CURD applications and HiddenField is something like write_only=True parameter for fields.
References:
DRF - Modelviewset
HiddenField
CurrentUserDefault
There are few things you are doing wrong
First
Your model's name is Keywords it shouldn't be plural use Keyword and user field is ManyToMany so you should pluralise it
class Keyword(models.Model):
name = models.CharField(max_length=200)
users = models.ManyToManyField(User)
def __str__(self):
return self.name
Second
You are using Serializer instead of ModelSerializer
class KeywordSerializer(serializers.ModelSerializer):
class Meta:
model = Keywords
fields = ('id', 'name', 'users')
read_only_fields = ('users',)
def create(self, validated_data):
keyword = super().create(validated_data)
keyword.users.add(self.context['request'].user)
return keyword
Third
You don't have to write creation logic yourself use existing mixins
from rest_framework import mixins
class KeywordAPI(mixins.CreateModelMixin, generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated, ]
serializer_class = KeywordSerializer
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
For those who maybe have the same problem, here is the whole solution:
Thanks to Sardorbek Imomaliev and Jerin Peter George for helping me
serializer:
class KeywordSerializer(serializers.ModelSerializer):
users = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Keyword
fields = '__all__'
def create(self, validated_data):
users = validated_data.pop('users')
if Keyword.objects.filter(**validated_data).exists():
kw = Keyword.objects.filter(**validated_data).get()
kw.users.add(self.context['request'].user)
return kw
else:
raise serializers.ValidationError("This Keyword has not been set yet.")
model:
class Keyword(models.Model):
name = models.CharField(max_length=200)
users = models.ManyToManyField(User)
def __str__(self):
return self.name
view:
class KeywordAPI(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated, ]
serializer_class = KeywordSerializer
queryset = Keyword.objects.all()