def index(request):
# 模板语法可以传递的后端python数据类型
n = 123
f = 11.11
...
def func():
print('我被执行了')
return '你的另一半在等你'
class MyClass(object):
def get_self(self):
return 'self'
@staticmethod
def get_func():
return 'func'
@classmethod
def get_class(cls):
return 'cls'
def __str__(self):
return '你到底会不会?'
obj=MyClass()
return render(request, 'index.html', locals())
总结
《花括号》与《百分号》含义
- {{ }} 变量相关
- {% %} 逻辑相关
- {{ n }} 或 {{ f }} 或 {{ s }}
- 等等各种类型全部都可以传
- {{ func }}
- 函数
自动帮你加括号调用
,传的是返回值。切记,不支持给函数传参数
,会给你报错。
- 类 {{ MyClass }}
- 对象 {{ obj }}
- 传类名的时候,也会自动加括号调用(实例化)
- {{ obj.get_self }}
- {{ obj.get_func }}
- {{ obj.get_class }}
- 内部能够自动判断出当前的变量名是否可以加括号调用,
- 如果可以就自动执行(针对函数名和类名)
- 只能用句点符 . ,哪怕字典列表嵌套
- d.info.3.hobby
类似于一些简单的内置方法,后端传来一个变量,前端展示关于变量的信息
Django内置 60+ 过滤器,我们了解10个左右,差不多了。后面碰到了再去记忆
通用格式
{{ 变量名|过滤器:'参数' }}
下面都可以包含在
标签中
{{ s|length }}
统计长度{{ b|default: '啥也不是' }}
默认值- 后端
file_size = 123421
,前端{{ file_size|filesizeformat }}
文件大小(自动帮你和1024做计算。){{ l|slice:'0:4:2' }}
切片操作info = 'aeioajflvnaowenvzbonod'
{{ info|truncatechars:9 }}
切取字符(例如文章摘要,加上三个点):egl = 'My name is jason, and my ...'
{{ egl|truncatedwords:9 }}
msg = 'I Love You and you?'
切取单词(不包含三个点了,只认空格):{{ l|cut:' ' }}
移除字符{{ l|join:'$' }}
拼接操作:{{ n|add:10 }}
拼接操作(加法{{ s|add:msg }}
日期格式化
后端
import datetime
current_time = datetime.datetime.now()
前端
{{ current_time|date:'Y-m-d H:i:s' }}
safe:取消转义
hhh = '
敏敏
'
不转义:{{ hhh }}
转义:{{ hhh|safe }}
如果需要后台进行表格的修改(例如书本打折)
# 修改
user_list = models.User.objects.filter(name__contains='egon')
for user_obj in user_list:
user_obj.age = user_obj.id + 1
user_obj.save()
from django.test import TestCase
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")
if __name__ == "__main__":
import django
django.setup()
from app01 import models
from app01 import views
filter()
values()
values_list()
返回结果的区别user_query = models.User.objects.filter(name__contains='egon')
print(user_query) # , ,。。。
user_query = models.User.objects.filter(name__contains='egon').values()
print(user_query) #
user_query = models.User.objects.filter(name__contains='egon').values_list()
print(user_query) #
all()
和filter()
- values() 和 values_list() # (重点)
get(pk=5)
- first() 和 last() # queryset 的第一个或最后一个元素
- distinct()
- order_by() 和 reverse()
- count() 统计数字
- exclude()
- exists()
filter 是筛选条件
values 和 values_list 是字段
要点objects,再点方法。
查询的返回的结果都是 queryset
# values,values_list
res = models.User.objects.values('name', 'age')
# 返回结果:queryset,列表套字典
print(res)
res = models.User.objects.values_list('name', 'age')
# 返回结果:queryset,列表套元组
print(res)
# 查看内部sql语句
print(res.query)
# SELECT `day64_user`. `name`, `day64_user`. `age` FROM `day64_user`
# distinct
# 默认主键别忽略了
res = models.User.objects.values('name', 'age').distinct()
print(res)
# 排序
res = models.User.objects.values_list().order_by('age') # 升序
res = models.User.objects.values_list().order_by('-age') # 降序
print(res)
# reverse 反转(前提是跟在order_by后面)
# count 统计个数
res = models.User.objects.all().count() # 统计个数
print(res)
# exclude 排除在外
# exists 基本用不到,返回bool
- 大于小于
- 某个范围内
- 包含不包含(区分大小写)开头结尾
- 时间字段中定位年月日
# 年龄大于35的数据
res = models.User.objects.filter(age__gt=35).values('name')
print(res)
# 年龄小于35的数据
res = models.User.objects.filter(age__lt=35).values('name')
print(res)
# 大于等于,小于等于
res = models.User.objects.filter(age__gte=35).values('name')
print(res)
# 年龄是18,或者32,或者40
res = models.User.objects.filter(age__in=[18, 32, 40]).values('name')
print(res)
# 年龄是18 到 40
res = models.User.objects.filter(age__range=[18, 40]).values('name')
print(res)
# 模糊查询:名字里含有s(默认区分大小写的)
res = models.User.objects.filter(name__contains='s').values('name')
print(res)
# 模糊查询:忽略大小写的)
res = models.User.objects.filter(name__icontains='s').values('name')
print(res)
# 开头是j,结尾是j
res = models.User.objects.filter(name__startswith='j').values('name')
print(res)
# 查询出注册时间是2020年,1月
res = models.User.objects.filter(register_time__month='1').values('name')
print(res)
# 一对多外键增删改查
# 增加
# 方法1:直接写实际字段
models.Book.objects.create(title='论语', price=998.23, publish_id=1)
models.Book.objects.create(title='聊斋', price=444.23, publish_id=2)
models.Book.objects.create(title='老子', price=333.23, publish_id=1)
# 方法2:虚拟字段 对象
# 给外键创建一个对象,加到一的外键字段中去
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='红楼梦', price=666.23, publish=publish_obj)
# 删除
models.Publish.objects.filter(pk=1).delete()
# update 方法
# 编号为1的书,把出版社修改为2
# 方法1,
models.Book.objects.filter(pk=1).update(publish_id=2)
# 方法2
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).update(publish=publish_obj)
# ------------------------------
# 区别在于:
# 1. **实际字段** 是数据库表格中的字段名称。
# 2. **虚拟字段** 是类定义中的外键名称
–
# 如何给书籍添加作者?
# 这里很尴尬,因为表格不是我们创的,句点符点不到。
book_obj = models.Book.objects.filter(pk=1).first()
# 类似于已经到了第三张关系表了
print(book_obj.authors)
# 书籍id=1的书,绑定一个id为1的作者,两个三个都可以,因为一本书可以有多个作者。
book_obj.authors.add(1)
book_obj.authors.add(2, 3)
author_obj = models.Author.objects.filter(pk=1).first()
author_obj1 = models.Author.objects.filter(pk=2).first()
author_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.authors.add(author_obj1, author_obj2)
'''
add 方法给第三张关系表添加数据
括号内可以传数字,也可以传对象,并且都支持多个。
'''
# 删除
# book为1的书,作者2没了。
book_obj.authors.remove(2)
# 同样支持多个和对象
# 多对多关系的修改 -- set方法
# 找到一个 book 对象,点虚拟字段,点set方法
# 括号内必须是一个可迭代对象,元祖列表都可以。
book_obj.authors.set((1, 2))
'''
可以是数字,也可以是对象。
'''
# 清空
# 在第三张关系表格中,清空书籍与作者的绑定关系。
# 括号内不要加任何参数
book_obj.authors.clear()
什么是聚合查询?
max、min、sun、count、avg
需要先导入5个模块from django.db.models import Max, Min, Sum, Count, Avg
和数据库有关,要不在django.db.models
,要不在django.db
from day64 import models
from django.db.models import Max, Min, Sum, Count, Avg
# 1. 统计书的平均价格
res = models.Book.objects.aggregate(Avg('price'))
print(res)
# 2. 上述方法一次性使用
res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Count('price'), Avg('price'))
print(res)
分组之后,只能获取到分组的依据,组内其他字段都无法直接获取了。
严格模式:ONLY_FULL_GROUP_BY
“每个”,按什么分组
models
后面点什么,按照什么分组。
author_num
是我们自己定义的字段,用来存储统计每本书对应的作者个数。
# 1. 统计每一本书的作者个数
res = models.Book.objects.annotate(author_num=Count('authors')).values('title', 'author_num')
print(res)
# 2. 统计每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
print(res)
# 3. 统计不止一个作者的图书
# 步骤:统计每本书几个作者,过滤出作者大于1的。
res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title', 'author_num')
print(res)
'''
只要结果还是一个queryset对象,就可以无限次点queryset方法。
比如点filter(),点到一滴都不剩。
'''
# 4. 查询每个作者出的书的总价格。
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name', 'sum_price')
print(res)
# 5. 想按照指定的字段分组,如何处理?
# models.Book.objects.values('price').annotate()
# 只要出现分组报错,需要修改数据库严格模式。
# 1. 查询卖出数量大于库存数量的书籍(条件不是具体的数字)
from django.db.models import F
res = models.Book.objects.filter(maichu__gt=F('kucun'))
print(res)
# 2. 所有书的加个,提升500块.
# 看一眼数据库,都加了500
# res = models.Book.objects.update(price=F('price') + 500)
# print(res)
# 3. 所有书的名称后面加上爆款两个字
# F 不能直接做字符串的拼接,需要导入模块。
from django.db.models.functions import Concat
from django.db.models import Value
res = models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
print(res)
# 1. 查询卖出数量大于 100 或者价格小于 600 的书籍
# Q 包裹,可以用连接符了。
from django.db.models import Q
# 与或非
res = models.Book.objects.filter(Q(maichu__gt=100) , Q(maichu__lt=600))
res = models.Book.objects.filter(Q(maichu__gt=100) | Q(maichu__lt=600))
res = models.Book.objects.filter(~Q(maichu__gt=100) | Q(maichu__lt=600))
print(res)
# Q的高阶用法,实现搜索功能,条件左边也是字符串,不需要是变量名
# Q 对象 默认还是 AND 关系。
q = Q()
# 修改拼接关系
q.connector = 'or'
q.children.append(('maichu__gt', 100))
q.children.append(('maichu__gt', 100))
# filter 括号内支持 Q 对象
res = models.Book.objects.filter(q)
print(res)
- 事务的四个特性 ACID
- 回滚 rollback
- 确认 commit
from django.db import transaction
try:
with transaction.atomic():
# sql1
# sql3
# sql3
# 在 with 代码块中所有的 orm 操作都是同一个事务
pass
except Exception as e:
print(e)
print('执行其他操作')
常用字段 | 说明 | 参数 |
---|---|---|
AutoField | 主键字段 | primary_key=True |
CharField | varchar,verbose_name 字段注释,max_length | |
IntegerField | int | |
BigIntegerField | big int | |
DecimalField | max_digits=8,decimal_places=2 | |
EmailField | varcahr(254) | |
DateFiled | date | |
DateTimeField | datetime,auto_now,auto_now_add: | |
BooleanFiled(Field) | 传值False/True,数据库里存0或1 | |
TextField(Field) | 存大段内容(文章,博客……)没有字数限制,bbs作业的文章字段会用。 | |
FileField | 文件字段,upload_to="/data" 给该字段传一个文件对象,会自动将文件保存到/data目录下,然后将文件路径保存到数据库中。/data/a.txt,后面bbs作业也会讲。 | upload_to="/data" |
- orm 惰性查询
- only 与 defer (只查某字段与就不查某字段)
- select_related 与 prefetch_related (外键查询,双下划线联表操作)
如果你仅仅书写了orm语句,在后面根本没有用到该语句所查询出来的参数,
那么 orm 会自动识别,直接不执行
例如
res = models.Book.objects.all()
print(res) # 没这句不执行上面的。
# 默认 all 会查询全部。
- 都是返回
列表套对象
- only
只查括号内字段
,点别的字段,就要重新走数据库- defer 别的都查,
就不查括号内字段
。
想要获取书籍表中所有书的名字
res = models.Book.objects.values('title')
for d in res:
print(d.get('title'))
用 only 获取书籍表中所有书的名字以及价格。
# 如果要查看别的属性,那就还要走数据库查询语句。
res = models.Book.objects.only('title')
# 取出《only 列表套对象》
for i in res:
# 点击only括号内的字段,不会走数据库
print(i.title)
# 对于 only 括号内没有的字段,会重新走数据库查询。
print(i.price)
用 defer 获取书籍表中所有书的名字以及价格
# 除了括号里的没有,其他都有
res = models.Book.objects.defer('title')
for i in res:
print(i.price)
- select_related 和 prefetch_related 都和
跨表操作
有关- 内部
直接联表操作
,一次性将大表所有数据封装给查询出来的对象。- 无论点击book表的数据,还是publish的数据,无需再走数据库查询。
- select_related 括号里只能放
外键字段的一对一,一对多
。- prefetch_related 内部就是
内部子查询
。
res = models.Book.objects.all()
for i in res:
# 《书名》和《出版社名》在两张表上,
# 每查询一次,都要走一次数据库。
print(i.publish.name)
用 select_related
res = models.Book.objects.select_related('publish')
for i in res:
print(i.publish.name)
# prefetch_related 内部就是内部子查询。
# 使用者感觉不粗差距
res = models.Book.objects.prefetch_related('publish')
for i in res:
print(i.publish.name)
res = models.Book.objects.select_related('外键字段1__外键字段2__外键字段3')
for i in res:
print(i.publish.name)
- 模板制作
- 首页
- 书的增删改查
性别,学历,工作经验,客户来源……
能够列举完全的字段,那么一般情况下采用choices 参数
取的时候用get_字段_display()
方法
models.py
class User(models.Model):
username = models.CharField(max_length=32)
age = models.IntegerField()
# 性别
gender_choice = (
(1, '男'),
(2, '女'),
(3, '其他'),
)
gender = models.IntegerField(choices=gender_choice)
'''
gender 字段存的还是数字,
保证 choices 字段和字段内类型一致即可。
'''
# 存,没有被列举出来的数字也可以存
models.User.objects.create(username='jason', age=18, gender=1)
models.User.objects.create(username='egon', age=84, gender=2)
models.User.objects.create(username='tank', age=40, gender=3)
models.User.objects.create(username='tony', age=30, gender=4)
# 取,用 get_字段_display() 方法
user_obj = models.User.objects.filter(pk=1).first()
print(user_obj.gender, user_obj.get_gender_display())
# 如果没有对应字段,那存的是什么,取出还是什么
user_obj = models.User.objects.filter(pk=4).first()
print(user_obj.gender, user_obj.get_gender_display())
- MTV:django 给自己起的别名,本质还是 MVC
- models,templates,views。
- models,views,
controller(urls.py,)
- 全自动
- 纯手动
- 半自动
例如,图书与作者关系
class Book(models.Model):
...
authors = models.ManyToManyField(to='Author')
...
优点
- 不用写代码,方便,快速
缺点
- 第三张表的扩展性极差
class Book2Author(models.Model):
...
book_id = models.ForeignKey(to='Book')
author_id = models.ForeignKey(to='Author')
...
优点
- 第三张表完全取决于你
缺点
- 代码太多,无法使用 orm 提供的正反向查询和简单方法(add,set,remove,clear···)
- 不推荐使用《纯手动》
class Book(models.Model):
...
authors = models.ManyToManyField(
to='Author',
through='Book2Author',
through_field=('book', 'author'),
)
...
class Author(models.Model):
...
# Book2Author 还是需要自己写,在 Book 中关联
class Book2Author(models.Model):
...
book_id = models.ForeignKey(to='Book')
author_id = models.ForeignKey(to='Author')
...
半自动可以使用正反向查询,但是依旧不能使用 add,set,remove,clear···
- 需要掌握《全自动》和《半自动》,一般采用《半自动》
局部筛选,异步提交
例子:github 注册
- 动态获取用户名,实时发到后端检测
我们学过的发送请求的方式
- 地址栏输入url,只有get
- a标签的href属性,只有get
- from表单,get | post
- ajax,get | post
->我们现在大部分都是 ajax。
- 不是新的编程语言,是一种使用现有标准的新方法(例如装饰器)
- 不需要重新加载整个网页,就与服务器交互,更新网页数据。
- 我们只学 jQuery 封装之后的版本。原生的不太用。(不知 jQuery,其他框架也可以,就是关键字不同,换汤不换药)
- 所以一定要确保导入了 jQuery。
- 后端
views.py
导入模块from django.http import JsonResponse
视图函数
返回return JsonResponse(back_dic)
前端页面
导入 jQuery
<input type="text" name="" id="d1">+
<input type="text" name="" id="d2">=
<input type="text" name="" id="d3">
<p>
<button id="btn">点我button>
p>
<script>
// 用 jquery 给按钮绑定一个点击事件
$('#btn').click(function(){
// 朝后端发送 ajax 请求
$.ajax({
url: '',
type: 'post',
dataType: 'Json',
data: {'i1': $('#d1').val(), 'i2': $('#d2').val()},
success:function(args){
// alert(args)
$('#d3').val(args)
}
})
})
script>
- 最后一个success是回调函数(形参写什么都可以)。无论输入啥,最后都是返回给回调函数。不是浏览器。
- 如果要返回字典的话,需要用 json 格式。
- 不管什么格式,前端(这个页面)收到的参数都是字符串格式。
def ab_ajax(request):
if request.method == "POST":
print(request.POST)
i1 = request.POST.get('i1')
i2 = request.POST.get('i2')
i3 = int(i1) + int(i2)
return HttpResponse(i3)
return render(request, 'ab_ajax.html')
- 如果后端返回的是
HttpResoponse
,只能在前端,Json.parse()
自己解决- 如果后端返回的是
JsonResponse
,前端会自动帮你反序列化dataType: 'Json',
from django.http import JsonResponse
def ab_ajax(request):
...
# 这里 d 是个字典。
return JsonResponse(d)
...
以后写 ajax 的时候,可以直接加上,以防万一。
(主要研究 POST 请求的编码格式)
向后端发psot请求的两种方式
- form 表单
- ajax
三种格式
- urlencoded
- formdata
- json
- 发送文本:默认 encoded
- 查看方法:检查 – Network – 左侧 name – 在请求头(Request Heads)里面找 content-type == encoded
- 最底下的 view source,可以看到
name=jason&password=123
django 后端会自动帮忙就解析到 request.POST 中。
enctype="multipart/form-data"
如果是 encoded
只要长得是name=jason&password=123
,都会帮你解析到 request.POST 。
但是如果把编码格式,改成formdata
,
字符还是会帮你解析到 request.POST .
文件
会解析到request.FILES
中
默认也是 urlencoded
所以,无论哪种,都可以通过 request.POST 中获得。
防止前后端
不全用
Django框架写的。
前后端一定要保证编码格式和数据真正格式是一致的。
前端
data: JSON.stringify({'username': 'jason', 'age': ''18})
转换成JSON格式contentType: 'application/json', // 指定编码格式,
- 数据是真正的 json 格式
后端
要判断 ajax 请求:if(request.is_ajax()):
- 在 request.POST 里面找不到,要在
request.body
中自己去找。- 如果
前端生成内置对象
,并且有文件
,一定要拒绝编码处理,文件在后端的request.FILES
中寻找,普通键值对在request.POST
中寻找。
<button class="btn btn-success" id="d1">点我button>
<script>
// 用 jquery 给按钮绑定一个点击事件
$('#d1').click(function(){
$.ajax({
url: '',
type: 'post',
// 转换成 json 格式
data: JSON.stringify({'username': 'jason', 'age': '18'}),
// 指定编码格式
contentType: 'application/json',
success:function(){
}
})
})
script>
if(request.is_ajax()):
pass
django对于ajax 的 json 数据,不会做任何处理。
在 request.body 里面
针对 json 格式数据,需要手动处理。
# 第一种写法(全面)
if(request.is_ajax()):
# bytes -- str -- dict
json_bytes = request.body
json_str = json_bytes.decode('utf-8')
json_dict = json.loads(json_str)
print(json_dict, type(json_dict))
# 第二种写法(简便)
if(request.is_ajax()):
# bytes -- dict (json.loads() 会先自动解码,再自动反序列化)
json_bytes = request.body
json_dict = json.loads(json_bytes)
print(json_dict, type(json_dict))
向后端发送普通键值对,也发送文件
p*3>inp
前端(通过内置对象发送,建议背诵)
<p>username:<input type="text" name="" id="d1">p>
<p>password<input type="text" name="" id="d2">p>
<p><input type="file" name="" id="d3">p>
<button class="btn btn-info" id="d4">点我,有你好看button>
<script>
// 点击按钮,向后端发送普通键值对和文件
$('#d4').on('click', function() {
// 1. 先生成一个内置对象
let formDataObj = new FormData();
// 2. 可以添加普通键值对,也可以添加文件
formDataObj.append('username', $('#d1').val());
formDataObj.append('password', $('#d2').val());
formDataObj.append('myfile', $('#d3')[0].files[0]);
// 3. 将对象基于 ajax 发送给后端
$.ajax({
url: '',
type: 'post',
// 直接放对象即可
data: formDataObj,
// 必须指定两个参数,告诉后端编码和浏览器,别对数据动手动脚
contentType: false,
processData: false,
success: function(args){
}
})
})
script>
后端
def ab_file(request):
if request.is_ajax():
if request.method == 'POST':
print(request.POST)
print(request.FILES)
# 结果:
#
# ]}>
pass
return render(request, 'ab_file.html', locals())
- 需要使用对象 formData
- 指定两个关键的参数,
拒绝编码和数据处理
- 后端可以直接直接识别
formData
对象并且自动解析,将普通键值对
封装到request.POST
中,将文件
封装到request.FILES
中。
gender_choice
models.IntegerField(choices=gender_choice)e
models.py
class User(models.Model):
username = models.CharField(max_length=32)
age = models.IntegerField()
# 性别
gender_choice = (
(1, '男'),
(2, '女'),
(3, '其他'),
)
gender = models.IntegerField(choices=gender_choice)
取的时候用 get_字段_display() 方法
例如user_obj.get_gender_display()
# 存,没有被列举出来的数字也可以存
models.User.objects.create(username='jason', age=18, gender=1)
models.User.objects.create(username='egon', age=84, gender=2)
models.User.objects.create(username='tank', age=40, gender=3)
models.User.objects.create(username='tony', age=30, gender=4)
# 取,用 get_字段_display() 方法。--》男
user_obj = models.User.objects.filter(pk=1).first()
print(user_obj.gender, user_obj.get_gender_display())
# 如果没有对应字段,那存的是什么,取出还是什么。--》4
user_obj = models.User.objects.filter(pk=4).first()
print(user_obj.gender, user_obj.get_gender_display())
例如图书表与作者表关系,是多对多。
一本书可以有多个作者,一位作者可以有多本书。
authors = models.ManyToManyField(...),
authors = models.ManyToManyField(to='Author')
models.ForeignKey(to='Book')
class Book(models.Model):
...
authors = models.ManyToManyField(
to='Author',
through='Book2Author',
through_field=('book', 'author'),
)
...
class Author(models.Model):
...
# Book2Author 还是需要自己写,在 Book 中关联
class Book2Author(models.Model):
...
book_id = models.ForeignKey(to='Book')
author_id = models.ForeignKey(to='Author')
...
class Book(models.Model):
...
authors = models.ManyToManyField(to='Author')
...
优点
缺点
class Book2Author(models.Model):
...
book_id = models.ForeignKey(to='Book')
author_id = models.ForeignKey(to='Author')
...
<input type="text" name="" id="d1">+
<input type="text" name="" id="d2">=
<input type="text" name="" id="d3">
<p>
<button id="btn">点我button>
p>
<script>
// 用 jquery 给按钮绑定一个点击事件
$('#btn').click(function(){
// 朝后端发送 ajax 请求
$.ajax({
url: '',
type: 'post',
dataType: 'Json',
data: {'i1': $('#d1').val(), 'i2': $('#d2').val()},
success:function(args){
// alert(args)
$('#d3').val(args)
}
})
})
script>
def ab_ajax(request):
if request.method == "POST":
print(request.POST)
i1 = request.POST.get('i1')
i2 = request.POST.get('i2')
i3 = int(i1) + int(i2)
return HttpResponse(i3)
return render(request, 'ab_ajax.html')
dataType: 'Json',
import json
def ab_ajax(request):
...
# 这里 d 是个字典。
return JsonResponse(d)
...
无论哪一种,最后都可以通过 request.POST 获取。
前端向后端发psot请求的两种方式
三种格式
name=jason&password=123
记得 enctype="multipart/form-data"
name=jason&password=123
,都会帮你解析到 request.POST 。前后端一定要保证编码格式和数据真正格式是一致的。
比如说好的列表,最后发了个字符串。
contentType: 'application/json', // 指定编码格式,
<button class="btn btn-success" id="d1">点我button>
<script>
// 用 jquery 给按钮绑定一个点击事件
$('#d1').click(function(){
$.ajax({
url: '',
type: 'post',
// 转换成 json 格式
data: JSON.stringify({'username': 'jason', 'age': '18'}),
// 指定编码格式
contentType: 'application/json',
// 回调函数
success:function(){}
})
})
script>
if(request.is_ajax()):
pass
if(request.is_ajax()): ...
json.loads(json_bytes)
,转换成字典,即可# 第一种写法(全面)
if(request.is_ajax()):
# bytes -- str -- dict
json_bytes = request.body
json_str = json_bytes.decode('utf-8')
json_dict = json.loads(json_str)
print(json_dict, type(json_dict))
# 第二种写法(简便)
if(request.is_ajax()):
# bytes -- dict (json.loads() 会先自动解码,再自动反序列化)
json_bytes = request.body
json_dict = json.loads(json_bytes)
print(json_dict, type(json_dict))
步骤
formDataObj.append('myfile', $('#d3')[0].files[0]);
前端
<p>username:<input type="text" name="" id="d1">p>
<p>password<input type="text" name="" id="d2">p>
<p><input type="file" name="" id="d3">p>
<button class="btn btn-info" id="d4">点我,有你好看button>
<script>
// 点击按钮,向后端发送普通键值对和文件
$('#d4').on('click', function() {
// 1. 先生成一个内置对象
let formDataObj = new FormData();
// 2. 可以添加普通键值对,也可以添加文件
formDataObj.append('username', $('#d1').val());
formDataObj.append('password', $('#d2').val());
formDataObj.append('myfile', $('#d3')[0].files[0]);
// 3. 将对象基于 ajax 发送给后端
$.ajax({
url: '',
type: 'post',
// 直接放对象即可
data: formDataObj,
// 必须指定两个参数,告诉后端编码和浏览器,别对数据动手动脚
contentType: false,
processData: false,
// 回调函数
success: function(args){}
})
})
script>
后端
def ab_file(request):
if request.is_ajax():
if request.method == 'POST':
print(request.POST)
print(request.FILES)
# 结果:
#
# ]}>
return render(request, 'ab_file.html', locals())
总结
解决办法
def ab_ser(request):
import json
from django.http import JsonResponse
user_queryset = models.User.objects.all()
user_list = []
for user_obj in user_queryset:
temp = {
'pk': user_obj.pk,
'username': user_obj.username,
'age': user_obj.age,
'gender': user_obj.get_gender_display(),
}
user_list.append(temp)
return JsonResponse(user_list, safe=False)
结果(人工智能换行)
[
{"pk": 1, "username": "jason", "age": 18, "gender": "\u7537"},
{"pk": 2, "username": "egon", "age": 84, "gender": "\u5973"},
{"pk": 3, "username": "tank", "age": 40, "gender": "\u5176\u4ed6"},
{"pk": 4, "username": "tony", "age": 30, "gender": 4}
]
def ab_ser(request):
from django.core import serializers
user_queryset = models.User.objects.all()
# 自动帮你变成 json 格式字符串,而且非常全面
res = serializers.serialize('json', user_queryset)
return HttpResponse(res)
结果
[
{
"model": "day66.user",
"pk": 1,
"fields": {"username": "jason", "age": 18, "gender": 1}
},
{
"model": "day66.user",
"pk": 2,
"fields": {"username": "egon", "age": 84, "gender": 2}
},
{
"model": "day66.user",
"pk": 3,
"fields": {"username": "tank", "age": 40, "gender": 3}
},
{
"model": "day66.user",
"pk": 4,
"fields": {"username": "tony", "age": 30, "gender": 4}
}
]
def ab_batch_insert(request):
'''
1. 先给Book,插入一万条数据
1. 所有数据查询并展示到页面。
:param request:
:return:
'''
for i in range(10000):
models.Book.objects.create(title='第%i本书' %i)
book_queryset = models.Book.objects.all()
return render(request, 'ab_batch_insert.html', locals())
models.Book.objects.bulk_create(book_list)
def ab_batch_insert(request):
book_list = []
for i in range(10000):
book_obj = models.Book(title='第%i本书' %i)
book_list.append(book_obj)
models.Book.objects.bulk_create(book_list)
book_queryset = models.Book.objects.all()
return render(request, 'ab_batch_insert.html', locals())
# 1. 访问哪一页?通过get请求,如果没有,就展示第一页
current_page = request.GET.get('page', 1)
# 数据类型转换
try:
current_page = int(current_page)
except Exception:
current_page = 1
# 2. 每页展示多少条?
per_page_num = 10
# 分页规律
# 3. 起始位置
start_page = (current_page - 1) * per_page_num
# 4. 终止位置
end_page = current_page * per_page_num
'''
这要计算页面显示多少页的按钮
'''
# 展示所有数据 (切片操作)
book_queryset = models.Book.objects.all()[start_page:end_page]
# 1. 访问哪一页?通过get请求,如果没有,就展示第一页
current_page = request.GET.get('page', 1)
# 数据类型转换
try:
current_page = int(current_page)
except Exception:
current_page = 1
# 1. 每页展示多少条?
per_page_num = 10
# 规律 start_page = (current_page - 1) * per_page_num
# 1. 起始位置
start_page = (current_page - 1) * per_page_num
# 1. 终止位置
end_page = current_page * per_page_num
# 分页,计算页面要显示多少页的按钮
book_list = models.Book.objects.all()
all_count = book_list.count()
page_count, more = divmod(all_count, per_page_num)
if more:
page_count += 1
page_html = ''
xxx = current_page
if current_page < 6:
current_page = 6
for i in range(current_page-5, current_page+5):
# 做一个高亮显示
if xxx == i:
page_html += '- %s
' %(i, i)
else:
page_html += '- %s
' %(i, i)
# 展示所有数据(切片操作)
book_queryset = models.Book.objects.all()[start_page:end_page]
return render(request, 'ab_batch_insert.html', locals())
@property
,将方法伪装成属性,不需要加括号也可以调用myPage.py
class Pagination(object):
def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
"""
封装分页相关数据
:param current_page: 当前页
:param all_count: 数据库中的数据总条数
:param per_page_num: 每页显示的数据条数
:param pager_count: 最多显示的页码个数
"""
try:
current_page = int(current_page)
except Exception as e:
current_page = 1
if current_page < 1:
current_page = 1
self.current_page = current_page
self.all_count = all_count
self.per_page_num = per_page_num
# 总页码
all_pager, tmp = divmod(all_count, per_page_num)
if tmp:
all_pager += 1
self.all_pager = all_pager
self.pager_count = pager_count
self.pager_count_half = int((pager_count - 1) / 2)
@property
def start(self):
return (self.current_page - 1) * self.per_page_num
@property
def end(self):
return self.current_page * self.per_page_num
def page_html(self):
# 如果总页码 < 11个:
if self.all_pager <= self.pager_count:
pager_start = 1
pager_end = self.all_pager + 1
# 总页码 > 11
else:
# 当前页如果<=页面上最多显示11/2个页码
if self.current_page <= self.pager_count_half:
pager_start = 1
pager_end = self.pager_count + 1
# 当前页大于5
else:
# 页码翻到最后
if (self.current_page + self.pager_count_half) > self.all_pager:
pager_end = self.all_pager + 1
pager_start = self.all_pager - self.pager_count + 1
else:
pager_start = self.current_page - self.pager_count_half
pager_end = self.current_page + self.pager_count_half + 1
page_html_list = []
# 添加前面的nav和ul标签
page_html_list.append('''
)
first_page = '- 首页
' % (1)
page_html_list.append(first_page)
if self.current_page <= 1:
prev_page = '- 上一页
'
else:
prev_page = '- 上一页
' % (self.current_page - 1,)
page_html_list.append(prev_page)
for i in range(pager_start, pager_end):
if i == self.current_page:
temp = '- %s
' % (i, i,)
else:
temp = '- %s
' % (i, i,)
page_html_list.append(temp)
if self.current_page >= self.all_pager:
next_page = '- 下一页
'
else:
next_page = '- 下一页
' % (self.current_page + 1,)
page_html_list.append(next_page)
last_page = '- 尾页
' % (self.all_pager,)
page_html_list.append(last_page)
# 尾部添加标签
page_html_list.append('''
views.py
from utils.myPage import Pagination
前端
{% for book_obj in page_queryset %}
{{ book_obj.title }}
{% endfor %}
{{ page_obj.page_html|safe }}
不使用 ajax 校验数据,直接返回给前端。
- 渲染 html 代码
- 校验数据
- 展示提示信息
需求
- 写一个 html 页面,获取用户名密码,form 表单提交数据
- 后端判断用户名和密码是否符合一定条件
1. 用户名中不能含有金瓶梅
1. 密码不能少于三位- 将提示信息展现到 html 页面中。
解决思路
- 用户名和密码的输入框后面,放一个看不见的span标签,用模板语法给它传值。
- 模板语法内容:
{{ back_dic.username }}
- 自己定义一个字典:
back_dic = {'username': '', 'password': ''}
。- key 是每个变量的名字,value 先是空的。
- 如果有错误,修改 key 对应的 value。
- 写一个分支,如果校验正确,返回 back_dic,提示正确。如果校验错误,返回 back_dic,提示输入有误。
小例子
前端页面
<form action="" method="post">
<p>username:
<input type="text" class="form-control" name="username">
<span style="color: red">{{ back_dic.username }}span>
p>
<p>password:
<input type="text" class="form-control" name="password">
<span style="color: red">{{ back_dic.password }}span>
p>
<input type="submit" class="btn- btn-info">
form>
后端视图
def ab_form(request):
back_dic = {'username': '', 'password': ''}
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if '金瓶梅' in username:
back_dic['username'] = '不符合社会主义核心价值观'
if len(password) < 3:
back_dic['password'] = '太短了'
return render(request, 'ab_form.html', locals())
注意
from django import forms
(导入模块)class MyForm(forms.Form):
(定义类)username = forms.CharField(..., ...)
(类似于orm语句)def clean_username(self): ...
(局部钩子)def clean(self): ...
(全局钩子)views.py
from django import forms
# forms 组件
class MyForm(forms.Form):
username = forms.CharField(min_length=3, max_length=8, label='用户名',
error_messages={'min_length': '最少3位',
'max_length': '最多8位',
'required': '不能为空',
})
password = forms.CharField(min_length=3, max_length=8, label='密码',
error_messages={'min_length': '最少3位',
'max_length': '最多8位',
'required': '不能为空',
})
confirm_password = forms.CharField(min_length=3, max_length=8, label='密码',
error_messages={'min_length': '最少3位',
'max_length': '最多8位',
'required': '不能为空',
})
email = forms.EmailField(error_messages={'invalid': '格式错误',
'required': '不能为空',
})
# 局部钩子
def clean_username(self):
username = self.clean_data.get('username')
if '666' in username:
self.add_error('username', '名称不能包含666')
return username # 钩子放回去
# 全局钩子
def clean(self):
password = self.clean_data.get('password')
confirm_password = self.clean_data.get('confirm_password')
if not password == confirm_password:
self.add_error('confirm_password', '两次密码不一致')
return self.cleaned_data # 钩子放回去,返回全部数据
python django 的测试环境,测试forms组件
找到需要测试的文件
Pycharm 下方,点击一下:python console
from app01 import models
from app01 import views
form_obj = views.MyForm({'username':'jason', 'password':'123', 'email':'112233'})
form_obj.is_vaild() # 返回True或False,数据是否全部合法
form_obj.cleaned_data() # 返回一个字典,包含正确的kv对
form_obj.errors() # 返回一个字典,包含错误的kv对,错误字段+错误原因(list)
'''
多传一个不需要的值
'''
form_obj = views.MyForm({'username':'jason', 'password':'123', 'email':'112233', 'hobby':'study'})
form_obj.is_vaild() # True
form_obj.cleaned_data() # 返回一个字典,除了多出来的hobby
form_obj.errors() # 空字典:{}
'''
少传一个kv值对
'''
form_obj = views.MyForm({'username':'jason', 'password':'123'})
form_obj.is_vaild() # False
form_obj.cleaned_data() # 返回一个字典,两个正确的
form_obj.errors() # {'email':['This field is required.']}
有几个参数,只找几个参数,多了的不管,少了有错
mytests.py
# 默认情况下,所有的字段必须传值
form_obj = views.MyForm({'username': 'jason', 'password': '123',})
print(form_obj.is_valid())
print(form_obj.cleaned_data)
print(form_obj.errors)
# 结果
# {'username': 'jason', 'password': '123'}
# < ul class ="errorlist" > < li > email < ul class ="errorlist" > < li > This field is required.< / li > < / ul > < / li > < / ul >
需求
写一个页面,在页面中获取用户名、密码、邮箱。
有了form组件,所有需要获取用户输入的标签,都不用自己写了。
def ab_form(request):
# 1. 产生一个空对象
form_obj = MyForm()
# 2. 直接将空对象传给 html 页面
return render(request, 'index.html', locals())
缺点:封装程度太高,只适合本地测试
<form action="" method="post">
{{ form_obj.as_p }}
{{ form_obj.as_ul }}
{{ form_obj.as_table }}
<input type="submit" class="btn- btn-info">
form>
优点:封装程度适中,可以自己决定 inp 在哪里,label 在哪里
缺点;要写的代码太多,一般情况下也不用
<form action="" method="post">
<p>第 2 种渲染方式p>
<p>{{ form_obj.username.label }} {{ form_obj.username }}p>
<p>{{ form_obj.password.label }} {{ form_obj.password }}p>
<p>{{ form_obj.email.label }} {{ form_obj.email }}p>
<input type="submit" class="btn- btn-info">
form>
username = forms.CharField(min_length=3, max_length=8, label='用户名',)
<form action="" method="post">
{% for form in form_obj %}
<p>
{{ form.label }} : {{ form }}
<span style="color: red">{{ form.errors }}span>
p>
{% endfor %}
<input type="submit" class="btn- btn-info">
form>
class MyForm(forms.Form):
username = forms.CharField(min_length=3, max_length=8, label='用户名',)
password = forms.CharField(min_length=3, max_length=8, label='密码',)
confirm_password = forms.CharField(min_length=3, max_length=8, label='密码',)
email = forms.EmailField()
推荐写法
一开始一定要产生一个
form_obj
空对象
并且 if 判断:if reuqest.method == 'POST':
内和外的form_obj
都要名字一致。
这样,你输入了什么,最后还会原原本本返回给前端页面,看上去和没消失一样。
def login(request):
form_obj = MyForm()
if reuqest.method == 'POST':
form_obj = MyForm(request.POST)
if form_obj.is_valid():
return HttpResponse('OK')
else:
pass
return render(request, 'login.html', locals())
- 前端校验弱不禁风,如果要打破前端校验,直接修改检查即可。,默认浏览器不做校验,标签内添加属性 novalidate :
- 在后方展示错误提示信息
{{ form.errors.0 }}
要点0。因为默认form.errors是一个字典,会帮你自动生成标签
<form action="" method="post">
{% for form in form_obj %}
<p>
{{ form.label }} : {{ form }}
<span style="color: red">{{ form.errors.0 }}span>
p>
{% endfor %}
<input type="submit" class="btn- btn-info">
form>
在特定节点触发
局部钩子,例如:用户名,需要给
某个字段
增加校验规则的时候使用。
全局钩子,例如:确认密码,多个字段
增加校验规则的时候使用
def clean_username(self):
def clean(self):
username = self.clean_data.get('username')
获某个 key
self.add_error('username':'xxx')
给某个 key 添加错误信息
return username # 钩子放回去
局部钩子
return self.cleaned_data # 钩子放回去,返回全部数据
全局钩子
widget=forms.widget.TextInput(attrs={'class': 'form-control'})
attrs 属性,里面以字典形式放 css 属性
代码展示:
class MyForm(forms.Form):
username = forms.CharField(
min_length=3, max_length=8, label='用户名',
error_messages={'min_length': '最少3位',
'max_length': '最多8位',
'required': '不能为空',
})
password = forms.CharField(
min_length=3, max_length=8, label='密码',
error_messages={'min_length': '最少3位',
'max_length': '最多8位',
'required': '不能为空',
})
confirm_password = forms.CharField(
min_length=3, max_length=8, label='密码',
error_messages={'min_length': '最少3位',
'max_length': '最多8位',
'required': '不能为空',
})
email = forms.EmailField(
error_messages={'invalid': '格式错误',
'required': '不能为空',
})
# 局部钩子
def clean_username(self):
username = self.clean_data.get('username')
if '666' in username:
self.add_error('username', '名称不能包含666')
return username # 钩子放回去
# 全局钩子
def clean(self):
password = self.clean_data.get('password')
confirm_password = self.clean_data.get('confirm_password')
if not password == confirm_password:
self.add_error('confirm_password', '两次密码不一致')
return self.cleaned_data # 钩子放回去,返回全部数据
字段 | 含义 | 填充 |
---|---|---|
lable | 字段名 | |
error_message | 自定义报错信息 | |
initial | 默认值 | |
required | 字段是否必须填写 |
Json博客园(django-form组件)
参考:
phone = forms.CharField(
validators=[
RegexValidator(r'[0-9]+&', '请输入数字'),
RegexValidator(r'159[0-9]+&', '必须以159开头'),
]
)
其他类型渲染
Json博客园(django-form组件)
select,checkbox,radio自己看博客,拷贝过来改改即可使用。
代码示例
hobby = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
label="爱好",
initial=[1, 3],
widget=forms.widgets.SelectMultiple()
)
字段只要是单选框,都是
字段 = forms.ChoiceField(...)
,
字段只要是多选框,都是字段 = forms.MultipleChoiceField(...)
,
widget = forms.widgets.什么()
,就是什么选择标签
- 先写面条版,完成功能再去考虑优化
- 尝试封装函数
- 尝试面向对象
- forms 组件
- cookie 与 session
- django 中间件(目前最好的)
- csrf 跨站请求伪造
- 视图函数(CBV)如何添加装饰器
form_obj.is_valid()
def is_valid(self):
"""
Returns True if the form has no errors. Otherwise, False. If errors are
being ignored, returns False.
"""
return self.is_bound and not self.errors
- self.is_bound 和 not self.errors 必须同时为 True
- self.is_bound 只要有值,就是 True。
- errors 通过 @property,伪装成属性。
# 关于 bound
def __init__(self, data=None, ...):
self.is_bound = data is not None or files is not None
# 关于 errors
@property
def errors(self):
"Returns an ErrorDict for the data provided for the form"
if self._errors is None:
self.full_clean()
return self._errors
# 精髓所在,校验功能,钩子功能,都出自于 full_clean 方法。
def full_clean(self):
"""
Cleans all of self.data and populates self._errors and
self.cleaned_data.
"""
self._errors = ErrorDict()
if not self.is_bound: # Stop further processing.
return
self.cleaned_data = {}
# If the form is permitted to be empty, and none of the form data has
# changed from the initial data, short circuit any validation.
if self.empty_permitted and not self.has_changed():
return
self._clean_fields() # 校验字段源码
self._clean_form()
self._post_clean()
- 有了反射,可以自己通过字符串的形式,给对象添加/修改/删除属性方法等等操作。
- 主要有 setattr / getattr / hasattr / delattr方法
- 如果要修改函数的话,使用lambda表达式。
'''
定义一个类
'''
>>> class Foo(object):
... def __init__(self):
... pass
...
>>> obj = Foo()
'''
继承关系
'''
>>> isinstance(obj, Foo)
True
>>> issubclass(Foo, object)
True
>>> setattr(obj, 'sb', True)
>>> obj.sb
True
>>> obj.sb()
Traceback (most recent call last):
File "" , line 1, in <module>
TypeError: 'bool' object is not callable
'''
设置成函数,加括号调用,注意参数要把对象自己传进去。
'''
>>> setattr(obj, 'sb', lambda self, name: print(name + 'sb'))
>>> obj.sb('egon')
Traceback (most recent call last):
File "" , line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'name'
>>> obj.sb(obj, 'egon')
egonsb
>>> setattr(obj, 'sb', lambda self, name: print(name + '_dsb'))
>>> getattr(obj, 'sb')(obj, 'egon')
egon_dsb
>>>
>>> setattr(obj, 'p', lambda : print('xxx'))
>>> obj.p()
xxx
>>> name = 'p'
>>> getattr(obj, name)()
xxx
>>>
- 用户一次登录成功以后,信息自动保存在
浏览器本地
。- 之后访问的时候,自动将
保存在本地浏览器的用户名和密码
发送给服务端
- 服务端获取后自动验证。
- 保存在浏览器的 kv键值对,都可以叫 cookie
- 都是键值对的形式
- 安全优化 1
1. 当登录成功以后,服务端产生一个随机字符串,交给浏览器客户端保存
2. 服务端用 kv键值对的形式保存这个随机字符串
3. 形式:随机字符串1:用户1信息
4. 之后访问,都带着这个随机字符串
5. 隐患:如果你拿到了随机字符串,也可以冒充当前用户访问。- 安全优化 2
1. session
保存在服务端
的 kv键值对(可以有多个)基于 cookie 工作
,其实大部分保存用户状态的操作,都是通过 cookie 的。- 缺点:经不住量大
因为http协议是无状态的,如果一个人1000次访问,1000次都是初见。
查看方法
浏览器 / applications / cookies,里面有很多键值对
- 所有要用户登录的网站,都要保存cookie
- 当然浏览器可以选择拒绝保存(设置里面)
1. 后果:然后登录页面都没了
''' 设置 '''
obj = HttpResponse()
obj.set_cookie(key, value)
obj.set_signed_cookie(key, value, salt='盐')
''' 获取 '''
request.COOKIES.get(key)
request.get_signed_cookie(key, salt='盐')
''' 原本的 views 函数返回值 '''
return HttpResponse(...)
''' 操作 cookies 就改成这样 '''
obj1 = HttpResponse()
...
return obj1
- 目标函数外面套上装饰器,把 request 拿出来
1. 获取目标url。
1. 如果从 cookie 获取到了用户名,那就执行目标函数(跳转过去)
1. 如果没有,就重定向到登录页面,redirect(’/day66/login/?next=%s’ %target_url)- 登录页面中,分离出 next 参数。
1. 如果登录上了,就走到下一个。
1. 万一下一个是空,就到home
# 装饰器
def login_auth(func):
# request 要单独拿出来
def inner(request, *args, **kwargs):
target_url = request.get_full_path()
if request.COOKIES.get('username'):
res = func(request, *args, **kwargs)
return res
else:
return redirect('/day66/login/?next=%s' %target_url)
return inner
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'jason' and password == '123':
target_url = request.GET.get('next') # 获取用户上一次想要访问的 ur(这个结果可能是 None)
if target_url:
obj = redirect(target_url)
else:
obj = redirect('/day66/home/')
# 保存用户状态(可以设置超时时间,3秒,ie浏览器就是expires=3)
obj.set_cookie('username', 'jason666', max_age=3)
# 跳转到一个用户登录才能看到的页面
return obj
return render(request, 'login.html', locals())
@login_auth
def home(request):
return HttpResponse('我是 home 登录才能看到我哦')
@login_auth
def index(request):
return HttpResponse('我是 index 登录才能看到我哦')
@login_auth
def func(request):
return HttpResponse('我是 func 登录才能看到我哦')
max_age=3
3 秒超时expires
(针对 ie)
@login_auth
def logout(request):
obj = redirect('/day66/login/')
# 写上要删除的键值对
obj.delete_cookie('username')
return obj
session 保存在服务端,返回给客户端一个随机字符串
sessionid : 随机字符串
应用场景
- 用户登录
- 当需要保存一段数据的时候,也可以操作session表,例如验证码
''' 设置 session '''
request.session['key'] = value
''' 获取 session '''
request.session.get('key')
注意
- 有一张默认的django_session表来存储数据.
- session_key,
- session_data,
- expire_date, 过期时间
- 在新项目中,一定要要执行过数据库迁移命令,才能使用
- 查看方法
1. chrome / Application / storage / cookie
2. Safari / 存储空间 / cookie
过期时间
默认的过期时间是14天,但是可以自己手动设置。
def set_session(request):
# 设置 session
request.session['hobby'] = 'girl'
request.session['hobby1'] = 'girl1'
# 设置超时时间
request.session.set_expiry(0)
return HttpResponse('嘿嘿嘿')
设置 session 的时候,内部
- 自动帮你生成一个随机字符串
- 将随机字符串和对应的数据存储到 django_session 表中(不是直接生效的,分为两小步)
- 在内存中产生操作数据的缓存
- 在经过中间件 SessionMiddlewre 的时候,才真正操作数据库
- 随机字符串返回给浏览器保存
def get_session(request):
print(request.session.get('hobby'))
return HttpResponse('哈哈哈')
获取session 的时候,内部
- 自动从浏览器请求头中获取 sessionid 对应的随机字符串
- 拿着随机字符串去 django_session 表中查找对应数据
- 比对
- 比对上了,就取出对应数据,并封装到 request.session 字典中
- 比对不上,request.session.get()返回none
其他
- session
可以设置多个键值对
- django_session 表中
数据条数取决于浏览器
。
- 同一个计算机同一个浏览器
只有一条数据
生效。- 同一个ip,同一个计算机,不同浏览器,就不同数据。
- 当过期的时候,短暂可能出现多条,但是内部会自动清理。
- 主要为了节省服务端资源
- session 也
可以设置过期时间
(四种类型的参数)。
request.session.set_expiry(0)
设置过期时间
request.session.set_expiry(0)
的参数
- 整数 -> 秒数
- 日期对象 -> 到指定日期失效
- 0 -> 关闭浏览器就自动失效
- 不写 -> 取决于 django 内部全局 session 失效时间(默认14天)
# 浏览器和服务端都清空(推荐使用)
request.session.flush()
# 只删服务端(不太用)
request.session.delete()
- MySQL
- 文件
- redis
- memache
- ···
导入模块
CBV中,django不建议直接给方法加装饰器,要导入模块
from django.utils.decorators import method_decorator
# 导入 views 和 方法装饰器
from django import views
from django.utils.decorators import method_decorator
class MyLogin(views):
# 方式 1
@method_decorator(login_auth)
def get(self):
return HttpResponse('get 请求')
def post(self):
return HttpResponse('post 请求')
可以装多个,可以针对不同的方法加不同的装饰器
from django import views
from django.utils.decorators import method_decorator
@method_decorator(login_auth, name='get')
@method_decorator(login_auth, name='post')
class MyLogin(views):
def get(self):
return HttpResponse('get 请求')
def post(self):
return HttpResponse('post 请求')
直接作用于所有方法
from django import views
from django.utils.decorators import method_decorator
class MyLogin(views):
@method_decorator(login_auth)
def dispatch(self, request, *args, **kwargs):
pass
def get(self):
return HttpResponse('get 请求')
def post(self):
return HttpResponse('post 请求')