how to get foreignkey fields in serializer - django-models

In serializer, class I am trying to get the category details such as "name" but the following code gives me the foreignkey id
models.py
class Category(MP_Node, Timestamps):
name = models.CharField(_('Name'), max_length=255, db_index=True)
class VideoCategory(Category):
image = models.ImageField(upload_to='video_categories', blank=True, null=True, max_length=255)
class VideoCategoryVideo(BaseModel, Timestamps, SoftDelete):
video = models.ForeignKey(Video, on_delete=models.CASCADE)
category = models.ForeignKey(VideoCategory, on_delete=models.CASCADE, null=True)
serializers.py
class VideoCategoryVideoSerializer(serializers.ModelSerializer):
class Meta:
model = VideoCategoryVideo
fields = ('category', )
class VideosDetailsListSerializer(serializers.ModelSerializer):
category = serializers.SerializerMethodField()
class Meta:
model = Video
fields = ('id', 'create_date', 'category')
def get_category(self, data):
cate = VideoCategoryVideo.objects.filter(video=data.id)
category = VideoCategoryVideoSerializer(cate, many=True)
return category.data
result is:
"category": [
{
"category": 1
}]
but the expected result is
"category": [
{
"name": "cate_name"
}]

You can add a CharField to VideoCategoryVideoSerializer and specify the source of the value like this:
class VideoCategoryVideoSerializer(serializers.ModelSerializer):
name = serializers.CharField(source='category.name', read_only=True)
class Meta:
model = VideoCategoryVideo
fields = ('category', 'name')
This will tell the serializer to get the value for name from the related category.

Related

How can I send the Name of the Item instead of PK in Django-Rest-Framework using POSTMAN?

