2018-12-10 rest_framework总结

django-rest-framework,是一套基于Django 的 REST 框架,是一个强大灵活的构建 Web API 的工具包。 使用django-rest-framework实现前后分离。

rest总结目录

  1. 安装与配置
  2. 模型定义
  3. 路由
  4. 视图
  5. 序列化
  6. 条件过滤
  7. 增删改查方法自定义
  8. to_representation方法
  9. get_queryset方法
  10. get_object方法

1. 安装与配置

  安装djangorestframework

pip install djangorestframework==3.4.6

  安装过滤

pip install django-filter

  配置settings.py文件

  在工程目录中的settings.py文件的INSTALLED_APPS中需要添加rest_framework

INSTALLED_APPS = [
    ...

    'rest_framework',
]

2. 模型定义

  定义文章模型类和文章分类的模型类,并指定分类和文章之间的一对多的关联关系

from django.db import models

class Atype(models.Model):
    t_name = models.CharField(max_length=10)

    class Meta:
        db_table = 'a_type'


class Article(models.Model):
    title = models.CharField(max_length=10)
    desc = models.CharField(max_length=100)
    content = models.TextField()
    is_delete = models.BooleanField(default=0)
    create_time = models.DateTimeField(auto_now_add=True)
    atype = models.ForeignKey(Atype, null=True)

    class Meta:
        db_table = 'article'

3. 路由

  定义路由需要注意:

    使用router.register注册的url为资源,而且资源只能为名词不能为动词。
    定义的资源不要加'/'斜杠,在访问地址的时候,URL中会默认的添加反斜杠'/'
from django.conf.urls import url

from rest_framework.routers import SimpleRouter

# 导入应用article中的views.py文件
from article import views


# 生成路由对象
router = SimpleRouter()
# 路由管理资源art
router.register('art', views.ArticleView)

urlpatterns = [
    url(r'list/', views.list_art),
]
# router.urls生成资源对应的路由地址
# 例如地址: /art/ 、 /art/id/
urlpatterns += router.urls

4. 视图

  定义基于类的视图ArticleView,可以通过继承父类来实现创建/检索/更新/删除等操作,而开发者只需要轻松地构造可重复使用的行为即可。而这些常见的创建/检索/更新/删除等操作是在REST框架的mixin类中实现的。

  如下定义基于类的视图ArticleView,视图中实现序列化类ArticleSerializer和过滤类ArticleFiler:

from rest_framework import mixins, viewsets

from article.article_filter import ArticleFiler
from article.article_serializer import ArticleSerializer
from article.models import Article


class ArticleView(viewsets.GenericViewSet,
                  mixins.ListModelMixin,
                  mixins.DestroyModelMixin,
                  mixins.CreateModelMixin,
                  mixins.UpdateModelMixin,
                  mixins.RetrieveModelMixin):
    # 查询返回的数据
    queryset = Article.objects.filter(is_delete=0)
    # 序列化返回的文章数据
    serializer_class = ArticleSerializer
    # 过滤
    filter_class = ArticleFiler

  分析:

    queryset: 指明在查询数据时使用的查询集。
    serializer_class: 指明该视图在进行序列化或反序列化时使用的序列化器。
    filter_class: 指明过滤URL中传递参数的过滤器。

5. 序列化

  定义序列化模型的ArticleSerializer类,并继承于serializers.ModelSerializer。其中可以自定义序列化字段的最大长度max_length和最小长度min_length以及错误信息的自定义error_messages。而fields字段表示序列化后用于展示的字段。

from rest_framework import serializers

from article.models import Article


class ArticleSerializer(serializers.ModelSerializer):
    desc = serializers.CharField(min_length=10,
                                 max_length=100,
                                 error_messages={
                                     'required': '描述必填',
                                     'max_length': '描述不超过100字符',
                                     'min_length': '描述不少于10字符'
                                 })
    title = serializers.CharField(max_length=10,
                                  error_messages={
                                      'required': '标题必填',
                                  })
    content = serializers.CharField(min_length=10,
                                    error_messages={
                                      'required': '内容必填',
                                    })
    class Meta:
        # 序列化的模型
        model = Article
        # 需要序列化的字段
        fields = ['title', 'desc', 'content', 'id', 'atype']

  分析:

    model: 指明该序列化器处理的数据字段从模型类Article参考生成。
    fields: 指明该序列化器包含模型类中的哪些字段,'all'指明包含所有字段。

6. 条件过滤

  定义过滤模型的ArticleFiler类,并继承与filters.FilterSet。如下定义过滤的字段title、desc、content、min_time、max_time,且过滤的title、desc、content为模糊搜索。

  定义如下的过滤字段,搜索的URL定义如下所示:

http://127.0.0.1/xxx/art/?title=小明&desc=django&content=学习&max_time=2018-12-12&min_time=2018-11-11。该URL表名模糊搜索标题title、desc、content,且创建时间大于2018-11-11且创建时间小于2018-12-12

from rest_framework import filters
import django_filters

from article.models import Article


class ArticleFiler(filters.FilterSet):
    # 过滤URL中title参数
    title = django_filters.CharFilter('title', lookup_expr='contains')
    # 过滤URL中的desc参数
    desc = django_filters.CharFilter('desc', lookup_expr='contains')
    # 过滤URL中的content参数
    content = django_filters.CharFilter('content', lookup_expr='contains')
    # 过滤URL中时间最小值min_time
    min_time = django_filters.DateTimeFilter('create_time', lookup_expr='gt')
    # 过滤URL中时间最大值max_time
    max_time = django_filters.DateTimeFilter('create_time', lookup_expr='lt')

    class Meta:
        model = Article
        fields = ['title', 'desc', 'content', 'create_time']

