Django的学习纪要

Django流程 mvt 创建工程 路由的引导

web 应用程序
b发送请求 -->uwsgi-->Django框架-->接收请求处理业务逻辑返回响应-->b
本质 接收请求 业务逻辑处理 返回响应

获取请求request
构造response对象

git 主分支 创建自己的分支 开发功能 测试 将开发的功能合并到分支

MVC
model orm数据库模型类
view 视图 展示层
controller 业务逻辑层

MVT
Models ORM数据库模型类 进行数据库的crud
View 接收请求 业务逻辑处理 返回响应
Template 模板渲染返回给view view通过响应返回给客户端 template(封装 前端文件)


django-admin startproject 项目名
创建django项目

项目结构
├── bookmanager
│   ├── __init__.py
│   ├── settings.py 项目的整体配置文件
│   ├── urls.py 项目的url的配置文件
│   └── wsgi.py 项目与wsgi兼容web服务器的入口
└── manage.py 通过这个脚本管理工程

python manage.py runserver (ip:端口) 运行程序

可以指定端口 也可以不指定使用默认

flask中的蓝图
对程序进行模块化的处理 便于维护和阅读

子应用 -->类似与flask中的蓝图
创建子应用 python manage.py startapp 子应用名字
创建子应用后的项目结构
├── book-->子应用名
│   ├── admin.py-->用于跟网站后台管理站点配置信息相关
│   ├── apps.py-->用于配置当前子应用的相关信息
│   ├── __init__.py
│   ├── migrations-->用于存放数据库迁移的历史文件
│   │   ├── __init__.py
│   │   └── __pycache__
│   │   └── __init__.cpython-35.pyc
│   ├── models.py-->用于保存数据库模型类
│   ├── __pycache__
│   │   ├── admin.cpython-35.pyc
│   │   ├── apps.cpython-35.pyc
│   │   ├── __init__.cpython-35.pyc
│   │   └── models.cpython-35.pyc
│   ├── tests.py-->用于开发测试用例 编写单元测试
│   └── views.py-->用于编写web应用视图
├── bookmanager-->项目名
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-35.pyc
│   │   ├── settings.cpython-35.pyc
│   │   ├── urls.cpython-35.pyc
│   │   └── wsgi.cpython-35.pyc
│   ├── settings.py-->项目的整体配置文件
│   ├── urls.py-->项目的url配置文件
│   └── wsgi.py-->项目与WSIG兼容的web服务器入口
├── db.sqlite3
└── manage.py-->项目管理文件 通过这个文件管理项目


注册子应用 在 settings中INSTALLED_APPS 注册子应用--> "子应用名.apps.子应用名首字母大写+Config"

配置pycharm的虚拟环境 可以使用which python 快速查看路径

一对多关系 通过多的一方的外键进行联系

使用Django进行数据库开发地步骤
定义模型类
在django中不需要定义主键字段 在生成列表时会自动添加并且值为自动增长

一, 在models.py中定义模型类(django的模型类继承自models.Model)
class 类名(models.Model)

数据库的配置
默认是将数据放到sqlite数据库中
在settings中 进行mysql配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', #数据库引擎改为mysql
'HOST':'127.0.0.1', # mysql的主机
'PORT':'3306', # mysql的端口
'USER':'root', # mysql的用户
'PASSWORD':'mysql', # mysql的账户密码
'NAME':'数据库名', # ,mysql使用的数据库 需要创建对应的数据库
}
}
更改之后在init__py中 pymysql.install_as_MySqldb

二, 数据库迁移
命令行中生成迁移文件 python manage.py makemigrations
执行迁移 python manage.py migrate 执行完是存在于默认的sqlite3数据表中文件中 就是settings中的默认数据库中,配置过mysql就是在MySQL中

站点管理(内容发布,公共访问 两部分)
内容发布由网站管理员进行数据地增删改查
django能根据定义的模型类自动地生成管理模块

Django使用管理模块地步骤
1本地化语言和时区 settings中 LANUAGES_CODE = "zh-hans" TIME_ZON = "Asia/Shanghai"
2创建后台管理账户 python manage.py createsuperuser
3注册模型类
在子应用地admin中注册模型类 admin.site.register(模型类名) 在后台中增加
4发布内容到数据库
在models中重写 def__str__ (): 方法 return self.name

