用于多个模型的 Django 嵌套序列化程序,带有链接的外键

Django nested serializer for multiple models, with chained foreignKeys

让我们以这3个简单模型为例。一个城市可以有多个店铺,一个店铺可以有多个商品

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']

在views.py中,在get_queryset()中,我使用.select_related().all()来获取外来对象。 ProductSerializer 将给我所有产品,并将获取 foreignKey 商店,我还将获得找到该产品的商店名称。

ShopSerializer,以类似的方式,将给我所有商店名称,以及可以找到该商店的所有城市。

但是我怎样才能制作一个序列化程序,它会同时从所有 3 个表中检索? 我想要的领域是: 字段=['product_name','shop_name','city_name']

我知道,我将得到的那个列表会有重复,可以被认为是 1NF 或 2NF,与我作为数据库 3NF 的模型设计相反。但这是我想要的查询。

我实际上是在考虑对我的数据库进行反规范化,所以我可以很容易地实现这一点。

我的第二个问题是,将其非规范化为 1NF 并重复是否更好,这样我就可以减少这 3 个表之间 'id' 上的 CPU 密集内连接?我对此进行了很多研究,通常懒惰的答案是:尝试两种变体、基准测试,然后自己决定。

您的问题将有不同的方法可用。一次从数据库中获取相关对象实际上并不依赖于序列化程序本身。这是在您的视图层中完成的,您可以在其中将 select_related('shop__city') 附加到您的查询集。通过附加这个,您将在单个查询中将 shopshop.city 值预加载到 Product 对象上。

序列化这些字段的一种简单方法是在序列化程序字段中设置 source,如下所示:

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']

结论

根据上面的描述,下面的代码片段只会对数据库进行一次查询

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

像这样使用来源:

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']

第二个问题。你可以这样做:

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)

通过使用它,您可以在模板或您要用于前端的任何内容中轻松访问城市和购买每种产品,并且数据库实现产品商店和城市的压力较小