How to iterate ManyToMany filed in serializer class - django

I have this Event model:
# models.py
class Event(models.Model):
name = models.CharField(max_length=200))
user = models.ManyToManyField(User))
This is my ListCreateAPIView class:
class EventListView(LoginRequiredMixin, generic.ListView):
model = Event
This is my ListCreateAPIView class:
class Events(generics.ListCreateAPIView):
permission_classes = (IsAuthenticated,)
queryset = Event.objects.all()
serializer_class = EventSerializer
This is my serialiser class:
#serializers,py
class EventSerializer(serializers.ModelSerializer):
class Meta:
fields = (
‘id',
'name',
'user',
)
model = Event
And this is my REST request response:
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"name": "My event1",
"user": [
1,
4
],
"registered": true // <—- I NEED THIS
},
{
"id": 2,
"name": "Other event",
"user": [
2,
4,
6
],
"registered": false // <—- I NEED THIS
}
]
}
What I am gonna need in REST response is property “registered”, which will contain true or false if current user is registered on particular event or not.
My approach was to control if current user is in list of users of event. I tried to get this information in serialiser class this way:
#serializers,py
class EventSerializer(serializers.ModelSerializer):
registered = serializers.SerializerMethodField(‘_user’)
def _user(self, obj):
request = getattr(self.context, 'request', None)
if request.user in self.user:
return True
else:
return False
class Meta:
fields = (
‘id',
'name',
'user',
)
model = Event
But I get AttributeError: 'NoneType' object has no attribute 'user'
How can I solve this problem?

You can try like this using exists() with filter method:
class EventSerializer(serializers.ModelSerializer):
registered = serializers.SerializerMethodField(‘_user’)
def _user(self, obj):
request = self.context.get('request', None)
if request and obj.user.filter(pk=request.user.pk).exists():
return True
else:
return False

In view.py add
For This "registered": true // <—- I NEED THIS
model_name.object.filter(column='required value')

Related

How to show fields from another serializer in Django rest framework

