django-rest-framework框架学习

现在新一代web应用都开始采用前后端分离的方式来进行,淘汰了以前的服务器端渲染的方式。前后端分离方式有许多好处,比如
1、可以前后端并行开发,提高开发效率
2、页面都在客户端进行渲染,提高了渲染速度,减小了服务器的压力
3、一套api提供给多个客户端使用,而且不限制客户端的类型(web,app均可)

提到前后端分离必然会提到restful,目标这是最流行的接口规范,想要了解的可以去<a href="http://www.ruanyifeng.com/blog/2011/09/restful">阮老师的博客</a>看一下
越来越多的大型应用开始使用restful规范,restful强调一种无状态,资源的方式
在django中很著名的一个框架是django-rest-framework,帮我们减少了很多工作量,尤其在序列化与反序列化成我们需要的格式帮了我们省了很多事
在这里就记录一下个人的学习过程

django-rest-framework

实现一组api的基本流程

  1. models.py中创建自己需要的数据模型
  2. 创建serializers.py在其中写出适合的serializer类,能够正确的序列化与反序列化
  3. views.py中写出合适的api类,只需要继承rest_frameworkgenerics中的某个类,重写我们需要的方法实现合适的逻辑即可
  4. urls.py中配置所需要的url

一组api的demo

创建一个model

在创建好的app下的model.py中创建一个student模型

class Student(models.Model):
    student_id = models.IntegerField()
    name = models.CharField(max_length=20)
    age = models.IntegerField()

例子很简单,三个字段,分别表示学号,姓名,年龄

然后同步数据库,在数据库中创建表
在终端中输入

python manage.py makemigrations

python manage.py migrate

在数据库里插入几条测试数据后如图所示


django_rest_framework_study_student_model

实现序列化类

官方文档:http://www.django-rest-framework.org/api-guide/serializers/
创建serializers.py在其中实现序列化类
由于这个序列化是和我们的模型想对应的相关,所以可以直接继承serializers.ModelSerializer

class StudentSerializer(serializers.ModelSerializer):

    class Meta:
        model = Student
        fields = ('id', 'student_id', 'name', 'age')

其他相关的参数可以查阅官方文档,都有详细的介绍
这里我们只简单的显示所有的字段

实现视图api类

官方文档: http://www.django-rest-framework.org/api-guide/generic-views/

在这里只需要继承相关的类,复写父类方法即可

