Customize Create funciton in DRF viewset - django

I want to customize the user viewset i.e. when the user registers the user account was created as well.
im doing this :
class CustomUserCreate(APIView):
permission_classes = [AllowAny]
def post(self, request):
serializer = RegisterUserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
if user:
profile_serializer = ProfileShortSerializer(data = request.data)
profile = profile_serializer.save()
return Response(status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)```
is this the right approch?

Related

Django rest framework custom permission for ViewSet

this is my modelViewSet
class UserViewSet(viewsets.ModelViewSet):
def list(self, request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
def create(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
pass
def retrieve(self, request, pk):
user = get_object_or_404(User, pk=pk)
serializer = UserSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK)
def get_permissions(self):
if self.action == "list":
permission_classes = [
IsAdminUser,
]
elif self.action == "create":
permission_classes = [AllowAny]
else:
permission_classes = [AccountOwnerPermission]
return [permission() for permission in permission_classes]
and this is the custom permission class
class AccountOwnerPermission(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
print(object)
print(request.user)
return obj == request.user
i access this view from another user and it show me user retrieve, and that 2 prints on
AccountOwnerPermission won't run. can someone tell me what is wrong with what i did and why has_object_permission wont run.
i change has_object_permission to has_permission and it works, but i dont have access to obj on the other hand
From the docs:
If you're writing your own views and want to enforce object level permissions, or if you override the get_object method on a generic view, then you'll need to explicitly call the .check_object_permissions(request, obj) method on the view at the point at which you've retrieved the object.
So you'll need to call check_object_permissions in your retrieve to be able to trigger has_object_permission:
def retrieve(self, request, pk):
user = get_object_or_404(User, pk=pk)
self.check_object_permissions(request, user) # Add this line
serializer = UserSerializer(user)
return Response(serializer.data, status=status.HTTP_200_OK)

How to add extra actions for endpoint routing in django rest frameworks

I'm new to django rest framework, and I'm trying to learn drf. I didn't know how to achieve this features. I want some things like(Extra Actions) as shown in screenshot to navigate all the endpoint.
Here is an example from official DRF documentation:
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = User.objects.all()
serializer_class = UserSerializer
#action(detail=True, methods=['post'])
def set_password(self, request, pk=None):
user = self.get_object()
serializer = PasswordSerializer(data=request.data)
if serializer.is_valid():
user.set_password(serializer.data['password'])
user.save()
return Response({'status': 'password set'})
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
#action(detail=False)
def recent_users(self, request):
recent_users = User.objects.all().order_by('-last_login')
page = self.paginate_queryset(recent_users)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(recent_users, many=True)
return Response(serializer.data)
For more, please visit: Viewset actions

Django Rest FrameWork Add Model With User Foreign Key

I'm using Django version 3 and the Rest Framework, i have a model with a user foreignkey.
so every time a model is saved the user also need to be saved.
To be able to add or to edit a model via rest you need to be authenticated using token authentication.
I'm using ClassBasedViews, the problem is that i can't find a way to add a model because in my serializer the field user is excluded because i don't want it to be editable.
models.py:
class Chambre(models.Model):
local_id=models.PositiveIntegerField(unique=True)
nom=models.CharField(max_length=255)
user=models.ForeignKey(User,on_delete=models.CASCADE,blank='true')
class Meta:
unique_together = ('local_id', 'user',)
serializers.py:
class ChambreSerializer(serializers.ModelSerializer):
class Meta:
model = Chambre
exclude =['user',]
views.py:
class ChambreListApi(APIView):
"""
List all chambres, or create a new chambre.
"""
authentication_classes=(TokenAuthentication,)
permission_classes=(IsAuthenticated,)
def get(self, request, format=None):
chambres = Chambre.objects.filter(user=request.user)
serializer = ChambreSerializer(chambres, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = ChambreSerializer(data=request.data)
if serializer.is_valid():
serializer.save(commit=False)
serializer.user=request.user
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Try moving the logic that sets the user to the serializer. By default GenericAPIView will pass the request to the serializer via the context.
class ChambreSerializer(serializers.ModelSerializer):
class Meta:
model = Chambre
exclude =['user',]
def create(self, validated_data):
validated_data["user"] = self.context["request"].user
return super().create(validated_data)
When I followed the accepted solution posted by schillingt, I had to add context={'request': request} when I instantiated the serializer. The Django REST Framework docs mention adding context to a serializer here.
serializers.py
class NoteCreateSerializer(serializer.ModelSerializer):
class Meta:
model = Note
exclude = ['owner',]
def create(self, validated_data):
validated_data['owner'] = self.context['request'].user
return super().create(validated_data)
views.py
#api_view['POST']
def create_note(request):
note_serializer = NoteCreateSerializer(
data = request.data,
context = {'request': request} # this will populate 'self.context'
# in the serializer instance
)
# ...
Thanks so much for this solution, though! It ended an hours-long struggle.
thanks for the reply that was very useful, i've solved it by adding method to serializer.py like this :
class ChambreSerializer(serializers.ModelSerializer):
class Meta:
model = Chambre
exclude =['user',]
def set_the_user(self,request):
self.user=request.user
def create(self, validated_data):
validated_data["user"] = self.user
return super().create(validated_data)
and in the views.py :
def post(self, request, format=None):
serializer = ChambreSerializer(data=request.data)
serializer.set_the_user(request)
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)

Django REST framework: restricting user access for objects

I'm trying to build a REST API for books:
/api/book
/api/book/{book_id}
A user should have access to his books only. The way I'm doing this now is by filtering the result using username i.e Book.objects.all().filter(owner=request.user)
views.py
class Book_List(APIView):
permission_classes=(permissions.IsAuthenticated)
def get(self, request, format=None):
**books= Book.objects.all().filter(owner=request.user)**
serializer = BookSerializer(books, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = BookSerializer(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)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
#/book/{pk}
class Book_Detail(APIView):
permission_classes = (permissions.IsAuthenticated)
def get_object(self, pk, request):
try:
return Book.objects.get(pk=pk, owner=request.user)
except Playlist.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
book = self.get_object(pk, request)
serializer = BookSerializer(playlist, context={'request': request})
return Response(serializer.data)
def put(self, request, pk, format=None):
book= self.get_object(pk)
serializer = BookSerializer(playlist, data=request.data, context={'request': request})
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
book= self.get_object(pk)
book.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
serializers.py
class BookSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Book
fields = ('url', 'owner','title', 'created_date', 'shared_with', 'tracks')
class UserSerializer(serializers.HyperlinkedModelSerializer):
books = serializers.HyperlinkedRelatedField(many=True,view_name='playlist-detail', read_only=True)
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = User
fields = ('url', 'username', 'owner', 'books')
But is this the correct way?
Does Django Rest Framework provide any in-built solution for this?
Does the solution lie in permissions? If yes, then how do we set it for all objects created by a user (I understand the for getting a particular object we can put a permission check like obj.user==request.user). Am I right?
You could use the ModelViewset, which contains all the logic for the typical CRUD:
class BooksViewSet(ModelViewset):
serializer_class = BookSerializer
permission_classes=[permissions.IsAuthenticated, ]
def get_queryset(self):
return Books.objects.filter(owner=self.request.user)
def perform_create(self, serializer):
serializer.data.owner = self.request.user
super(BooksViewSet, self).perform_create(serializer)

DjangoRestFramework - How to pass a serializer form to the frontend?

I'm using the DjangoRestFramework. I have a UserSerialzer in my serializers.py file:
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'password', 'email', )
This is my urls.py file:
urlpatterns = [
url(r'^$', views.HomePageView.as_view()),
url(r'^users$', views.user_list.as_view()),
url(r'^users/(?P<pk>[0-9]+)$', views.user_detail.as_view()),
]
and this is my views.py file:
class HomePageView(TemplateView):
template_name = "home.html"
def get_context_data(self, **kwargs):
context = super(HomePageView, self).get_context_data(**kwargs)
# context['users'] = User.objects.all()
return context
class user_list(APIView):
"""
List all users, or create a new user.
"""
serializer_class = UserSerializer
def get(self, request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
def post(self, request):
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 user_detail(APIView):
"""
Get, update or delete a specific user.
"""
serializer_class = UserSerializer
def get_object(self, pk):
try:
return User.objects.get(pk=pk)
except User.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
def get(self, request, pk):
user = self.get_object(pk)
serializer = UserSerializer(user)
return Response(serializer.data)
def put(self, request, pk):
user = self.get_object(pk)
serializer = UserSerializer(user, data=request.DATA)
if serialzier.is_valid():
serializier.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
user = self.get_object(pk)
user.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
When I go to 127.0.0.1:8000/users, DjangoRestFramework has the API page which shows a list of users (JSON objects) and it also has a form which has "username", "password" and "email". This form seems to be validating correctly (checks if the email is a real email, and checks if username is unique and less than 30 characters). Is there a way for me to pass this form to the frontend when a user goes to 127.0.0.1:8000 (calling HomePageView)?
I'm in the process of using AngularJS on the frontend (not sure if this information helps or not).
Well here are a few things I think we might need to point out. Django forms are normally what you would use to create a new user, with a post request just like you are trying to do through DRF. Now you can do this through DRF, but thats not really what it is for, django forms would be more appropriate for what you are doing. Unless of course you are building an API that you want API users to be able to use to create new users on your platform, in which case continue onward. I am linking a tutorial I used when I first started using DRF with Angular, maybe you will find it helpful.
http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html