三, 操作数据库
在后台站点中添加数据


视图和url(公共访问)

定义视图 在子应用地views中 其实就是python的函数

视图函数的第一个参数必须是request实例对象
视图函数必须返回一个HttpResponse
def index(request): # request用于接收用户的请求
return HttpResponse("ok")

django的路由设置
首先在项目的urls.py中进行路由匹配 -----是在settings中的ROOT_URLCONFIG=""进行指定 一般不做修改
url()会和浏览器中输入的进行顺次匹配 有满足的就引导到相应的子应用中
urlpatterns = [url(r"^路由/"), admin.site.urls]
url的第一个参数是正则匹配 正则匹配不会对http://ip:port/和get post进行匹配
第二个参数是 引导url()匹配到自己写的视图中

添加规则引导url匹配到自己写的视图中
url(r"^", include("book.urls")),
在子应用中创建urls.py-->urlpatterns = [url(r"^用户输入的url$", 视图函数名)]
在视图函数中接收请求 处理业务逻辑 构造响应 返回响应


模板 Template
创建 模板文件夹 在settins中设置模板文件夹
在视图函数中return render(reuuest, "模板文件",context=context)
在模板文件中接收context的键{{ 键 }}

# 安全机制
ALLOWED_HOSTS = ["*"] * 表示已所有的方式访问 可以指定url进行访问

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
__file__指的是哪个文件
os.path.abspath(__file__) 文件的绝路径
os.path.dirname(os.path.abspath(__file__))文件的文件夹的绝对路径
os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 文件的文件夹的文件夹的绝对路径==项目文件夹

配置静态文件 static
访问静态资源的url http//:ip"port/+STATIC_URL/+静态资源文件名字
STATIC_URL = '/static/'
告知系统静态资源去哪里找
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static")
]

apps.py 是和当前子应用的配置相关
class BookConfig(AppConfig):
name = 'book'
如:verbose_name = "名字" # 可以配置子应用在站点中的名字

MVT基本介绍
----------------------------------------------------------------------------------------------------
详细
models -- 模型 数据的增删改查

定义字段名 不能使用连续的下划线__ 不能使用mysql和python的关键字
字段类型
null是数据库范畴的 blank是表单验证范畴的
CharFiled,IntegerFiled...
字段选项
db_index 提高查询的效率 给表中的字段添加索引
unique , null ...

修改默认表名
class Meta:
db_table = "表名"

一对多 on_delete
一删除 多删除cascade 同步操作 级联
一删除 多不删保护 拒绝protected
一删除 多外键设可以为空 set null

插入数据 注意字段名是否一致

修改 数据库编码格式 alter database 库名 cahrset utf8;

shell 工具 在shell中操作数据库
python xxx.py shell 类似于ipython

基本查询
从模型类中导入表
使用django查询语句进行查询 如 a = 表名.objects.all() 查询到 表中的所有数据赋值给a 查询集

增加数据
1 book = BookInfo(
字段 = '值'
)
book.save() 调用save()添加到数据库

2 表名.objects.create(
字段='值',
) 直接加到数据库

修改数据
1
book = 表名.objects.get(id=1) # 通过id查询到对应的数据
book.name = '值' # 修改实力对象的属性值
book.save() # 调用save()保存到数据库

2
表名.objects.filter(id=1).update(
字段='值',
字段='值'
)

删除数据
1
book = 表名.objects.get(id=1)
book.delete()

2
表名.objects.filter(id=1).delete()
表名.objects.get(id=1).delete()
删除数据很危险 一般是修改is_delete的值(标记位)


查询数据
基本查询 get, all, count
表名.objects.all() -->查询结果集 列表
表名.objects.get(id=1)-->数据对象 单一结果 数据不存在时会报数据不存在异常try它!!
表名.objects.all().count()
表名.objects.count()获取查询结果集的个数

get查询出单一结果 filter过滤出多个结果(0,1,n都是多个) exclude排除掉符合条件剩下的
表名.objects.filter(查询字段__运算符=值) 返回的查询结果集列表
表名.objects.get(查询字段__运算符=值) 返回的单一的数据对象
表名.objects.exclude(查询字段__运算符=值)

日期的前后 其实就是年龄的大小 数字越大值越小 1990-1-1>1999-1-1
在1990年之后出生的人 生日是小于(lt)1990年

