Django/RestFramework:如何从含外键的模型中获取数据
嘿,我来帮你理清楚在Django和DRF里怎么获取这类带外键的关联数据~ 结合你给出的Shops和Product模型,我分ORM查询和DRF序列化两个场景来说,都是日常开发里常用的方法:
一、Django ORM层面获取关联数据
不管是在视图还是脚本里,用ORM直接查关联数据很直观:
- 正向查询:直接通过外键字段访问就行。比如拿到一个
Product实例p,p.productsubcategory就能获取对应的子分类对象,p.shop拿到所属店铺;如果是店铺实例s,s.shopkeeper就能拿到店主。
要是查多个对象,记得用select_related避免N+1查询问题,比如:# 一次性查出所有商品,以及对应的子分类和店铺 products = Product.objects.select_related('productsubcategory', 'shop').all() - 反向查询:从关联的父模型查子模型,默认是
模型名_set。比如要查某店铺的所有商品,用shop.product_set.all();查某店主的所有店铺用shopkeeper.shops_set.all()。
嫌默认名字别扭的话,可以给外键加related_name改个直观的名字,比如在Product模型里:
之后就能用shop = models.ForeignKey(Shops, on_delete=models.CASCADE, related_name='products')shop.products.all()来查店铺的商品了,舒服多了~
二、DRF序列化器返回关联数据
这部分是做API时最常用的,几种方式按需选:
- 默认主键返回:DRF默认会把外键字段序列化成对应对象的ID,比如
Product序列化后shop字段就是店铺的ID,这种适合只需要关联ID的场景。 - 返回字符串表示:用
StringRelatedField,前提是你给关联模型定义了__str__方法。比如给Shops加:
然后在def __str__(self): return self.nameProductSerializer里:
这样序列化后shop = serializers.StringRelatedField()shop字段就会返回店铺的名字。 - 嵌套序列化器(最常用):直接把关联对象的序列化器嵌套进来,返回完整的关联数据。比如先写好关联模型的序列化器,再嵌套:
然后在视图里用这个序列化器,记得配合from rest_framework import serializers from .models import Shops, Product, ProductSubCategory, Shopkeeper # 先定义店主序列化器 class ShopkeeperSerializer(serializers.ModelSerializer): class Meta: model = Shopkeeper fields = ['id', 'name'] # 按需选字段 # 店铺序列化器,嵌套店主信息 class ShopsSerializer(serializers.ModelSerializer): shopkeeper = ShopkeeperSerializer(read_only=True) # 如果要返回店铺的所有商品,加下面这行(记得给Product的外键加related_name='products') # products = ProductSerializer(many=True, read_only=True) class Meta: model = Shops fields = ['id', 'name', 'category', 'contactNumber', 'address', 'dateCreated', 'shopkeeper'] # 商品子分类序列化器 class ProductSubCategorySerializer(serializers.ModelSerializer): class Meta: model = ProductSubCategory fields = ['id', 'name'] # 商品序列化器,嵌套子分类和店铺 class ProductSerializer(serializers.ModelSerializer): productsubcategory = ProductSubCategorySerializer(read_only=True) shop = ShopsSerializer(read_only=True) class Meta: model = Product fields = ['id', 'productsubcategory', 'shop']select_related优化查询:from rest_framework import generics from .models import Product from .serializers import ProductSerializer class ProductList(generics.ListAPIView): queryset = Product.objects.select_related('productsubcategory', 'shop').all() serializer_class = ProductSerializer - 自定义字段(SerializerMethodField):如果需要自定义关联数据的返回逻辑,用这个。比如在
ProductSerializer里加个字段返回店铺的名字:
这样序列化后就会多一个shop_name = serializers.SerializerMethodField() def get_shop_name(self, obj): return obj.shop.nameshop_name字段,返回店铺名称。
小技巧:读写分离
如果需要在创建商品时传入店铺/子分类的ID,同时返回时显示详细信息,可以分开定义读写字段:
class ProductSerializer(serializers.ModelSerializer): # 只读字段,返回详细信息 productsubcategory_detail = ProductSubCategorySerializer(source='productsubcategory', read_only=True) shop_detail = ShopsSerializer(source='shop', read_only=True) # 只写字段,接收ID productsubcategory = serializers.PrimaryKeyRelatedField(queryset=ProductSubCategory.objects.all()) shop = serializers.PrimaryKeyRelatedField(queryset=Shops.objects.all()) class Meta: model = Product fields = ['id', 'productsubcategory', 'productsubcategory_detail', 'shop', 'shop_detail']
内容的提问来源于stack exchange,提问作者Abid Mehmood
相关产品推荐
相关产品推荐

