在我们的view.py中获取前端form表单发来的一个数据比较简单,比如text、radio、password等
v = request.POST.get("username")
获取多个数据,比如checkbox、select
v = request.POST.getlist("favor")
前端代码
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="fname" />
<input type="submit" value="提交" />
form>
view.py
obj = request.FILES.get("fname")
# obj.name 文件名
# obj.size 文件大小
print(obj, type(obj))
f = open(obj.name, "wb") # 当前路径新建一个文件,名叫源文件的名字
for i in obj.chunks(): # 本地下载,chunks生成器迭代器的知识点
f.write(i)
f.close()
FBV:function base view
就是经常用的,urls.py中请求对应views.py中的函数
urls.py
path('index/', views.index),
views.py
def index(request):
if request.method == "POST":
return render(request, 'index.html')
elif request.method == "GET":
return render(request, 'index.html')
FBV装饰器
def auth(func): # FBV装饰器
def inner(request, *args, **kwargs):
username = request.COOKIES.get("username111")
print("index", username)
if not username:
return render(request, 'login.html')
return func(request, *args, **kwargs)
return inner
@auth
def index(request):
username = request.COOKIES.get("username111")
# #username = request.COOKIES['username111']
# print("index", username)
# if not username:
# return render(request, 'login.html')
return render(request, 'index.html', {"username": username})
CBV:class base view
urls.py中请求对应views.py中的类,和FBV不同点就是,不需要自己去判断提交方式是哪一种了
提交方式有:[‘get’, ‘post’, ‘put’, ‘patch’, ‘delete’, ‘head’, ‘options’, ‘trace’]
urls.py
path('home/', views.Home.as_view()),
views.py
from django.views import View
class Home(View):
# 请求来了,先调用dispatch,基于反射找到相应的提交方式
#这样重写父类的dispatch可以定制功能
def dispatch(self, request, *args, **kwargs):
#调用父类的dispatch
print('before')
result = super(Home, self).dispatch(request, *args, **kwargs)
print('after')
return result
def get(self, request):
print(request.method)
return render(request, 'home.html')
def post(self, request):
print(request.method)
return render(request, 'home.html')
CBV装饰器
def auth(func): # FBV装饰器
def inner(request, *args, **kwargs):
username = request.COOKIES.get("username111")
print("index", username)
if not username:
return render(request, 'login.html')
return func(request, *args, **kwargs)
return inner
from django import views
from django.utils.decorators import method_decorator
@method_decorator(auth, name='dispatch') # 和下面的dispatch函数一样的功能
class Order(views.View): # CBV装饰器
"""
@method_decorator(auth) # 装饰内部所有函数
def dispatch(self, request, *args, **kwargs):
return super(Order, self).dispatch(request, *args, **kwargs) """
# @method_decorator(auth) # 装饰这一个函数
def get(self, request):
username = request.COOKIES.get("username111")
return render(request, 'index.html', {"username": username})
def post(self, request):
username = request.COOKIES.get("username111")
return render(request, 'index.html', {"username": username})
创建类
1、根据类自动创建数据库表
在app下的models.py
from django.db import models
# Create your models here.
#app01_userinfo
class UserInfo(models.Model):
# 隐含的,自动创建id列,自增,主键
# 用户名列、字符串类型、指定长度
# 字段:字符串、数字、时间、二进制、自增(primary_key)
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
Django提供了大量的字段,其中大部分字段都是给Django自带的后台管理系统使用的
字段
AutoField(Field)
- int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)
class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
自定义无符号整数字段
class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED'
PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',
BooleanField(Field)
- 布尔值类型
NullBooleanField(Field):
- 可以为空的布尔值
CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度
TextField(Field)
- 文本类型
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both"
URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
FloatField(Field)
- 浮点型
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field)
- 二进制类型
参数
null 数据库中字段是否可以为空
db_column 数据库中字段的列名
db_tablespace
default 数据库中字段的默认值
primary_key 数据库中字段是否为主键
db_index 数据库中字段是否可以建立索引
unique 数据库中字段是否可以建立唯一索引
unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month 数据库中字段【月】部分是否可以建立唯一索引
unique_for_year 数据库中字段【年】部分是否可以建立唯一索引
auto_now 创建时,自动生成时间
auto_now_add 更新时,自动更新时间
如:
ctime = models.DateTimeField(auto_now_add=True, null=True)
uptime = models.DateTimeField(auto_now=True, null=True)
自动更新时:1更新时间不会发生变化,2有效,所以要用第二种
写法一:
obj = UserGroup.objects.filter(id=1).update(caption="CEO")
写法二:
obj = UserGroup.objects.filter(id=1).first()
obj.caption="CEO"
obj.save()
verbose_name Admin中显示的字段名称
blank Admin中是否允许用户输入为空
editable Admin中是否可以编辑
help_text Admin中该字段的提示信息
choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
如:
user_type_choices = (
(1, '超级用户'),
(1, '普通用户'),
(1, '普普通用户'),
)
user_type_id = models.IntegerField(choices=user_type_choices, default=1)
error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息;
字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
如:{'null': "不能为空.", 'invalid': '格式错误'}
validators 自定义错误验证(列表类型),从而定制想要的验证规则
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
test = models.CharField(
max_length=32,
error_messages={
'c1': '优先错信息1',
'c2': '优先错信息2',
'c3': '优先错信息3',
},
validators=[
RegexValidator(regex='root_\d+', message='错误了', code='c1'),
RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
EmailValidator(message='又错误了', code='c3'), ]
)
写好类之后,查看settings.py中的app是否注册了,如果注册,执行命令,生成数据库表
#django生成表结构、修改表结构
python3 manage.py makemigrations
python3 manage.py migrate
2、根据类对数据库表中的数据进行操作
# 创建
# 1、
models.UserInfo.objects.create(username='root', password='123')
#models.UserGroup.objects.create(caption="dba")
# 2、
dic = {'username': 'test', 'password': '123'}
models.UserInfo.objects.create(**dic)
# 3、
obj = models.UserInfo(username='severen', password="123")
obj.save()
# 查
result = models.UserInfo.objects.all()
result是一个QuerySet类型,这个类型是Django提供的,我们去理解的就话,就当它是一个列表即可,列表内的元素是UserInfo的对象
result = models.UserInfo.objects.filter(username='root')
result = models.UserInfo.objects.filter(username='root', password='123')
for row in result:
print(row.id, row.username, row.password)
obj = models.UserInfo.objects.filter(id=nid).first()
# 取单条数据,如果不存在,就直接报错
# models.UserInfo.objects.get(id=nid)
# 删除
models.UserInfo.objects.all().delete()
models.UserInfo.objects.filter(id=4).delete()
# 更新
models.UserInfo.objects.all().update(password='666')
# gte -> 大于等于;lte -> 小于等于
models.UserInfo.objects.filter(id__gt=1).update(password='666') # id > 1
# UserInfo表中的user_group字段关联UserGroup表的主键,建立外键关系
# 数据库里面会自动生成字段名user_group_id
user_group = models.ForeignKey("UserGroup", to_field='uid', default=1, on_delete=models.CASCADE) # 如果没有指定to_field字段,默认和关联表的主键关联
在进行操作的时候,可以取到obj.user_group和obj.user_group_id
obj.user_group是一个对象
obj.user_group_id是数值
# way one
v1 = models.Business.objects.all()
# QuerySet, 内部元素是对象
# [obj(id, caption, code), obj, obj...]
# way two
v2 = models.Business.objects.all().values('id', 'caption')
# QuerySet, 内部元素是字典
# [{'id':1, 'caption':'运维部'},{},{}...]
# way three
v3 = models.Business.objects.all().values_list('id', 'caption')
# QuerySet, 内部元素是元组
# [(1, '运维部'),(2, '开发'),()...]
<h1>业务线列表(对象)h1>
<ul>
{% for row in v1 %}
<li>
{{row.id}}-{{row.caption}}-{{row.code}}
li>
{% endfor %}
ul>
<h1>业务线列表(字典)h1>
<ul>
{% for row in v2 %}
<li>
{{row.id}}-{{row.caption}}
li>
{% endfor %}
ul>
<h1>业务线列表(元组)h1>
<ul>
{% for row in v3 %}
<li>
{{row.0}}-{{row.1}}
li>
{% endfor %}
ul>
# way 1
v1 = models.Host.objects.all()
# 双下划线 way 2
v2 = models.Host.objects.filter(nid__gt=0).values('nid', 'hostname', 'ip', 'port', 'b_id', 'b__caption')
for row in v2:
print(row["nid"],row["hostname"],row["ip"],row["port"],row["b_id"],row["b__caption"])
# way 3
v3 = models.Host.objects.filter(nid__gt=0).values_list('nid', 'hostname', 'ip', 'port', 'b_id', 'b__caption')
<h1>主机列表(对象)h1>
<div>
<input id='add_host' type='button' value='添加' />
div>
<table border='1'>
<thead>
<tr>
<th>序号th>
<th>主机名th>
<th>IPth>
<th>端口th>
<th>业务线名称th>
<th>操作th>
tr>
thead>
<tbody>
{% for row in v1 %}
<tr hid='{{row.nid}}' bid='{{row.b_id}}'>
<td>{{forloop.counter}}td>
<td>{{row.hostname}}td>
<td>{{row.ip}}td>
<td>{{row.port}}td>
<td>{{row.b.caption}}td>
<td>
<a class='edit'>编辑a>|<a class='delete'>删除a>
td>
tr>
{% endfor %}
tbody>
table>
<h1>主机列表(字典)h1>
<table border='1'>
<thead>
<tr>
<th>主机名th>
<th>IPth>
<th>端口th>
<th>业务线名称th>
tr>
thead>
<tbody>
{% for row in v2 %}
<tr hid='{{row.nid}}' bid='{{row.b_id}}'>
<td>{{row.hostname}}td>
<td>{{row.ip}}td>
<td>{{row.port}}td>
<td>{{row.b__caption}}td>
tr>
{% endfor %}
tbody>
table>
<h1>主机列表(元组)h1>
<table border='1'>
<thead>
<tr>
<th>主机名th>
<th>IPth>
<th>端口th>
<th>业务线名称th>
tr>
thead>
<tbody>
{% for row in v3 %}
<tr hid='{{row.0}}' bid='{{row.4}}'>
<td>{{row.1}}td>
<td>{{row.2}}td>
<td>{{row.3}}td>
<td>{{row.5}}td>
tr>
{% endfor %}
tbody>
table>
way1:自定义关系表
优点:想生成多少列数据就多少列
缺点:需要自己写第三张表
class Host(models.Model):
nid = models.AutoField(primary_key=True)
hostname = models.CharField(max_length=32, db_index=True) # db_index: 索引
ip = models.GenericIPAddressField(protocol="both", db_index=True) # protocol: 支持ipv4还是ipv6
port = models.IntegerField()
b = models.ForeignKey("Business", to_field="id", on_delete=models.CASCADE)
class Application(models.Model):
name = models.CharField(max_length=32)
class HostToApp(models.Model):
hobj = models.ForeignKey(to="Host", to_field="nid", on_delete=models.CASCADE)
aobj = models.ForeignKey(to='Application', to_field='id', on_delete=models.CASCADE)
way2:自动创建关系表
优点:不要自己写第三张表,自动生成
缺点:最多生成三列数据类型
class Host(models.Model):
nid = models.AutoField(primary_key=True)
hostname = models.CharField(max_length=32, db_index=True) # db_index: 索引
ip = models.GenericIPAddressField(protocol="both", db_index=True) # protocol: 支持ipv4还是ipv6
port = models.IntegerField()
b = models.ForeignKey("Business", to_field="id", on_delete=models.CASCADE)
class Application(models.Model):
name = models.CharField(max_length=32)
r = models.ManyToManyField("Host") # django将自动帮助创建关联表
way1
# 通过类来操作数据库
obj = models.HostToApp.objects.create(hobj=1, aobj=2)
way2
obj = models.Application.objects.get(id=1)
obj.name
# 添加
obj.r.add(1)
obj.r.add(2)
obj.r.add(3,4,5)
obj.r.add(*[1,2,3,4])
# 删除
obj.r.remove(1)
obj.r.remove(2,3)
obj.r.remove(*[1,2,3])
# 清空
obj.r.clear()
# 更新
obj.r.set([3,4,5]) # 清空之前的所有,变成我现在设置的这个
# 获取值
obj.r.all() # 所有相关的主机对象"列表" QuerySet
先查看request这个对象的类型
print(type(request))
得到
<class 'django.core.handlers.wsgi.WSGIRequest'>
导入这个类,进入这个类查看信息
from django.core.handlers.wsgi import WSGIRequest
会发现有一个environ变量,这个变量封装了所有了的请求信息
为保证网页的安全性,防止别人恶搞我们的网站,比如给我们的网站评论一堆html代码,该代码能导致页面进入死循环,我们有两种方法,
1、前端
{{ page_msg|safe }}
2、后端
from django.utils.safestring import mark_safe
page_msg = """
dsdaad
dahuadhudahuad
"""
page_msg = mark_safe(page_msg)
1、设定每页显示数据条数
2、用户输入页码(第一页、第二页…)
3、设定显示多少页号
4、获取当前数据总条数
5、根据设定显示多少页号和数据总条数计算出,总页数
6、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置
7、在数据表中根据起始位置取值,页面上输出数据
8、输出分页html,如:[上一页][1][2][3][4][5][下一页]
views.py
from django.utils.safestring import mark_safe
LIST = []
for i in range(199):
LIST.append(i)
def usr_list(request):
all_count = len(LIST)
per_page_count = 20
total_html_count = 9
current_page = int(request.GET.get('p', 1))
total_page_count, y = divmod(all_count, per_page_count) # 商和余数
if y:
total_page_count += 1
print(total_page_count)
if total_page_count < total_html_count: # 总页数<能显示的页数
start_index = 1
end_index = total_page_count + 1
else:
if current_page <= (total_html_count+1)/2: # 当前页<=能显示的页数+1的一半
start_index = 1
end_index = total_html_count + 1
else:
if current_page + (total_html_count-1)/2 > total_page_count: # 当前页+能显示的页数-1的一半 > 总页数
start_index = total_page_count - total_html_count + 1
end_index = total_page_count + 1
else:
start_index = current_page - (total_html_count-1)/2
end_index = current_page + (total_html_count+1)/2
print(start_index,end_index)
start = (current_page - 1) * per_page_count
end = current_page * per_page_count
data = LIST[int(start): int(end)]
page_list = []
if current_page-1 < 1:
prev = "上一页"
else:
prev = "上一页" % str(current_page-1)
page_list.append(prev)
for i in range(int(start_index), int(end_index)):
if current_page == i:
temp = "%s" % (i, i)
else:
temp = "%s" % (i, i)
page_list.append(temp)
if current_page+1 > total_page_count:
nex = "下一页" % total_page_count
else:
nex = "下一页" % str(current_page + 1)
page_list.append(nex)
jump = """ Go
"""
page_list.append(jump)
page_str = "".join(page_list)
page_str = mark_safe(page_str)
return render(request, "usr_list.html", {'data': data, 'page_str': page_str})
usr_list.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>usr_listtitle>
<style>
.pagination .page{
display: inline-block;
padding: 5px;
margin: 5px;
background-color: yellow;
}
.pagination .page.active{
background-color: red;
}
style>
head>
<body>
<ul>
{% for i in data %}
<li>{{i}}li>
{% endfor %}
ul>
<div class="pagination">
{{ page_str }}
div>
body>
html>
不够规范,把上面的内容封装到一个类里面去
在app同级目录下新建一个文件夹utils
新建文件pagination.py,把类放进去
# Author: 73
from django.utils.safestring import mark_safe
class Page:
def __init__(self, current_page, all_count, per_page_count=20, total_html_count=9):
self.current_page = current_page
self.all_count = all_count
self.per_page_count = per_page_count
self.total_html_count = total_html_count
@property
def total_page_count(self):
x, y = divmod(self.all_count, self.per_page_count) # 商和余数
if y:
x += 1
print(x)
return x
@property
def start(self):
start = (self.current_page - 1) * self.per_page_count
return start
@property
def end(self):
end = self.current_page * self.per_page_count
return end
def page_str(self):
if self.total_page_count < self.total_html_count: # 总页数<能显示的页数
start_index = 1
end_index = self.total_page_count + 1
else:
if self.current_page <= (self.total_html_count + 1) / 2: # 当前页<=能显示的页数+1的一半
start_index = 1
end_index = self.total_html_count + 1
else:
if self.current_page + (self.total_html_count - 1) / 2 > self.total_page_count: # 当前页+能显示的页数-1的一半 > 总页数
start_index = self.total_page_count - self.total_html_count + 1
end_index = self.total_page_count + 1
else:
start_index = self.current_page - (self.total_html_count - 1) / 2
end_index = self.current_page + (self.total_html_count + 1) / 2
print(start_index, end_index)
page_list = []
if self.current_page - 1 < 1:
prev = "上一页"
else:
prev = "上一页" % str(self.current_page - 1)
page_list.append(prev)
for i in range(int(start_index), int(end_index)):
if self.current_page == i:
temp = "%s" % (i, i)
else:
temp = "%s" % (i, i)
page_list.append(temp)
if self.current_page + 1 > self.total_page_count:
nex = "下一页" % self.total_page_count
else:
nex = "下一页" % str(self.current_page + 1)
page_list.append(nex)
jump = """ Go
"""
page_list.append(jump)
page_str = "".join(page_list)
page_str = mark_safe(page_str)
return page_str
views.py导入这个类
from utils import pagination
def usr_list(request):
all_count = len(LIST)
current_page = int(request.GET.get('p', 1))
page_obj = pagination.Page(current_page, all_count)
data = LIST[int(page_obj.start): int(page_obj.end)]
return render(request, "usr_list.html", {'data': data, 'page_str': page_obj.page_str()})
django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。
全局:中间件 django.middleware.csrf.CsrfViewMiddleware
局部:
@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
注:from django.views.decorators.csrf import csrf_exempt,csrf_protect
1、普通表单
veiw中设置返回值:
return render(request, 'xxx.html', data)
html中设置Token:
{% csrf_token %}
2、Ajax
$(function(){
/*$.ajaxSetup({ // 全局配置, 对整个页面所有的ajax做一个配置
beforeSend: function(xhr, settings){ //表示在发送ajax请求之前,先执行这个函数
//xht:xmlhttprequest对象,所有的ajax操作的底层都是它来做的
xht.setRequestHeader("X-CSRFtoken", $.cookie('csrftoken'));
}
})*/
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
$('#btn').click(function(){
$.ajax({
url: '/login/',
type: "POST",
data: {"user": "root", "pwd": "123"},
//headers: {"X-CSRFtoken": $.cookie('csrftoken')}, // 请求头中不能有下划线
success: function(arg){
}
});
});
});
cookie的原理就是保存在用户浏览器的键值对
django提供Cookie的一些方法
request.COOKIES
request.COOKIES['username111']
request.COOKIES.get('username111')
response = render(request, 'index.html')
response = redirect('/index/')
# 设置cookie,关闭浏览器失效
response.set_cookie("key", 'value')
# 设置cookie,N秒之后失效
response.set_cookie("key", 'value', max_age=10)
# 设置cookie,截止时间失效
import datetime
current_time = datetime.datetime.utcnow()
current_time = current_time + datetime.timedelta(seconds=10)
response.set_cookie('key', 'value', expires=current_time)
# 带签名的cookie
obj = HttpResponse('ok')
obj.set_signed_cookie('username', 'kangbazi', salt='asdfasdf') # 加密
request.get_signed_cookie('username', salt="asdfasdf") # 解密
rep = HttpResponse(...) 或 rep = render(request, ...)
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐',...)
参数:
key, 键
value='', 值
max_age=None, 超时时间
expires=None, 超时时间,以datetime为单位
path='/', Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问
domain=None, Cookie生效的域名
secure=False, https传输
httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
def login(request):
if request.method == "GET":
return render(request, 'login.html')
if request.method == "POST":
u = request.POST.get('username')
p = request.POST.get('pwd')
print("login", u)
dic = user_info.get(u)
if not dic:
return render(request, 'login.html')
elif dic['p'] == p:
res = redirect('/index/') # ⚠️这行代码
res.set_cookie('username111', u, max_age=10) # 设置,max_age:超时时间
return res
else:
return render(request, 'login.html')
request.COOKIES.get('key')
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
参数:
default: 默认值
salt: 加密盐
max_age: 后台控制过期时间
def index(request):
username = request.COOKIES.get("username111") # 获取
print("index", username)
if not username:
return render(request, 'login.html')
return render(request, 'index.html', {"username": username})
session是保存在服务器端的键值对
session原理
首先在服务器端有一个字典
用户来登陆,登陆成功之后,会生成一个随机的字符串给用户
同时会把这个随机生成的字符串当作key放在大字典里,它的value是一个字典,该字典内容是用户的敏感信息,比如密码
⚠️注意:在Django中使用session前,需要migrate一下,因为Django默认情况下会把session保存在数据库里面
例子:
def login(request):
if request.method == "GET":
return render(request, "login.html")
elif request.method == "POST":
user = request.POST.get("username")
pwd = request.POST.get("pwd")
if user == 'root' and pwd == '123':
# 生成随机字符串
# 写到用户浏览器cookie
# 保存到session中
# 在随机字符串对应的字典中设置相关内容。。。
request.session['username'] = user
request.session['is_login'] = True
return redirect('/index/')
else:
return render(request, "login.html")
@csrf_exempt # 表示这个功能不需要验证了
def index(request):
# session中获取值
if request.session.get("is_login", None):
return render(request, "index.html")
else:
return HttpResponse("gun")
session可以保存在很多地方,比如说数据库、内存、缓存、文件等,具体保存在哪,在Django中可以进行配置。
1、数据库Session
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
a. 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认),这个设置为True,超时时间从当前操作开始算
b. 使用
def index(request):
# 获取、设置、删除Session中数据
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在则不设置
del request.session['k1']
# 所有 键、值、键值对
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()
# 用户session的随机字符串
request.session.session_key
# 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired()
# 检查 用户session的随机字符串 在数据库中是否
request.session.exists("session_key")
# 删除当前用户的所有Session数据
request.session.delete("session_key")
request.session.clear() == request.session.delete(request.session.session_key)
request.session.set_expiry(value)
* 如果value是个整数,session会在value秒数后失效。
* 如果value是个datatime或timedelta,session就会在这个时间后失效。
* 如果value是0,用户关闭浏览器session就会失效。
* 如果value是None,session会依赖全局session失效策略。
2、缓存Session
a. 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎
SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名
SESSION_COOKIE_SECURE = False # 是否Https传输cookie
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存
b. 使用
同上
3、文件Session
a. 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎
SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名
SESSION_COOKIE_SECURE = False # 是否Https传输cookie
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存
b. 使用
同上
4、缓存+数据库Session
数据库用于做持久化,缓存用于提高效率
a. 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎
b. 使用
同上
5、加密cookie Session
a. 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
b. 使用
同上
在django中,中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。
在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件,如下
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'Middle.m1.Row1',
'Middle.m1.Row2',
'Middle.m1.Row3',
]
中间件中可以定义四个方法,分别是:
以上方法的返回值可以是None和HttpResonse对象,如果是None,则继续按照django定义的规则向下执行,如果是HttpResonse对象,则直接将该对象返回给用户。
django1.10之前的版本流程:
django1.10之后的版本流程:
=自定义中间件
1、创建中间件类
先在manage.py同级目录下创建一个文件夹,取名随意,比如Middle
在该文件夹下新建一个python文件,比如m1.py
from django.utils.deprecation import MiddlewareMixin
class Row1(MiddlewareMixin):
def process_request(self, request):
print("第一排")
def process_view(self, request, view_func, view_func_args, view_func_kwargs):
print('13')
def process_response(self, request, response):
print("第一排2")
return response
# from django.shortcuts import HttpResponse
class Row2(MiddlewareMixin):
def process_request(self, request):
print("第二排")
# return HttpResponse("走")
def process_view(self, request, view_func, view_func_args, view_func_kwargs):
print('23')
def process_response(self, request, response):
print("第二排2")
return response
class Row3(MiddlewareMixin):
def process_request(self, request):
print("第三排")
def process_view(self, request, view_func, view_func_args, view_func_kwargs):
print('33')
def process_response(self, request, response):
print("第三排2")
return response
2、settings.py中注册中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'Middle.m1.Row1',
'Middle.m1.Row2',
'Middle.m1.Row3',
]
由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。
Django中提供了6种缓存方式:
1、配置
a、开发调试
# 此为开始调试用,实际内部不做任何操作
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎
'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
'OPTIONS':{
'MAX_ENTRIES': 300, # 最大缓存个数(默认300)
'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
},
'KEY_PREFIX': '', # 缓存key的前缀(默认空)
'VERSION': 1, # 缓存key的版本(默认1)
'KEY_FUNCTION' 函数名 # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
}
}
b、内存
# 此缓存将内容保存至内存的变量中
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
# 注:其他配置同开发调试版本
c、文件
# 此缓存将内容保存至文件
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache', # 'LOCATION': os.path.join(BASE_DIR, 'cache'), 需要在项目中新建一个cache文件夹
}
}
# 注:其他配置同开发调试版本
d、数据库
# 此缓存将内容保存至数据库
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table', # 数据库表
}
}
# 注:执行创建表命令 python manage.py createcachetable
e、Memcache缓存(python-memcached模块)
# 此缓存使用python-memcached模块连接memcache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': 'unix:/tmp/memcached.sock',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}
}
f、Memcache缓存(pylibmc模块)
# 此缓存使用pylibmc模块连接memcache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '127.0.0.1:11211',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '/tmp/memcached.sock',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}
}
g. Redis缓存(依赖:pip3 install django-redis)
settings.py
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100}
# "PASSWORD": "密码",
}
}
}
views.py
from django_redis import get_redis_connection
conn = get_redis_connection("default")
2、应用
a. 全站使用
适合场景:个人博客页面,不允许别人点赞、评论
使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
# 其他中间件...
'django.middleware.cache.FetchFromCacheMiddleware',
]
b. 单独视图缓存
方式一:
from django.views.decorators.cache import cache_page
@cache_page(60 * 15)
def my_view(request):
...
方式二:
from django.views.decorators.cache import cache_page
urlpatterns = [
re_path('^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),
]
c、局部视图使用
a. 引入TemplateTag
{% load cache %}
b. 使用缓存
{% cache 5000 缓存key %}
缓存内容
{% endcache %}
通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者。
应用场景:对所有在数据库创建数据的前后给它记录一条记录
Model signals
pre_init # django的models执行其构造方法前,自动触发
post_init # django的models执行其构造方法后,自动触发
pre_save # django的models对象保存前,自动触发
post_save # django的models对象保存后,自动触发
pre_delete # django的models对象删除前,自动触发
post_delete # django的models对象删除后,自动触发
m2m_changed # django的models中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
Management signals
pre_migrate # 执行migrate命令前,自动触发
post_migrate # 执行migrate命令后,自动触发
Request/response signals
request_started # 请求到来前,自动触发
request_finished # 请求结束后,自动触发
got_request_exception # 请求异常后,自动触发
Test signals
setting_changed # 使用test测试修改配置文件时,自动触发
template_rendered # 使用test测试渲染模板时,自动触发
Database Wrappers
connection_created # 创建数据库连接时,自动触发
from django.core.signals import request_finished
from django.dispatch import receiver
@receiver(request_finished)
def my_callback(sender, **kwargs):
print("Request finished!")
对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数。
在项目中新建一个sg.py文件,内部代码如下:
from django.core.signals import request_finished
from django.core.signals import request_started
from django.core.signals import got_request_exception
from django.db.models.signals import class_prepared
from django.db.models.signals import pre_init, post_init
from django.db.models.signals import pre_save, post_save
from django.db.models.signals import pre_delete, post_delete
from django.db.models.signals import m2m_changed
from django.db.models.signals import pre_migrate, post_migrate
from django.test.signals import setting_changed
from django.test.signals import template_rendered
from django.db.backends.signals import connection_created
def callback(sender, **kwargs):
print("xxoo_callback")
#print(sender,kwargs)
# xxoo.connect(callback)
# xxoo指上述导入的内容
pre_init.connect(callback)
在项目名目录下的__init__.py导入sg
import sg
定义信号
import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
注册信号
def callback(sender, **kwargs):
print("callback")
print(sender,kwargs)
pizza_done.connect(callback)
触发信号
from sg import pizza_done
pizza_done.send(sender="severen", toppings=123, size=456)
项目地址:
https://github.com/Stark-Xue/test2
https://github.com/Stark-Xue/test3