F 对象 两个字段相比较
查询阅读量大于评论量的图书
导入F对象
表名.objects.filter(字段__运算符=F("字段"))

Q对象 或者查询 相当于or
表名.objects.filter(Q(字段__运算符=值)|Q(字段__运算符=值))

并且查询 相当于and
表名.objects.filter(字段__运算符=值).filter(字段__运算符=值)
表名.objects.filter(字段__运算符=值, 字段__运算符=值)
表名.objects.filter(Q(字段__运算符=值)&Q(字段__运算符=值))

~Q 非查询 相当于not
表名.objects.filter(~Q(字段__运算符=值)


聚合函数
Avg Max Min Count Sum
需要使用aggregate()
表名.objects.aggregate(Ju合函数("字段名")) 结果是个字典 {"字段名_聚合函数名":值}


排序
查询结果集的排序
表名.objects.all().order_by("字段名") # 升序
表名.objects.all().order_by("-字段名")# 降序


关联查询 多表查询

查询 书籍 为1的所有 人物 信息
通过书籍查询人物信息
先查询id为1的书籍
book = BookInfo.objects.get(id=1)
通过关联模型类名小写_set获取人物信息
book.peopleinfo_set.all() -->相当于在BookInfo中的隐藏字段

查询人物为1 的书籍信息
先查询人物
person = PeopleInfo.objects.get(id=1)
再根据人物查询书籍
person.book.字段 -->该字段的信息

关联查询的筛选
查询图书 图书人物为郭靖
通过书籍(1)去关联人物(n)的时候的语法格式wei:
filter.(关联模型类名小写__字段名__运算符=值)
BookInfo.objects.filter(peopleinfo__name__exact="郭靖")

查询书名为"天龙八部"的所有人物
通过人物(n)关联书籍(n)的时候的语法格式为:
filter(外键值__字段名__运算符='值') # 多的一方是有外键的可以直接使用
PeopleInfo.objects.filter(book__name="天龙八部")


查询结果集
QuerSet[<对象1>,<对象2>]
all() filter() exclude() order_by() 返回的是查询结果集 可使用exists()判断结果集中是否有数据
查询结果集两大特性
惰性 -->查询结果集只有在使用时才生成sql语句 不使用就不进行查询 节省数据库性能
缓存 -->第一次查询数据的时候,内存中没有缓存数据,就到(数据库)硬盘中获取数据,第二次之后的查询就优先从内存中读取数据 使用变量接收查询结果集 在多次使用的时候 可以从内存中进行加载 提高性能

查询结果集支持索引 和切片操作(切片操作不持支负数)

查看MySql日志 可以查看对数据库的操作记录
使用vim 打开/etc/mysql/mysql.conf.d/mysql.cnf mysql配置文件
sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
将68,69两行的注释代码(默认注释关闭日志)打开
general_log_file =/var/log/mysql/mysql.log
general_log =1

重启mysql服务
sudo service mysql restart

使用 sudo tail -f /var/log/mysql/mysql.log 打开mysql日志文件 实时查看数据库日志内容
每次操作数据库都会记录操作日志


URLconf
路由的频繁变化时可以:reverse反解析-------前后端不分离可以使用使用
正则匹配的路由 视图函数名 指定路由的名字即 正则匹配的路由的名字
子应用的url(r'^', booklist,name="路由的名字")
在子应用的视图函数中使用reverse("指定的路由的名字") 通过指定的路由的名字反解析得到对应的路由
修改的时候修改路由就可以 reverse 类似于flask中的url_for()

防止在不同的应用中路由重名
通过项目url(r'^', include('book.urls',namespace='子应用名')) # 命名空间最好设置为不能重复的子应用名
在子应用的url(r'^', booklist,name="路由的名字") # 指定路由的名字
在子应用的视图函数中使用reverse("子应用名:路由的名字") # 使用reverse反解析得到对应的路由

view 视图

视图函数必须接收一个HttpRequest对象
视图函数必须返回一个HttpResponse对象或 子对象 作为响应 子对象JsonResponse HttpResponseRedirect

HttpRquest 对象
请求传递参数的四种形式
一 提取url的特定部分 在服务端路由中使用正则截取-->/baidu/index/2019
二 查询字符串 querystring keyword = 关键字--->如商品的搜索
三 请求体body中发送的数据 post请求 form ajax json xml-->如登陆注册
四 请求头 http报文的headers中


一 提取url的特定部分-->在服务端路由中使用正则截取
1 URL路径参数
通过正则分组获取的数据 会传递给视图函数 视图参数使用参数接收 (两种方式)
.位置参数
url(r"^(\d+)/(\d+)$",视图函数名)

def people(request,category_id,book_id ): # 位置参数的位置要一一对应

return HttpResponse("xxxx")

.关键字参数-->根据正则分组设置的标识符进行赋值 可以避免位置错误
url(r"^(?P<category_id>\d+)/(?P<book_id>\d+)$",视图函数名)-->(分组) 使用正则匹配分组里的值

def people(request,book_id,category_id): # 位置参数的位置不需一一对应,但是名字必须一致

return HttpResponse("xxxx")


二 查询字符串-->在get请求的路径后面/?key=value&key=value 多个值之间使用&来分割
?前面的访问的路径 ?后面是参数-->get请求的参数
?后面的查询字符串不参与正则匹配 只匹配请求的路径

传过来的参数可以通过 request.GET 获取到 获取到的值是一个QueryDict:{"key":["value1","value2"],..}
QueryDict-->可以实现一键多值的字典
通过键获取QueryDict中多个值使用 getlist("key")


三 请求体中post请求 form ajax json xml 等 或者借助postman工具
403 Foribidden 禁止访问 -->需要传CSRFToken
form-->request.POST.getlist("a")

json
请求体中的json请求 实质就是在请求头(http报文的headers)中添加了ContentType/application/json
json-->是双引号
1接收json数据request.body
2接收到的数据是bytes类型 需要进行转换.decode()
3转换之后是str类型 需要使用.loads()进行转换 转换为字典类型<-->dumps()字典转字符串
4字典数据就可以 通过键获取值


四 请求头 在http报文的headers中
META
接收请求头的信息 request.META 得到的是一个子类类型的json数据 可以直接使用get获取值

request的其它几个信息
method请求方法
user请求的用户对象 如果用户登录 获取用户信息 如果没有登陆 Django会提供一个匿名用户AnonymousUser
FILES 请求图片


响应HttpResponse(返回的内容,响应状态码)
返回的内容content(bytes类型)
返回的类型content_type MIME类型 格式为 大类/小类 ->(application/json image/jpg)
响应状态码 status=状态码
403 forbidden 500 server error 200 ok 404 not found 300重定向 405方法不被允许


HttpResponse的子类

1 JsonResponse()-->前端发送的json格式请求 后端返回json格式响应 类字典格式
data = {
'a':'1'
}
JsonResponse(data)-->可以将字典数据转换为json格式数据-->源码的json.dumps方法和设置content_type
HttpResponse(data, content_type="application/json")
需要将data转换格式data_json = data.dumps(data)-->默认的格式是text
需要传入content_type="application/json" -- JsonResponse(data)已通过源码实现 对移动端有影响

将字格式转换为json格式 需要将safe参数设为False 完全知道数据安全的情况下(类字典数据格式)

2redirect 重定向

return redirect("/路由") # 重定向到指定的路由


状态保持 套路
1 先从概念入手 cookie将数据保存在客户端 session将数据保存在服务端

2 再从原理(流程)入手
第一次请求没有cookie 服务端返回一个cookie(放在响应头中) 之后的请求就在请求头中携带cookie
在响应中设置cookie
response = HttpResponse("set_cookie")
response.set_cookie("username",username)
return response
获取cookie
request.COOKIES 是一个字典
通过.get()获取cookie的值

删除cookie
response.delete_cookie("username")

session 需要依赖于cookie
发送带有用户名和密码的post请求
将session数据保存在内存中
将sessionid放在cookie中返回给浏览器
浏览器发送带有sessionid的cookie
服务端根据sessionid获取用户信息
返回响应信息

设置session
第一次登陆
request.GET.get("username")
request.session['username']=username

获取session
request.session.get("username")

删除指定session
del rquest.session["username"]

删除所有session 保留sessionid
request.session.clear()

删除session所有信息
request.session.flush()

设置session的有效期
requset.set_expiry(value)

3 应用场景 如 购物车(cookie)

4 自己遇到的问题,个人见解

Django 默认启用session 默认保存在数据库中
将session保存到redis 设置session的引擎 为redis
安装dajngo-redis 设置缓存CACHES
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/7',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}

SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'


类视图 面向对象的视图函数 可以对应不同的请求方法来完成对应的功能 封装 继承 多态
定义类视图
# 继承自View 需要导入
# 类视图中定义的方法 就是http 的请求方式
# 经过url的引导 url的第二个参数引导到相应的视图函数中 view.RegisterView.as_view()
class RegisterView(View): # 继承自View 需要导入

def get(self,request): # GET请求方式

return HttpResponse()或其子类

def post(self,request): # POST请求方式

return HttpResponse()或其子类

类视图使用时需要使用装饰器
1装饰器 装饰在 url 的第二个参数 路由函数名处 可行
2@method_decorator()装饰在类上 装饰的函数时dispatch
3Mixin-->多继承的实际应用 就是使用多继承
使用super逐一调用所有父类 遵循mro顺序 mor顺序是死的
class AAA(object):
def play:
pass

class BBB(object):
def eat:
pass

class Man(BBB,object):
play-->只play

class Woamn(AAA,object):
eat-->只eat

calss Cat(AAA,BBB,object) #多继承
play eat-->都搞


多继承就是只想 重写as_view方法

1 定义装饰器
def login_required(view_func):
def wrapper(request):
if True:
return view_func(request)
else:
return HttpResponse("请先登录")
return wrapper

2 定义Mixin类
class LoginRequiredMinxi(object):---->B | View-->C
@classmethod
def as_view():
view = super.as_view():
view = 装饰器(view)
return view
3 定义类视图继承自Mixin类 和 View类 Mixin类会为类视图中所有的方法添加装饰器
class Profile(LoginRequiredMixin, View): --->A

def get(self, request):

return HttpResponse("多继承个人中心展示")

def post(self, request):

return HttpResponse("多继承个人中心资料修改")


多继承--->只针对A进行分析
重写父类super时遵循mro顺序
从模型类中导入表 表.__mro__ 查看mro顺序
直接在pycharm中查看继承关系

中间件 -->类似于flask中的hook函数
# 中间件 ---> 类似flask中 hook函数
def book_middleware(get_response):
# 初始化的位置
# 类似于before_first_request
print('init')

def middleware(request):
# 请求前
# 类似于 before_request
print("before_request")

response = get_response(request)

# 响应后
# 类似于 after_request
print("after_request")

return response

return middleware

# 在settings 中注册中间键
# MIDDLEWARE中 "子应用名.中间件模块名.中间件函数名"
before_first_request 会执行两次 将DEBUG = True设为False就好了
befor_request 是按照注册顺序执行
after_request 是反序执行
1befor_request1
2befor_request2
2after_request2
1after_request1

站点操作

前后端不分离--Flask xjzx b--请求--s接收--查询db--渲染模板--返回数据--返回的是html页面(html页面是后端通过数据返回的) 只能提供浏览器使用

前后端分离 对外提供统一的API(视图,接口) 统一返回json数据


移动端要的是json数据 不能返回html了 就要前后端分离


RESTful API风格 定义接口的规范
请求相关
使url更加简洁 将类型 id 版本 域名 等参数放到url中

1将接口尽量 放在专有域名下
2将api的版本号放入url----GitHub是将版本号放在请求头中的
3定义api的具体资源尽量使用名词的复数形式
如:
获取单个产品:http://127.0.0.1:8080/AppName/rest/products/1
获取所有产品: http://127.0.0.1:8080/AppName/rest/products


HTTP动词 请求方式(对应SQL操作)
GET(SELECT)-->从服务器获取资源一项或多项 R
POST(CREATE)-->在服务器新建数据 C
PUT(UPDATE)-->在服务器更新数据(改变后的数据由前端提供)U
DELETE(DELETE)-->从服务器删除资源 D
示例
新增书籍 - POST/books/

不常用的
PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。
HEAD:获取资源的元数据。
OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

将常用参数(limit,page,sortby)放在查询字符串中过滤返回结果


响应相关
返回更加具体的响应状态码
返回资源对象 或资源对象列表
超媒体 返回的类json数据 里面是媒体文件的链接 最终是json数据
尽量使用json 避免使用xml(标签语言)

Django REST Framework 序列化 -->将对象(模型)数据<-->操作数据库<-->字典(json)数据<-- 反序列化
--->23日实现

Django REST Framework 实现的事情
1 将请求的json数据转换为模型对象
2 将模型对象转换为响应的json数据
3 对模型的增删改查

序列化器serializer 做的2件事 --> 序列化和反序列化

序列化器的作用
Serializer字段处理原始值(json)和内部数据(模型)类型之间的转换 它们还处理验证输入值
1 模型-->json
2 处理验证输入值
3 json-->模型


模型-->json
定义一个序列化器
1继承自这个类 serializers.Serializer
2序列化器的字段分为三部分
字段名: 和模型的字段名一致
字段类型: 和模型中的类型一致
字段选项: read_only只读=True
lable 注释
max_length最大长度

class BookInfoSerializer(serializers.Serializer):
"""将模型对象转换为json数据(字典)"""
模型字段的名字:模型的值
name=serializers.CharField()
pub_date=serializers.DateField()
id=serializers.IntegerField()
...其它所有字段


序列化 将模型传递给序列化器 模型转字典操作

在视图中
获取一个模型
book = BookInfo.objects.get(id=1)
创建序列化器
单个模型序列化器
serializer = BookInfoSerializer(instace=book) isntance= 实例对象(模型对象)
多模型序列化器
serializer = BookInfoSerializer(instace=book, many=True)
通过序列化器的data属性来获取转换后的字典数据
serializer.data
将字典数据转换为字典数据
JsonResponse(serializer.data)

OrderedDict-->是有序字典[ OrderedDict([('id',1),('name','天龙八部'),...]) ]

#外键 只是获取外键的数据 并不修改外键的而数据 所以要设置read_only=True
book=serializer.PrimaryKeyReliateField(label='图书', read_only=True)

#将外键所关联的数据传递给它 他自己会默认匹配相关的数据 获取关联的书籍id
book=serializer.PrimiaryKeyRelatedField(label='图书',queryset=BookInfo.objects.all())-->对应数值

#指向模型对应的__str__()方法返回的数据 获取关联的书籍名
book = serializers.StringRelatedField(label='图书')-->对应书籍名

# 得到关联的书籍的所有信息-->人物对应的书籍的所有信息
book = BookInfoSerializer()

序列化器的本质其实就是字段 Field

通过关联模型类名小写_set获取人物信息
book.peopleinfo_set.all()-->相当于在BookInfo中的隐藏字段 一的一方的外键对应多个人物 获取多个人物

反序列化 将接收的json数据转换成模型对象
一 验证前端提交的数据--前端的数据不可信

二 保存

一 验证数据数据的类型和选项
1对数据的类型进行验证 在序列化器中定义的类型 必须满足 字段的类型 其实就是一种验证
比如: 日期date 前端就必须传一个日期类型的数据'2000-1-1'

2对选项的验证 如 max_length(CharField) ,min_value(Integer) read_only等

3对某一个 数据进行单独验证 (进一步验证) 在序列化器中
def validate_字段名(self,value)
验证代码
抛出异常raise serializers.ValidationError("异常信息")
return value-->返回值必须 !

4多个字段进行验证你
可以实现def validate(self,attrs) 方法
def validate(self,attrs): # attrs-->就是在初始化序列化器是传过来的data=data
# 验证代码
抛出异常raise serializers.ValidationError("异常信息")
rturn attrs-->必须返回值

5对单个字段定义自己的验证方法
先定义一个方法
def aaa(self):
raise serializers.ValidationError("异常信息")
在序列化器的字段的选项中添加方法名validators['aaa']

验证数据的基本流程
组织数据 -->接收的数据
data = {"pub_date":"2000-1-1"}-->默认的字段+选项 要求必须传入可使用required=True/False进行设置

创建序列化器
serializer = BookInfoSerializer(data=data(要验证的数据))

调用序列化器的验证方法来实现验证
serializer.is_valid(raise_exception = True)


二 保存 数据
序列化器的反序列化
1接收前端提交的数据

2验证数据
使用序列化器实现验证
不验证的数据不能入库 必须验证

3保存数据
当我们使用save()方法进行保存数据的时候
需要自己实现create()方法 实现(重写)就是为了将数据保存到模型中
def create(self, validated_data):
传过来的data经过验证之后完全符合验证规则的时候 validated_data就应该等于data
book = BookInfo.objects.create(**validated_data)-->解包
return book

4返回响应


更新数据传递模型实例对象 和 验证数据的时候 需要手动实现update()方法
serializer = BookInfoSerializer(instace=book, data=data)
def update(self, instance, validated_data):
传过来的data经过验证之后完全符合验证规则的时候 validated_data就应该等于data
instance.name = validated_data.get("name", instance.name)-->修改属性值
instance.save()-->模型类的save()方法
return instance-->返回对象


ModelSerializer 与 Serializer

ModelSerializer继承自(Serializer)
1自动实现create()update()方法
2根据模型自动生成字段
3必须有model
class BookInfoModelSerializer(serializers.ModelSerializer):
class Meta:
model = BookInfo
# 默认显示所有字段
fields = '__all__'
# fields = ['id', 'readcount'..]指定显示哪些字段

# 修改自动生成的字段的信息
extra_kwargs = {
# '字段名':{
# '选项1':'值1',
# '选项2':'值2'
# }
}

# 指定只读属性的字段
read_only_fields = ['字段1', '字段2']

# ModelSerializer 自动实现
from book.serializers import BookInfoModelSerializer
serializer = BookInfoModelSerializer()
serializer


Serializer继承自(BaseSerializer)
自己定义使用


视图
rest framework 的三种视图

1 APIVAiew 继承自View 是DRF的基类 一级视图
和View使用方法一样
请求和响应是自己实现的
接收请求的数据 不用判断 post,put是request.data get是request.query_paramas
响应 统一使用Response()返回数据 里面依然是字典/json数据 状态码

from rest_framework import APIView
class BookAPIView(APIView):
def get(self, request): # 获取书籍信息
books = BookInfo.objects.all()
serializer = BookInfoModelSerializer(instance=books, many=True)
return Response(serializer.data)


2 GenericAPIView 通用视图 二级视图

from rest_framework.generics import GenericAPIView

用法和APIView基本一样
为标准列视图 和详细信息视图添加了常用的行为
提供每个具体通用视图是通过 GenericAPIView 与一个或者多个 Mixin类 组合而成的

GenericAPIView的两个方向
1GenericAPIView 对列表视图和详情视图 做了一些 属性 和 方法 的抽取
2和Mixin类配合使用

属性
queryset = BookInfo.objects.all() 设置查询结果集
serializer_class = BookInfoModelSerializer 设置序列化器
lookup_field = 'xx' 设置url中的关键字参数

方法
get_queryset 获取 queryset中的查询结果集
get_serializer 获取 serializer_class 所对应的序列化器 同时完成序列化器的初始化工作
get_object 获取 lookup_field = 'xx' 中的xx指定的数据 根据xx查询出来的数据
列表视图
class BookGenericAPIView(GenericAPIView):
def get(self, request):
# books = self.queryset
books = self.get_queryset()
# serializer = self.serializer_class(books, many=True)
serializer = self.get_serializer(books, many=True)
return Response(serializer.data)

详情视图
class BookDetailGenericAPIView(GenericAPIView):
queryset = BookInfo.objects.all() 不是到具体用的哪个 就查询所有
serializer_class = BookInfoModelSerializer 设置序列化器
lookup_field = 'xx' 设置url中的关键字参数
def get(self, request, pk):->默认四通过pk来获取url中的id的信息的 可以自己修改lookup_field='id'
book = self.get_object(pk)
serializer = self.get_serializer(instance = book)
return Response(serializer.data)


GenericAPIView和Mixin类配合使用(ListModelMixin, CreateModelMixin, GenericAPIView)
get list()方法 -- 获取数据
post create()方法--新增数据
get retrieve()--的得到一条数据
put update() --修改一条数据
delete destory()-- 删除一条数据

三级视图
ListAPIview ,CreatAPIView...
queryset=BookInfo.objects.all()
serializer=BookInfoModelSerializer

视图集 ViewSet将一组相关的视图的逻辑放到一个类中 成为视图集
在视图集中不能由相同的方法名 如get获取全部信息 get获取某一信息
所以视图集提供了
list() create() retieve() update() partial_update()局部更新 destory()等方法 避免了方法重名

ViewSet不提供任何方法处理程序 需要我们自己重写该类并,明确实现action方法


url的引导
无参数时是查询所有 url(r'^books/$',视图集名.as_view({"get":"list"})
有参数时是查询一个 url(r'^books/(?P<pk>\d+)/$',视图集名.as_view({"get":"retrieve"})

自动生成url
从rest.framework.routers中导入DefaultRouter
初始化路由器router=DefaultRouter()
注册路由器router.register(r'books',views.视图集名,base_name='xxx') 就不需要再urlpatterns中添加了
只需要 urlpatterns += router.urls 就可以

优势 自动生成url 重复的逻辑可以合并成一个类,定义一次属性 queryset 和 serializer_class


GenericViewSet
继承自GenericAPIView,作用也与GenericAPIVIew类似,提供了get_object、get_queryset等方法便于列表视图与详情信息视图的开发。在GenericAPIView中,没有提供任何动作action方法,需要我们自己覆盖该类并混合所需的混合类,或者明确定义操作实现action方法


ModelViewSet 继承自 五个Mixin和GenericViewSet
等于是二级视图GenericAPIView和Mixin类的配合使用 一次配合了五个Mixin类
需要提供queryset 和 serializer
使用@action(methods=['post'], detail=True)装饰器配合使用


ReadOnlyModelViewSet
继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin。
与ModelViewSet它一样,它还包括各种操作的实现,但不同于ModelViewSet只提供“只读”操作,
list()而且retrieve()


其它功能

概念 配置 效果

认证 一般和权限配合使用
配置
DRF的配置key
REST_FRAMEWORK = {# 全局认证
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication', # 基本认证 仅适用于测试
'rest_framework.authentication.SessionAuthentication', # session认证
)
}
认证失败
401未认证
403权限被禁止

权限
Django提供的四种权限 AllowAny IsAuthenticated IsAdminUser IsAuthenticatedOrReadOnly
配置
DRF的配置key
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated', # 登陆用户 通过认证的用户 才可以访问
)
}
某一个视图让所有人都可以访问
再单个视图中设置权限
从 rest_framework.permissions导入 权限
在视图中相加相应的权限permission_calssses=[AllowAny]-->这个权限大于settings中的
还可以单独设置身份认证authentication_calsses=[]

