为什么要进行前后端分离
- 可pc、app、pad多端适应
- SPA开发模式的流行--单页web应用(只有一html页面)
- 可实现前后端开发职责清(不分离时,前端是通过后端给的变量并渲染出来方式拿到数据!!后端是通过前端准备好的模版,并替换其中变量方式传数据)
- 不分时开发效率问题,前后端相互等待
- 不分时前端一直配合着后端,能力受限制
- 不分时后端开发语言和模板高度耦合,导致开发语言依赖严重--现在可把语言和模板分离开
前后端分离缺点
- 前后端学习门槛增加
- 数据依赖导致文档重要性增加
- 前端工作量加大
- SEO难度加大
- 后端开发迁移成本增加
restfull api目前是前后端分离的最佳实践
- 轻量的,直接通过http方式进行数据交互,不需要额外的协议,支持post/get/put/delete操作
- 面向资源,一面了然,具有自解释性
- 数据描述简单,一般是通过json或者xml做数据通信(后端准备好数据以json形式返回给前端,前端拿到的就是直接可用的对象)
理解RESTful架构:概念参考http://www.ruanyifeng.com/blog/2011/09/restful.html
实战参考http://www.ruanyifeng.com/blog/2014/05/restful_api.html
二.开始drf
1.项目准备
开始项目:
(python36env) [vagrant@CentOS7 devops]$ python manage.py startapp idcs
新建项目目录下apps包用于放将来很多的app--idcs也放其中。
(1)urls.py:--注意为了让其能识别idcs/urls有两方式:
从pycharm上识别:点击apps包右键--mark as--source root(意思是以apps目录为查找模块的目录)
从代码层面识别:把apps目录加到全局path路径BASE_DIR中,
import os import sys BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0,os.path.join(BASE_DIR, "apps"))
--sys.path中存着django所有可搜索加载的目录,并插到最开始的地方所以用insert(0)。并加在django项目下。
激活app:
INSTALLED_APPS = [ ....... 'idcs' ]
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^idcs', include("idcs.urls")),
]
(2)idcs/urls.py: 定义一空变量即可
from django.conf.urls import include, url
urlpatterns = [
]
2.drf安装及配置:
(python36env) [vagrant@CentOS7 devops]$ pip install djangorestframework
INSTALLED_APPS = ( ...... 'rest_framework' )
然后重启pycharm重新同步远程环境。
3.序列化模型
(1)准备模型models.py:
from django.db import models class Idc(models.Model): name = models.CharField("机房名",max_length=32) address = models.CharField("机房地址",max_length=200) phone = models.CharField("机房联系电话",max_length=15) email = models.EmailField("机房联系email") letter = models.CharField("idc字母简称",max_length=5) def __str__(self): return self.name class Meta: db_table = 'resources_idc'
同步数据:
(python36env) [vagrant@CentOS7 devops]$ python manage.py makemigrations idcs
(python36env) [vagrant@CentOS7 devops]$ python manage.py migrate idcs
(2)序列化模型:在idcs下新建serializers.py
from rest_framework import serializers class IdcSerializer(serializers.Serializer): """ Idc 序列化类 """ id = serializers.IntegerField() name = serializers.CharField() address = serializers.CharField() phone = serializers.CharField() email = serializers.EmailField() letter = serializers.CharField()
a.把模型中的所有字段和id字段都拿过来--定义它的变量类型(根据模型的字段类型定义序列化--是为了返回给前端什么样的字段/数据类型)
b.使用序列化
为了测试,所以先手动往库中添加点记录(添加到idc模型中)。
(python36env) [vagrant@CentOS7 devops]$ python manage.py shell In [1]: from idcs.models import Idc In [2]: idc = Idc() In [3]: idc.name = "昆明机房" In [4]: idc.address = "昆明" In [5]: idc.phone = "1234568" In [6]: idc.email = "rock@51reboot.com" In [7]: idc.letter = "km" In [8]: idc.save()
In [9]: idc.id = None
In [10]: idc.name = "大理机房"
In [11]: idc.address = "大理"
In [12]: idc.letter = "dl"
In [13]: idc.save()
In [14]: Idc.objects.all() 查询如下有两条记录了
Out[14]: <QuerySet [<Idc: 昆明机房>, <Idc: 大理机房>]>
先导入idc序列化类,并准备一条记录,然后序列化类(要实例化类--把记录传给它)并保存在一变量中,
把此实例化后的对象用data属性一打印出来就直接是json格式的数据了:
In [16]: from idcs.serializers import IdcSerializer In [17]: idc = Idc.objects.get(pk=1) In [18]: idc Out[18]: <Idc: 昆明机房> In [19]: serializer = IdcSerializer(idc) In [20]: serializer Out[20]: IdcSerializer(<Idc: 昆明机房>): id = IntegerField() name = CharField() address = CharField() phone = CharField() email = EmailField() letter = CharField() In [21]: serializer.data Out[21]: {'id': 1, 'name': '昆明机房', 'address': '昆明', 'phone': '1234568', 'email': 'rock@51reboot.com', 'letter': 'km'} In [22]: a = serializer.data In [23]: type(a) Out[23]: rest_framework.utils.serializer_helpers.ReturnDict
但它是rest字典类型的数据,所以要给它转成json,且用rest提供的方法转(不能用json.dumps):
先实例化JSONRenderer方法,并给它传一数据----如下结果中,这才是标准的返回给前端的数据
In [25]: from rest_framework.renderers import JSONRenderer In [26]: jr = JSONRenderer() In [27]: jr.render(serializer.data) Out[27]: b'{"id":1,"name":"xe6x98x86xe6x98x8exe6x9cxbaxe6x88xbf","address":"xe6x98x86xe6x98x8e","phone":"1234568","email":"rock@51reboot.com","letter":"km"}'
In [28]: content = jr.render(serializer.data) 保存到一变量中,就可直接通过httpresponse对象直接返回给前端
c.用序列化做字段的简单验证serializers.py:
from rest_framework import serializers class IdcSerializer(serializers.Serializer): """ Idc 序列化类 """ id = serializers.IntegerField(read_only=True) #只读的,即忽略可不传 name = serializers.CharField(required=True,max_length=32) #意思是提交数据时此字段必须填且不能为空 address = serializers.CharField(required=True,max_length=256) phone = serializers.CharField(required=True,max_length=15) email = serializers.EmailField(required=True) letter = serializers.CharField(required=True,max_length=5)
d.怎样把前端提交过来的数据content插入到数据库中---反序列化(即把bytes类型转成模型对象)
首先它是一byte类型,是一种流
In [31]: from django.utils.six import BytesIO In [32]: stream = BytesIO(content) In [33]: stream Out[33]: <_io.BytesIO at 0x7fe29ac4b410> In [35]: from rest_framework.parsers import JSONParser 后可看到如下中是标准的json了 In [36]: data = JSONParser().parse(stream) In [37]: data Out[37]: {'id': 1, 'name': '昆明机房', 'address': '昆明', 'phone': '1234568', 'email': 'rock@51reboot.com', 'letter': 'km'}
In [38]: serializer = IdcSerializer(data=data) 反序列化后如下就生成标准的原始数据了
In [39]: serializer
IdcSerializer(data={'id': 1, 'name': '昆明机房', 'address': '昆明', 'phone': '1234568', 'email': 'rock@51reboot.com', 'letter': 'km'}):
id = IntegerField()
name = CharField()
address = CharField()
phone = CharField()
email = EmailField()
letter = CharField()
但是可以看到上述所有字段中都没有规则,所以得让其重新生效一次--要重新加载IdcSerializer类(因为这个类我已经修改过了,加了验证规则),而上述拿到的序列类它是没有验证规则
先退出django shell,再重新进入
In [1]: from idcs.serializers import IdcSerializer In [2]: data = {'id': 1, 这是前端传过来的数据 ...: 'name': '昆明机房', ...: 'address': '昆明', ...: 'phone': '1234568', ...: 'email': 'rock@51reboot.com', ...: 'letter': 'km'} In [3]: serializer = IdcSerializer(data=data) 反序列化 后如下中可看到有验证规则了 In [4]: serializer Out[4]: IdcSerializer(data={'id': 1, 'name': '昆明机房', 'address': '昆明', 'phone': '1234568', 'email': 'rock@51reboot.com', 'letter': 'km'}): id = IntegerField(read_only=True) name = CharField(max_length=32, required=True) address = CharField(max_length=256, required=True) phone = CharField(max_length=15, required=True) email = EmailField(required=True) letter = CharField(max_length=5, required=True)
In [5]: serializer.is_valid() 进行验证
Out[5]: True
e.此时数据已准备好,验证规则也有了,也验证完了,此时我需要拿到干净的数据(不等于前端传过来的数据)
In [6]: serializer.validated_data 此时就拿到了干净的数据 Out[6]: OrderedDict([('name', '昆明机房'), ('address', '昆明'), ('phone', '1234568'), ('email', 'rock@51reboot.com'), ('letter', 'km')])
f.拿到干净的数据后要想保存得重写create方法,并把已经验证过的数据给它再返回
serializer.py中加入如下代码:
from rest_framework import serializers from .models import Idc class ..... def create(self, validated_data): return Idc.objects.create(**validated_data)
退出django shell再进入并再把前端传的数据拿过来且把id去掉,就表示新建(没有传id django认为是创建数据,有传id表示认为修改数据),--因为有save方法时它会自动判断你是调用create还是update方法
所以对数据进行序列化--验证---保存,如下所示一条记录就保存了且做了验证
In [2]: from idcs.serializers import IdcSerializer In [3]: data = {'id': 1, ...: 'name': '昆明机房', ...: 'address': '昆明', ...: 'phone': '1234568', ...: 'email': 'rock@51reboot.com', ...: 'letter': 'km'} In [4]: del data["id"] In [5]: serializer = IdcSerializer(data=data) In [6]: serializer.is_valid() Out[6]: True In [7]: serializer.save() Out[7]: <Idc: 昆明机房>
In [8]: from idcs.models import Idc 测试有无验证成功,如下有三条记录说明成功了
In [9]: Idc.objects.all()
Out[9]: <QuerySet [<Idc: 昆明机房>, <Idc: 大理机房>, <Idc: 昆明机房>]>
g.我上述所有操作都是只序列化一条记录,那如何序列化多条记录
给它传一queryset即可(Idc.objects.all(), many=True)表示有多条。
In [11]: data = IdcSerializer(Idc.objects.all(), many=True) In [12]: data Out[12]: IdcSerializer(<QuerySet [<Idc: 昆明机房>, <Idc: 大理机房>, <Idc: 昆明机房>]>, many=True): id = IntegerField(read_only=True) name = CharField(max_length=32, required=True) address = CharField(max_length=256, required=True) phone = CharField(max_length=15, required=True) email = EmailField(required=True) letter = CharField(max_length=5, required=True)
那如何拿到数据?--通过JSONRenderer,并传给它上述的data数据,且它是序列化类所以用data属性,最终如下它就是一多标准数据的列表了
In [14]: from rest_framework.renderers import JSONRenderer
In [15]: content = JSONRenderer().render(data.data)
In [16]: content
Out[16]: b'[{"id":1,"name":"xe6x98x86xe6x98x8exe6x9cxbaxe6x88xbf","address":"xe6x98x86xe6x98x8e","phone":"1234568","email":"rock@51reboot.com","letter":"km"},{"id":2,"name":"xe5xa4xa7xe7x90x86xe6x9cxbaxe6x88xbf","address":"xe5xa4xa7xe7x90x86","phone":"1234568","email":"rock@51reboot.com","letter":"dl"},{"id":3,"name":"xe6x98x86xe6x98x8exe6x9cxbaxe6x88xbf","address":"xe6x98x86xe6x98x8e","phone":"1234568","email":"rock@51reboot.com","letter":"km"}]'
扩展:update方法
class.....
def create(self, validated_data):
return Idc.objects.create(**validated_data)
def update(self, instance, validated_data): #对已经验证过的非常干净的数据进行修改,instance是当前的对象
instance.name = validated_data.get("name", instance.name) #哪些字段可以修改,且默认名是
instance.address = validated_data.get("address", instance.address)
instance.phone = validated_data.get("phone", instance.phone)
instance.email = validated_data.get("email", instance.email)
instance.save() #保存
return instance #返回