How to serialize ManyToManyField - python

I want to serialize ManyToManyField but at the same time, I am looking for something which updates the same using ModelViewSet. I am able to serialize it but when I am updating it I am not able to. I know I can make a separate API for that but due to some requirements, I need to stick to one endpoint. Here is my code
class ComponentSerializers(serializers.ModelSerializer):
class Meta:
model = coreModel.Component
fields = '__all__'
class MonitorSerializers(serializers.ModelSerializer):
device = ComponentSerializers(read_only=True, many=True)
class Meta:
model = models.Monitor
fields = '__all__'
read_only_fields = ('id', 'created_at', 'updated_at',)
and views.py is
class MonitorViewSet(viewsets.ModelViewSet):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
queryset = models.Monitor.objects.all()
filter_backends = (DjangoFilterBackend,OrderingFilter,SearchFilter)
filter_class = superFilter.MonitorFilters
serializer_class = serializers.MonitorSerializers

If you want update ManytoMany or Related objects
Override
def update(self, validated_data):
in MonitorSerializers
class MonitorSerializers(serializers.ModelSerializer):
device = ComponentSerializers(read_only=True, many=True)
device_ids = serializers.ListField(write_only=True,
child = serializers.IntegerField(min_value = 1))
....
def update(self, instance, validated_data):
# Remove component data and save in variable
iscomponentdataexists = 'device_ids' in validated_data
if iscomponentdataexists :
componentdata= validated_data.pop('device_ids')
instance = super().update(instance, validated_data) # Update Monitor Data
# looping through new device_ids list
if iscomponentdataexists :
for deviceid in componentdata:
try:
obj = coreModel.Component.objects.get(id=deviceid)
instance.devices.add(obj)
except coreModel.Component.DoesNotExist:
pass
instance.save()
return instance
Remove read_only=True from device

Related

can not use different fields for each object in serializer django rest framework

I want to create a udemy like web app and using django rest framework for the backend and mysql as database.
i have a model named 'Lessons' that contains list of all lessons and one of the fields is 'video-link'. also i have another model names Purchases that have two ForeignKey fields :Users and Lessons. i want to show the Lessons to all users but for the download field i have to lookup the pair (User , Lesson) in Purchases and if He has the course i will show him the download field.
My View Set
class LessonsViewSet(viewsets.ModelViewSet):
queryset = models.Assignments.objects.all()
authentication_classes = (TokenAuthentication,)
def get_serializer_class(self):
if self.request.user.is_staff :
print(self.request.user)
return serializers.FullAccessLessonsSerializer
elif self.request.user.is_active:
return serializers.PartialAccessLessonsSerializer
print(self.request.user)
return serializers.BasicAccessLessonsSerializer
My Serializers
Full access for admins:
class FullAccessLessonsSerializer(serializers.ModelSerializer):
class Meta:
model = models.Assignments
fields = ('id', 'title', 'description', 'dllink' )
Basic access for unauthenticated users:
class BasicAccessLessonsSerializer(serializers.ModelSerializer):
class Meta:
model = models.Assignments
fields = ('id', 'title', 'description')
and partial access for students :
class PartialAccessAssignmentsSerializer(serializers.ModelSerializer):
"""A serializer for all Lessons"""
def __init__(self, *args, **kwargs):
fields = kwargs.pop('fields', None)
super(PartialAccessAssignmentsSerializer, self).__init__(*args,**kwargs)
print(self.fields.get('id'))
self.fields.pop('dllink')
class Meta:
model = models.Assignments
fields = ('id','title','description','dllink' )
I have done anything I could found but I can not figure it out. I either an get error or removing all dl links.
If you want dllink is None when user can't access.use this:
class FullAccessLessonsSerializer(serializers.ModelSerializer):
dllink = serializers.SerializerMethodField()
def get_prescription_accept(self, instance):
result = True # lookup the pair (User , Lesson) in Purchases
if result:
return instance.dllink
else:
return ''
class Meta:
model = models.Assignments
fields = ('id', 'title', 'description', 'dllink' )
If you want pop dllink from data,use:
class FullAccessLessonsSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
data = super(FullAccessLessonsSerializer, self).to_representation(instance)
result = True # lookup the pair (User , Lesson) in Purchases
if not result:
data.pop('dllink')
return data
class Meta:
model = models.Assignments
fields = ('id', 'title', 'description', 'dllink' )

