I have a Django rest api. In my model I have a user as a foreign key. When I do a post with the, I do not want that the user needs to provide his own user. But if the user does not provide his user credentials, the serializer won't be valid and then won't be saved. I have found that we can access to serialized dat before validation with initial_data so I am doing like this to save the user automatically from the token provided. The user need to provide everything except his own user. Is it ok or am I doing something not recommended ?
#api_view(['POST'])
#permission_classes([IsAuthenticated])
def add_mesure(request):
serializer = MesureSerializer(data=request.data)
serializer.initial_data['user'] = request.user.id
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
As you are already taking the token as a form of user verification, hence you can omit the user field from serializer (otherwise user might put someone else's id for example) and then pass the request object to serializer to get user from it during saving. Like this:
#serializer
class MesureSerializer(ModelSerializer):
class Meta:
exclude = ['user',]
...
def create(self, validated_data):
validated_data['user'] = self.context['request'].user
return super().create(validated_data)
Also, to pass the value of request, you can use context parameter of the serializer.
#view
serializer = MesureSerializer(data=request.data, context={'request':request})
Related
I tried to override validate method at TokenVerifySerializer but this raises AttributeError.
from rest_framework_simplejwt.serializers import TokenVerifySerializer
from rest_framework_simplejwt.views import TokenVerifyView
class CustomTokenVerifySerializer(TokenVerifySerializer):
def validate(self, attrs):
data = super(CustomTokenVerifySerializer, self).validate(attrs)
data.update({'fullname': self.user.fullname})
return data
class CustomTokenVerifyView(TokenVerifyView):
serializer_class = CustomTokenVerifySerializer
But that does work when using TokenObtainPairSerializer and TokenObtainPairView.
The above snippet raises AttributeError with 'CustomTokenVerifySerializer' object has no attribute 'user'.
The way adding custom fields in data is right I think. The problem is in self.user.fullname. Because the serializer doesn't have the user field. If you need user info in the serializer you have to get the user from DB.
But In this case, I think you need a current user or authenticated user. To get the authenticated user in the serializer you need to pass the user or request object as a context to the serializer. Here is an example:
def validate(self, attrs):
request = self.context.get('request', None)
if request:
user = request.user
And you have to initialize the serializer as below:
serializer = MySerializer(
data=request.data,
context={
'request': request
}
)
Very new to the Django Rest Framework, so would appreciate some help with this one. I get the error in the title when I try and do a POST request in Postman with an appropriate auth token.
I've made a table that I want to send a POST request to, but having issues with getting a user FK to be accepted as one of the columns. Plz see model/serializer/view below:
Model
class TestData (models.Model):
TestSDG = models.DecimalField(decimal_places=0, max_digits=2, default=0)
user = models.ForeignKey("auth.User", related_name="testdata", on_delete=models.CASCADE)
Serializer
class TestDataSerializer(serializers.ModelSerializer):
class Meta:
model = TestData
fields = ('id', 'TestSDG')
View
#csrf_exempt
def testDataApi(request, id=0):
if request.method == 'GET':
testdata = TestData.objects.all()
testdata_serializer = TestDataSerializer(testdata,many=True)
return JsonResponse(testdata_serializer.data,safe=False)
elif request.method == 'POST':
testdata_data=JSONParser().parse(request)
testdata_serializer=TestDataSerializer(data=testdata_data)
if testdata_serializer.is_valid():
testdata_serializer.save(user=request.user)
return JsonResponse("Added Successfully", safe=False)
The POST request works fine if I don't use the user as a foreign key, and I change testdata_serializer.save(user=request.user) back to testdata_serializer.save(), but I want the table to require a user's id.
Appreciate any help, thank you.
You should be using a ModelViewset in your views.py file - then you can override the update method on your serializer:
views.py
from rest_framework.viewsets import ModelViewSet
class TestDataViewSet(ModelViewSet):
queryset = TestData.objects.all()
serializer_class = TestDataSerializer
serializers.py
class TestDataSerializer(serializers.ModelSerializer):
...
def update(self, instance, validated_data):
# get user id from validated data:
user_id = validated_data.pop('user_id')
# get user:
user = User.objects.get(id=user_id)
# set user on instance:
instance.user = user
instance.save()
# continue with update method:
super().update(instance, validated_data)
You mentioned that you are using an auth token. Try verifying in your view testDataApi if request.user was correctly set with an auth.User object. Try logging it with something like below to make sure that it is correctly set to the user for the provided token:
#csrf_exempt
def testDataApi(request, id=0):
print(type(request.user), request.user) # Should display the user for the provided token.
...
If it isn't set, then you have to configure how it would correctly map an auth.User object from a provided token. You might want to look at the following:
AuthenticationMiddleware - Sets the request.user object.
AUTHENTICATION_BACKENDS - Custom authentication of a token and then return the associated auth.User object
DEFAULT_AUTHENTICATION_CLASSES - Only if using djangorestframework. Sets the request.user object.
TokenAuthentication, JSONWebTokenAuthentication, etc. - Only if using djangorestframework. Some implementations that authenticates tokens. Perhaps they weren't configured correctly.
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)
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.
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)