Pass custom context to Django model - django

I'm using django-rest-framework.
All models in my app contain User field and I want to write to this field link to current user.
How can I pass user object to model?
I've tired to write User link in SerializerClass, but I think it's not the best solution.
In view:
def perform_create(self, serializer):
serializer.save(created_by=self.user)
Model:
class Tracker(models.Model):
serial_id = models.PositiveIntegerField(unique=True)
created_by = models.ForeignKey(MyUser)

You dont have to. DRF can do that for you.
As here.
Since you haven't shared your complete code I am inclined to share a VERY SIMPLE implementation without writing a lot of code yourself and relying on DRF's ability is:
from rest_framework import generics
class MyView(generics.CreateAPIView):
queryset = Tracker.objects
serializer_class = TrackerSerializer #Assuming that this is your serializer
def post(self , request , *args , **kwargs):
return self.create(request , *args , **kwargs)
DRF will itself take care of the relationships. This code works assuming that you have authenticaton set up and thus request.user is not an Anonymous User.

To grab the logged in user from a generic view, you should use self.request.user:
In view:
def perform_create(self, serializer):
serializer.save(created_by=self.request.user)

Related

In Django REST Framework, how to filter list based on user?

I have two models, User and Book. Users own books and can only be seen by their owners.
NOTE: the book model is handled in a separate database, so I can't use a foreign key on Book pointing to User. Not sure if this matters.
If I'm authenticated, and send a GET /books request, I want only the books owned by the user to be shown. If I'm not authenticated, I should get a 403 error.
Where should I implement this logic?
I could do it in the View, with something like this:
class BookView(APIView):
"""
Get books
"""
permission_classes = (IsAuthenticated, IsBookOwner,)
queryset = Book.objects.all()
serializer_class = BookSerializer
def post(self, request):
# create a book
def get(self, request):
books = Book.objects.filter(owner_id=request.user.owner_id)
serializer = self.serializer_class(books, many=True)
return Response(serializer.data)
class IsBookOwner(permissions.BasePermission):
"""
Object-level permission to only allow seeing his own books
"""
def has_object_permission(self, request, view, obj):
# obj here is a Book instance
return obj.owner_id == request.user.owner_id
Is this the correct way to do it? Also, is the IsBookOwner permission doing anything here?
User model dont have owner_id field. You should change request.user.owner_id to request.user.id
For get request you dont need IsBookOwner Permission. You already check owner in your queryset. if you need to check book owner entire view, it is okay.
books = Book.objects.filter(owner_id=request.user.owner_id)

make view accessible to only specific users (i.e. who created that model) in django rest