限流--限制访问的频次 减轻服务器的压力(是一个安全机制)
配置REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',# 限制匿名用户
'rest_framework.throttling.UserRateThrottle'# 限制登陆用户
),
'DEFAULT_THROTTLE_RATES': {# 设置频次
'anon': '100/day',# 匿名用户频次
'user': '1000/day'# 登陆用户频次 单位 day hour minute seconds
}
}

过滤 根据条件进行数据的筛选filter_name = []
安装pip install django_filters
INSTALLED_APPS = [
...
'django_filters',
...
]

配置
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
在视图中设置过滤的字段filter_fields=['字段','字段',...]/?id=10

排序
从 rest_framework导入OrderingFilter
filter_backends = [OrderingFilter]
设置排序的字段
ordering_fields = ['字段','字段',...]/?ordering=readcount

分页
从rest_framework.pagination中导入LimitOffsetPagination
pagination_class = LimitOffsetPagination

PageNumberPagination

版本

异常处理
统一处理数据
处理数据库的异常
定义和系统一样的函数exception_handler
判断响应是否是None是的话就判断是不是数据库异常 自己定义方法解决

DRF的异常类

APIException 所有异常的父类
ParseError 解析错误
AuthenticationFailed 认证失败
NotAuthenticated 尚未认证
PermissionDenied 权限决绝
NotFound 未找到
MethodNotAllowed 请求方式不支持
NotAcceptable 要获取的数据格式不支持
Throttled 超过限流次数
ValidationError 校验失败


自动生成接口文档
pip install coreapi
设置路由 放在根url中
访问哪个路由就可以


------------------------------------------------------------------------------------------------

B2B business to business 企业对企业 电子商务的主流形式

C2C custom to custom 个人对个人

B2C 企业对个人

C2B 个人对企业 消费者个人的需求进行定制

O2O线上对线下

F2C 工厂到个人

B2B2C 企业对企业对个人


积攒 bug

传递的不是字典数据safe=False
获取数据时可以设置个默认值
404 的本质是
路由错误
序列化器继承自这个类 serializers.Serializer

猜你喜欢

转载自www.cnblogs.com/yintaiping/p/10826920.html