为什么 DRF 中有时候返回的 JSON 中图片是带域名的, 有时候是不带域名的呢?
解析:
带域名的结果是在 view 中对模型类序列化的, DRF 在序列化图片的时候 会检查上下文有没有 request, 如果有, 就给图片加上域名,
比如说我们视图用的是 apiview():
我们需要序列化数据的时候, 加 context={"request":request}
TestSerilaizer(instance=instance, context={"request":request})
然后这样序列化器就取到了 request 对象, 然后你再测试下图片 url 即可
还有一种情况, 在序列化器里调用序列化器的时候, 也会碰到这种情况, 当然也必须要这样解决,
goods_json = GoodsSerializer(good_ins, many=False, context={'request': self.context['request']}).data #注意具体语法
我再说几种传参的方法, 这几种方法在开发过程中也会经常的遇到,
在提供序列化器对象的时候, REST framework 会向对象的 context 属性补充三个数据: request,format,view, 这三个数据对象可以在定义序列化器时使用.
serializer = AccountSerializer(account, context={'request': request})
解析, 如果是 apiview, 你视图函数里就写第一行格式, 确保序列化器里可以取到 requsts 对象, 当然如果视图函数继承的非 apiview, 那么可以忽略,
我们可以在序列化器里取很多参数方便我们使用, 如
- def validate(self, attrs):# 在提供序列化器对象的时候, REST framework 会向对象的 context 属性补充三个数据:
- # request,format,view, 这三个数据对象可以在定义序列化器时使用
- mobile = self.context['view'].kwargs['mobile']
这样就可以取到 url 里的 mobile 参数, 来我们接着看,
在视图里这样取也可以, 比如当我们重写视图的 get 方法时: 我们这样取 pk 得值
- from rest_framework.generics import ListAPIView
- class NewGoodsView(ListAPIView):
- serializer_class = GoodeInfoSerializer
- queryset = GoodInfo.objects.all()
- def get(self,request,*args,**kwargs):
- goods = GoodInfo.objects.filter(type_id=kwargs['pk']).order_by('-id')[:2]
- ser = self.get_serializer(goods,many=True)
- return Response(ser.data)
然后看看下面这张截图, 当访问商品详情时 点击量 + 1,
然后再继续看看, 只要是我们请求后台时前端请求 header 头中携带 token 认证, 然后我们在后台就可以用 request 取到当前的用户对象, 如
- class MyCartInfoView(ListAPIView):
- '''
- 我的购物车页面
- '''
- serializer_class = CartInfoSerializer
- queryset = Cart.objects.all()
- def get_queryset(self):
- return self.queryset.filter(user = self.request.user,selected=True)
下面, 我再介绍四种我们经常会用到的方法
get_objects() :
接着看下面, 我们在 URL 中并没有携带参数, 这样的话视图找不到 PK 会抛出异常, 当我们重写 get_objects() 方法后,
- class UserDetalView(RetrieveAPIView):
- '''
- 个人中心信息展示
- '''
- serializer_class = UserDetailSerializer
- permission_classes = (IsAuthenticated,)
- # 重写 get_object 方法, 返回用户指定信息
- def get_object(self):
- return self.request.user
解析: get_objects()
返回详情视图所需的模型类数据对象, 默认使用 lookup_field 参数来过滤 queryset. 在视图中可以调用该方法获取详情信息的模型类对象.
若详情访问的模型类对象不存在, 会返回 404.
get_queryset():
返回视图使用的查询集, 是列表视图与详情视图获取数据的基础, 默认返回 queryset 属性, 可以重写, 例如:
get_serializer(self, args, *kwargs)
返回序列化器对象, 被其他视图或扩展类使用, 如果我们在视图中想要获取序列化器对象, 可以直接调用此方法.
- class CartCountView(GenericAPIView):
- '''
- detail 页面购物车数量渲染
- '''
- serializer_class = CartCountSerializer
- queryset = Cart.objects.all()
- def get(self,request):
- ser = self.get_serializer(self.get_queryset().filter(user=request.user),many=True)
- count = len(ser.data)
- return Response({'data':count})
- get_serializer_class(self)
返回序列化器类, 默认返回 serializer_class, 可以重写, 例如: 三级联动的实现,
先看看序列化器,
- class AreaSerializer(serializers.ModelSerializer):
- '''
- 行政区信息序列化器
- '''
- class Meta:
- model = Area
- fields = ('id', 'name')
- class SubAreaSerializer(serializers.ModelSerializer):
- '''
- 子集行政区信息序列化器
- '''
- subs = AreaSerializer(read_only=True,many=True)
- class Meta:
- model = Area
- fields = ('id', 'name', 'subs')
来看看视图,
- class AreaViewSet(CacheResponseMixin,ReadOnlyModelViewSet):
- # 关闭分页, 因为高级视图集默认是有分页操作的, 而我们这里前端页面选择省市县, 是不需要分页的
- pagination_class = None
- # queryset = Area.objects.all()
- # serializer_class = AreaSerializer
- '''
- list: 返回所有的省份信息
- retrieve: 返回特定省或市下的下属城市
- '''
- def get_queryset(self):
- '''
- 返回视图使用的查询集,
- 是列表视图与详情视图获取数据的基础,
- 默认返回 queryset 属性
- ''' if self.action =='list':
- return Area.objects.filter(parent=None)
- else:
- return Area.objects.all()
- def get_serializer_class(self):
- if self.action == 'list':
- return AreaSerializer
- else:
- return SubAreaSerializer
来看看 url
- from django.conf.urls import url
- from . import views
- from rest_framework.routers import DefaultRouter
- router = DefaultRouter()
- router.register('areas',views.AreaViewSet,base_name='area')
- urlpatterns = [
- ]
- urlpatterns += router.urls
drf 模型序列化器默认仅返回数据库中已存在字段, 如果想新增输出字段, 改如何操作?
例如: 输出用户角色时, 顺便输出当前角色总共有多少用户.
先举个例子:
- class Role(models.Model):
- """角色表, 一的一方"""
- name = models.CharField(max_length=30, unique=True, verbose_name='角色名称') # 媒体运营, 广告运营, 活动运营, 财务, 技术, 唯一, 必填
- desc = models.CharField(max_length=100, null=True, blank=True, verbose_name='角色描述') # 非必填
- class Meta:
- db_table = 'tb_role'
- verbose_name = '角色'
- verbose_name_plural = verbose_name
- def __str__(self):
- """控制对象输出内容"""
- return self.name
- class User(BaseModel):
- """用户表, 多的一方"""
- account = models.CharField(max_length=30, unique=True, verbose_name='登录账户') # 必填, 唯一
- password = models.CharField(max_length=100, null=True, blank=True, default='888888',
- verbose_name='登录密码') # 非必填, 默认 888888, 长度 100 是为了以后加密扩展
- username = models.CharField(max_length=30, null=True, blank=True, verbose_name='用户名称') # 非必填
- role = models.ForeignKey(Role, on_delete=models.CASCADE, related_name='user', verbose_name='角色')
- class Meta:
- db_table = 'tb_user'
- verbose_name = '用户'
- verbose_name_plural = verbose_name
- def __str__(self):
- """控制对象输出内容"""
- return self.account
接着看序列化器,
- class RoleModelSerializer(serializers.ModelSerializer):
- """角色模型序列化器"""
- user_count = serializers.SerializerMethodField(label='用户数量') # 新增数据库不存在字段用户数量
- class Meta:
- model = Role
- fields = ['id', 'name', 'desc', 'user_count']
- def get_user_count(self, obj):
- """
- 返回当前角色用户数量
- 固定写法, obj 代表 Role 实例对象, 模型类配置了反向引用 user 代表当前角色用户
- """
- number = obj.user.count()
- return number
注意:
user_count 字段在数据库中不能存在, 下面写方法的时候前面加 get_ 就可以, 这样就得到我们需要的数据了.
在此方法里需要调用序列化器, 我们直接调用即可, 举例:
- ad_goods = serializers.SerializerMethodField() #位于中间部分 goods 商品的 img 大图片显示
- def get_ad_goods(self, obj):
- goods_json = {}
- ad_goods = IndexAd.objects.filter(category_id=obj.id,) #过滤 goods 在广告表中的数据
- if ad_goods:
- good_ins = ad_goods[0].goods #取一条
- goods_json = IndexGoodsSerializer(good_ins, many=False, context={'request': self.context['request']}).data
- return goods_json
- HiddenField()
HiddenField 的值不依靠输入, 而需要设置默认的值, 不需要用户自己 post 数据过来, 也不会显式返回给用户, 最常用的就是 user!!
我们在登录情况下, 进行一些操作, 假设一个用户去收藏了某一门课, 那么后台应该自动识别这个用户, 然后用户只需要将课程的 id post 过来, 那么这样的功能, 我们配合 CurrentUserDefault() 实现.
下面是一个用户留言功能的实现:
- class LeavingMessageSerializer(serializers.ModelSerializer):
- '''
- 用户留言
- '''
- # 获取当前登录的用户
- user = serializers.HiddenField(
- default=serializers.CurrentUserDefault()
- )
- #read_only: 只返回, post 时候可以不用提交, format: 格式化输出
- add_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M')
- class Meta:
- model = UserLeavingMessage
- fields = ("user", "message_type", "subject", "message", "file", "id" ,"add_time")
来源: http://www.bubuko.com/infodetail-2939252.html