How to make calculations after requesting with M2M fields (DRF) - django

class Product(models.Model):
name = models.CharField(max_length=255)
ht = models.CharField(default=0)
wd = models.CharField(default=0)
len = models.CharField(default=0)
class Parcel(models.Model):
product_list = models.ManyToManyField(Product)
class ParcelSerializer(serializers.ModelSerializer):
class Meta:
model = Parcel
fields = '__all__'
class ProductSerializer(serializers.ModelSerializer):
product_volume = serializers.ReadOnlyField()
class Meta:
model = Product
fields = '__all__'
class ParcelCreateView(generics.CreateAPIView):
queryset = Package.objects.all()
serializer_class = PackageSerializer
class ParcelListView(generics.ListAPIView):
serializer_class = ParcelSerializer
class ProductCreateView(generics.CreateAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
class ProductListView(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
My output is:
[
{
"id": 1,
"products": [
1
]
},
{
"id": 2,
"products": [
1,
2
]
}
]
http://127.0.0.1:8000/parcel/create
How can I get product id and product volume as below when I post parcel create? I didn't understand what to do because it's ManyToManyField. After the request comes, how can I get product ids from the body and calculate their volume
[
{
"id": 1,
"products": [
1 : Result #Volume of ID : 1 Product (wd*ht*len)
]
},
{
"id": 2,
"products": [
1 : Result #Volume of ID : 1 Product (wd*ht*len),
2 : Result #Volume of ID : 1 Product (wd*ht*len)
]
}
]

You can work with a serializer in your ParcelSerializer:
class ProductSerializer(serializers.ModelSerializer):
# …
pass
class ParcelSerializer(serializers.ModelSerializer):
products = ProductSerializer(many=True)
class Meta:
model = Parcel
fields = '__all__'
You thus need to define the ProductSerializer first, and then use that in the ParcelSerializer.
If you want to return the outcome of str(…) on a Product, you can work with the StringRelatedField [drf-doc]:
class ParcelSerializer(serializers.ModelSerializer):
products = StringRelatedField(many=True)
class Meta:
model = Parcel
fields = '__all__'
then this will thus produce a list of strings.

Related

serializer only certain fields in foreing key relation

I'm trying to serializer two nested models linked by a foreing key:
class Category(models.Model):
sp = models.ForeignKey('species.Sp', on_delete=models.CASCADE, related_name='species_category')
category_name = models.CharField(max_length=50)
class Catch(models.Model):
weight = models.IntegerField()
category = models.ForeignKey('species.Category', on_delete=models.CASCADE,)
I know is possible to use depth option, but it serialize all the fields of the related model, for example:
class CatchesSerializer(serializers.ModelSerializer):
class Meta:
model = Catch
fields = ['id', 'weight', 'category', ]
depth = 1
returns
[
{
"id": 335,
"weight": 4710,
"category": {
"id": 1,
"category_name": "1",
"sp": 41
}
},
...
]
How is the way to serialize only certains fields of the related model? for example:
[
{
"id": 335,
"weight": 4710,
"category": {
"sp": 41,
}
},
...
]
Serializer can be nested, you can try:
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['sp']
class CatchesSerializer(serializers.ModelSerializer):
category = CategorySerializer()
class Meta:
model = Catch
fields = ['id', 'weight', 'category']

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 in one serialize pull out child objects

This my models
class Dictionary(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
parentId = models.UUIDField(editable=True, null=True)
name = models.CharField(max_length=100)
date_create = models.DateTimeField(auto_now=True)
date_end = models.DateTimeField(auto_now=False, null=True)
class Teacher(models.Model):
name = models.CharField(max_length=100)
message = models.CharField(max_length=300)
status = models.OneToOneField(Dictionary, on_delete=models.CASCADE)
this is my urls
from django.urls import path
from . import views
urlpatterns = [
path('get', views.GetViewSet.as_view({'get': 'list'})),
]
This is ViewSet
class GetViewSet(viewsets.ModelViewSet):
MyApiObj = null
#property
def api_object(self):
return namedtuple("ApiObject", self.request.data.keys())(*self.request.data.values())
def get_serializer_class(self):
GeneralSerializer.Meta.model = apps.get_model(app_label=self.MyApiObj.app, model_name=self.MyApiObj.object)
return GeneralSerializer
def post(self, request):
self.MyApiObj = self.api_object
return self.select_api()
def select_api(self):
queryset = QueryHelper.select(self.MyApiObj)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Serializer
class GeneralSerializer(serializers.ModelSerializer):
class Meta:
model = None
fields = '__all__'
My post parameters to django
{
"app":"leads",
"object":"Teacher",
"settings":{
},
"data":{
}
}
answer:
[
{
"id": 1,
"name": "John",
"message": "Hi everyone",
"status": "e3b86ed4-8794-413b-994c-b1ec0a43eebe"
}
]
Problem is Dictionary(status) model give me id(uuid) but i need whole object without creating new serializer for Dictionary. i do univeral serializer for all models in my app
Try this:
class DictionarySerializer(serializers.ModelSerializer):
class Meta:
model = Dictionary
fields = '__all__'
class GeneralSerializer(serializers.ModelSerializer):
status = DictionarySerializer(required=True)
class Meta:
model = None
fields = '__all__'
But it is not good for me because 1) Without other serializer 2) I need Universal serializer for all models and with child model in all models of my project. Help me please)
I need something like this
[
{
"id": 1,
"status": {
"id": "e3b86ed4-8794-413b-994c-b1ec0a43eebe",
"parentId": "dc6cf7da-b82c-11e9-a2a3-2a2ae2dbcce4",
"name": "Spravochnik1",
"date_create": "2019-08-06T09:30:49.355439Z",
"date_end": "2019-08-06T09:29:57Z"
},
"name": "John",
"message": "Hi everyone"
}
]
for nested serialization check full ref here
and for your case add depth = 1
class GeneralSerializer(serializers.ModelSerializer):
status = DictionarySerializer(required=True)
class Meta:
model = None
fields = '__all__'
depth = 1

