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']
Related
I pass the list of tag ids in nested serializer during POST. I want to get all the fields of the object in Response, I got only the id.
My output:
{
"id": 87,
"tags": [
1,
2
]...
I need it to be like this:
{
"id": 0,
"tags":
[
{
"id": 0,
"name": "breakfast",
"color": "#E26C2D",
"slug": "breakfast"
} ...
How can I do it?
My code:
class RecipesSerializer(serializers.ModelSerializer):
author = serializers.StringRelatedField(read_only=True, default=serializers.CurrentUserDefault())
image = Picture2Text(required=False, allow_null=True)
ingredients = IngredientsSerializer(many=True, required=False)
class Meta:
model = Recipes
fields = (
'id',
'tags',
'author',
'ingredients',
'is_favorited',
'is_in_shopping_cart',
'name',
'image',
'text',
'cooking_time',
)
def create(self, validated_data):
new_recipe = Recipes.objects.create(
author=self.context['request'].user,
name=validated_data['name'],
image=validated_data['image'],
text=validated_data['text'],
cooking_time=validated_data['cooking_time'],
)
new_recipe.save()
for ingredient in validated_data['ingredients']:
ingredient_obj = Ingredients.objects.get(id=ingredient['id'])
RecipeIngredients.objects.create(
recipe=new_recipe,
ingredients=ingredient_obj,
amount=ingredient['amount']
)
amount = ingredient['amount']
ingredient_obj.amount = amount
ingredient_obj.save()
new_recipe.ingredients.add(ingredient_obj)
for tag in validated_data['tags']:
tag_obj = Tags.objects.get(id=tag.id)
new_recipe.tags.add(tag_obj)
new_recipe.save()
return new_recipe
I tried to explicit the tag serializer but it didn`t work. I did:
class RecipesSerializer(serializers.ModelSerializer):
author = serializers.StringRelatedField(read_only=True, default=serializers.CurrentUserDefault())
image = Picture2Text(required=False, allow_null=True)
tags = TagWithinRecipeSerializer(many=True, read_only=True)
With:
class TagWithinRecipeSerializer(serializers.ModelSerializer):
id = serializers.IntegerField()
class Meta:
fields = ('id', 'name', 'color', 'slug')
model = Tags
But it didn't work:
File "/home/aladinsane/.local/lib/python3.10/site-packages/rest_framework/serializers.py", line 205, in save
self.instance = self.create(validated_data)
File "/home/aladinsane/Development/foodgram-project-react-master-home/backend/api/serializers.py", line 194, in create
for ingredient in data['ingredients']:
NameError: name 'data' is not defined
How can I get what I need? Any help will be appreciated.
I have a model Jurisdiction that has M2M relation with Franchise. And I use ModelViewSet on Jurisdiction.
models.py
class Franchise(models.Model):
name = models.CharField('Name', max_length=255, db_index=True)
class Jurisdiction(models.Model):
franchise = models.ManyToManyField(
Franchise,
verbose_name='Franchise',
related_name='jurisdictions',
blank=True
)
name = models.CharField(
"Jurisdiction name",
max_length=255,
db_index=True,
unique=True,
)
phone_number = models.CharField(
"Phone number",
max_length=12,
validators=[phone_number_regex],
unique=True,
)
My views.py
class JurisdictionViewSet(viewsets.ModelViewSet):
queryset = Jurisdiction.objects.all().prefetch_related('franchise')
serializer_class = JurisdictionSerializer
serializers.py
class JurisdictionSerializer(serializers.ModelSerializer):
franchise = FranchiseSerializer(many=True)
class Meta:
model = Jurisdiction
fields = (
'id', 'name', 'phone_number', 'franchise',
)
And serializer shows me:
{
"id": 1,
"name": "Test juris 1",
"phone_number": "200-000-1233",
"franchise": [
{
"id": 1,
"name": "Test franchise 1",
},
{
"id": 2,
"name": "Test franchise 2",
}
}
How I can get a list view of every m2m relation as a different object in serializer? I tried to change the to_representation method of serializer but nothing works
expected output:
[
{
"id": 1,
"name": "Test juris 1",
"phone_number": "200-000-1233",
"franchise_name": "Test franchise 1"
},
{
"id": 1,
"name": "Test juris 1",
"phone_number": "200-000-1233",
"franchise_name": "Test franchise 2"
}
]
You can customize the ListSerializer of your ModelSerializer. Specifically the to_representation method. For example:
from rest_framework import serializers
class CustomJurisdictionListSerializer(serializers.ListSerializer):
def to_representation(self, data):
iterable = data.all() if isinstance(data, models.Manager) else data
response = []
for item in iterable:
item_representation = self.child.to_representation(item)
for franchise in item.franchise.all():
representation = item_representation.copy()
representation['franchise_name'] = franchise.name
response.append(representation)
return response
Then remove franchise from JurisdictionSerializer and set the custom list serializer:
class JurisdictionSerializer(serializers.ModelSerializer):
class Meta:
model = Jurisdiction
fields = (
'id', 'name', 'phone_number',
)
list_serializer_class = CustomJurisdictionListSerializer
I'm struggling to write a Django GET that returns the following looking response:
{
"lists": [
{
"id": "123",
"list_order": [
{
"id": "123_1",
"order": 1,
"list_id": "123",
"item_id": 9876,
"item": {
"id": 9876,
"name": "item1",
"location": "California"
}
},
{
"id": "123_2",
"order": 2,
"list_id": "123",
"item_id": 2484,
"item": {
"id": 2484,
"name": "item2",
"location": "California"
}
}
],
"updated_date": "2018-03-15T00:00:00Z"
}
]
}
Given a list_id, the response returns the basic information on the list ("id", "updated_date"), as well as the order of items in the list. Inside each item in the list order, it also grabs the related item details (nested in "item"). I'm able to get this response without the "item" details ("id", "name", "location" fields) and with no error:
{
"lists": [
{
"id": "123",
"list_order": [
{
"id": "123_1",
"order": 1,
"list_id": "123",
"item_id": 9876
},
{
"id": "123_2",
"order": 2,
"list_id": "123",
"item_id": 2484
}
],
"updated_date": "2018-03-15T00:00:00Z"
}
]
}
Again there is no error, and I can retrieve the first nested level without any issue. The problem is getting the "item" information to show within each "list_order". Below are my models, serializers, and views.
models.py
class Lists(models.Model):
id = models.CharField(null=False, primary_key=True, max_length=900)
updated_date = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'tbl_lists'
class Items(models.Model):
id = models.BigIntegerField(primary_key=True)
name = models.TextField(blank=True, null=True)
location = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'tbl_items'
class ListOrder(models.Model):
id = models.CharField(null=False, primary_key=True, max_length=900)
list_id = models.ForeignKey(Lists, db_column='list_id', related_name='list_order')
item_id = models.ForeignKey(Items, db_column='item_id', related_name='items')
order = models.BigIntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'tbl_list_order'
serializers.py
class ItemsSerializer(serializers.ModelSerializer):
class Meta:
model = Items
fields = '__all__'
class ListOrderSerializer(serializers.ModelSerializer):
item = ItemsSerializer(many=False, read_only=True)
class Meta:
model = ListOrder
fields = '__all__'
class ListsSerializer(serializers.ModelSerializer):
list_order = ListOrderSerializer(many=True, read_only=True)
class Meta:
model = Lists
fields = '__all__'
views.py
class ListsViewSet(generics.ListCreateAPIView):
"""
API endpoint that returns a list with its meta-information
"""
queryset = Lists.objects.all()
serializer_class = ListsSerializer
def get_queryset(self):
list_id = self.kwargs['list_id']
filters = [Q(id=list_id)]
return Lists.objects.filter(*filters)
def list(self, request, list_id):
queryset = self.get_queryset()
list_serializer = ListsSerializer(queryset, many=True)
return Response({ 'lists': list_serializer.data })
I'm pretty new to Django and like what it offers so far, though maybe I'm thinking of doing this in too much of a "SQL" way. I've read about select_related() and prefetch_related(), but not sure how I would apply it to this case. Any assistance is greatly appreciated and let me know if there's any other information I can provide.
In your ListOrderSerializer you are trying to serialize item. while in ListOrder model you used the field name item_id
Solution:
In ListOrderSerializer:
class ListOrderSerializer(serializers.ModelSerializer):
item_id = ItemsSerializer(many=False, read_only=True)
...
Sorry for this newbie question, but I simply cannot find my way reading the manual.
models
#Subject
class TemaPai(models.Model):
subject = models.TextField()
disciplines = models.ManyToManyField(Materia)
# order = models.IntegerField(blank=True, null=True)
def __str__(self):
return self.subject
class Meta:
verbose_name_plural = "temas-pais (subjects)"
# Order and junction
class TemaPaiOrdem(models.Model):
subject = models.ForeignKey(TemaPai, on_delete=models.CASCADE)
discipline = models.ForeignKey(Materia, on_delete=models.CASCADE)
order = models.IntegerField()
def __str__(self):
var2 = self.subject.subject
var = self.discipline.discipline
return var2 + ' - ' + var
class Meta:
verbose_name_plural = "Temas-pais-ordem"
unique_together = ('subject', 'discipline')
serializers
class TemaPaiSerializer(serializers.ModelSerializer):
disciplines = serializers.PrimaryKeyRelatedField(many=True, queryset=Materia.objects.all())
class Meta:
model = TemaPai
fields = ('id', 'subject', 'url', 'disciplines')
class TemaPaiOrdemSerializer(serializers.ModelSerializer):
discipline = serializers.PrimaryKeyRelatedField(queryset=Materia.objects.all())
subject = serializers.PrimaryKeyRelatedField(queryset=TemaPai.objects.all())
class Meta:
model = TemaPaiOrdem
fields = ('id', 'subject','discipline', 'order')
Well, TemaPaiOrdemSerializer is giving me a list like this:
[
{
"id": 1,
"subject": 1,
"discipline": 1,
"order": 1
},
{
"id": 2,
"subject": 2,
"discipline": 1,
"order": 11
}
]
It is fine. But I want to retrieve the subject string representation (from TemaPai model) as well, as a read_only field. So my desired list would be something like:
[
{
"id": 1,
"subject": 1,
"subject_name": "Introduction",
"discipline": 1,
"order": 1
},
{
"id": 2,
"subject": 2,
"subject_name": "Advanced stuff",
"discipline": 1,
"order": 11
}
]
I am trying to use
subject_name = serializers.ReadOnlyField(source:'subject')
with no success. Any hint would be appreciated.
Use ,
subject_name = serializers.StringRelatedField(source='subject',read_only=True)
hence your serializer will be like,
class TemaPaiOrdemSerializer(serializers.ModelSerializer):
discipline = serializers.PrimaryKeyRelatedField(queryset=Materia.objects.all())
subject = serializers.PrimaryKeyRelatedField(queryset=TemaPai.objects.all())
subject_name = serializers.StringRelatedField(source='subject',read_only=True)
class Meta:
model = TemaPaiOrdem
fields = ('id', 'subject', 'subject_name', 'discipline', 'order')
You can use . in source argument for lookup related model's field like this:
subject_name = serializers.ReadOnlyField(source='subject.subject')
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.