1.3 缓存、验证码、登录、表单验证
Django 中的缓存
-
接口及用法
from django.core.cache import cache # 在缓存中设置 age = 123, 10秒过期 cache.set('age', 123, 10) # 获取 age a = cache.get('age') print(a) # 自增 x = cache.incr('age') print(x)
-
使用 Redis 做缓存后端
-
安装
pip install django-redis
-
settings 配置
CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/0", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "PICKLE_VERSION": -1, } } }
-
-
利用过期时间可以处理一些定时失效的临时数据, 比如手机验证码
-
面试题:
- 如何用Redis做缓存?
Cookie、 Session 机制剖析
-
回顾 HTTP 协议
- host
- linux: /etc/hosts
- windows: C:/Windows/System32/drivers/etc/hosts
- host
-
产生过程
- 浏览器: 向服务器发送请求
- 服务器: 接受并创建 session 对象 (该对象包含一个 session_id)
- 服务器: 执行 views 函数, 并得到一个 response 对象
- 服务器: 执行 response.set_cookie(‘sessionid’, session_id) 将 session_id 写入 cookie
- 服务器: 将 response 传回浏览器
- 浏览器: 读取 response 报文, 从 Cookies 取出 session_id 并保存
-
后续请求
- 浏览器: 向服务器发送请求, session_id 随 Cookies 一同发给 Server
- 服务器: 从 Headers 的 Cookies 中取出 session_id
- 服务器: 根据 session_id 找出对应的数据, 确认客户端身份
-
Django 中的代码实现
class SessionMiddleware(MiddlewareMixin): def __init__(self, get_response=None): self.get_response = get_response engine = import_module(settings.SESSION_ENGINE) self.SessionStore = engine.SessionStore # 设置 Session 存储类 def process_request(self, request): # 从 Cookie 获取 sessionid session_key = request.COOKIES.get('session_id') # 通过 session_key 获取之前保存的数据 request.session = self.SessionStore(session_key) def process_response(self, request, response): try: # View 函数结束后, 获取 session 状态 accessed = request.session.accessed modified = request.session.modified empty = request.session.is_empty() except AttributeError: pass else: # 如果 Cookie 中有 sessionid, 但 session 为空, # 说明 view 中执行过 session.flush 等操作, # 直接删除 Cookie 中的 session if 'session_id' in request.COOKIES and empty: response.delete_cookie( settings.SESSION_COOKIE_NAME, path=settings.SESSION_COOKIE_PATH, domain=settings.SESSION_COOKIE_DOMAIN, ) else: if accessed: patch_vary_headers(response, ('Cookie',)) if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty: # 设置过期时间 if request.session.get_expire_at_browser_close(): max_age = None expires = None else: max_age = request.session.get_expiry_age() expires_time = time.time() + max_age expires = cookie_date(expires_time) # 保存会话数据, 并刷新客户端 Cookie if response.status_code != 500: try: request.session.save() except UpdateError: raise SuspiciousOperation( "The request's session was deleted before the " "request completed. The user may have logged " "out in a concurrent request, for example." ) # 让客户端将 sessionid 添加到 Cookie 中 response.set_cookie( 'session_id', request.session.session_key, max_age=max_age, expires=expires, domain=settings.SESSION_COOKIE_DOMAIN, path=settings.SESSION_COOKIE_PATH, secure=settings.SESSION_COOKIE_SECURE or None, httponly=settings.SESSION_COOKIE_HTTPONLY or None, ) return response
-
面试题:
- 如何区分 Cookie、Session?
- 请描述 HTTP 协议
验证短信
- urls.py 增加新的url路由映射
- url(r’api/user/submit/vcode’, user_api.submit_vcode)
- 实现路由映射:
- submit_vcode
- 获取手机号、验证码
- 取出缓存里的对应这个手机号的验证码
- 检查验证码一致,登录或自动注册
- 不一致返回错误
- submit_vcode
个人资料接口规划
- 获取个人资料接口
- 修改个人资料接口
- 上传个人头像接口
Profile 模型设计 (仅作参考)
Field | Description |
---|---|
location | 目标城市 |
min_distance | 最小查找范围 |
max_distance | 最大查找范围 |
min_dating_age | 最小交友年龄 |
max_dating_age | 最大交友年龄 |
dating_sex | 匹配的性别 |
vibration | 开启震动 |
only_matche | 不让为匹配的人看我的相册 |
auto_play | 自动播放视频 |
开发中的难点
- Profile 与 User 两个模型是什么关系 ?
- 企业中不使用外键如何构建 “表关联” ?
- 接口中有太多字段批量提交时应如何验证 ?
- 如何上传头像 ?https://docs.djangoproject.com/zh-hans/2.0/topics/http/file-uploads/
- 大型项目中如何保存大量的静态文件 ?
- 上传文件、发送验证码、图像处理等较慢操作应如何处理才能让用户等待时间更短 ?
数据库表关系的构建
-
关系分类
- 一对一关系
- 一对多关系
- 多对多关系
-
外键的优缺点
- 优点:
- 由数据库自身保证数据一致性和完整性, 数据更可靠
- 可以增加 ER 图的可读性
- 外键可节省开发量
- 缺点:
- 性能缺陷, 有额外开销
- 主键表被锁定时, 会引发外键对应的表也被锁
- 删除主键表的数据时, 需先删除外键表的数据
- 修改外键表字段时, 需重建外键约束
- 不能用于分布式环境
- 不容易做到数据解耦
- 优点:
-
应用场景
- 适用场景: 内部系统、传统企业级应用可以使用 (需要数据量可控, 数据库服务器数量可控)
- 不适用场景: 互联网行业不建议使用
-
手动构建关联
- 一对一: 主表 id 与 子表 id 完全一一对应
- 一对多: 在 “多” 的表内添加 “唯一” 表 id 字段
- 多对多: 创建关系表, 关系表中一般只存放两个相关联的条目的 id
- 博客案例思考
- 用户和文字的关系:一对多
- 用户和收藏关系:多对多
- 用户-角色-权限关系
- 用户-角色:一对多
2. 角色-权限:多对多
-
可通过
property
的方式对子表进行关联操作-
property 用法
class Box: def __init__(self): self.l = 123 self.w = 10 self.h = 80 @property def V(self): return self.l * self.w * self.h b = Box() print(b.V)
-
对子表关联操作
class User(models.Model): ... user_id = models.IntergerField() ... @property def user_profile(self): if not hasattr(self, '_profile'): self._profile = Profile.objects.get(user_id=self.user_id) return self._profile class User(models.Model): ... demo_id = models.IntegerField() ... @property def demo(self): if not hasattr(self, '_demo'): self._demo = Demo.objects.get(id=self.demo_id) return self._demo
class Demo(models.Model):
xxx = models.CharField()
yyy = models.CharField()user = User.objects.get(id=123)
print(user.demo.xxx)
print(user.demo.yyy)
```-
也可以使用 cached_property 对属性值进行缓存
from django.utils.functional import cached_property class User(models.Model): year = 1990 month = 10 day = 29 @cached_property def age(self): today = datetime.date.today() birth_date = datetime.date(self.year, self.month, self.day) times = today - birth_date return times.days // 365
-
- 面试题:
- 复杂SQL查询:分组、连接
- Student表有:id和name字段
- Score表有:student_id, exam_id, object_id, score
- 求:
- 某次考试成绩最好的学生
- 进阶思考:每次考试成绩前三的学生?
- 提示:窗口函数。
- 复杂SQL查询:分组、连接
Django 中的 Form 表单验证
-
Django Form 核心功能:数据验证
-
网页中
<form>
标签<form>
标签的 method 只能是 POST 或 GET- method=POST 时,表单数据在请求的 body 部分
- method=GET 时, 表单数据会出现在 URL 里
-
Form 对象的属性和方法
form.is_valid()
: 表单验证form.has_changed()
: 检查是否有修改form.clean_<field>()
: 针对某字段进行特殊清洗和验证form.cleaned_data['fieldname']
: 清洗后的数据存放于这个属性
-
Form 的定义和使用
from django import forms class TestForm(forms.Form): TAGS = ( ('py', 'python'), ('ln', 'linux'), ('dj', 'django'), ) fid = forms.IntegerField() name = forms.CharField(max_length=10) tag = forms.ChoiceField(choices=TAGS) date = forms.DateField() POST = {'fid': 'bear', 'name': 'hello-1234567890', 'tag': 'django', 'date': '2017/12/17'} form = TestForm(POST) print(form.is_valid()) print(form.cleaned_data) # cleaned_data 属性是 is_valid 函数执行时动态添加的 print(form.errors)
-
ModelForm 可以通过相应的 Model 创建出 Form
class UserForm(ModelForm): class Meta: model = User fields = ['name', 'birth']
云服务市场
- AWS:亚马逊:Amazon Web Service,全球领先
- 云服务器:EC2:elastic cloud computer
- 云存储:S3:simple storage service
- 云数据库:RDS:relationship database service:MySQL
- 消息队列:SQS:Simple Queue Service
- 全文检索:
- 云数据仓库:redshift
- Azure:微软:非常多的linux服务器,各种服务都有
- Google Cloud
- 阿里云:国内第一
- 云服务器:ECS
- 云存储:OSS
- 云数据库:RDS
- 腾讯云
- …
- 不值一提的云:
- 金山云
- 百度云
- 华为云
- 专用云:
- 存储:
- 七牛
- 图片缩放:
- 新建图片样式
- 选择自己想要的缩放方式
- 起名字
- 保存
- 使用方法:
- http://pvhv7tfry.bkt.clouddn.com/space_city.jpg?imageView2/1/w/200/h/200/q/75|imageslim
- 图片的渐进式显示:
1.
- 图片缩放:
- 又拍
- 七牛
- 直播:
- 短信:云之讯
- 邮件:sendcloud
- 存储:
- 上云之后服务的构建方法:
- 只用云服务器,其他都自己在云服务器里实现
- 不依赖云,换云或离开的成本极低
- 用的人多,开发周期长
- 充分利用各种云服务
- 节约人力成本,用的人少,开发周期短
- 依赖于云服务,换云或者离开云的成本高
- 在云上自己做高可用
- 只用云服务器,其他都自己在云服务器里实现
- 不上云,
- 传统业务
- 纯流量型业务:视频分发
- 面试题:
- 你们用了什么云服务?
- 都用了哪些功能?
- 多少钱?