# 学生列表
class StudentList(generics.ListCreateAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer

    # 创建一个student
    def create(self, request, *args, **kwargs):
        serializer = StudentSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        
# 单个学生
class StudentDetail(generics.RetrieveUpdateAPIView):
    serializer_class = StudentSerializer
    queryset = Student.objects.all()
    lookup_field = 'name'

    # 得到一个数据集
    def get_queryset(self):
        return Student.objects.filter(name=self.kwargs['name'])

    # get方法返回一个student
    def get(self, request, *args, **kwargs):
        # 获取url中的参数
        # http://127.0.0.1:8000/api/students/aaa/?test=123
        # 取test的值
        print(self.request.GET.get('test', None))

        queryset = self.get_queryset()
        serializer = StudentSerializer(queryset, many=True)
        return Response({
            'data': serializer.data,
            #'sorce': StudentSorceSerializer(StudentSorce.objects.all(), many=True).data
        })

    # 更新某一个学生的信息
    def update(self, request, *args, **kwargs):
        pass

其实默认最少只要实现两个参数或方法即可querysetserializer_class
但很多时候逻辑需要自己处理,必须按照自己的逻辑去复写父类属性
在get方法中的self.request.GET.get('test', None)是取出get方法传递的参数
lookup_field 应用于执行单个模型实例的对象查找的模型字段。默认为'pk'。请注意,使用超链接的API时,您需要确保双方的API意见和串行类设置查找字段,如果你需要使用一个自定义值
在get_queryset()中实现的是按照name查询的,因为很多时候客户端并不知道自己的id是多少,
serializer.data是对模型序列化成的字典,如果想拼成需要的格式,需要提前构造合适的字典,在用Response()方法帮我们序列化成json类型

在urls中自定义资源的url

urls.py中输入

from django.conf.urls import url
from .views import StudentList, StudentDetail

urlpatterns = [
    url(r'students/$', StudentList.as_view()),
    url(r'students/(?P<name>[a-zA-Z]+)/$', StudentDetail.as_view()),
]

分别配置好对列表和单个的url

然后运行项目就可以看到我们需要的数据

完整代码

在原来的基础上进行了扩展,在显示学生信息的基础上,我们还想显示该名学生的成绩
所以新的model.py

from django.db import models

class Student(models.Model):
    student_id = models.IntegerField()
    name = models.CharField(max_length=20)
    age = models.IntegerField()

class StudentSorce(models.Model):
    student_id = models.IntegerField()
    math = models.FloatField()
    english = models.FloatField()
    chiness = models.FloatField()

我们在这里没有使用外键,毕竟现在很多大牛说不使用外键,在应用中控制逻辑嘛
当然这里也可以使用models.ForeignKey,会让操作变得更简单的一点

serializers.py为:

from rest_framework import serializers
from api.models import Student, StudentSorce

class StudentSorceSerializer(serializers.ModelSerializer):
    avg = serializers.SerializerMethodField('get_avg_sorce')

    class Meta:
        model = StudentSorce
        fields = ('math', 'english', 'chiness', 'avg')
    # 自定义方法构造的字段(计算成绩的平均值)
    def get_avg_sorce(self, obj):
        return (obj.math + obj.english + obj.chiness) / 3.0

class StudentSerializer(serializers.ModelSerializer):
    sorce = serializers.SerializerMethodField('get_student_sorce')

    class Meta:
        model = Student
        fields = ('id', 'student_id', 'name', 'age', 'sorce')
    # 使用学号查出该学生的成绩
    def get_student_sorce(self, obj):
        print obj.student_id
        return StudentSorceSerializer(StudentSorce.objects.filter(student_id=obj.student_id), many=True).data

views.py为:

# coding:utf-8
from __future__ import print_function
from django.shortcuts import render
from rest_framework import generics
from api.serializers import StudentSerializer
from .models import Student, StudentSorce
from rest_framework.response import Response
from rest_framework import status
from api.serializers import StudentSorceSerializer

# 学生列表
class StudentList(generics.ListCreateAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer

    # 创建一个student
    def create(self, request, *args, **kwargs):
        serializer = StudentSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    '''
    def list(self, request, *args, **kwargs):
        print('list')
        return Response(StudentSerializer(Student.objects.filter(student_id__gt=102), many=True).data)
        '''

# 单个学生
class StudentDetail(generics.RetrieveUpdateAPIView):
    serializer_class = StudentSerializer
    queryset = Student.objects.all()
    lookup_field = 'name'

    # 得到一个数据集
    def get_queryset(self):
        return Student.objects.filter(name=self.kwargs['name'])

    # get方法返回一个student
    def get(self, request, *args, **kwargs):
        # 获取url中的参数
        # http://127.0.0.1:8000/api/students/aaa/?test=123
        # 取test的值
        print(self.request.GET.get('test', None))

        queryset = self.get_queryset()
        serializer = StudentSerializer(queryset, many=True)
        return Response({
            'data': serializer.data,
            'sorce': StudentSorceSerializer(StudentSorce.objects.all(), many=True).data
        })

    # 更新某一个学生的信息
    def update(self, request, *args, **kwargs):
        pass

访问http://127.0.0.1:8000/api/students/的结果为:

django_rest_framwork_study_listresult

访问http://127.0.0.1:8000/api/students/aaa/的结果为:
django_rest_framework_study_detailresult

注册功能

官方文档:http://www.django-rest-framework.org/api-guide/serializers/
在这里我们使用django自带的auth模块
只需要修改serializer.py和view.py和urls.py三个文件即可
serializers.py

# coding=utf-8
import re
from rest_framework import serializers
from django.contrib.auth.models import User

class AccountFrom(object):
    def __init__(self, email, username, password, repeat_password):
        self.email = email
        self.username = username
        self.password = password
        self.repeat_password = repeat_password


class AccountSerializer(serializers.Serializer):
    email = serializers.EmailField()
    username = serializers.CharField()
    password = serializers.CharField()
    repeat_password = serializers.CharField()

    def create(self, validated_data):
        User.objects.create_user(username=validated_data.get('username'),
                                 email=validated_data.get('email'),
                                 password=validated_data.get('password'))
        return AccountFrom(username=validated_data.get('username'),
                           email=validated_data.get('email'),
                           password=validated_data.get('password'),
                           repeat_password=validated_data.get('repeat_password'))

    def update(self, instance, validated_data):
        instance.email = validated_data.get('email', instance.email)
        instance.username = validated_data.get('username', instance.username)
        instance.password = validated_data.get('password', instance.password)
        # User.objects.update()
        instance.save()
        return instance

    def validate_email(self, value):
        try:
            result_email = User.objects.get(email=value)
        except User.DoesNotExist:
            return value
        raise serializers.ValidationError('该邮箱已注册')

    def validate_password(self, value):
        self.password = value
        if re.match('^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$', value):
            return value
        else:
            raise serializers.ValidationError('密码必须由6-20个字母和数字组成')

    def validate_repeat_password(self, value):
        self.repeat_password = value
        if self.password and value and self.password != value:
            raise serializers.ValidationError("两次输入的值不相同")
        return value

    def validate_username(self, value):
        if re.match("^[A-Za-z][A-Za-z0-9_.]*$", value):
            return value
        else:
            raise serializers.ValidationError("用户名只能有数字字母下划线组成")

以validate_开头后面接字段名的函数均为验证函数,如果不符合验证规则,将返回异常信息

views.py

# 注册模块
class AccountDetail(generics.CreateAPIView):
    serializer_class = AccountSerializer
    queryset = User.objects.all()

    def create(self, request, *args, **kwargs):
        serializer = AccountSerializer(data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

继承generics.CreateAPIView只允许http的post方法
这里只简单实现了用户注册

url.py

urlpatterns = [
    url(r'register/$', AccountDetail.as_view()),
]

访问http://127.0.0.1:8000/account/register,提供合法的email、username、password、repeat_password将会创建一个账户

注:

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

推荐阅读更多精彩内容