how to display tabularinline in api django rest framework?

I am with the demand of a system to manage the schedule of a cinema and to generate an api.
models.py
class Movie(models.Model):
title = models.CharField('título', max_length=250)
synopsis = models.TextField('sinopse', max_length=500)
year = models.IntegerField('ano')
# ... #
class Exhibition(models.Model):
movie = models.ForeignKey(Movie, verbose_name='Filme')
start = models.DateField('Início')
finish = models.DateField('Encerramento')
class Schedule(models.Model):
CINE_DERBY = 'CD'
CINE_CASAFORTE = 'CCF'
CINEMA = (
(CINE_CASAFORTE, 'Cinema Casa Forte'),
(CINE_DERBY, 'Cinema Derby')
)
data = models.DateTimeField('data')
local = models.CharField('local', max_length=5, choices=CINEMA)
exhibition = models.ForeignKey(Exhibition, verbose_name='Em cartaz')
admin.py
class ScheduleInline(admin.TabularInline):
model = Schedule
extra = 1
class MovieModelAdmin(admin.ModelAdmin):
list_display = ['title', 'synopsis', 'year']
class ExhibitionModelAdmin(admin.ModelAdmin):
inlines = [ScheduleInline]
list_display = ['movie', 'start', 'finish']
serializer.py
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movie
fields = '__all__'
depth = 1
class ScheduleSerializer(serializers.ModelSerializer):
class Meta:
model = Schedule
fields = ['id', 'data', 'local', 'exhibition']
depth = 1
class ExhibitionSerializer(serializers.ModelSerializer):
movie = MovieSerializer(read_only=True)
movieId = serializers.PrimaryKeyRelatedField(write_only=True,
queryset=Movie.objects.all(),
source='movie')
schedule = ScheduleSerializer(many=True, read_only=True)
class Meta:
model = Exhibition
fields = ['movie', 'movieId', 'start', 'finish', 'schedule']
views.py
class MovieListViewSet(viewsets.ModelViewSet):
serializer_class = MovieSerializer
queryset = Movie.objects.all()
class ScheduleListViewSet(viewsets.ModelViewSet):
serializer_class = ScheduleSerializer
queryset = Schedule.objects.all()
class ExhibitionListViewSet(viewsets.ModelViewSet):
serializer_class = ExhibitionSerializer
queryset = Exhibition.objects.all()
I'm having trouble getting the movie times displayed on the display. I did based on the documentation of nested relationships, but the inline tabular part does not work: schedule is not displayed.
I would like api to generate the following:
[
{
"movie": {
"id": 1,
"title": "Vingadores: Guerra Infinita",
"synopsis": "Homem de Ferro, Thor, Hulk e os Vingadores se unem para combater seu inimigo mais poderoso, o maligno Thanos. Em uma missão para coletar todas as seis pedras infinitas, Thanos planeja usá-las para infligir sua vontade maléfica sobre a realidade.",
"year": 2018,
},
"schedule": [
{
"id": 1,
"data": "2018-04-26T14:00:00Z",
"local": "CFD",
},
{
"id": 2,
"data": "2018-05-03T20:00:00Z",
"local": "CFCF",
},
],
"start": "2018-04-30",
"finish": "2018-08-24"
}
]
The problem you are likely hitting is that DRF is looking for a field or a property on your Exhibition model called schedule but this doesn't exist.
I don't believe DRF can handle a reverse relation using just a field definition, you have to be more specific. Luckily DRF does make it easy to be more specific.
You can make use of the SerializerMethodField.
For example:
class ExhibitionSerializer(serializers.ModelSerializer):
movie = MovieSerializer(read_only=True)
movieId = serializers.PrimaryKeyRelatedField(write_only=True,
queryset=Movie.objects.all(),
source='movie')
schedule = serializers.SerializerMethodField()
class Meta:
model = Exhibition
fields = ['movie', 'movieId', 'start', 'finish', 'schedule']
def get_schedule(self, obj):
return [ScheduleSerializer(s).data for s in obj.schedule_set.all()]
It work for me. Thank you.
Take in addition for one_to_one relations. ))
#staticmethod
def get_picture(obj):
return PictureSerializer(obj.picture).data if hasattr(obj, 'picture') else 'no_picture_found'

