这一篇和之前的不太一样,主要讲的也不是django本身的技术问题,而是开发中的注意事项
Django 与开发
git的使用–关于分支
由Git pro中了解到,每一个分支应该代表了当前要完成的任务,也就是说,每一次开发的时候,应该是以开发特性作为分支的名字。当然,也可以使用next等表示开发版本的
- 每次合并结束后,记得要将原先的分支删除掉。
- 分支的名字不要用_, 而是用-
- 每次提交都只做一件事情,比如修改了文档1并且增加了文档2。
- 每个功能都新建一个分支,比如一个分支用于写文档另一个用于开发。
- 某些功能如果正在开发中,我们可以在标题中加上WiP(Work in process)表示当前的工作正在开发中,不能直接进行合并。
- 如果要master和当前分支同步的话,不要用个git pull origin master,而是要git rebase master,从而将之前的
API的相关作用
API是将不同模块连接的重要纽带,所以当我们开始项目后,就要及时的给出API,而不是先写代码。
Field的相关
django对数据库的支持很好,但是一次性记下来始终还是太难了,这里记下文件路径:[django/forms/field.py]
http相关知识
毕竟是网页,http的相关指示也是要求有的
http状态码
200——交易成功
201——提示知道新文件的URL
202——接受和处理、但处理未完成
203——返回信息不确定或不完整
204——请求收到,但返回信息为空
205——服务器完成了请求,用户代理必须复位当前已经浏览过的文件
206——服务器已经完成了部分用户的GET请求
http请求类型
GET:向特定的资源发出请求。
POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的创建和/或已有资源的修改。
PUT:向指定资源位置上传其最新内容。
DELETE:请求服务器删除Request-URI所标识的资源。
API的内容要求
- URI后面都要有/
rest framework — serialize(序列化)
当我们需要将我们的model中的数据传输的时候,总不能现场封装一个json吧(以前java里面居然干过这样的蠢事。。。)所以这里使用serialize将我们指定的类数据序列化,
例如:
1 | from datetime import datetime |
首先定义一个类,有一些基本的属性。
1 | from rest_framework import serializers |
然后用序列化定义,定义相应的内容以及对应的数据库内容。如果使用了django框架的话,此时可以将自己的model类直接赋值给对应的model属性.
之后,如果我们将comment序列化处理后,将会得到:
1 | serializer = CommentSerializer(comment) |
一个序列化处理后的结果。此处可以看见,此时的data已经变成python内建的dict,于是如果此时我们想要传输json的话,我们只需要使用自带的json封装即可:
1 | from rest_framework.renderers import JSONRenderer |
同样的是,这个类还能够进行反序列化:
1
2
3
4
5
6
7
8
9
10
11
from django.utils.six import BytesIO
from rest_framework.parsers import JSONParser
stream = BytesIO(json)
data = JSONParser().parse(stream)
serializer = CommentSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# {'content': 'foo bar', 'email': 'leila@example.com', 'created': datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)}
rest framework — ViewSet & Router
1 | from django.utils.six import BytesIO |
这个viewset能够帮助开发者更加专注与models的开发和交互,并且能够让url能够自动化构建。
我们这里介绍项目中会遇到的几个方法:
viewsets.ModelViewSet:
这个类会提供list
, create
, retrieve
,update
和 destroy
方法。这些方法能够帮助我们更快的构建页面。
通过继承其对应的类可以实现渲染效果:
1 | class ExampleViewSet(ModelViewSet): |
然后我们可以在注册一个我们的url
1 | router = DefaultRouter() |
这样的话,通过访问指定来的url(xxx/coments/)就能够访问到一个用于调试用的界面。由于我们继承的是ModelViewSet,所以我们函数中带有list
,delete
,update
,create
,retrive
的功能。具体体现:
list
:queryset中的内容全部展示在当前页面create
:提供一个post方法retrive
:可访问(/comments/123/)update
:提供了一个put更改delete
:(/comments/123/)中有一个delete按钮
提高效率
查看源代码可知,在retrive或者list方法中,会调用一个叫做get_object的方法,那么如果我们不想让这个ViewSet获得默认的数据,或者想让其通过别的方式获得数据的话,可以通过重构这个函数来实现这个功能。
自动生成的url
上面讲了viewset,这里讲一下router的用途。Router就是一个封装好的路由类,通过注册–>加入url的方式完全自动生成动态的url:
1 | router.register('comments', CommentViewSet) |
如上,此时匹配的url规则就会包括[/comments/id],当然id是否可以匹配取决于注册的CommentViewSet是否支持update这类方法。
资源的嵌套
当然RESTFUL中提倡资源嵌套,如[/author/1/comment],为了实现资源嵌套,一个router肯定是不够的,而中间的1又是一个变量,这样的话我们可以用提供的装饰器解决:
1 | class A(xxx): |
这里通过使用了装饰器,指定了当我们使用post方法的时候,我们就能够像访问到[/A/id/my-url]的位置,如果不指定名字的话,那么这个my-url就会替换成def后的名字。
资源的嵌套–ViewSet结合
然而,有时候我们就是这么懒,想要用现有的方法,但是对于另一些方法又想要让其访问到ViewSet以外的url,并且此时还要是另一个ViewSet中的数据库的内容。这就要用到Router的Bind技术:
1 | class MoneyViewSet(ModelViewSet): |
假如如上,MoneyViewSet中,我们定义了一个方法getMoney,所以要从Money的数据库中查找数据,但是此时我们的url设计为[/bank/{id}/money/](因为要找到对应的bank),所以此时我们同时需要取得bank的id并且访问对应点money的数据库。显然,我们要是直接注册MoneyViewSet,就会形成[/money/{id}],显然就不是我们要的url。为了保持我们的设计原则,我们这里可以这样处理:
首先在urls.py中绑定我们的ViewSet:
方便的测试APIClient
很多时候,我们测试的过程中可能包含了【管理员】与【用户】的差异。当我们为了测试时模拟这个用户的过程,我们可以用APIClient:
假设我们此时项目有一个用户对象类为Uesr,继承了来自django自带的AbstractUser:
1 | self.user = User.objects.create(username="user") |
这个过程中,就能够模拟出一个登陆的会话,该会话中我们就是以用户名为"admin"登陆的。