Convert URL of an object to its database instance

I created an extra function in my View that receives a list with hyperlinked references to some ResourceGroup objects, but I don't know how to convert them to database instances
class ResourceViewSet(viewsets.ModelViewSet):
queryset = Resource.objects.all()
serializer_class = ResourceSerializer
#action(methods=['put'], detail=True)
def groups_append(self, request, pk=None):
instance = self.get_object()
groups = request.data.get("groups")
for resource_group in groups:
instance.groups.add(WHAT_HERE(resource_group))
instance.save()
return Response(self.get_serializer(instance, many=False).data)
This is the request:
PUT http://.../api/resources/1/groups_append/
with body:
{"groups": ["http://.../api/resource_groups/1/", ...]}
ResourceSerializer:
class ResourceSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Resource
fields = ('resource_id', 'object_id', 'type', 'system', 'path', 'groups', 'job_set')
def update(self, instance, validated_data):
instance.object_id = validated_data.get('object_id', instance.object_id)
instance.type = validated_data.get('type', instance.type)
instance.system = validated_data.get('system', instance.system)
instance.path = validated_data.get('path', instance.path)
instance.save()
return instance
ResourceGroupSerializer:
class ResourceGroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = ResourceGroup
fields = ('resource_group_id', 'label', 'resource_set')
def update(self, instance, validated_data):
instance.label = validated_data.get('label', instance.label)
instance.save()
return instance
use HyperlinkedRelatedField for groups in ResourceSerializer or just create a new serializer for this action(the main idea is to get the data using a serializers not just directly from the request body) like this:
class ResourceSerializer(serializers.HyperlinkedModelSerializer):
groups = serializers.HyperlinkedRelatedField(
many=True,
read_only=True,
view_name='groups-detail' ## name of the groups detail url
)
class Meta:
model = Resource
....
then edit your action as below:
#action(methods=['put'], detail=True)
def groups_append(self, request, pk=None):
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
### then get the groups from the validated data
groups = serializer.validated_data.get('groups', [])
....
....
References:
1- hyperlinkedrelatedfield

How to set the DestroyAPIView method not real delete my instance?

I have a model, like bellow:
class BModel(models.Model):
name = models.CharField(max_length=11)
status = models.CharField(default="is_active") # if delete: deleted
a = models.ForeignKey(AModel)
def delete(self, using=None, keep_parents=False):
pass # there I want to set the BModel's status to `deleted`
Serializer:
class BModelSerializer(ModelSerializer):
class Meta:
model = BModel
fields = "__all__"
Its Views is bellow:
class BModelListAPIView(ListAPIView):
serializer_class = BModelSerializer
permission_classes = []
queryset = BModel.objects.all()
class BModelDestroyAPIView(DestroyAPIView):
serializer_class = BModelSerializer
permission_classes = []
queryset = BModel.objects.all()
My requirement is when use Django-Rest-Framework delete my BModel, I want to set the BModel's status field to deleted, not real delete the instance. How to access it?
when I tried to write a delete method to my Model, there comes the default delete method:
def delete(self, using=None, keep_parents=False):
I have some questions about this:
Is this is for the BModel's instance?
Whether should through the delete method to access my requirement?
DestroyAPIView has a perform_destroy method. You can override that and add your logic of deletion. For eg:
class BModelDestroyAPIView(DestroyAPIView):
serializer_class = BModelSerializer
permission_classes = []
queryset = BModel.objects.all()
def perform_destroy(self, instance):
instance.delete_flag = True
instance.save()
The delete method of BModel will override the default delete method. Which will also affect the Django Admin. You can also add a custom delete method to Manager of that Model. Refer