I want to show extra fields in response, showing similar products or related products in the category while am in a product detail page.
Eg:
If am viewing a single product detail page and in the bottom of page the related products list must be also show there. So while in response of related products, I want to showhighest_offer_price, product_offer_discount, category_offer_discount of ProductDetailserilaizer.
#Serializer.py
class RelatedProductSerializer(ModelSerializer):
class Meta:
model = Products
fields = ['product_name', 'slug', 'base_price', 'images']
class ProductDetailserializer(ModelSerializer):
product_offer_discount = SerializerMethodField()
category_offer_discount = SerializerMethodField()
highest_offer_price = SerializerMethodField()
description = ProductDescription()
extra_images = SerializerMethodField()
related_products = SerializerMethodField()
class Meta:
model = Products
fields = [
"id",
"product_name",
"slug",
"highest_offer_price",
"category_offer_discount",
"product_offer_discount",
"description",
"base_price",
"stock",
"is_available",
"images",
"extra_images",
"related_products"
]
def to_representation(self, instance):
print('INSTANCE', instance)
rep = super().to_representation(instance)
rep['description'] = ProductDescriptionSerializer(instance.description, many=True).data
return rep
def get_related_products(self, obj):
products= Products.objects.filter(category=obj.category).exclude(id=obj.id)
return RelatedProductSerializer(products, many=True, context=self.context).data
def get_extra_images(self, obj):
images = obj.extra_images.all()
return ProductImageSerializer(images, many=True, context=self.context).data
#Views.py
class ProductDetailView(APIView):
def get(self, request, category_slug, product_slug):
try:
single_product = Products.objects.get(
category__slug=category_slug, slug=product_slug
)
except:
raise exceptions.NotFoundError()
serializer = ProductDetailserializer(
single_product, context={
"request": request,
}
)
return Response(serializer.data)
#Response
{
"id": 1,
"product_name": "Pendant 1",
"slug": "pendant-1",
"highest_offer_price": null,
"category_offer_discount": null,
"product_offer_discount": null,
"description": [
{
"title": "First title",
"description": "First description pendant 1"
},
{
"title": "second title",
"description": "second description pendant 1"
}
],
"base_price": 2500,
"stock": 97,
"is_available": true,
"images": "http://127.0.0.1:8000/media/photos/products/pendant3.webp",
"extra_images": [
{
"images": "http://127.0.0.1:8000/media/photos/product-images/pendant3.webp"
},
{
"images": "http://127.0.0.1:8000/media/photos/product-images/pendant3_BQJRObf.webp"
},
{
"images": "http://127.0.0.1:8000/media/photos/product-images/pendant3_QGmLbXC.webp"
}
],
"related_products": [
{
"product_name": "Pendant 2",
"slug": "pendant-2",
"base_price": 3500,
"images": "http://127.0.0.1:8000/media/photos/products/pendant2.webp"
},
{
"product_name": "Pendant 3",
"slug": "pendant-3",
"base_price": 1500,
"images": "http://127.0.0.1:8000/media/photos/products/281031cw114n.webp"
},
{
"product_name": "pendant 4",
"slug": "pendant-4",
"base_price": 1500,
"images": "http://127.0.0.1:8000/media/photos/products/281031cw114n_Nxbx7lT.webp"
}
]
Am new to this and am complicating it too much i guess Because I have already wrote multiple serializers for Product model.
It's perfectly fine to have multiple serializers for the same model if the data differs between them.
If you're worrying about re-writing the same code multiple times, on different serializers, you could always make a base serializer for your Products model and then extend it as needed.
Like this:
# Make a BaseProductSerializer that inherits from Modelserializer and contains all fields and methods that all Products serializers need.
class BaseProductSerializer(Modelserializer):
product_offer_discount = SerializerMethodField()
category_offer_discount = SerializerMethodField()
highest_offer_price = SerializerMethodField()
def get_product_offer_discount(self):
return # whatever this method should do...
def get_category_offer_discount(self):
return # whatever this method should do...
def get_highest_offer_price(self):
return # whatever this method should do...
# Make a serializer for related products that inherits from BaseProductSerializer and specify the Meta data.
class RelatedProductSerializer(BaseProductSerializer):
class Meta:
model = Products
fields = [
'product_name',
'slug',
'base_price',
'images',
'highest_offer_price',
'product_offer_discount',
'category_offer_discount'
]
# Make a serializer for product details that inherits from BaseProductSerializer and add any extra fields/methods that you need.
class ProductDetailserializer(BaseProductSerializer):
description = ProductDescription()
extra_images = SerializerMethodField()
related_products = SerializerMethodField()
class Meta:
model = Products
fields = [
"id",
"product_name",
"slug",
"highest_offer_price",
"category_offer_discount",
"product_offer_discount",
"description",
"base_price",
"stock",
"is_available",
"images",
"extra_images",
"related_products"
]
def to_representation(self, instance):
print('INSTANCE', instance)
rep = super().to_representation(instance)
rep['description'] = ProductDescriptionSerializer(instance.description, many=True).data
return rep
def get_related_products(self, obj):
products= Products.objects.filter(category=obj.category).exclude(id=obj.id)
return RelatedProductSerializer(products, many=True, context=self.context).data
def get_extra_images(self, obj):
images = obj.extra_images.all()
return ProductImageSerializer(images, many=True, context=self.context).data
Hope this helps.

Django REST Framework: Show only latest of nested object, return as un-nested JSON

What I'm trying to do in Django REST Framework: Return only latest nested object in list for an object and return it as JSON, with the sub-object un-nested.
My models:
class BaseObject(models.Model):
name = models.TextField()
object_type = models.ForeignKey(ObjectType)
class ObjectStatus(models.Model):
baseobject_id = models.ForeignKey('objects.BaseObject', related_name='status')
object_status = models.IntegerField()
object_status_timestamp = models.DateTimeField()
My serializers:
class ObjectStatusSimplifiedSerializer(serializers.ModelSerializer): #helper serializer to simplify status objects
class Meta:
model = ObjectStatus
fields = ['object_status', 'object_status_timestamp']
class ObjectStatusListSerializer(serializers.ModelSerializer): #request for last status of several objects
status = ObjectStatusSimplifiedSerializer(many=True)
class Meta:
model = BaseObject
fields = ['id', 'name', 'object_type', 'status']
My current view:
class ObjectStatusListView(generics.ListCreateAPIView):
serializer_class = ObjectStatusListSerializer
def get_queryset(self):
queryset = BaseObject.objects.all()
id = self.request.query_params.getlist('id')
if id:
queryset = queryset.filter(id__in=id)
return queryset
Current URL:
url(r'^objectstatus/status/list$', views.ObjectStatusListView.as_view()),
So now, when going to, for example, [...]/objectstatus/status/list?id=9, the result I get looks like this:
[
{
"id": 9,
"name": "r5",
"object_type": "router",
"status": [
{
"object_status": 1,
"object_status_timestamp": "2019-10-24T09:40:15.605391Z"
},
{
"object_status": 2,
"object_status_timestamp": "2019-10-24T09:40:28.133296Z"
},
{
"object_status": 3,
"object_status_timestamp": "2019-10-24T09:40:40.829486Z"
},
{
"object_status": 1,
"object_status_timestamp": "2019-10-24T09:40:53.333332Z"
}
]
}
]
What I want is to display only the object status with the most recent timestamp.
Also, I can't figure out how to flatten the JSON object, like this:
[
{
"id": 9,
"name": "r5",
"object_type": "router",
"object_status": 1,
"object_status_timestamp": "2019-10-24T09:40:53.333332Z"
}
]
With the following serializer, you should get the desired output. We filter the status list and get only the latest one and then we flatten the structure as you need.
class ObjectStatusListSerializer(serializers.ModelSerializer): #request for last status of several objects
status = serializers.SerializerMethodField(read_only=True)
class Meta:
model = BaseObject
fields = ['id', 'name', 'object_type', 'status']
def get_status(self, obj):
return ObjectStatusSimplifiedSerializer(instance=obj.status.order_by('object_status_timestamp').first()).data
def to_representation(self, obj):
"""Move fields from status to main object representation."""
representation = super().to_representation(obj)
status_representation = representation.pop('status')
for key in status_representation:
representation[key] = status_representation[key]
return representation
you can try change serializer to like this. I assum your ObjectType have field is name for line code object_type.name
class ObjectStatusSimplifiedSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField()
object_type = serializers.SerializerMethodField()
#staticmethod
def get_name(instance):
return instance.status.name
#staticmethod
def get_object_type(instance):
return instance.status.object_type.name
class Meta:
model = ObjectStatus
fields = ['id', 'name', 'object_type', 'object_status', 'object_status_timestamp']
class ObjectStatusListSerializer(serializers.ModelSerializer):
status = serializers.SerializerMethodField()
#staticmethod
def get_status(instance):
queryset = ObjectStatus.objects.filter(baseobject_id=instance).order_by('-object_status_timestamp')[:1]
if queryset.count():
return ObjectStatusSimplifiedSerializer(queryset, many=True).data
return []
class Meta:
model = BaseObject
fields = ['id', 'name', 'object_type', 'status']

Django - needs to have a value for field "id" before this many-to-many relationship can be used

I have two serializers
class CheckItemSerializer(serializers.ModelSerializer):
class Meta:
model = CheckItem
fields = (
'item_name',
'amount',
)
class PaymentActionSerializer(serializers.ModelSerializer):
items = CheckItemSerializer(many=True, required=False)
class Meta:
model = PaymentAction
fields = [
'booking_number',
'date',
'guest_name',
'isRefund',
'lcode',
'payment_type',
'positions',
'total',
'items',
'id'
]
def create(self, validated_data):
action = PaymentAction.objects.create(**validated_data)
action.save()
if validated_data.get('items', None) is not None:
items = validated_data.pop('items')
if items is not None:
for item in items:
item_name = item['item_name']
amount = item['amount']
new_item = CheckItem.objects.create(
item_name=item_name,
amount=amount
)
new_item.save()
action.items.add(new_item)
action.save()
return action
and json
{"lcode": 123,
"total": 1,
"isRefund": false,
"booking_number": "333",
"guest_name": "me",
"positions": "1 night",
"date": "2019-07-22 00:00",
"payment_type": "nal",
"items": [
{
"item_name": "glazka",
"amount": "100"
},
{
"item_name": "glazka2",
"amount": "150"
}
]
}
and I get error
"<PaymentAction: PaymentAction object>" needs to have a value for field "id" before this many-to-many relationship can be used.
What am I doing wrong ?
You passed the items parameter to your PaymentAction object as well, but since at that point, your PaymentAction has not (yet) a primary key, it can not add these items to a many-to-many field.
You thus should pop that from the validated_data first:
def create(self, validated_data):
items = validated_data.pop('items', None)
action = PaymentAction.objects.create(**validated_data)
if items is not None:
items = [CheckItem.objects.create(**item) for item in items]
action.items.add(*items)
return action

Django Rest Framework - Marking extra actions for routing

I have the next view on my API
class CapsuleViewSet(viewsets.ModelViewSet):
queryset = Capsule.objects.all()
serializer_class = CapsuleSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
#action(detail=True)
def modules(self, request, pk=None):
capsule = self.get_object()
capsule_modules = Module.objects.filter(capsule=capsule)
serializer = ModuleSerializer(capsule_modules, many=True)
return Response(serializer.data)
When i try to get all the modules related to a capsule, the URL of the imageField of a module is incomplete.
When i call http://0.0.0.0:8000/api/capsules/1/ it returns:
{
"capsuleID": 1,
"capsuleName": "sdfads",
"capusuleDetails": "asdf",
"capsuleImageURL": "http://0.0.0.0:8000/media/capsulas/logocitbm.png",
"userStars": 0,
"pallete": {
"palleteID": 1,
"palleteName": "Default smartraining",
"colors": [
{
"colorID": 1,
"colorName": "A1",
"colorCode": "#sdfs"
},
{
"colorID": 2,
"colorName": "A2",
"colorCode": "#dsfksdoif"
},
{
"colorID": 3,
"colorName": "A3",
"colorCode": "#sdjfjgioj"
}
]
}
}
but when i call http://0.0.0.0:8000/api/capsules/1/modules/ i get:
[
{
"moduleID": 1,
"moduleName": "sdfa",
"moduleDetails": "así",
"moduleImageURL": "/media/modulos/9_-_4._Detalle_C%C3%A1psula.png",
"userScore": 0
}
]
the moduleImageURL is incomplete, why?
these are my serializers:
class CapsuleSerializer(serializers.ModelSerializer):
pallete = PalleteSerializer(read_only=True)
class Meta:
model = Capsule
fields = ('capsuleID', 'capsuleName', 'capusuleDetails', 'capsuleImageURL', 'userStars', 'pallete')
class ModuleSerializer(serializers.ModelSerializer):
class Meta:
model = Module
fields = ('moduleID', 'moduleName', 'moduleDetails', 'moduleImageURL', 'userScore')
After some research i find that i need pass the context to the ModelSerializer like this:
serializer = ModuleSerializer(capsule_modules, many=True, context={'request': request})

Populating a tastypie resource for a multi-table inheritance Django model

Given the following code I was wondering how to populate RecordsResource with each real record data:
models.py
class Record(models.Model):
content_type = models.ForeignKey(ContentType, editable=False, null=True)
user = models.ForeignKey(User, related_name='records')
issued = models.DateTimeField(auto_now_add=True)
date = models.DateField()
def save(self, *args, **kwargs):
if not self.content_type:
self.content_type = ContentType.objects.get_for_model(self.__class__)
super(Record, self).save(*args, **kwargs)
def as_leaf_class(self):
model = self.content_type.model_class()
if model == self.__class__:
return self
return model.objects.get(pk=self.id)
class Record1(Record):
# some fields
# ...
class RecordN(Record):
# some fields
api.py
class BaseModelResource(ModelResource):
class Meta(object):
authentication = ApiKeyPlusWebAuthentication()
authorization= Authorization()
cache = SimpleCache()
throttle = CacheDBThrottle(
throttle_at=350,
# 1 day
expiration=86400
)
if settings.DEBUG:
serializer = PrettyJSONSerializer()
def obj_create(self, bundle, request=None, **kwargs):
return super(BaseModelResource, self).obj_create(bundle, request, user=request.user)
def apply_authorization_limits(self, request, object_list):
return object_list.filter(user=request.user)
class BaseRecordResource(BaseModelResource):
class Meta(BaseModelResource.Meta):
filtering = {
'date': ALL
}
excludes = ['issued']
class RecordsResource(BaseRecordResource):
class Meta(BaseRecordResource.Meta):
resource_name = 'records'
queryset = Record.objects.all()
class Record1Resource(BaseRecordResource):
class Meta(BaseRecordResource.Meta):
resource_name = 'record1'
queryset = Record1.objects.all()
# ...
class RecordNResource(BaseRecordResource):
class Meta(BaseRecordResource.Meta):
resource_name = 'recordn'
queryset = RecordN.objects.all()
Ok, I just solved it. I've simplified the code.
Given the following code...
models.py
from django.db import models
from model_utils.managers import InheritanceManager
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
# https://github.com/carljm/django-model-utils#inheritancemanager
objects = InheritanceManager()
class Restaurant(Place):
custom_field = models.BooleanField()
class Bar(Place):
custom_field = models.BooleanField()
api.py
from core.models import Place, Restaurant, Bar
# http://django-tastypie.readthedocs.org/en/latest/cookbook.html#pretty-printed-json-serialization
from core.utils import PrettyJSONSerializer
from tastypie.resources import ModelResource
class PlaceResource(ModelResource):
class Meta:
queryset = Place.objects.select_subclasses()
resource_name = 'place'
serializer = PrettyJSONSerializer()
class RestaurantResource(ModelResource):
class Meta:
queryset = Restaurant.objects.all()
resource_name = 'restaurant'
serializer = PrettyJSONSerializer()
class BarResource(ModelResource):
class Meta:
queryset = Bar.objects.all()
resource_name = 'bar'
serializer = PrettyJSONSerializer()
Output
http://localhost:8000/api/v1/bar/?format=json
{
"meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 1
},
"objects": [
{
"address": "dawdaw",
"custom_field": true,
"id": "1",
"name": "dwdwad",
"resource_uri": "/api/v1/bar/1/"
}
]
}
OK
http://localhost:8000/api/v1/restaurant/?format=json
{
"meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 1
},
"objects": [
{
"address": "nhnhnh",
"custom_field": true,
"id": "2",
"name": "nhnhnh",
"resource_uri": "/api/v1/restaurant/2/"
}
]
}
OK
http://localhost:8000/api/v1/place/?format=json
{
"meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 2
},
"objects": [
{
"address": "dawdaw",
"id": "1",
"name": "dwdwad",
"resource_uri": "/api/v1/place/1/"
},
{
"address": "nhnhnh",
"id": "2",
"name": "nhnhnh",
"resource_uri": "/api/v1/place/2/"
}
]
}
What I want to achieve
{
"meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 2
},
"objects": [
{
"address": "dawdaw",
"custom_field": true,
"id": "1",
"name": "dwdwad",
"resource_uri": "/api/v1/bar/1/"
},
{
"address": "nhnhnh",
"custom_field": true,
"id": "2",
"name": "nhnhnh",
"resource_uri": "/api/v1/restaurant/2/"
}
]
}
Solution:
from core.models import Place, Restaurant, Bar
# http://django-tastypie.readthedocs.org/en/latest/cookbook.html#pretty-printed-json-serialization
from core.utils import PrettyJSONSerializer
from tastypie.resources import ModelResource
class RestaurantResource(ModelResource):
class Meta:
queryset = Restaurant.objects.all()
resource_name = 'restaurant'
serializer = PrettyJSONSerializer()
class BarResource(ModelResource):
class Meta:
queryset = Bar.objects.all()
resource_name = 'bar'
serializer = PrettyJSONSerializer()
class PlaceResource(ModelResource):
class Meta:
queryset = Place.objects.select_subclasses()
resource_name = 'place'
serializer = PrettyJSONSerializer()
def dehydrate(self, bundle):
# bundle.data['custom_field'] = "Whatever you want"
if isinstance(bundle.obj, Restaurant):
restaurant_res = RestaurantResource()
rr_bundle = restaurant_res.build_bundle(obj=bundle.obj, request=bundle.request)
bundle.data = restaurant_res.full_dehydrate(rr_bundle).data
elif isinstance(bundle.obj, Bar):
bar_res = BarResource()
br_bundle = bar_res.build_bundle(obj=bundle.obj, request=bundle.request)
bundle.data = bar_res.full_dehydrate(br_bundle).data
return bundle
In RecordsResource class, you need to add model field as well (see https://github.com/tomchristie/django-rest-framework/blob/master/djangorestframework/resources.py#L232-234)
class RecordsResource(BaseRecordResource):
model = Record
class Meta(BaseRecordResource.Meta):
resource_name = 'records'
queryset = Record.objects.all()
Explaining from the beginning:
There are three styles of inheritance that are possible in Django.
Often, you will just want to use the parent class to hold
information that you don't want to have to type out for each child
model. This class isn't going to ever be used in isolation, so
Abstract base classes are what you're after.
If you're subclassing an existing model (perhaps something from
another application entirely) and want each model to have its own
database table, Multi-table inheritance is the way to go.
Finally, if you only want to modify the Python-level behavior of a
model, without changing the models fields in any way, you can use
Proxy models.
The choice here is Multi-table inheritance
Multi-table inheritance
The second type of model inheritance supported by Django is when each model in the hierarchy is a model all by itself. Each model corresponds to its own database table and can be queried and created individually. The inheritance relationship introduces links between the child model and each of its parents (via an automatically-created OneToOneField) Ref
To go from Record to Recordx where 1 <= x <= n you do a_example_record = Record.objects,get(pk=3) and then check what type of Recordx it is by using something like below
if hasattr(a_example_record, 'record1'):
# ...
elif hasattr(a_example_record, 'record2'):
# ...
So now that we know how to get the children from the parent and we need to provide TastyPie with a queryset in its meta, you need to write a custom queryset backed by a custom manager on the Record model that takes all your records (More here Custom QuerySet and Manager without breaking DRY?), checks what type of child it is and appends it to a queryset or a list. You can read about appending here How to combine 2 or more querysets in a Django view?