I have one model which has user as its ForeignKey attribute which is auto fill ie. logged in user is filled there. I have made token authentication. Only Authenticated // i mean authorized users can visit that view. But i am planning to make such that only the user which had created that model object can only update the content of that object.
For example:
class Something(models.Model):
sth_name = models.CharField(max_length=18)
sth_qty = models.IntegerField()
user = models.ForeignKey(User)
on my View:
I override perform_create() to associate to above model automaticall.
def perform_create(self, serializer):
return serializer.save(user=self.request.user)
What do i exactly need to do? I have to write some permissions method, But I am really stuck.
Yes, you need to create an object level permission. The DRF tutorial covers this nicely here: http://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/#object-level-permissions
Specifically, create a file permissions.py in your app, and add this permission there:
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit it.
"""
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.user == request.user
Then, in your view class which has the update resource for the Something model (probably SomethingDetail), add the permission_classes field:
class SomethingDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Something.objects.all()
serializer_class = SomethingSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
Just add the user when retrieving the object
obj = get_object_or_404(Something, pk=pk, user=request.user)
Note that this will throw 404. If you want 403 error, use custom condition to check the user and raise PermissionDenied. If you want to do this for multiple views, put the condition logic in a decorator.

How to make a request in DRF that points to another API endpoint in same Django app

very quick question that is how to nest request in Django-rest-framework. I have end point A that I make POST on and want to make another request to point B in it's serializer perform_create method. This API end points are actually written in same Django application.
Serializer for API A
class ReadingCreate(CreateAPIView):
permission_classes = [IsOwnerOrReadOnly]
serializer_class = ReadingCreateSerializer
def perform_create(self, serializer):
#HERE I WANT TO MAKE REQUEST TO POINT B
serializer.save(user_profile= UserProfile.objects.get(user=self.request.user))
I am familiar with library such as request but I hope there is a better way since, I also need to send token for authentication and I am like in same file. This problem seems simple but I clearly don't know how to do it properly.
Update:
To explain more, "request" should find a book based on the isbn that I send it through ReadingCreateSerializer. But first I need to find a book (Google API), then save it to my DB. This needs to be done because book model is independent of UserProfile object and Reading is not (has additional information). That is what my "request" does.
Of course this could be done with two chain requests from client but I don't want that.
Serializer:
class ReadingCreateSerializer(serializers.HyperlinkedModelSerializer):
isbn = serializers.CharField(required=True, max_length=20)
class Meta:
model = Reading
fields = ['isbn', 'notes', 'rating', 'start_reading_date', 'end_reading_date']
What I tried based on the answer: part of view and part of serializer
def perform_create(self, serializer):
self.request.method = 'POST'
serializer.save(user_profile=UserProfile.objects.get(user=self.request.user), request=self.request)
def save(self, **kwargs):
isbn = self.validated_data['isbn']
request = kwargs.get("request", {})
request.data = {'isbn': isbn}
post_book(request)
What I found is that I can't import views (in my example post_book) to serializers I guess that is not allowed by Django.
This will execute your API class.
APIClassB.as_view()(self.request)
If you need to change request method
self.request.method = 'POST'
APIClassB.as_view()(self.request)

django rest framework cannot POST data

I implemented a basic rest api with the django rest framework. It works perfectly using the browsable api or communicating to it with requests. Next step would be submitting data to the rest api.
Here is what I have done so far.
settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.AllowAny',),
'PAGINATE_BY': 10
}
[UPDATE:]
models.py
class Request(models.Model):
name = models.TextField()
def save(self, *args, **kwargs):
super(Request, self).save(*args, **kwargs) # Call the "real" save() method.
serializers.py
class RequestSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Request
fields = ('id', 'name')
views.api
class RequestsViewSet(viewsets.ModelViewSet):
queryset = Request.objects.all()
serializer_class = RequestSerializer
Using the browsable api I see that those are the options supported:
Allow: GET, HEAD, OPTIONS
Obviously, POST (and also PUT) is missing.
What I am doing wrong?
Thanks!
Solved it by adding the post method to the modelviewset (in the view):
def post(self, request, format=None):
...
Thanks for helping!
Well, I think you only need to call save method on the model object to persist the object in the database.
First, import model to the view, instantiate a model object in the view, then call save method on the newly created object. If you have model connected to the backend, that will persist your changes.
models.py
class YourModel(models.Model):
name = models.CharField()
views.py
from models import YourModel
def yourView(request):
yourObject = YourModel(name='John')
yourObject.save()
...
Check also Django documentation for models here

Django Rest Framework bulk updates inserting instead of updating

I'm trying to build out a bulk update view for a specific model using Django Rest Framework. In the short term, it only needs to update one field (toggling an invite from submitted=False to submitted=True), but I'd like it to be able to provide more functionality in the future. Whenever I test the view, however, a new object is being created instead of the current one being modified.
I feel like this must be a simple mistake on my part, but I can't figure out what's going on. The serializer object appears to be ignoring the value for "id" passed in through JSON, which may be contributing to the issue. Current code is:
class InviteBulkUpdateView(generics.UpdateAPIView):
def get_queryset(self):
order = self.kwargs['order']
invite = get_objects_for_user(self.request.user, 'sourcing.view_invite')
return invite.filter(order=order)
serializer_class = InviteInputSerializer
def put(self, request, *args, **kwargs):
data = request.DATA
serializer = InviteInputSerializer(data=data, many=True)
if serializer.is_valid():
serializer.save()
return Response(status=status.HTTP_200_OK)
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
class InviteInputSerializer(serializers.ModelSerializer):
class Meta:
model = Invite
fields = ('id', 'order', 'team', 'submitted')
Can anybody shed some light onto what I might be doing wrong?
Just in case somebody is looking for a library to handle this, I wrote a Django-REST-Framework-bulk which allows to do that in a couple of lines (the example only does bulk update but the library also allows bulk create and delete):
from rest_framework_bulk import ListCreateBulkUpdateAPIView
class FooView(ListCreateBulkUpdateAPIView):
model = FooModel
You're not passing object instances to your serializer. (Thus it will create new instances rather than update.) See the docs on dealing with multiple objects in serializers where you'll see your QuerySet passed in.
Django has update method to handle that. You may want to read full info from django documentation.
Here is a sample code where you can use to update given field for multiple records:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.exceptions import APIException
class Room_Update_ViewSet(APIView):
def put(self, request,*args, **kwargs):
hotel_id = self.kwargs.get('hotel_id')
room_ids = self.request.query_params.get('room_ids')
room_ids = list(map(int, room_ids.split(',')))
try:
Room.objects.filter(hotel_id=hotel_id,id__in=room_ids).update(booked_status=False)
instances = Room.objects.filter(hotel_id=hotel_id,id__in=room_ids)
serializer = RoomSerializer(instance=instances, many=True)
return Response(serializer.data,status=status.HTTP_200_OK)
except Exception as e:
print("Error udating rooms-->",e)
raise APIException