Passing URL parameters to a nested serializer

I have two serializers, one of which is nested:
class PaperSerializer(serializers.ModelSerializer):
class Meta:
model = Paper
class AuthorSerializer(serializers.ModelSerializer):
papers = PaperSerializer(
many=True,
read_only=True,
source='paper_set'
)
class Meta:
model = Author
I want to get a list of Authors which shows only their published Papers (Boolean field exists on the model).
I would like to call the API like /api/v1/authors/?show_published_only=true.
After some digging around, I discovered that you can pass the context from the ViewSet to the Serializer:
views.py
class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
filter_fields = (
'show_published_only',
)
def get_serializer_context(self):
return {'request': self.request}
Now, create a new serializer FilteredPaperSerializer which inherits from serializers.ListSerializer, then override the to_representation() method to filter the queryset:
serializers.py
class FilteredPaperSerializer(serializers.ListSerializer):
def to_representation(self, data):
# Get the parameter from the URL
show_published_only = self.context['request'].query_params['show_published_only']
data = data.filter(is_published=show_published_only)
return super(FilteredPaperSerializer, self).to_representation(data)
class AuthorSerializer(serializers.ModelSerializer):
papers = FilteredPaperSerializer(
many=True,
read_only=True,
source='paper_set'
)
class Meta:
model = Author
NB: Don't forget to convert the fetched URL parameter to a Boolean or relevant data type for your model, I neglected to do it in the write-up above.

How to find object by its id in Django Rest Framework

I can't have object by its id in Django Rest Framework. I have a such model:
class BlogPost(models.Model):
title = models.CharField(max_length=128)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
Then I write a serializer:
class BlogPostSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPost
fields = ('title', 'content', 'created')
In my views.py I have this:
class BlogPostListFilter(dajngo_filter.FilterSet):
blog_post_id = django_filters.NumerFilter(name = 'id')
class Meta:
model = BlogPost
fiields = ['blog_post_id']
class BlogPostList(generics.ListCreateAPIView):
queryset = BlogPost.objects.all()
serializer_class = BlogPostSerializer
permission_classes = (AllowAny,)
filter_class = BlogPostListFilter
paginate_by = 100
And such code in my urls:
url(r'^blogpost/$', ListCreateAPIView.as_view(model=BlogPost), name='blogpost-list'),
But when I write in browser http://example.com/blogpost/?blog_post_id=1 I have all objects
If you really want to use ListCreateAPIView. You need to make some changes:
urls.py:
url(r'^blogpost/(?P<post_id>\w+)$', views.BlogPostList.as_view(),name='blogpost-list'),
views.py
class BlogPostList(generics.ListCreateAPIView):
serializer_class = BlogPostSerializer
permission_classes = (AllowAny,)
filter_class = BlogPostListFilter
paginate_by = 100
def get_queryset(self):
queryset = BlogPost.objects.filter(pk=self.kwargs['post_id'])
return queryset
But I think that Django Rest Framework provides better Class Based Views for your use case, such as RetrieveAPIView. As far as I understand, it seems that you just want to get an object, and this generic view is for a list of objects.
In my case, I stumbled upon this question looking to access the object id while overriding a ModelViewSet's retrieve method. After some research and experimentation, I discovered that the object id is stored in a dictionary called self.kwargs in the 'pk' key.
I am using djangorestframework==3.11.0.
class MealItemViewSet(viewsets.ModelViewSet):
queryset =MyModel.objects.all()
serializer_class = serializers.MyModelSerializer
def retrieve(self, request, *args, **kwargs):
# The Primary Key of the object is passed to the retrieve method through self.kwargs
object_id = self.kwargs['pk']
I hope this answer helps another forlorn StackOverflow wanderer at some point!

Categories