I have three models, as shown below:
class TagsModel(models.Model):
title = models.CharField(max_length=200)
def __str__(self):
return self.title
class ImagesModel(models.Model):
title = models.CharField(max_length=500, default='image')
image_cdn = models.TextField(null=True, blank=True)
image = models.ImageField(upload_to='articles/images/', null=True, blank=True)
timestamp = models.DateTimeField(auto_now=True)
update = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class ArticlesModel(models.Model):
title = models.CharField(max_length=1000)
category = models.CharField(max_length=40, choices=category_choices, default=('General', 'General'))
summary = models.TextField(blank=False, null=True, max_length=5000)
tags = models.ManyToManyField(TagsModel, blank=True)
image = models.ImageField(blank=True, null=True, upload_to='articles/article-image/')
image_cdn = models.TextField(blank=True, null=True)
image_src = models.ForeignKey(ImagesModel, related_name='Image_cdn', on_delete=models.PROTECT, null=True)
images = models.ManyToManyField(ImagesModel, blank=True)
json = models.JSONField(null=True, blank=True)
html = models.TextField(blank=True, null=True)
is_published = models.BooleanField(default=False)
update = models.DateTimeField(auto_now_add=True)
timestamp = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('articles:article_detail', kwargs={'article_id': self.id})
And in the view.py
class ArticlesView(APIView):
def get(self, request):
articles_list = ArticlesModel.objects.all()
images_list = ImagesModel.objects.all()
images_serializer = ImagesSerializer(images_list, many=True)
articles_serializer = ArticlesListSerializer(articles_list, many=True)
return Response({
'images':images_serializer.data,
'articles':articles_serializer.data
})
So when I send request I get results like this:
The problem here is that I get the ids of Images and tags and not the objects themselves!
I am asking if there's a way in django/DRF to get the objects (images, tags) included with the queries of Articles and not only their ids?
Solution
class ProductSerializer(serializers.ModelSerializer):
cate = serializers.SerializerMethodField('get_cate')
def get_cate(self,obj):
return [cate.name for cate in obj.cate.all()]
class Meta:
model = ProductModel
fields = "__all__"
Response output
[
{
"id": 1,
"cate": [
"Category5",
"Category6"
],
"name": "Apple",
"price": "12.00",
"released_at": "2022-10-18T13:16:01Z"
},
{
"id": 2,
"cate": [
"Category1",
"Category2",
"Category3"
],
"name": "Kiwi",
"price": "20.00",
"released_at": "2022-10-18T13:16:01Z"
},
{
"id": 3,
"cate": [
"Category2",
"Category4",
"Category5",
"Category6"
],
"name": "Tomato",
"price": "25.00",
"released_at": "2022-10-18T13:16:01Z"
}
]
This is called serializer relations, You can find the solution to your issue & more suggestions in this article https://www.django-rest-framework.org/api-guide/relations/
Related
I'm using Django RestFramework to create a simple eCommerce API where one product could have many images and I would like to get the URLs of all these images on a json field.
For now, I got the first image url using "imagembicicleta_set.all.first.image.url" on the serializer, but I need all URLs list:
{
"count": 7,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"nome": "Specialized Roubaix",
"marca__nome": "Specialized",
"categoria": "Bicicletas de Estrada",
"atividades": [
1
],
"terrenos": [
"Asfalto"
],
"preco": "16999.00",
"ano": 1,
"absolute_url": "/bicicletas/Specialized/specialized-roubaix-2020/",
"img_url": "/media/images/bicicletas/roubaix1.jpeg"
},
{
"id": 2,
"nome": "Specialized Roubaix Sport",
"marca__nome": "Specialized",
Following how is my setup:
Models.py
class Bicicleta(models.Model):
id = models.AutoField(primary_key=True)
nome = models.CharField(max_length=200, blank=False, null=False)
slug = models.SlugField(unique=True)
status = models.IntegerField(choices=STATUS_CHOICES, default=1, blank=False, null=False)
descricao = RichTextField(blank=True, null=True)
marca = models.ForeignKey(MarcaBicicleta, blank=True, null=True, on_delete=models.SET_NULL)
...
class ImagemBicicleta (models.Model):
bicicleta = models.ForeignKey(Bicicleta, default=None, on_delete=models.CASCADE)
image = models.ImageField(upload_to='images/bicicletas')
Serializer.py
class BicicletasSerializer(serializers.ModelSerializer):
marca__nome = serializers.CharField(source='marca.nome')
categoria = serializers.CharField(source='categoria.nome')
terrenos = serializers.StringRelatedField(many=True)
absolute_url = serializers.URLField(source='get_absolute_url', read_only=True)
img_url = serializers.URLField(source='imagembicicleta_set.all.first.image.url', read_only=True) #I could get the first image using this
class Meta:
model = Bicicleta
fields = ['id', 'nome', 'marca__nome', 'categoria', 'atividades', 'terrenos', 'preco', 'ano', 'absolute_url', 'img_url']
views.py
class BicicletasView(generics.ListAPIView):
serializer_class = BicicletasSerializer
queryset = Bicicleta.objects.all()
filter_backends = (DjangoFilterBackend, SearchFilter)
filterset_fields = ['marca', 'terrenos', 'status']
search_fields = {'nome': ['icontains'], }
How could I get all images URLs in the field?
Per example, if a product has 3 different images, I would expect to have the img field like this:
"img_url": [ "/media/images/bicicletas/roubaix1.jpeg","/media/images/bicicletas/roubaix2.jpeg","/media/images/bicicletas/roubaix3.jpeg" ],
You can add a method serializer which will collect all the urls for each individual object like this:
class BicicletasSerializer(serializers.ModelSerializer):
marca__nome = serializers.CharField(source='marca.nome')
categoria = serializers.CharField(source='categoria.nome')
terrenos = serializers.StringRelatedField(many=True)
absolute_url = serializers.URLField(source='get_absolute_url', read_only=True)
img_url = serializers.SerializerMethodField()
def get_image_url(self , instance):
return ImagemBicicleta.objects.filter(bicicleta=instance).values_list('image',flat=True)
class Meta:
model = Bicicleta
fields = ['id', 'nome', 'marca__nome', 'categoria', 'atividades', 'terrenos', 'preco', 'ano', 'absolute_url', 'img_url']
I am trying to get all the bills and their customer-related details (i.e. 'customer_code', 'email' etc.) with it.
However, source='user.customer_code does not seem to have any effect at all. What am I missing?
I have been following along this:
this stackoverflow post with no luck.
My two models:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(db_index=True, unique=True, max_length=200)
customer_code = models.CharField(max_length=300, blank=True, null=True, default=None)
class Bill(models.Model):
customer = models.ForeignKey(
User, on_delete=models.CASCADE, blank=True, null=True, related_name="customer_bill"
)
payable_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
View:
class BillView(APIView):
def get(self, request, format=None):
q = Bill.objects.all().select_related('customer')
s = BillSerializer(q, many=True)
return JsonResponse({
"bill": s.data
})
Serializer:
class BillSerializer(serializers.ModelSerializer):
customer_code = serializers.CharField(source='user.customer_code', read_only=True)
class Meta:
model = Bill
fields = ('id','payable_amount','customer_code') # binding customer_code here
Current Output:
"bill": [
{
"id": 1,
"payable_amount": "1000.00"
},
{
"id": 2,
"payable_amount": "2000.00"
}
]
Expected Result:
"bill": [
{
"id": 1,
"payable_amount": "1000.00",
"customer_code": "CUS10001" # want this to be attached
},
{
"id": 2,
"payable_amount": "2000.00",
"customer_code": "CUS10002" # want this to be attached
}
]
I'm trying to make an API for a butcher.
With this API and the website that I will build by the next, the client will be able to make his order remotly.
Here is my probleme.
With the order form, the client send me JSON data like here :
{
"user": 8,
"orderDay": "2020-06-24",
"deliveryDay": "2020-06-30",
"deliveryAddress": "Place des Fêtes",
"comment": "",
"orderDetail":
[
{
"product": 2,
"byProduct": 2,
"quantity": 43
},
{
"product": 3,
"byProduct": 3,
"quantity": 5
}
]
}
These data must be saved in the database.
These are the models that I use : models.py
class order(models.Model):
user = models.ForeignKey(memberArea, on_delete=models.CASCADE)
comment = models.TextField(null=True, blank=True)
orderDay = models.DateTimeField(auto_now_add=True)
deliveryDay = models.DateField()
deliveryAddress = models.CharField(max_length=255)
state = models.CharField(max_length=255)
price = models.TextField(null=True, blank=True)
response = models.TextField(null=True, blank=True)
class orderDetail(models.Model):
order = models.ForeignKey(order, on_delete=models.CASCADE)
product = models.ForeignKey(product, on_delete=models.CASCADE)
byProduct = models.ForeignKey(byProduct, on_delete=models.CASCADE)
quantity = models.CharField(max_length=255)
class product(models.Model):
name = models.CharField(max_length=255)
prix_uni = models.TextField(null=True, blank=True)
prix_kg = models.TextField(null=True, blank=True)
dispo = models.BooleanField(null=True, blank=True)
category = models.ForeignKey(category, on_delete=models.CASCADE)
redu = models.TextField(null=True, blank=True)
class byProduct(models.Model):
product = models.ForeignKey(product, on_delete = models.CASCADE)
name = models.CharField(max_length=255)
I make a serializer file like this serializer.py
class orderDetailSerializer(serializers.ModelSerializer):
order = serializers.PrimaryKeyRelatedField(many=False, queryset = order.objects.all())
class Meta:
model = orderDetail
fields = '__all__'
class OrderSerializer(serializers.ModelSerializer):
orderDetail = orderDetailSerializer(many=True)
class Meta:
model = order
fields = ['user', 'comment', 'deliveryAddress', 'deliveryDay', 'orderDetail']
def create(self, validated_data):
order_detail_data = validated_data.pop('orderDetail')
new_order = order.objects.create(**validated_data)
new_order.save()
for product in order_detail_data:
order_detail = orderDetail.objects.create(order=new_order, **product)
new_order.orderDetail.add(order_detail.id)
return new_order
And this is my view : views.py:
#Make an order
#api_view(['POST'])
def order(request, format=None):
if request.method == 'POST':
serializer = OrderSerializer(data=request.data)
data = {}
if serializer.is_valid():
serializer.save()
data['response'] = "Your order went well"
return Response(data)
return Response(serializer.errors)
When I try to run my code, it tells me that the order data is missing :
{
"orderDetail": [
{
"order": [
"This field is required."
]
},
{
"order": [
"This field is required."
]
}
]
}
I don't know how to add this because the order_id that I need is created at the same time that the orderDetail.
Thank's by advance for helping me.
you should make order field readonly in orderDetailSerializer:
class orderDetailSerializer(serializers.ModelSerializer):
class Meta:
model = orderDetail
fields = '__all__'
read_only_fields = ('order',)
when developing my application I ran into a problem
AssertionError at /api/update/
`child` is a required argument.
Request Method: GET
Request URL: http://45.56.80.77/api/update/?token=1234567&ifloorplan=2
Django Version: 3.0.6
Exception Type: AssertionError
Exception Value:
`child` is a required argument.
Exception Location: /root/Env/ifloorplan/lib/python3.8/site-packages/rest_framework/serializers.py in __init__, line 591
Python Executable: /usr/local/bin/uwsgi
Python Version: 3.8.2
Python Path:
['.',
'',
'/usr/lib/python38.zip',
'/usr/lib/python3.8',
'/usr/lib/python3.8/lib-dynload',
'/root/Env/ifloorplan/lib/python3.8/site-packages']
Server time: Mon, 1 Jun 2020 07:18:31 +0000
that is, I tried to update and create in the database a record of this kind
[
{
"id": 2,
"level": [
{
"id": 2,
"plan_level": {
"img": null,
"img_height": null,
"img_width": null,
"position_x": null,
"position_y": null,
"level": null
},
"images": [
{
"id": 2,
"cam": [
{
"id": 2,
"ken_burns": {
"effect": null,
"start_pos_x": null,
"start_pos_y": null,
"finish_pos_x": null,
"finish_pos_y": null,
"start_width": null,
"start_height": null,
"finish_width": null,
"finish_height": null,
"cam": null
},
"rotation": 0.34,
"pos_x": 21.0,
"pos_y": 234.0,
"width": 1234.0,
"height": 234.0,
"img": 2
},
{
"id": 3,
"ken_burns": {
"effect": null,
"start_pos_x": null,
"start_pos_y": null,
"finish_pos_x": null,
"finish_pos_y": null,
"start_width": null,
"start_height": null,
"finish_width": null,
"finish_height": null,
"cam": null
},
"rotation": 4.0,
"pos_x": 354.0,
"pos_y": 345.0,
"width": 345.0,
"height": 345.0,
"img": 2
}
],
"img": "/media/ifloorplans_source/test.jpg",
"level": 2
}
],
"tabLabel": "1 st",
"plan": 2
}
],
"textColor": "WQ2123",
"bgColor": "SA2132",
"camColor": "CV1234",
"author": 14
}
]
here is my serializer.py
from django.contrib.auth.models import User
from .models import (
IFloorPlan,
Level,
PlanImg,
Image,
Cam,
KenBurns,
)
from rest_framework import serializers
class KenBurnsSerializer(serializers.ListSerializer):
# plan = IFloorPlanSerializer(required=True)
def update(self, instance, validated_data):
pass
def create(self, validated_data):
predictions = [KenBurns(**item) for item in validated_data]
return KenBurns.objects.bulk_create(predictions)
class Meta:
model = KenBurns
fields = '__all__'
class CamSerializer(serializers.ListSerializer):
ken_burns = KenBurnsSerializer()
def update(self, instance, validated_data):
pass
def create(self, validated_data):
predictions = [Cam(**item) for item in validated_data]
return Cam.objects.bulk_create(predictions)
class Meta:
model = Cam
fields = '__all__'
class ImageSerializer(serializers.ListSerializer):
cam = CamSerializer(many=True)
def update(self, instance, validated_data):
pass
def create(self, validated_data):
predictions = [Image(**item) for item in validated_data]
return Image.objects.bulk_create(predictions)
class Meta:
model = Image
fields = '__all__'
class PlanImgSerializer(serializers.ListSerializer):
# plan = IFloorPlanSerializer(required=True)
def update(self, instance, validated_data):
pass
def create(self, validated_data):
predictions = [PlanImg(**item) for item in validated_data]
return PlanImg.objects.bulk_create(predictions)
class Meta:
model = PlanImg
fields = '__all__'
class LevelSerializer(serializers.ListSerializer):
plan_level = PlanImgSerializer()
images = ImageSerializer(many=True)
def update(self, instance, validated_data):
pass
def create(self, validated_data):
predictions = [Level(**item) for item in validated_data]
return Level.objects.bulk_create(predictions)
class Meta:
model = Level
fields = '__all__'
class IFloorPlanSerializer(serializers.Serializer):
level = LevelSerializer(many=True)
class Meta:
list_serializer_class = LevelSerializer
model = IFloorPlan
fields = '__all__'
views.py
from django.shortcuts import render
from django.http.response import JsonResponse
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .serializers import IFloorPlanSerializer
from rest_framework.views import APIView
from .models import IFloorPlan
from public.models import ApiToken
# Create your views here.
#api_view(['GET', ])
def get_ifloorplan(request):
if "token" in request.GET and "ifloorplan" in request.GET:
token_obj = ApiToken.objects.filter(token=request.GET["token"])
if token_obj:
plans = IFloorPlan.objects.filter(id=request.GET["ifloorplan"])
serializer = IFloorPlanSerializer(plans, many=True)
return Response(serializer.data)
else:
return JsonResponse({"message": "Don't valid token"}, status=status.HTTP_400_BAD_REQUEST)
else:
return JsonResponse({"message": "Don't valid GET parameter"}, status=status.HTTP_400_BAD_REQUEST)
#api_view(['PUT', 'GET',])
def update_ifloorplan(request):
if "token" in request.GET and "ifloorplan" in request.GET:
token_obj = ApiToken.objects.filter(token=request.GET["token"])
if token_obj:
plans = IFloorPlan.objects.filter(id=request.GET["ifloorplan"])
serializer = IFloorPlanSerializer(plans, data=request.data, many=True)
if serializer.is_valid():
serializer.save()
return JsonResponse({"message": "Update successful"}, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
else:
return JsonResponse({"message": "Don't valid token"}, status=status.HTTP_400_BAD_REQUEST)
else:
return JsonResponse({"message": "Don't valid GET parameter"}, status=status.HTTP_400_BAD_REQUEST)
#api_view(['POST', 'GET'])
def create_ifloorplan(request):
if "token" in request.GET:
token_obj = ApiToken.objects.get(token=request.GET["token"])
if token_obj:
plans = IFloorPlan(author=token_obj.user)
serializer = IFloorPlanSerializer(plans, data=request.data, many=True)
if serializer.is_valid():
serializer.save()
return JsonResponse({"message": "Create successful"}, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
else:
return JsonResponse({"message": "Don't valid token"}, status=status.HTTP_400_BAD_REQUEST)
else:
return JsonResponse({"message": "Don't valid GET parameter"}, status=status.HTTP_400_BAD_REQUEST)
models.py
from django.db import models
from django.contrib.auth.models import User
class IFloorPlan(models.Model):
textColor = models.CharField(max_length=10, blank=True, null=True)
bgColor = models.CharField(max_length=10, blank=True, null=True)
camColor = models.CharField(max_length=10, blank=True, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
class Level(models.Model):
plan = models.ForeignKey(IFloorPlan, on_delete=models.CASCADE, related_name='level', blank=True, null=True)
tabLabel = models.CharField(max_length=100, blank=True, null=True)
class PlanImg(models.Model):
level = models.ForeignKey(Level, on_delete=models.CASCADE, related_name='plan_level', blank=True, null=True)
img = models.ImageField(upload_to='ifloorplans_source/', blank=True, null=True)
img_height = models.FloatField(blank=True, null=True)
img_width = models.FloatField(blank=True, null=True)
position_x = models.FloatField(blank=True, null=True)
position_y = models.FloatField(blank=True, null=True)
class Image(models.Model):
level = models.ForeignKey(Level, on_delete=models.CASCADE, related_name='images', blank=True, null=True)
img = models.ImageField(upload_to='ifloorplans_source/', blank=True, null=True)
class Cam(models.Model):
rotation = models.FloatField(blank=True, null=True)
pos_x = models.FloatField(blank=True, null=True)
pos_y = models.FloatField(blank=True, null=True)
img = models.ForeignKey(Image, on_delete=models.CASCADE, related_name='cam', blank=True, null=True)
width = models.FloatField(blank=True, null=True)
height = models.FloatField(blank=True, null=True)
class KenBurns(models.Model):
cam = models.ForeignKey(Cam, on_delete=models.CASCADE, related_name='ken_burns', blank=True, null=True)
effect = models.BooleanField(default=False, blank=True, null=True)
start_pos_x = models.FloatField(blank=True, null=True)
start_pos_y = models.FloatField(blank=True, null=True)
finish_pos_x = models.FloatField(blank=True, null=True)
finish_pos_y = models.FloatField(blank=True, null=True)
start_width = models.FloatField(blank=True, null=True)
start_height = models.FloatField(blank=True, null=True)
finish_width = models.FloatField(blank=True, null=True)
finish_height = models.FloatField(blank=True, null=True)
I can’t solve this problem already the second day
At first there was a problem with the inability to update multiple tables. So I decided to use the list serializer to update and create records in the table. I will be glad of any help
A ListSerializer is basically a container for other serializers and can only contain one specific serializer, which is set as it's "child" serializer.
Your LevelSerializer extends ListSerializer but does not provide a this child attribute. This is typically not the way to use a ListSerializer - extending it is only needed for very special cases. In the common case, you want create a normal serializer for the Level model and set it as a field with many=True as argument. This will create a list serializer under the hood as described in in the documentation
You make the mistake several times, but this should get you started in the right direction:
class LevelSerializer(serializers.ModelSerializer)
class Meta:
model = Level
fields = ('plan', 'tabLabel')
class IFloorPlanSerializer(serializers.Serializer):
level = LevelSerializer(many=True)
To extend the list serializer and provide a custom update method:
class LevelListSerializer(serializers.ListSerializer):
child = LevelSerializer()
def update(self, instance, validated_data):
# instance is the queryset, validated_data a list of dicts
# See [the documentation](https://github.com/encode/django-rest-framework/blob/master/docs/api-guide/serializers.md#customizing-multiple-update) for an example.
class IFloorPlanSerializer(serializers.Serializer):
level = LevelListSerializer()
I have first made category crud, and then product crud with many-to-many relation with category.
models.py (category):
class Category(models.Model):
name = models.CharField(max_length=191, blank=False, null=False)
description = models.TextField(blank=True, null=True)
models.py (product):
class Product(models.Model):
product_code = models.CharField(max_length=191, blank=False, null=False)
name = models.CharField(max_length=191, blank=False, null=False)
description = models.TextField(blank=False, null=False)
price = models.DecimalField(max_digits=19, decimal_places=2)
photo = models.ImageField(upload_to='pictures/products/', max_length=255, null=False, blank=False)
category = models.name = models.ManyToManyField(Category)
How to achieve following result:
{
"categories": [
{
"id": 1,
"name": "Indoor Muscle Training",
"description": null,
"products":{
"name":"product_name",
"code":"product_code"
}
},
{
"id": 2,
"name": "Outdoor Muscle Training",
"description": null,
"products":{
"name":"product_name",
"code":"product_code"
}
}
]
}
using serializer-method field can be an option for this case. Our goal is get product information from category serializer. So for this
class CategorySerializer(serializers.ModelSerializer):
products = serializers.SerializerMethodField()
class Meta:
model = Category
fields = ('') # add relative fields
def get_products(self, obj):
products = obj.product_set.all() # will return product query set associate with this category
response = ProductSerializer(products, many=True).data
return response