Reputation: 75
Let's use these 3 simple models for example. A city can have multiple shops, and a shop can have multiple products
models.py
class City(models.Model):
name=models.CharField(max_length=300)
class Shop(models.Model):
name = models.CharField(max_length=300)
city = models.ForeignKey(City, related_name='related_city', on_delete=models.CASCADE)
class Product(models.Model):
name=models.CharField(max_length=300)
shop=models.ForeignKey(Shop, related_name='related_shop', on_delete=models.CASCADE)
serializers.py
class CitySerializer(serializers.ModelSerializer):
class Meta:
model = City
fields=['id','name']
class ShopSerializer(serializers.ModelSerializer):
related_shop = CitySerializer(many=True, read_only=True)
class Meta:
model = Shop
fields=['id','name','related_city']
class ProductSerializer(serializers.ModelSerializer):
related_shop = ShopSerializer(many=True, read_only=True)
class Meta:
model = Product
fields=['id','name','related_shop']
In views.py, in the get_queryset(), I am using .select_related().all() to fetch the foreign objects. ProductSerializer will give me all products, and will fetch the foreignKey shops, and I will also get the names of the shops where this product is found.
ShopSerializer, in a similar way, will give me all shops names, and all the cities where this shop can be found.
But how can I make a serializer, that will retrieve from all 3 tables at once? The fields I want are: fields=['product_name','shop_name', 'city_name']
I know that, that list I will get will have repetitions, and can be considered 1NF or 2NF, opposed to the model design I have as database 3NF. But that is the query that I want.
I was actually thinking of denormalizing my database, so I can easily achieve this.
My second question is, is it better to denormalize it to 1NF, and have repetitions, so that I reduce the CPU intensive inner joins on 'id' between these 3 tables? I researched a lot about this, and usually the lazy answer is: Try both variants, benchmark, and decide yourself.
Upvotes: 1
Views: 2142
Reputation: 2102
use source like this:
class ProductSerializer(serializers.ModelSerializer):
shop_name = serializers.CharField(source='shop.name')
city_name = serializers.CharField(source='shop.city.name')
class Meta:
model = Product
fields = ['id', 'name', 'shop_name', 'city_name']
for second question. you may act like this:
class City(models.Model):
name=models.CharField(max_length=300)
class Shop(models.Model):
name = models.CharField(max_length=300)
class Product(models.Model):
name=models.CharField(max_length=300)
city=models.ForeignKey(City, related_name='product_city', on_delete=models.CASCADE)
shop=models.ForeignKey(Shop, related_name='product_shop', on_delete=models.CASCADE)
by using this you can access to city and shop for each product easily in template or whatever your gonna use for forntend and there is less pressure on database to achieving product's shop and city
Upvotes: 1
Reputation: 305
There will be different approaches available for your question. Fetching related objects from the database at once does not depend on the serializer itself actually. That's done in your view layer where you can append select_related('shop__city')
to your queryset. By appending this you will have both shop
and the shop.city
values preloaded on Product
objects in a single query.
A simple way of serializing those fields is to set source
in the serializer fields like below:
class ProductSerializerV2(serializers.ModelSerializer):
shop_name = serializers.CharField(source='shop.name')
city_name = serializers.CharField(source='shop.city.name')
class Meta:
model = Product
fields = ['name', 'shop_name', 'city_name']
According to the descriptions above, the below snippet will only have a single query to the database
p = Product.objects.select_related('shop__city').last()
print(ProductSerializerV2(p).data)
# {'name': 'product-z', 'shop_name': 'shop-z', 'city_name': 'z'} as a sample output
Upvotes: 2