For example i have a few models:
class Parent(Model):
api_key = CharField(max_length=250)
class Child(Model):
parent = ForeignKey(Parent, related_name='children')
status = CharField(max_length=250)
I wrote view based on ListAPIView, and serializer:
class ChildSerializer(ModelSerializer):
class Meta:
fields = ['id']
I need to take all Children with parent by find Parent by api_key and return this as:
{
children:[
{'id':1},
{'id':2}
]}
But i take this:
[
{'id':1},
{'id':2}
]
Well, you can define a ParentSerializer which will also return the child objects data with it:
class ParentSerializer(ModelSerializer):
child = ChildSerializer(many=True)
class Meta:
fields = ['api_key', 'child']
Then you need to find the parent by api_key and pass the Parent object to this serializer. Like this:
class ParentView(RetrieveAPIView):
queryset = Parent.objects.all()
lookup_field = 'api_key'
serializer_class = ParentSerializer
lookup_url_kwarg = 'api_key'
Finally set the url to:
path('/parent/<str:api_key>/', ParentView.as_view(), name='parent-detail')
Update
If you need to get the data from request.GET(url querystring), then it much simpler. Try like this:
class ParentView(ListAPIView):
queryset = Parent.objects.all()
serializer_class = ParentSerializer
def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs)
api_key = request.GET.get('api_key')
if api_key:
return queryset.filter(api_key=api_key)
return queryset
Related
I am new to DRF. I want to get saved the model.
In models.py, PackageDetails and PhysicalDetail have foreignkey relationship to Member
My serializers.py is as follows:
from rest_framework import serializers
from .models import Member, PackageDetails, PhysicalDetail
class PackageDetailsSerializer(serializers.ModelSerializer):
is_expired = serializers.SerializerMethodField()
members_expiry_date = serializers.SerializerMethodField()
class Meta:
model = PackageDetails
exclude = ['id']
extra_fields = ['is_expired', 'members_expiry_date']
def get_is_expired(self, instance):
return instance.is_expired
def get_members_expiry_date(self, instance):
return instance.members_expiry_date
class PhysicalDetailSerializer(serializers.ModelSerializer):
class Meta:
model = PhysicalDetail
exclude = ['id']
class MemberSerializer(serializers.ModelSerializer):
physical_details = PhysicalDetailSerializer(many=True)
package_details = PackageDetailsSerializer(many=True)
class Meta:
model = Member
fields = '__all__'
extra_fields = ['physical_details', 'package_details']
def create(self, validated_data):
physical_detail_data = validated_data.pop("physical_details")
package_detail_data = validated_data.pop("package_details")
member = Member.objects.create(**validated_data)
PhysicalDetail.objects.create(member=member, **physical_detail_data)
PackageDetails.objects.create(member=member, **package_detail_data)
return member
views.py :
class MemberViewset(viewsets.ModelViewSet):
queryset = Member.objects.all()
serializer_class = MemberSerializer
class PackageDetailViewset(viewsets.ModelViewSet):
queryset = PackageDetails.objects.all()
serializer_class = PackageDetailsSerializer
class PhysicalDetailViewset(viewsets.ModelViewSet):
queryset = PhysicalDetail.objects.all()
serializer_class = PhysicalDetailSerializer
In GET request it worked well.. but in POST request with the same json format it responses the following:
{
"physical_details": [
"This field is required."
],
"package_details": [
"This field is required."
]
}
I've provided the fields.. so why this happening..
You removed those from dict using pop()
The pop() method removes and returns an element from a dictionary having the given key.
Try using get() instead
The get() method returns the value for the specified key if the key is in the dictionary.
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
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.
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!
I have serialised one of my models that has a foreign key in. I get 'Parent' object is not iterable
models.py
class Parent(models.Model):
# Parent data
class Child(models.Model):
parent = ForeignKey(Parent)
serializer.py
class ChildSerializers(serializers.ModelSerializer):
parent = serializers.RelatedField(many=True)
class Meta:
model = ReportField
fields = (
'id',
'parent'
)
api.py
class ChildList(APIView):
def get(self, request, format=None):
child = Child.objects.all()
serialized_child = ChildSerializers(child, many=True)
return Response(serialized_child.data)
Im guessing i have to pass the parent list to the child list but not sure of the best way to do it
attempt api.py
class ChildList(APIView):
def get(self, request, format=None):
child = Child.objects.all()
parent = Parent.objects.all()
serialized_child = ChildSerializers(child, many=True)
serialized_parent = ChildSerializers(parent, many=True)
return Response(serialized_child.data, serialized_parent.data)
Why using many=True. Parent is just a single field, no need to use explicit serializer field. Just get rid of these many=True
-answered by mariodev in comment.
You can do something like this using python collections as an intermediate
#serializers.py
class TimelineSerializer(serializers.Serializer):
childs= childSerializer(many=True)
parents = parentSerializer(many=True)
#apiviews.py
from collections import namedtuple
Timeline = namedtuple('Timeline', ('childs', 'parents'))
def list(self, request):
timeline = Timeline(
childs=Child.objects.all(),
parents=Parent.objects.all(),
)
serializer = TimelineSerializer(timeline)
return Response(serializer.data)
If your using a ModalSerializer then you have two foreign keys in a modal, then use "to_representation" function like this
def to_representation(self, instance):
rep = super().to_representation(instance)
rep['pcatalog_name'] = CatalogSerializer(instance.pcatalog_name).data
rep['pcategory_name'] = CategorySerializer(instance.pcategory_name).data
return rep
Replace ForeignKey by ManyToManyField to clarify the serializer field with many = True