So I am creating a E-Commerce API using the Django Rest Framework and I have been trying to send the name of the Item instead of the PK of the Item to create an order.
These are the models I am using:
class Product(models.Model):
product_tag = models.CharField(max_length=10)
name = models.CharField(max_length=100)
description = models.CharField(max_length=500, null=True, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
price = models.IntegerField()
stock = models.IntegerField()
image = models.ImageField(default="default.png")
in_stock = models.BooleanField(default=True)
date_created = models.DateField(auto_now_add=True)
class Meta:
ordering = ["-date_created"]
def __str__(self):
return self.name
class PlacedOrder(models.Model):
ordered_by = models.ForeignKey(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
phone = models.CharField(max_length=15)
address = models.CharField(max_length=100)
zipcode = models.CharField(max_length=100)
items = models.ManyToManyField(Product)
total_price = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ["-created_at"]
def __str__(self):
return f"{self.ordered_by}"
This is my serializer that I am working with to create an order:
class PlacedOrderSerializer(serializers.ModelSerializer):
ordered_by = serializers.ReadOnlyField(source="ordered_by.email")
class Meta:
model = PlacedOrder
fields = (
"id",
"created_at",
"ordered_by",
"first_name",
"last_name",
"phone",
"address",
"zipcode",
"items",
"total_price",
)
Here is the view I am using to create an order:
class CreateOrder(generics.ListCreateAPIView):
permission_classes = [IsAuthenticated]
queryset = PlacedOrder.objects.all()
serializer_class = PlacedOrderSerializer
def perform_create(self, serializer):
serializer.save(ordered_by=self.request.user)
This is my input in POSTMAN:
{
"first_name": "yes",
"last_name": "no",
"phone": "0100000000",
"address": "whatever address",
"zipcode": "254",
"items": [
1,
1,
2
],
"total_price": "69"
}
and this is the output:
{
"id": 13,
"created_at": "2022-10-26T20:56:08.789574Z",
"ordered_by": "bal#bal.com",
"first_name": "yes",
"last_name": "no",
"phone": "0100000000",
"address": "whatever address",
"zipcode": "254",
"items": [
1,
2
],
"total_price": 69
}
I basically want it to take the name and quantity of the items instead of the PK of the product in the input.
I have tried using RelatedField but that makes it so that "items" just goes null to the backend without taking any products and makes a blank order.
Try this approach:
class PlacedOrderSerializer(serializers.ModelSerializer):
# HERE
ordered_by = serializers.StringRelatedField(many=True, read_only=True)
class Meta:
model = PlacedOrder
fields = (
"id",
"created_at",
"ordered_by",
"first_name",
"last_name",
"phone",
"address",
"zipcode",
"items",
"total_price",
)
But make sure to add self.name to the models __str__.
You need to use a SlugRelatedField [DRF-doc] here, like:
class PlacedOrderSerializer(serializers.ModelSerializer):
ordered_by = serializers.ReadOnlyField(source="ordered_by.email")
items = serializers.SlugRelatedField(
many=True,
slug_field='name',
queryset=Product.objects
)
class Meta:
model = PlacedOrder
fields = (
# ...
)
This will thus make the serializer interpret the name fields, and query the Product objects with that name, and add these to the many-to-many relation.
Note that this will result in a query for each item, so if the list of items can be long, you might want to consider a different strategy, like serializing a list of product PKs, and then in the view construct a mapping name -> product, and use that to construct the many-to-many relation.
Note: It is normally better to use a slug field (a short identifier), than a name field for these kinds of relations. Slugs are meant to be database friendly (only contain ascii characters, no spaces, etc), and furthermore slugs normally do not change, whilst a name can. So if you later rename a Product, then the order will no longer point to the correct product.

How to make it by list comprehension

I need to get the child list under the parent list as a group.
class ServiceSerializer(serializers.ModelSerializer):
cleaning_type = serializers.CharField(source='cleaning_type.cleaning_type_name')
class Meta:
model = Service
fields = ('id', 'cleaning_type','service_name')
class ServiceTypeViewSet(ModelViewSet):
serializer_class = ServiceSerializer
http_method_names = ["get"]
queryset = Service.objects.all()
def get_queryset(self):
"""
This view should return a list of all the service types.
"""
servicename_list = Service.objects.all()
return servicename_list
It shows:
[
{
"id": 1,
"cleaning_type": "Lite service",
"service_name": "Floors",
},
{
"id": 2,
"cleaning_type": "Lite service",
"service_name": "Bathrooms",
},
{
"id": 3,
"cleaning_type": "Lite service",
"service_name": "Kitchen",
}
]
I want this to be in the following format:
[
{
id: 1,
cleaning_type: 'Lite service',
service_name: ['Floors', 'bathroom', 'kitchen'],
},
{
id: 2,
cleaning_type: 'Moving cleaning',
service_name: ['Kitchen Including All Appliances And Cabinets'],
},
]
That means all child elements will be under a separate parent list. Not separate by separate.
models.py is here:
Cleaning Type Model:
class CleaningType(models.Model):
cleaning_type_name = models.CharField(
_("Select Cleaning Type"), blank=True, null=True, max_length=255)
price = models.DecimalField(default=0,max_digits=6, decimal_places=2)
def __str__(self):
return self.cleaning_type_name
Service Model:
class Service(models.Model):
cleaning_type = models.ForeignKey(
CleaningType, on_delete=models.CASCADE)
service_name = models.CharField(
_("Service Name"), blank=True, null=True, max_length=255)
#string type added
def __str__(self):
return str(self.service_name)
I want sub categories under parent caterories. Here cleaning_type is the parent category and service is the child category of cleaning_type. i.e : cleaning_type >> service_type
First you need to move the cleaningtype field into the ServiceType model.
And I think it's better to set the related_name from the view of the target model.
class ServiceType(models.Model):
cleaningtype = models.ForeignKey(
CleaningType, related_name='service_types', on_delete=models.CASCADE)
...
class Service(models.Model):
servicetype = models.ForeignKey(
ServiceType, related_name='services', on_delete=models.CASCADE)
service_name = models.CharField(
_("Service Name"), blank=True, null=True, max_length=255)
#string type added
def __str__(self):
return str(self.service_name)
And in CleaningTypeSerializer
class CleaningTypeSerializer(serializers.ModelSerializer):
service_objects = ServiceTypeSerializer(many = True, read_only = True)
service_types = serializers.SerializerMethodField()
class Meta:
Model = CleaningType
fields = ('id', 'cleaning_type', 'service_types',)
def get_service_types(self, obj):
return list(obj.service_types.values_list('service_type').distinct())
Finally in views.py,
class CleaningTypesViewSet(ModelViewSet):
serializer_class = CleaningTypeSerializer
queryset = CleaningType.objects.all()
You can get the list of CleaningType data from this CleaningTypesViewSet.

Count the no of movies in django rest framework

I want to count the no of movies worked by the actors in api. i have tried this but not working
models.py
class Actor(models.Model):
actors = models.CharField(max_length=100)
class Genre(models.Model):
genre = models.CharField(max_length=100)
class Movie(models.Model):
name = models.CharField(max_length=100)
actors = models.ManyToManyField(Actor, related_name="actor_movies")
genre = models.ManyToManyField(Genre,related_name="genre_movies")
serializers.py
class ActorSerializer(serializers.HyperlinkedModelSerializer):
actor_movies = serializers.RelatedField(source='actor.movies', read_only = True)
class Meta:
model = Actor
fields = ['id','actors','actor_movies']
class GenreSerializer(serializers.HyperlinkedModelSerializer):
genre_movies = serializers.RelatedField(source='genre.names', read_only = True)
class Meta:
model = Genre
fields = ['id','genre','genre_movies']
class MovieSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Movie
fields = ['url','name','actors','genre']
im getting output like this
[
{
"id": 1,
"genre": "Ajith"
},
{
"id": 2,
"genre": "Vijay"
}
]
in actual output i need the total no of movies he worked also.
You can use a SerializerMethodField:
class ActorSerializer(serializers.HyperlinkedModelSerializer):
count_actor_movies= serializers.SerializerMethodField()
def get_count_actor_movies(self, instance):
return instance.actor_movies.count()
class Meta:
model = Actor
fields = ['id','actors','count_actor_movies']
For more details, the official docs are here.

Filtering the fields listed by the CreateModelMixin based on ForeignKey in Django Rest Framework

Hello, I am starting to learn Django Rest Framework, i have come across a problem, that i couldn't solve, that is described below:
I have defined three models in models.py:
Endpoint Model
class Endpoint(models.Model):
name = models.CharField(max_length=128)
created_by = models.CharField(max_length=128)
created_at = models.DateTimeField(auto_now_add=True, blank=True)
def __str__(self) -> str:
return self.name
MLAlgorithm model:
class MLAlgorithm(models.Model):
name = models.CharField(max_length=128)
created_by = models.CharField(max_length=128)
created_at = models.DateTimeField(auto_now_add=True, blank=True)
parent_endpoint = models.ForeignKey('Endpoint', on_delete=models.CASCADE, related_name="mlalgorithms")
def __str__(self) -> str:
return self.name
ABTest model:
class ABTest(models.Model):
title = models.CharField(max_length=1000)
created_by = models.CharField(max_length=128)
created_at = models.DateTimeField(auto_now_add=True, blank=True)
parent_endpoint = models.ForeignKey('Endpoint', on_delete=models.CASCADE, related_name="abtests")
parent_mlalgorithm_1 = models.ForeignKey('MLAlgorithm', on_delete=models.CASCADE, related_name="parent_mlalgorithm_1")
parent_mlalgorithm_2 = models.ForeignKey('MLAlgorithm', on_delete=models.CASCADE, related_name="parent_mlalgorithm_2")
def __str__(self) -> str:
return self.title
and defined their serializers:
Endpoint Serializer:
class EndpointSerializer(serializers.ModelSerializer):
class Meta:
model = Endpoint
read_only_fields = ("id", "name", "created_by", "created_at")
fields = read_only_fields
MLAlgorithm Serializer:
class MLAlgorithmSerializer(serializers.ModelSerializer):
class Meta:
model = MLAlgorithm
read_only_fields = (
"id", "name", "created_by", "created_at", "parent_endpoint")
fields = read_only_fields
ABTest Serializer:
class ABTestSerializer(serializers.ModelSerializer):
class Meta:
model = ABTest
read_only_fields = ("id", "created_at")
fields = (
"id", "title", "created_by", "created_at", "parent_endpoint", "parent_mlalgorithm_1", "parent_mlalgorithm_2")
Then i created a viewset for the ABTest:
class ABTestViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, mixins.RetrieveModelMixin):
serializer_class = ABTestSerializer
queryset = ABTest.objects.all()
My problem is in the ABTestView:
for example, i have created two endpoints, and each endpoint has two algorithms:
endpoint_1:
algorithm_1
algorithm_2
endpoint_2:
algorithm_3
algorithm_4
but when i select a specific endpoint in the view, i want only the specific algorithms to be shown not all the algorithms available in the database
enter image description here
i don't know how to force algorithm filtering in the view, should i filter the algorithms by their endpoints in the view or before serialization, unfortunately i am struggling to implement it.
Thank you in advance.

How to add a snippet of category, used in a different snippet

How do I have a snippet of category be used in another snippet called resource? I want to have a list of Resources filtered by category and category is a snippet of its own. I'm getting this error when I try to create a Resource:
AttributeError at /admin/snippets/home/resource/add/
'ForwardManyToOneDescriptor' object has no attribute 'rel'
Here is my code:
class Resource(models.Model):
"""Snippet for Resources"""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
resource_name = models.CharField(max_length=128, blank=False, null=True)
phone_number = models.CharField(max_length=12, blank=True, null=True)
website = models.URLField(blank=True, null=True)
info = RichTextField(blank=True, null=True)
category = models.ForeignKey('ResourceCategory', on_delete=models.CASCADE, null=True, blank=True)
panels = [
MultiFieldPanel(
[
FieldPanel("resource_name"),
FieldPanel("phone_number"),
],
heading="Resource information"
),
MultiFieldPanel(
[
FieldPanel('website')
],
heading="Links"
),
MultiFieldPanel(
[
FieldPanel('info')
],
heading="Info"
),
MultiFieldPanel(
[
InlinePanel("category", label="Category")
]
)
]
def __str__(self):
"""String representation of this class"""
return self.resource_name
class Meta:
verbose_name = "Resource"
verbose_name_plural = "Resources"
register_snippet(Resource)
class ResourceCategory(models.Model):
"""Snippet for Resources"""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
category_name = models.CharField(max_length=128, blank=False, null=True)
panels = [
MultiFieldPanel(
[
FieldPanel('category_name')
],
heading="Category"
)
]
def __str__(self):
"""String representation of this class"""
return self.category_name
class Meta:
verbose_name = "Category"
verbose_name_plural = "Categories"
register_snippet(ResourceCategory)
The category field should use FieldPanel('category'), not InlinePanel. InlinePanel is for managing multiple child objects belonging to the snippet, but here a Resource only belongs to a single ResourceCategory.
(If you do intend to allow a resource to belong to multiple categories, you'll need to adjust your models so that Resource has a child model, containing a ParentalKey to Resource and a ForeignKey to ResourceCategory.)

Resources