7. 增删改查方法自定义

  mixins在djangorestframework中主要配合viewsets共同使用,实现http方法与mixins的相关类与方法进行关联。djangorestframework中有5类Minxin,他们与http方法对应如下:

mixins 作用 对应的请求方式
mixins.ListModelMixin 定义一个list方法,返回一个queryset的列表 Get
mixins.CreateModelMixin 定义一个create方法,创建一个实例 Post
mixins.RetriverModelMixin 定义一个retrieve方法,返回一个具体的实例 Get
mixins.UpdateModelMixin 定义一个update方法,对某个实例进行更新 Put/Patch
mixins.DestoryModelMixin 定义一个delete方法,删除某个实例 Delete

  具体分析:

7.1 CreateModelMixin

  源码:

class CreateModelMixin(object):
    """
    Create a model instance ==>创建一个实例
    """
    def create(self, request, *args, **kwargs):

    # 获取相关serializer
        serializer = self.get_serializer(data=request.data)

        # 进行serializer的验证
        # raise_exception=True,一旦验证不通过,不再往下执行,直接引发异常
        serializer.is_valid(raise_exception=True)

        # 调用perform_create()方法,保存实例
        self.perform_create(serializer)

        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
    # 保存实例
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}

  创建的逻辑思路图如下:


drf_create.png

  分析: 由上图可以看出这个类的一个逻辑,其中,perform_create( )对serializer直接进行save保存,当在一些情境下,我们需要对perform_create( )进行重写。

  那么我们现在可能有一个下面的需要:

  假设现在有一个course课程model,里面维持了一个数,记录课程收藏数,还存在一个用户收藏userfav的model(外键course指向课程模型),当一个用户对课程进行收藏,理论上现在post进来的应该是userfav的instance,显然,我们还需要对相应course的收藏数fav_num进行+1。 这个时候,我们就需要重写perform_create( )方法!

def perform_create(self, serializer):
    # 重写save的逻辑
    instance = serializer.save()
    course = instance.course
    course.fav_num += 1
    course.save()

7.2 ListModelMixin

  源码:

class ListModelMixin(object):
    """
    List a queryset.==> 列表页获取
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        # 这是一个分页功能,如果在viewset中设置了pagination_class,那么这里就会起作用
        # 获取当前页的queryset,如果不存在分页,返回None
        page = self.paginate_queryset(queryset)

        if page is not None:
        # 分页不为空,那么不能简单的执行Response(serializer.data)
        # 还需要将相关的page信息序列化在进行响应
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

分析: ListModelMixin一般用来获取列表页,大多数情况下比较简单,不需要重写相关的方法。

7.3 RetrieveModelMixin

  源码:

class RetrieveModelMixin(object):
    """
    Retrieve a model instance.==> 获取某一个对象的具体信息
    """
    def retrieve(self, request, *args, **kwargs):
        # 一般访问的url都为/obj/id/这种新式
        # get_object()可以获取到这个id的对象
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

  分析: 访问的URL都为/obj/id/这种新式才会调用该retrieve方法,其中get_object()可以获取到这个id的对象。开发中对retrieve这个方法的重写几率比较高,例如我们在增加点击数的时候,经常要对其进行一个重写。

7.4 UpdateModelMixin

  源码:

class UpdateModelMixin(object):
    """
    Update a model instance.==> 更新某个具体对象的内容
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

  分析: UpdateModelMixin实现逻辑基本整合了Create以及Retrieve,先得到具体的实例,再对其进行验证以及保存,如果需要对更新这个逻辑进行自定义,那么需要重写perform_update( )方法,而尽量少去重写update( )

7.5 DestroyModelMixin

  源码:

class DestroyModelMixin(object):
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

  DestroyModelMixin的逻辑也相对比较简单,当删除文章对象数据时,通过修改模型中的定义的is_delete字段,将is_delete字段置为1表示删除数据,这是逻辑删除,并不是物理删除。因此重构perform_destroy方法,如下:

def perform_destroy(self, instance):
    instance.is_delete = 1
    instance.save()

8. to_representation方法

  概念:序列化器的每个字段实际都是由该字段类型的to_representation方法决定格式的,可以通过重写该方法来决定格式。

  如下重定义to_representation方法,把每一列数据(其中instance代表每一列数据)进行修改重组,然后返回,如下示例将返回文章的类型字段进行修改再填充回数据中进行返回。

from rest_framework import serializers

from article.models import Article


class ArticleSerializer(serializers.ModelSerializer):
    desc = serializers.CharField(min_length=10,
                                 max_length=100,
                                 error_messages={
                                     'required': '描述必填',
                                     'max_length': '描述不超过100字符',
                                     'min_length': '描述不少于10字符'
                                 })
    title = serializers.CharField(max_length=10,
                                  error_messages={
                                      'required': '标题必填',
                                  })
    content = serializers.CharField(min_length=10,
                                    error_messages={
                                      'required': '内容必填',
                                    })

    class Meta:
        # 序列化的模型
        model = Article
        # 需要序列化的字段
        fields = ['title', 'desc', 'content', 'id', 'atype']

    def to_representation(self, instance):
        # 序列化是会默认调用该方法,返回的结果为当前instance对象的序列化结果
        data = super().to_representation(instance)
        if instance.atype:
            data['atype'] = instance.atype.t_name
        return data

9. get_queryset方法

  get_queryset(self):返回视图使用的查询集,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写,例如对查询结果集进行再次过滤,可以重写如下:

def get_queryset(self):
    queryset = self.queryset
    return queryset.filter(title__contains='django学习day01')

10. get_object方法

  get_object(self): 返回详情视图所需的模型类数据对象。在视图中可以调用该方法获取详情信息的模型类对象。若详情访问的模型类对象不存在,会返回404。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容