总结出写 API 的方法
使用 DRF 作为框架。关于DRF的详细使用,见我的另一篇文章 django restframework 的日常使用
* 如果单纯只是从一个 model 中读取,提交等,就建立一个serializer。
* 如果跨多个 model,并且提交后有操作的,比如注册、添加银行卡等。
在上面的基础上,添加 controller(注意不要把具体的逻辑代码写在 View 中,只要引用controller函数)
数据库设计
架构
细节
年龄这样的会随着时间发生变化的字段,怎样设计(在储存有生日的情况下)?
1. 使用属性
使用属性实时计算,优点是实现简单,缺点是不好做搜索与查询
2. 定时更新
每天凌晨定时计算所有用户的年龄,如果变化了,则更新。
缺点是实现复杂一点
优点是使其变成了普通字段,能做查询、搜索等工作
鉴权、登陆、安全
使用 Django 自带的账号体系
参照 restframework 的鉴权
使用 LoginView的方式,需要的地方:是否登陆,是否认证了身份证,是否认证了银行卡,把各个认证拆分开来,需要的地方可以组合起来
安全
需要花钱的api
采取反爬虫策略。用户体验与安全的兼容
首先进行ip认证,最多发送几次
超过ip认证次数后,需要输入验证码
密码信息进行 hash 后再保存
具体是使用 Django 的 make_password
阿里云 OSS
页面上功能太多了,只有根据文档以及搜索关键词来找到。
问:理清阿里云OSS的鉴权,如子账号、策略、角色
名词解释
bucket:
账号、子账号:一个主账号可以有多个子账号,子账号与主账号隔离,子账号之间隔离。
角色:
教科书式角色(Textbook-Role)
教科书式角色(或传统意义上的角色)是指一组权限集合,它类似于RAM里的Policy。如果一个用户被赋予了某种角色,也就意味着该用户被赋予了一组权限,然后该用户就能访问被授权的资源。
RAM角色(RAM-Role)
RAM角色不同于教科书式角色。RAM角色是一种虚拟用户(或影子账号),它是RAM用户类型的一种。这种虚拟用户有确定的身份,也可以被赋予一组权限(Policy),但它没有确定的身份认证密钥(登录密码或AccessKey)。与普通RAM用户的差别主要在使用方法上,RAM角色需要被一个授信的实体用户扮演,扮演成功后实体用户将获得RAM角色的临时安全令牌,使用这个临时安全令牌就能以角色身份访问被授权的资源。
一个账号、子账号只能扮演一种角色。当切换到角色身份后,将只能执行该角色身份被授权的所有操作,而登录时实体身份所对应的访问权限被隐藏。如果用户希望从角色身份回到实体身份,那么只需执行切回登录身份的操作,此时将拥有实体身份所对应的访问权限,而不再拥有角色身份所拥有的权限。
策略:授权策略是一组权限的集合。可以被账号、子账号、角色使用。账号、子账号、角色之间的策略是不相关的。比如账号有 A 权限,A 的子账号不会自动添加 A 权限。
STS临时授权访问 的流程
创建子账号
创建角色
给子账号加上分配角色的策略
创建对应的bucket策略
给角色加上对应的bucket策略
自定义权限
实现后台的权限自定义,Django 的 permissions 与组。
缓存
根据请求的参数缓存,比如请求了`中国`,现将`中国`的所有城市数据查找出来,写入 redis,然后再返回客户端。
如何管理redis key
项目名作为前缀
组合
通过函数组合,比如
def get_phone_redis_key(phone_num):
return 'PROJECT:{phone_num}'.format(phone_num)
这类函数写在一起,并在文档中说明
直接通过字符串 format
MOBILE_VERCODE_REDIS_KEY = settings.REDIS_PREFIX + '{mobile}:vercode'
MOBILE_VERCODE_REDIS_KEY.format(mobile)
API 文档
见我的其他博客:
api文档的设计与内容
测试
参考:
https://realpython.com/blog/python/test-driven-development-of-a-django-restful-api/
业务逻辑复杂,并且有很多需要预先创建的数据:如城市、行业等,工程量很大。所以直接连接外部数据库进行测试,使用Django Admin 或者与测试人员一起手动创建。
测试流程
测试 serializer
测试返回的字段有无遗漏
测试返回的字段类型是否正确
测试逻辑
创建主要的状态,然后测试返回的数据。
测试鉴权
测试单独的方法
测试外部接口
很多发送请求的测试,可以使用 httpmock。
如何生成测试数据?如果有多个状态,那么要使用 itertools 生成所有的状态。
* 与前端交互
在 API 文档以及测试的基础上。
* 提交给用户测试
发布
celery 的配置
--concurrency 与 worker 数目的不同。 celery worker
* 业务逻辑
很多地方要复用。
比如不同的身份鉴权,使用不同的 View 组合起来
数据库操作后,使用信号或者装饰器做对应操作。但是update好像不会触发信号,可能需要手动调用 save。这样隐性的地方要写在文档中
数据库设计
列出需要的 models,搭好骨架。
爱好、语言这样有多个的东西需要使用外键吗?还是使用Charfield, `|` 连接起来?
api 代码的编写
1. 写 form 模块
可以通过 form.errors 返回具体错误,也可以全部返回数据不完整
2. 选择
数据库的 Charfield 字段的选择使用英文而不是数字。这样能增强可读性。
选择要写为常量放在 model 里面,而不是直接字符串
3. MVC 结构
业务逻辑抽出来作为 controller 而不是混杂在 View 中 ,现在放到 services 文件夹中,以 model 作为文件名。
View 里面尽量调用 controller 中的函数解决,之后可以再整合 controller 中的函数。
services.py 与 utils 的区别
utils 就是工具,不应该包含业务逻辑,是所有的业务逻辑都可以调用的
service 包含了业务逻辑,一般只适用于各自的业务。service 中可以调用 utils
3. 使用 restframework
区分以下几种:
不可见 fields
只读fields
4. 单元测试
我总觉的生成测试数据很麻烦,尤其是各个表之间还有关联的测试数据。
使用 httmock 测试 api
5. 文档
文档中需要注明,该 api 是否需要登录才能调用
其他
花时间长的任务放入 worker 中,现在使用 celery 框架。
抛出异常与返回 None
如果能确定异常的类型,有对方的错误码,可以考虑抛出异常。
直接返回 None 的话,调用者更好判断
体会、总结
命名很重要。
如果命名混乱,不符合直接等等,会出现意想不到的bug
Django
URL slash
路径后统一包含 slash
Why django urls end with a slash?
sqlmigrate
使用 sqlmigrate 命令查看,报没有这个 INSTALLED_APPS 的错误
如何获取app name?
在 settings.INSTALLED_APPS 里面有一个 app 叫 `apps.app`,但是在 sqlmigrate 命令里面,要写为 python manage.py sqlmigrate app。
找不到 migrations 文件
加入一个文件叫做 `0055_city_upper_city.py`,那么命令写为 `python manage.py sqlmigrate app 0055_city_upper_city` 不需要文件后缀 `.py`
blank=True or null=True
blank
用于验证。如果为 True,表明这个字段可以不填
If True, the field is allowed to be blank. Default is False.
Note that this is different than null. null is purely database-related, whereas blank is validation-related. If a field has blank=True, form validation will allow entry of an empty value. If a field has blank=False, the field will be required.
null
If True, Django will store empty values as NULL in the database. Default is False.
Avoid using null on string-based fields such as CharField and TextField because empty string values will always be stored as empty strings, not as NULL. If a string-based field has null=True, that means it has two possible values for “no data”: NULL, and the empty string. In most cases, it’s redundant to have two possible values for “no data;” the Django convention is to use the empty string, not NULL.
For both string-based and non-string-based fields, you will also need to set blank=True if you wish to permit empty values in forms, as the null parameter only affects database storage (see blank).
Python 中的 empty values 是什么?
是 bool() 判断为 False 的。比如 '', None, 0, False
AppRegistryNotReady("Apps aren't loaded yet.")
see http://django.readthedocs.org/en/latest/releases/1.7.html#standalone-scripts
import django
django.setup()
If you’re using Django in a plain Python script — rather than a management command — and you rely on the
DJANGO_SETTINGS_MODULE
environment variable, you must now explicitly initialize Django at the beginning of your script with:>>> import django >>> django.setup()
Otherwise, you will hit an
AppRegistryNotReady
exception.
问: 上面这样与普通的Django注册有什么不同?
可能不能使用 management command
问:Django注册的作用、原理是什么?
model 文件循环引用,如外键、多对多之中
原理是懒加载
具体的做法是将 model 名改为字符串,比如 model.onetoone('User') 而不是 model.onetoone(User) 。