In django rest framework i am trying to add an object in a ModelSerializer with some custom functionality.
I want one of the fields to be set to self.request.user and i have the following view:
class GigSubmitView(generics.CreateAPIView):
permission_classes = (ProviderRW,)
serializer_class = serializers.GigSubmitSerializer
queryset = models.Gig.objects
def perform_create(self, serializer):
serializer.save(provider=self.request.user)
However that doesn't seem to be the correct answer. What should i do instead?
You dont need to override the perform create method to access request object in the serializer.
You have 2 options:
use serializer.context['request'] to access request object in serializer
request object is inserted by default in all generic views.
http://www.django-rest-framework.org/api-guide/serializers/#including-extra-context
you can use the current user default method to set the current user
http://www.django-rest-framework.org/api-guide/validators/#currentuserdefault
Related
In my API, I have a create view tied that references another record OneToOne. However, occasionally it seems that users send through two requests at once and the second fails due to a duplicate record clash:
class CreateProfileLink(generics.CreateAPIView):
def perform_create(self, serializer):
ins = serializer.save(user=self.request.user)
serializer_class = ProfileLinkSerializer
Is there a way I could override the create method to return the record if it already exists rather than creating it?
You could use get_or_create in your serializer class, by overriding its create() method:
class ProfileLinkSerializer(serializers.ModelSerializer):
...
class Meta:
model = Profile
fields = (...)
def create(self, validated_data):
profile, _ = Profile.objects.get_or_create(**validated_data)
return profile
Since you haven't provided your models.py, I am using Profile as a model name here. Make sure to replace it if it is different in your project.
With some help, I solved this issue.
My api is work, but today I found this error when I try to access '/api/v1/docs'
AttributeError at /api/v1/docs/
'NoneType' object has no attribute 'method'
I know that the error is here:
def get_fields(self):
fields = super().get_fields()
if self.context['request'].method in ['POST', 'PATCH', 'PUT']:
fields['products'] = serializers.ListField(
write_only=True,
child=serializers.IntegerField()
)
return fields
When I remove .method, the access to the /api/v1/docs/ works, but my solution to post some products in bundleproducts, doesn't work.
My code:
view.py
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
model = Product
class BundleProductViewSet(viewsets.ModelViewSet):
queryset = BundleProduct.objects.all()
serializer_class = BundleProductSerializer
model = BundleProduct
class BundleViewSet(viewsets.ModelViewSet):
queryset = Bundle.objects.all()
serializer_class = BundleSerializer
model = Bundle
This is probably caused by this serializer being used as a nested serializer in another serializer. So lets say the definition for the serializer in question is:
class MySerializer(serializers.Serializer):
...
And you have another serializer like this:
class OtherSerializer(serializers.Serializer):
my_field = MySerializer()
In this case, when instantiating an instance of OtherSerializer, its context is not passed automatically to MySerializer, so there would not be a request in the context of MySerializer. You can either add the context to nested serializer manually, or in the get_fields method, check that request exists in self.context and proceed accordingly.
Also, I am not sure what you are trying to accomplish, but if you provide a field with
write_only=True
in serializer class definition, the field would not be present when reading the serializer, i.e for get requests in general, which seems like what you are trying to do here. So adding the products field as write_only would have the same effect, you do not need to override get_fields method
Trying to manage django-guardian object-level permissions with django-rest-framework the most cleanly and canon possible.
I want to assign a read permission (module.view_object) of an object to to the user making the request when performing a POST.
My class-based view:
class ObjectList(ListCreateAPIView):
queryset = Object.objects.all()
serializer_class = ObjectSerializer
filter_backends = (DjangoObjectPermissionsFilter,)
# MyObjectPermissions inherit from DjangoObjectPermissions and just
# add the 'view_model' permission
permission_classes = (MyObjectPermissions,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
As described in the django-rest-framework documentation, I've overloaded perform_create in order to pass the user to the serializer.
But how do I assign the permission (using guardian.shortcuts.assign_perm) in the serializer. Is there no other way but to override the save method to assign the permission manually? Isn't there some kind of standard mechanism to manage a common behavior as this one?
You can assign the permission in the perform_create() method of the ViewSet.
I want to access the request object in my Views.py and Serializers.py in DRF.
My Views.py:
class ProductViewSet(viewsets.ReadOnlyModelViewSet):
"""
This viewset automatically provides `list` and `detail` actions.
"""
queryset = Product.objects.all()
serializer_class = ProductSerializer(context={'request': request})
My Serializers.py:
class ProductSerializer(serializers.HyperlinkedModelSerializer):
get_sr_price = serializers.SerializerMethodField('get_sr_price_func')
def get_sr_price_func(self, obj):
return self.request.user ??
class Meta:
model = Product
fields = (
'title', 'slug', 'product_stores', 'get_sr_price')
In Serializers.py I get ProductSerializer' object has no attribute 'request'. Also In views.py I get NameError: name 'request' is not defined
How do I access request object? Do I have to pass it from views to serializers? Also what's the difference between views.py and serializers.py? Generally I write all the business logic in Views.py ; here also should I do all the queries/filters in the views or should I do them in serializers or it doesn't make a difference. New to DRF please help.
You don't need to include request object in the context as the generic views passes request object to the serializer context.
DRF Source code snippet:
# rest_framework/generics.py
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request, # request object is passed here
'format': self.format_kwarg,
'view': self
}
In your serializer, you can access the request object using .context attribute.
The context dictionary can be used within any serializer field logic,
such as a custom .to_representation() method, by accessing the
self.context attribute.
class ProductSerializer(serializers.HyperlinkedModelSerializer):
get_sr_price = serializers.SerializerMethodField('get_sr_price_func')
def get_sr_price_func(self, obj):
return self.context['request'].user # access the request object
Serializers are the way external data is mapped from / to models (Django or simple Python classes).
Views are dealing with how the data will be shown. Throttling, pagination, authentication are managed by the view. They also handle the data set.
DRF provides a context to pass request specific data to the serializer without having to redefine the init. This is likely what you're looking for.
Using DRF and DRF-nested-routers
Here's my code:
class MemberViewSet(viewsets.ViewSet):
queryset = GroupMember.objects.all()
serializer_class = GroupMembersSerializer
def create(self, request, group_pk=None):
queryset = self.queryset.all()
serializer = GroupMembersSerializer(queryset)
return Response(serializer.data)
But once a new Member is posted the error "QuerySet' object has no attribute 'user' comes up
Any help?
To serialize a queryset (or list of objects) you need to pass many=True
serializer = GroupMembersSerializer(queryset, many=True)
Otherwise it thinks you want to serialize a single GroupMember instance, which is why it tried to access the user attribute on it
If isn't too late in your development and you have the choice, you may want to check out https://github.com/chibisov/drf-extensions. It does routers nesting in a non-intrusive manner - you won't be required to overwrite the viewsets basic methods.
I've learned form the past that drf-nested-routers will interfere with the underlying viewset methods which enable pagination and filtering on your class:
get_queryset
get_serializer_class
get_serializer
get_object
In my opinion affects too much from the Viewset design and functionality for what it offers.