django rest framework return a custom object using ModelSerializer and ModelViewSet

I have three models, three serializers, one modelviewset below.
I am using django-rest-framework to make a rest api for android.
The restaurant model was created first. Then I created a star model and an image model.
What I want to do is to add star and image objects into restaurant objects.
finally I've got what I want result but I think my viewset code looks like wrong..
Is there another way not to use "for loop"?
Models
class Restaurant(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
address = models.CharField(max_length=255)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
weather = models.ForeignKey(Weather, on_delete=models.CASCADE)
distance = models.ForeignKey(Distance, on_delete=models.CASCADE)
description = models.TextField('DESCRIPTION')
def __str__(self):
return self.name
class Star(models.Model):
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.IntegerField('RATING')
def __str__(self):
return self.restaurant
class RestaurantImage(models.Model):
id = models.AutoField(primary_key=True)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
path = models.CharField(max_length=255)
Serializer
class StarSerializer(serializers.ModelSerializer):
class Meta:
model = Star
fields = ('id', 'restaurant', 'user', 'rating', )
class RestaurantDetailSerializer(serializers.ModelSerializer):
category = CategorySerializer()
weather = WeatherSerializer()
distance = DistanceSerializer()
class Meta:
model = Restaurant
fields = ('id', 'name', 'address', 'category', 'weather',
'distance', 'description', )
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = RestaurantImage
fields = ('id', 'path', 'restaurant')
ViewSet
class RestaurantDetailInfoViewSet(viewsets.ModelViewSet):
queryset = Restaurant.objects.all()
serializer_class = RestaurantSerializer
def list(self, request, *args, **kwargs):
restaurant_list = Restaurant.objects.all()
restaurant_result = []
for restaurant in restaurant_list:
restaurantInfo = Restaurant.objects.filter(id=restaurant.pk)
restaurant_serializer = RestaurantDetailSerializer(restaurantInfo, many=True)
ratingAverageValue = Star.objects.filter(restaurant=restaurant.pk).aggregate(Avg('rating'))
images = RestaurantImage.objects.filter(restaurant=restaurant.pk)
image_serializer = ImageSerializer(images, many=True)
restaurant_dic = {
'restaurant': restaurant_serializer.data,
'ratingAverage': ratingAverageValue['rating__avg']
if ratingAverageValue['rating__avg'] is not None else 0,
'images': image_serializer.data
}
restaurant_result.append(restaurant_dic)
return Response(restaurant_result)
Result
[
{
"restaurant": [
{
"id": 1,
"name": "restaurant1",
"address": "address1",
"category": {
"c_id": 1,
"name": "foodtype1"
},
"weather": {
"w_id": 1,
"name": "sunny"
},
"distance": {
"d_id": 1,
"name": "inside"
},
"description": "description1"
}
],
"ratingAverage": 2.6667,
"images": [
{
"id": 1,
"path": "imagepath",
"restaurant": 1
}
]
},
Solution:
class RestaurantDetailSerializer(serializers.ModelSerializer):
category = CategorySerializer()
weather = WeatherSerializer()
distance = DistanceSerializer()
images = ImageSerializer(many=True, read_only=True)
ratingAverage = serializers.SerializerMethodField(read_only=True)
def get_ratingAverage(self, restaurant):
ratingAvgVal = Star.objects.filter(
restaurant=restaurant
).aggregate(Avg('rating'))['rating__avg']
return ratingAvgVal if ratingAvgVal is not None else 0
class Meta:
model = Restaurant
fields = ('id', 'name', 'address', 'category', 'weather',
'distance', 'description', 'images', 'ratingAverage', )
Explanation:
Here, I have nested the ImageSerializer in the RestaurantSerializer class, since you needed all the fields you've defined in ImageSerializer.
Then, for ratingAverage, I have used the SerializerMethodField which returns the value calculated (your logic) in the method I've defined for it, i.e. get_ratingAverage, which takes the Restaurant instance reference passed as an argument to the method for the field.