Django基础使用分享
Pycharm-Django
此时打开浏览器访问http://127.0.0.1:8000/,你将看到熟悉的火箭标志页面。
创建新的APP
1、使用python manage.py startapp demo
命令创建一个名为demo的应用,然后把其加入项目配置文件settings.py
的INSTALLED_APPS中。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'demo', # 注册app
]
2、然后编辑/urls.py
, 把demo应用的urls也加入到项目的urls中去,如下所示:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('demo.urls')) # 加入app对应urls
]
3、编写视图函数和 URL 配置
编辑demo/views.py
, 新增一个名为index的视图函数。每个视图函数的第一个默认参数都必需是request
, 它是一个全局变量。
Django把每个用户请求封装成了request
对象,它包含里当前请求的所有信息,比如请求路径request.path
, 当前用户request.user
以及用户通过POST提交的数据request.POST
。
index视图函数通过调用HttpReponse
方法打印输出当前的请求路径request.path
。
# demo/views.py
from django.http import HttpResponse
def index(request):
return HttpResponse("请求路径:{}" .format(request.path))
4、接下来我们要进行URL配置。
新建demo/urls.py
, 添加如下代码,其作用是将用户请求的地址与实际需要执行的视图函数相关联。
下例中当用户在浏览器中访问index/时,Django将解析url,并调用执行views.py中的index视图函数。
# demo/urls.py
from django.urls import path
from . import views
app_name = "demo"
urlpatterns = [
path('index/', views.index, name='index'),
]
MVT设计模式
1、创建一个app (与上面的步骤相同)
2、创建模型(M)
编辑目录下models.py
创建模型。
当你使用python manage.py makemigrations
和python manage.py migrate
命令时,Django会自动为你在数据库创建数据表(默认使用的数据库是免费的sqlite)。
# tasks/models.py
from django.db import models
class Status(models.TextChoices):
UNSTARTED = 'u', "Not started yet"
ONGOING = 'o', "Ongoing"
FINISHED = 'f', "Finished"
# Task模型
class Task(models.Model):
name = models.CharField(verbose_name="Task name", max_length=65, unique=True)
status = models.CharField(verbose_name="Task status", max_length=1, choices=Status.choices)
def __str__(self):
return self.name
3、编写视图并配置路由URL(V)
接下来我们要编辑视图views.py
,并新增一个视图函数, 用于展示任务清单。
该视图函数从数据库读取了对象列表,指定了渲染模板并向模板传递了数据。
# tasks/views.py
from django.shortcuts import render
from .models import Task
# 任务清单
def task_list(request):
# 从数据库获取Task对象列表
tasks = Task.objects.all()
# 指定渲染模板并向模板传递数据
return render(request, "tasks/task_list.html", { "tasks": tasks,})
光编写视图(views.py)还不够,我们还得为写好的视图函数配置路由,这样才能将视图函数与用户的请求地址建立好对应关系。
4、编辑或创建tasks/urls.py
, 添加如下代码:
# tasks/urls.py
from django.urls import path
from . import views
# namespace
app_name = 'tasks'
urlpatterns = [
# Retrieve task list
path('', views.task_list, name='task_list'),
]
这样当用户访问/tasks/时,Django将调用task_list
视图函数。这个视图函数将同时与数据库和模板进行交互。
编辑模板(T)
最后我们要创建task_list.html
用于展示视图传来的任务列表数据。
这个文件的完整路径为tasks/templates/tasks/task_list.html
。
Django提供了自己的模板语言,包括常见的判断和循环,专门用来渲染模板。
# tasks/templates/tasks/task_list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Task List</title>
</head>
<body>
<h3>Task List</h3>
{% for task in tasks %}
<p>{{ forloop.counter }}. {{ task.name }} - {{ task.get_status_display }}
</p>
{% endfor %}
</body>
</html>
当然此时如果你通过浏览器访问/tasks/, 还看不到任何内容,这是因为你的数据表里还没有任何数据。你可以通过django的admin添加或新增task_create
视图实现。
CRUD-DEMO
在 MVT设计模式 中添加代码。
1、使用ModelForm类创建了TaskForm,在创建任务或更新任务时需要用到这个表单。
# tasks/forms.py
from .models import Task
from django import forms
class TaskForm(forms.ModelForm):
class Meta:
model = Task
fields = "__all__"
2、编写路由URLConf及视图
# tasks/urls.py
from django.urls import path, re_path
from . import views
# namespace
app_name = 'tasks'
urlpatterns = [
# Create a task
path('create/', views.task_create, name='task_create'),
# Retrieve task list
path('', views.task_list, name='task_list'),
# Retrieve single task object
re_path(r'^(?P<pk>\d+)/$', views.task_detail, name='task_detail'),
# Update a task
re_path(r'^(?P<pk>\d+)/update/$', views.task_update, name='task_update'),
# Delete a task
re_path(r'^(?P<pk>\d+)/delete/$', views.task_delete, name='task_delete'),
]
# tasks/views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse
from .models import Task
from .forms import TaskForm
# Create a task
def task_create(request):
# 如果用户通过POST提交,通过request.POST获取提交数据
if request.method == "POST":
# 将用户提交数据与TaskForm表单绑定
form = TaskForm(request.POST)
# 表单验证,如果表单有效,将数据存入数据库
if form.is_valid():
form.save()
# 跳转到任务清单
return redirect(reverse("tasks:task_list"))
else:
# 否则空表单
form = TaskForm()
return render(request, "tasks/task_form.html", {"form": form, })
# Retrieve task list
def task_list(request):
# 从数据库获取任务清单
tasks = Task.objects.all()
# 指定渲染模板并传递数据
return render(request, "tasks/task_list.html", {"tasks": tasks, })
# Retrieve a single task
def task_detail(request, pk):
# 从url里获取单个任务的pk值,然后查询数据库获得单个对象
task = get_object_or_404(Task, pk=pk)
return render(request, "tasks/task_detail.html", {"task": task, })
# Update a single task
def task_update(request, pk):
# 从url里获取单个任务的pk值,然后查询数据库获得单个对象实例
task_obj = get_object_or_404(Task, pk=pk)
if request.method == 'POST':
form = TaskForm(instance=task_obj, data=request.POST)
if form.is_valid():
form.save()
return redirect(reverse("tasks:task_detail", args=[pk, ]))
else:
form = TaskForm(instance=task_obj)
return render(request, "tasks/task_form.html", {"form": form, "object": task_obj})
# Delete a single task
def task_delete(request, pk):
# 从url里获取单个任务的pk值,然后查询数据库获得单个对象
task_obj = get_object_or_404(Task, pk=pk)
task_obj.delete() # 删除然后跳转
return redirect(reverse("tasks:task_list"))
3、前端
# tasks/templates/tasks/task_list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Task List</title>
</head>
<body>
<h3>Task List</h3>
{% for task in tasks %}
<p>{{ forloop.counter }}. {{ task.name }} - {{ task.get_status_display }}
(<a href="{% url 'tasks:task_update' task.id %}">Update</a> |
<a href="{% url 'tasks:task_delete' task.id %}">Delete</a>)
</p>
{% endfor %}
<p> <a href="{% url 'tasks:task_create' %}"> + Add A New Task</a></p>
</body>
</html>
# tasks/templates/tasks/task_detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Task Detail</title>
</head>
<body>
<p> Task Name: {{ task.name }} | <a href="{% url 'tasks:task_update' task.id %}">Update</a> |
<a href="{% url 'tasks:task_delete' task.id %}">Delete</a>
</p>
<p> Task Status: {{ task.get_status_display }} </p>
<p> <a href="{% url 'tasks:task_list' %}">View All Tasks</a> |
<a href="{% url 'tasks:task_create'%}">New Task</a>
</p>
</body>
</html>
# tasks/templates/tasks/task_form.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% if object %}Edit Task {% else %} Create New Task {% endif %}</title>
</head>
<body>
<h3>{% if object %}Edit Task {% else %} Create New Task {% endif %}</h3>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<p><input type="submit" class="btn btn-success" value="Submit"></p>
</form>
</body>
</html>
执行下面命令后看效果:
python manage.py makemigrations
python manage.py migrate
python manage.py runserver
model
模型的组成
一个标准的Django模型分别由模型字段、META选项和方法三部分组成。
Django官方编码规范建议按如下方式排列:
- 定义的模型字段:包括基础字段和关系字段
- 自定义的Manager方法:改变模型
class Meta选项
: 包括排序、索引等等(可选)。def __str__()
:定义单个模型实例对象的名字(可选)。def save()
:重写save方法(可选)。def get_absolute_url()
:为单个模型实例对象生成独一无二的url(可选)- 其它自定义的方法。
模型的字段
models.Model
提供的常用模型字段包括基础字段和关系字段。
基础字段
CharField()
一般需要通过max_length = xxx 设置最大字符长度。
如不是必填项,可设置blank = True和default = ‘‘。
如果用于username, 想使其唯一,可以设置
unique = True
。如果有choice选项,可以设置 choices = XXX_CHOICES
TextField()
适合大量文本,max_length = xxx选项可选。
DateField() 和DateTimeField()
可通过default=xx选项设置默认日期和时间。
- 对于DateTimeField: default=timezone.now - 先要
from django.utils import timezone
- 如果希望自动记录一次修改日期(modified),可以设置:
auto_now=True
- 如果希望自动记录创建日期(created), 可以设置
auto_now_add=True
EmailField()
如不是必填项,可设置blank = True和default = 。
一般Email用于用户名应该是唯一的,建议设置unique = True
IntegerField(), SlugField(), URLField(),BooleanField()
可以设置blank = True or null = True。
对于BooleanField一般建议设置defaut = True or False
FileField(upload_to=None, max_length=100) - 文件字段
- upload_to = “/some folder/”:上传文件夹路径
- max_length = xxxx:文件最大长度
ImageField (upload_to=None, max_length=100,)- 图片字段
- upload_to = “/some folder/”: 指定上传图片路径
关系字段
OneToOneField(to, on_delete=xxx, options) - 单对单关系
- to必需指向其他模型,比如 Book or ‘self’ .
- 必需指定
on_delete
选项(删除选项): i.e, “on_delete = models.CASCADE
” or “on_delete = models.SET_NULL
” . - 可以设置 “
related_name = xxx
” 便于反向查询。
ForeignKey(to, on_delete=xxx, options) - 单对多关系
- to必需指向其他模型,比如 Book or ‘self’ .
- 必需指定
on_delete
选项(删除选项): i.e, “on_delete = models.CASCADE
” or “on_delete = models.SET_NULL
” . - 可以设置”default = xxx” or “null = True” ;
- 如果有必要,可以设置 “
limit_choices_to =
“, - 可以设置 “
related_name = xxx
” 便于反向查询。
ManyToManyField(to, options) - 多对多关系
- to 必需指向其他模型,比如 User or ‘self’ .
- 设置 “
symmetrical = False
“ 表示多对多关系不是对称的,比如A关注B不代表B关注A - 设置 “
through = 'intermediary model'
“ 如果需要建立中间模型来搜集更多信息。 - 可以设置 “
related_name = xxx
” 便于反向查询。
示例:一个人加入多个组,一个组包含多个人,我们需要额外的中间模型记录加入日期和理由。
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
对于OneToOneField
和ForeignKey
, on_delete
选项和related_name
是两个非常重要的设置,前者决定了了关联外键删除方式,后者决定了模型反向查询的名字。
on_delete删除选项
Django提供了如下几种关联外键删除选项, 可以根据实际需求使用。
CASCADE
:级联删除。当你删除publisher记录时,与之关联的所有 book 都会被删除。PROTECT
: 保护模式。如果有外键关联,就不允许删除,删除的时候会抛出ProtectedError错误,除非先把关联了外键的记录删除掉。例如想要删除publisher,那你要把所有关联了该publisher的book全部删除才可能删publisher。SET_NULL
: 置空模式。删除的时候,外键字段会被设置为空。删除publisher后,book 记录里面的publisher_id 就置为null了。SET_DEFAULT
: 置默认值,删除的时候,外键字段设置为默认值。SET()
: 自定义一个值。DO_NOTHING
:什么也不做。删除不报任何错,外键值依然保留,但是无法用这个外键去做查询。
related_name选项
related_name
用于设置模型反向查询的名字,非常有用。
# models.py
from django.db import models
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=60)
def __str__(self):
return self.name
# 将related_name设置为books
class Book(models.Model):
name = models.CharField(max_length=30)
description = models.TextField(blank=True, default='')
publisher = ForeignKey(Publisher,on_delete=models.CASCADE, related_name='books')
add_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.name
我们再来对比一下如何通过publisher查询其出版的所有书籍,你觉得哪个更好呢?
- 设置
related_name
前:publisher.book_set.all
- 设置
related_name
后:publisher.books.all
模型的META选项
abstract=True
: 指定该模型为抽象模型proxy=True
: 指定该模型为代理模型verbose_name=xxx
和verbose_name_plural=xxx
: 为模型设置便于人类阅读的别名db_table= xxx
: 自定义数据表名odering=['-pub-date']
: 自定义按哪个字段排序,-
代表逆序permissions=[]
: 为模型自定义权限managed=False
: 默认为True,如果为False,Django不会为这个模型生成数据表indexes=[]
: 为数据表设置索引,对于频繁查询的字段,建议设置索引constraints=
: 给数据库中的数据表增加约束。
模型的方法
标准方法
以下三个方法是Django模型自带的三个标准方法:
def __str__()
:给单个模型对象实例设置人为可读的名字(可选)。def save()
:重写save方法(可选)。def get_absolute_url()
:为单个模型实例对象生成独一无二的url(可选)
除此以外,我们经常自定义方法或Manager方法
ORM数据增删改查接口
我们用到的Article模型如下所示:
from django.db import models
class Article(models.Model):
title = models.CharField('标题', max_length=200, unique=True)
body = models.TextField('正文')
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
增
对于新增数据,Django提供了两种方法,save()
和create()
方法。
方法一:save方法
from XX.models import Article
article = Article(title="My first article", body="My first article body")
article.save()
注意: 该方法如果不主动选择save(), 创建的对象实例只会存于内存之中,不会保存到数据库中去。
正因为如此,Django还提供了更便捷的create方法。
方法二:create方法
article = Article.objects.create(title="My first article", body="My first article body")
为了避免重复创建数据表中已存在的条目,Django还提供了get_or_create
方法。
它会返回查询到的或新建的模型对象实例,还会返回这个对象实例是否是刚刚创建的。
obj, created = Article.objects.get_or_create(title="My first article", body="My first article body")
注意: 对Django自带auth模块中的User模型操作,比如创建新的用户时,请用create_user
方法。该方法会将密码自动加Hash存储到数据库中, 如下所示:
from django.contrib.auth.models import User
user = User.objects.create_user(username='john, email='john@gmail.com',password='somepwd')
方法三:bulk_create方法
在Django中向数据库中插入多条数据时,每使用save或create方法保存一条就会执行一次SQL。
而Django提供的bulk_create
方法可以一次SQL添加多条数据,效率要高很多,如下所示:
# 内存生成多个对象实例
articles = [Article(title="title1", body="body1"), Article(title="title2", body="body2"), Article(title="title3", body="body3")]
# 执行一次SQL插入数据
Article.objects.bulk_create(articles)
删
删即从数据表中删除一个已有条目。Django也允许同时删除一条或多条数据。
删除单条数据
# 删除第5篇文章
Article.objects.get(pk=5).delete()
删除部分数据
# 删除标题含有python的文章
Article.objects.filter(title__icontains="python").delete()
删除所有数据
# 慎用
Article.objects.all().delete()
改
改既可以用save方法,也可以用update方法。其区别在于save方法不仅可以更新数据中现有对象数据,还可以创建新的对象。而update方法只能用于更新已有对象数据。一般来说,如果要同时更新多个对象数据,用update方法或bulk_update方法更合适。
方法一: save方法
article = Article.objects.get(id=1)
article.title = "New article title"
article.save()
方法二:update方法更新单篇文章
article = Article.objects.get(id=1).update(title='new title')
方法三:update方法同时更新多篇文章
# 更新所有文章标题
article = Article.objects.filter(title__icontains='python').update(title='Django')
方法四: bulk_update方法
与bulk_create
方法类似,Django还提供了bulk_update
方法可以对数据库里的数据进行批量更新。
查
查主要使用get, filter及exclude方法,而且这些方法是可以联用的。
查询所有数据
# QuerySet类型,实例对象列表
Article.objects.all()
# 字典列表
Article.objects.all().values()
# 只获取title-字典形式
Article.objects.all().values('title')
# 只获取title列表- 元组形式,只有value,没有key
Article.objects.all().values_list('title')
# 只获取title列表,只有value
Article.objects.all().values_list('title', flat=True)
查询单条数据
article = Article.objects.get(id=11)
当上述查询有个问题,如果id不存在,会抛出错误。还有一种方式是使用filter
方法, 这样即使id不存在也不会报错。
article = Article.objects.filter(id=1).first()
一个更好的方式是使用Django提供的get_object_or_404
方法,如下所示:
from django.shortcuts import get_object_or_404
article = get_object_or_404(Article, pk=1)
查询多条数据
按大于、小于及不等于查询
# gte:大于等于,lte:小于等于
articles = Article.objects.filter(id__gte=2).filter(id__lte=11)
# 不等于
articles = Article.objects.exclude(id=10)
按范围查询
# 按范围查询,in或者range
articles = Article.objects.filter(id__range=[2, 11])
articles = Article.objects.filter(id__in=[3, 6,9])
字符串模糊查询
#标题包含python,若忽略大小写使用icontains
articles = Article.objects.filter(title__contains='python')
#标题以python开头,若忽略大小写使用istartswith
articles = Article.objects.filter(title__startswith='python')
#标题是python结尾的,若忽略大小写使用__iendswith
articles = Article.objects.filter(title__endswith='python')
按日期时间查询
# 查询2021年发表的文章
Article.objects.filter(created__year=2021)
# 查询2021年3月19日发表的文章
import datetime
Article.objects.filter(created__date=datetime.date(2021,3,19))
# 查询2021年1月1日以后发表的文章
Article.objects.filter(created__gt=datetime.date(2021, 1, 1))
# 与当前时间相比,查询即将发表的文章
from django.utils import timezone
Article.objects.filter(created__gt=timezone.now())
# 按绝对时间范围查询,查询2021年1月1日到6月30日发表文章
article = Aritlce.objects.filter(created__gte=datetime.date(2021, 1, 1),
pub_date__lte=datetime.date(2021, 6, 30))
# 按相对时间范围查询,用range查询3月1日以后30天内发表文章
startdate = datetime.date(2021, 3, 1)
enddate = startdate + datetime.timedelta(days=30)
Article.objects.filter(pub_date__range=[startdate, enddate])
切片、排序、去重
# 切片
articles = Article.objects.filter(created__year=2021)[:5]
# 排序:created正序,-表示逆序
articles = Article.objects.all().order_by('-created')
# 去重
Article.objects.filter(title__icontains='python').distinct()
高级Q和F方法
Q方法
有时候我们需要执行or逻辑的条件查询,这时使用Q方法就可以了,它可以连接多个查询条件。Q对象前面加~可以表示否定。
from django.db.models import Q
# 查询标题含有python或Django的文章
article = Article.objects.filter(Q(title__icontains='python')|Q(title__icontains='django'))
# 查询标题含有python,不含有Django的文章
article = Article.objects.filter(Q(title__icontains='python')|~Q(title__icontains='django'))
F方法
使用F()
方法可以实现基于自身字段值来过滤一组对象,它还支持加、减、乘、除、取模和幂运算等算术操作。
from django.db.models import F
Article.objects.filter(n_commnets__gt=F('n_pingbacks'))
Article.objects.filter(n_comments__gt=F('n_pingbacks') * 2)
Django-test
from django.test import TestCase
# Create your tests here.
class test(TestCase):
pass
路由配置
一个优美的URL不仅层次分明、逻辑清晰,而且便于搜索引擎收录。一个糟糕的URL不仅可读性差,而且易造成程序冲突。
URL conf是如何工作的?
假如我们有一个blog
的博客应用,你需要编写两个视图函数,一个用于展示文章列表,一个用于展示文章详情,你的urls.py
和views.py
正常情况下应如下所示:
# blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('blog/', views.index),
path('blog/articles/<int:id>/', views.article_detail),
]
# blog/views.py
def index(request):
# 展示所有文章
def article_detail(request, id):
# 展示某篇具体文章
那么上面这段代码是如何工作的呢?
- 当用户在浏览器输入
/blog/
时,URL收到请求后会调用视图views.py
里的index
方法,展示所有文章。 - 当用户在浏览器输入
/blog/article/<int:id>/
时,URL不仅调用了views.py
里的article_detail
方法,而且还把参数文章id通过<>
括号的形式传递给了视图。int这里代表只传递整数,传递的参数名字是id。
在上述代码中,我们通过urlpatterns
列表的url-视图映射关系列表起了决定性作用,起到了任务调度的作用。
注意:注意当你配置URL时,别忘了把你的应用(blog)的urls加入到项目的URL配置里(mysite/urls.py), 如下图所示:
from django.urls import include, path
urlpatterns = [
path('', include('blog.urls')),
...
]
path和re_path方法
Django提供了两种设计URL的方法: path
和re_path
,它们均支持向视图函数或类传递参数。
path
是正常参数传递,re_path
是采用正则表达式regex匹配。path
和re_path
传递参数方式如下:
path
方法:采用双尖括号<变量类型:变量名>
或<变量名>
传递。re_path
方法: 采用命名组(?P<变量名>表达式)
的方式传递参数。
下例中,分别以path
和re_path
定以了两个urls,它们是等效的,把文章的id(整数类型)传递给了视图。
re_path
里引号前面的小写r表示引号里为正则表达式, ^
代表开头,$
代表以结尾,\d+
代表正整数。
# blog/urls.py
from django.urls import path, re_path
from . import views
urlpatterns = [
path('blog/articles/<int:id>/', views.article_detail, name = 'article_detail'),
re_path(r'^blog/articles/(?P<id>\d+)/$', views.article_detail, name='article_detail'),
]
# blog/views.py
def article_detail(request, id):
# 展示某篇文章
在使用path
和re_path
方法设计urls需注意:
- url中的参数名要用尖括号,而不是圆括号;
- 匹配模式的最开头不需要添加斜杠
/
,但建议以斜杠结尾; - 使用
re_path
时不一定总是以$
结尾,有时不能加。
from django.urls import include, re_path
urlpatterns = [
re_path(r'^blog/', include('blog.urls')),
...
]
URL指向基于类的视图(View)
目前path
和re_path
都只能指向视图view里的一个函数或方法,而不能直接指向一个基于类的视图(Class based view)。
Django提供了一个额外as_view()
方法,可以将一个类伪装成方法。
这点在当你使用Django自带的类视图或自定义的类视图时非常重要。
具体使用方式如下:
# blog/urls.py
from django.urls import path, re_path
from . import views
urlpatterns = [
# path('blog/articles/', views.article_list, name = 'article_list'),
path('blog/articles/', views.ArticleList.as_view(), name='article_list'),
]
# View (in blog/views.py)
from django.views.generic import ListView
from .views import Article
class ArticleList(ListView):
queryset = Article.objects.filter(date__lte=timezone.now()).order_by('date')[:5]
context_object_name = 'article_list‘
template_name = 'blog/article_list.html'
模板
模板文件有两种, 一种是属于整个项目(project)的模板,一种是属于某个应用(app)的模板。
模板文件的放置路径必需正确, 否则Django找不到模板容易出现TemplateDoesNotExist
的错误。
项目模板
属于项目的模板文件路径一般是项目根目录下的templates
文件夹。
除此以外, 你还需要在settings.py
种显示地将模板目录设置为BASE_DIR
目录下的templates
文件夹。
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], # 设置模板目录
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
应用模板
属于单个应用的模板文件路径一般是app
目录下的app/templates/app
文件夹,这样做的好处是可以避免模板命名冲突。
下图以博客项目为例展示了项目模板文件和应用模板文件正确的存放路径。
myproject/ # 项目名
manage.py
myproject/
__init__.py
urls.py
wsgi.py
settings.py
blog/ # 应用名
__init__.py
models.py
managers.py
views.py
urls.py
templates/ # 应用模板文件
blog/
base.html
list.html
detail.html
templates/ # 项目模板文件
base.html
index.html
requirements/
base.txt
dev.txt
test.txt
prod.txt
对于上面这个项目布局,在使用render
方法指定渲染模板时,无需给出完整的路径,只给出相对于templates
的路径即可,比如:
# 指定项目模板
return render(request, "index.html", { "msg": "hello world!",})
# 指定应用模板
return render(request, "blog/list.html", { "articles": articles,})
Django-admin
1、初始化
python manage.py createsuperuser
2、根据提示设置用户名、邮箱和密码:
用户名 (leave blank to use 'admin'):
电子邮件地址:
Password:
Password (again):
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
MySQL
第一步: 安装MySQL
第二步 创建数据库名和用户
第三步 安装第三方库mysqlclient
Django项目中操作MySQL,官方推荐mysqlclient
这个库。
pip install mysqlclient
第四步 修改配置文件settings.py
修改项目文件夹里的settings.py
的文件,添加创建的数据库和用户信息。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'mydb', # 数据库名,Django不会帮你创建,需要自己进入数据库创建。
'USER': 'myuser', # 设置的数据库用户名
'PASSWORD': 'mypass', # 设置的密码
'HOST': 'localhost', # 本地主机或数据库服务器的ip
'PORT': '3306', # 数据库使用的端口
}
}
另一种设置方式是使用OPTIONS
和配置文件my.cnf
进行设置。
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'OPTIONS': {
'read_default_file': '/path/to/my.cnf',
},
}
}
# my.cnf
[client]
database = mydb
user = myuser
password = mypass
default-character-set = utf8
设置好后,连续使用如下命令如果没有出现错误,那么恭喜你已经在Django项目中使用MySQL数据库啦。
python manage.py makemigrations
python manage.py migrate
Cookie和Session
Cookie
django中使用cookie验证用户是否已登录的一个示例。
用户首次登录时设置cookie,再次请求时验证请求携带的cookie。
# 如果登录成功,设置cookie
def login(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = User.objects.filter(username__exact=username, password__exact=password)
if user:
response = HttpResponseRedirect('/index/')
# 将username写入浏览器cookie,有效时间为360秒
response.set_cookie('username', username, 360)
return response
else:
return HttpResponseRedirect('/login/')
else:
form = LoginForm()
return render(request, 'users/login.html', {'form': form})
# 通过cookie判断用户是否已登录
def index(request):
# 读取客户端请求携带的cookie,如果不为空,表示为已登录帐号
username = request.COOKIES.get('username', '')
if not username:
return HttpResponseRedirect('/login/')
return render(request, 'index.html', {'username': username})
Session
第一步:检查基本设置
Django中使用session首选需要确保settings.py
中已开启了SessionMiddleware
中间件。
'django.contrib.sessions.middleware.SessionMiddleware',
Django默认使用数据库存储每个session的sessionid, 所以你还需确保INSTALLED_APPS
是包含如下app:
'django.contrib.sessions',
当然你还可以使用更快的文件或缓存来存储会话信息,可以通过SESSION_ENGINE
设置就行。
第二步:使用session
request.session是一个字典,你可以在视图和模板中直接使用它。
# 设置session的值
request.session['key'] = value
request.session.set_expiry(time): 设置过期时间,0表示浏览器关闭则失效
# 获取session的值
request.session.get('key',None)
# 删除session的值, 如果key不存在会报错
del request.session['key']
# 判断一个key是否在session里
'fav_color' in request.session
# 获取所有session的key和value
request.session.keys()
request.session.values()
request.session.items()
另外,settings.py
还有两项有关session比较重要的设置:
1、SESSION_COOKIE_AGE:以秒为单位,session的有效时间,可以通过set_expiry
方法覆盖。
2、SESSION_EXPIRE_AT_BROWSER_CLOSE:默认为Flase,是否设置为浏览器关闭,会话自动失效。
Session使用示例:
下面是django中使用session进行用户登录和登出的一个示例。
用户首次登录时设置session,退出登录时删除session。
# 如果登录成功,设置session
def login(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = User.objects.filter(username__exact=username, password__exact=password)
if user:
# 将username写入session,存入服务器
request.session['username'] = username
return HttpResponseRedirect('/index/')
else:
return HttpResponseRedirect('/login/')
else:
form = LoginForm()
return render(request, 'users/login.html', {'form': form})
# 通过session判断用户是否已登录
def index(request):
# 获取session中username
username = request.session.get('username', '')
if not username:
return HttpResponseRedirect('/login/')
return render(request, 'index.html', {'username': username})
# 退出登录
def logout(request):
try:
del request.session['username']
except KeyError:
pass
return HttpResponse("You're logged out.")
例子:通过session控制不让用户连续评论两次的例子。
实际应用中我们还可以通过session来控制用户登录时间,记录访问历史,记录购物车信息等等。
from django.http import HttpResponse
def post_comment(request, new_comment):
if request.session.get('has_commented', False):
return HttpResponse("You've already commented.")
c = comments.Comment(comment=new_comment)
c.save()
request.session['has_commented'] = True
return HttpResponse('Thanks for your comment!')
上传文件
Django文件上传需要考虑的重要事项
文件或图片一般通过表单进行。用户在前端点击文件上传,然后以POST方式将数据和文件提交到服务器。
服务器在接收到POST请求后需要将其存储在服务器上的某个地方。
Django默认的存储地址是相对于根目录的/media/文件夹,存储的默认文件名就是文件本来的名字。
上传的文件如果不大于2.5MB,会先存入服务器内存中,然后再写入磁盘。
如果上传的文件很大,Django会把文件先存入临时文件,再写入磁盘。
Django默认处理方式会出现一个问题,所有文件都存储在一个文件夹里。
不同用户上传的有相同名字的文件可能会相互覆盖。
另外用户还可能上传一些不安全的文件如js和exe文件,我们必需对允许上传文件的类型进行限制。
因此我们在利用Django处理文件上传时必需考虑如下3个因素:
- 设置存储上传文件的文件夹地址
- 对上传文件进行重命名
- 对可接受的文件类型进行限制(表单验证)
注意:以上事项对于上传图片是同样适用的。
目录结构
需要将file_upload
这个app加入到我们项目里,然后设置/media/和/STATIC_URL/文件夹。
上传的文件都会放在/media/文件夹里。我们还需要使用css和js这些静态文件,所以需要设置STATIC_URL。
#file_project/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'file_upload',# 新增
]
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static"), ]
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
#file_project/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('file/', include("file_upload.urls")),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
- 创建模型
使用File模型包括file
和upload_method
两个字段。通过upload_to
选项指定了文件上传后存储的地址,并对上传的文件名进行了重命名。
#file_upload/models.py
from django.db import models
import os
import uuid
# Create your models here.
# Define user directory path
def user_directory_path(instance, filename):
ext = filename.split('.')[-1]
filename = '{}.{}'.format(uuid.uuid4().hex[:10], ext)
return os.path.join("files", filename)
class File(models.Model):
file = models.FileField(upload_to=user_directory_path, null=True)
upload_method = models.CharField(max_length=20, verbose_name="Upload Method")
注意:如果你不使用ModelForm
,你还需要手动编写代码存储上传文件。
- URLConf配置
本项目一共包括3个urls, 分别对应普通表单上传,ModelForm上传和显示文件清单。
#file_upload/urls.py
from django.urls import re_path, path
from . import views
# namespace
app_name = "file_upload"
urlpatterns = [
# Upload File Without Using Model Form
re_path(r'^upload1/$', views.file_upload, name='file_upload'),
# Upload Files Using Model Form
re_path(r'^upload2/$', views.model_form_upload, name='model_form_upload'),
# View File List
path('file/', views.file_list, name='file_list'),
]
- 使用一般表单上传文件
我们先定义一个一般表单FileUploadForm
,并通过clean方法对用户上传的文件进行验证,如果上传的文件名不以jpg, pdf或xlsx结尾,将显示表单验证错误信息。
#file_upload/forms.py
from django import forms
from .models import File
# Regular form
class FileUploadForm(forms.Form):
file = forms.FileField(widget=forms.ClearableFileInput(attrs={'class': 'form-control'}))
upload_method = forms.CharField(label="Upload Method", max_length=20,
widget=forms.TextInput(attrs={'class': 'form-control'}))
def clean_file(self):
file = self.cleaned_data['file']
ext = file.name.split('.')[-1].lower()
if ext not in ["jpg", "pdf", "xlsx"]:
raise forms.ValidationError("Only jpg, pdf and xlsx files are allowed.")
# return cleaned data is very important.
return file
注意: 使用clean方法对表单字段进行验证时,别忘了return验证过的数据,即cleaned_data
。
只有返回了cleaned_data, 视图中才可以使用form.cleaned_data.get(‘xxx’)获取验证过的数据。
对应一般文件上传的视图file_upload
方法如下所示:
当用户的请求方法为POST时,我们通过form.cleaned_data.get('file')
获取通过验证的文件,并调用自定义的handle_uploaded_file
方法来对文件进行重命名,写入文件。
如果用户的请求方法不为POST,则渲染一个空的FileUploadForm
在upload_form.html
里。定义了一个file_list
方法来显示文件清单。
#file_upload/views.py
from django.shortcuts import render, redirect
from .models import File
from .forms import FileUploadForm, FileUploadModelForm
import os
import uuid
from django.http import JsonResponse
from django.template.defaultfilters import filesizeformat
# Create your views here.
# Show file list
def file_list(request):
files = File.objects.all().order_by("-id")
return render(request, 'file_upload/file_list.html', {'files': files})
# Regular file upload without using ModelForm
def file_upload(request):
if request.method == "POST":
form = FileUploadForm(request.POST, request.FILES)
if form.is_valid():
# get cleaned data
upload_method = form.cleaned_data.get("upload_method")
raw_file = form.cleaned_data.get("file")
new_file = File()
new_file.file = handle_uploaded_file(raw_file)
new_file.upload_method = upload_method
new_file.save()
return redirect("/file/")
else:
form = FileUploadForm()
return render(request, 'file_upload/upload_form.html',
{'form': form, 'heading': 'Upload files with Regular Form'}
)
def handle_uploaded_file(file):
ext = file.name.split('.')[-1]
file_name = '{}.{}'.format(uuid.uuid4().hex[:10], ext)
# file path relative to 'media' folder
file_path = os.path.join('files', file_name)
absolute_file_path = os.path.join('media', 'files', file_name)
directory = os.path.dirname(absolute_file_path)
if not os.path.exists(directory):
os.makedirs(directory)
with open(absolute_file_path, 'wb+') as destination:
for chunk in file.chunks():
destination.write(chunk)
return file_path
注意:
handle_uploaded_file
方法里文件写入地址必需是包含/media
/的绝对路径,如果/media/files/xxxx.jpg,而该方法返回的地址是相对于/media/文件夹的地址,如/files/xxx.jpg。存在数据中字段的是相对地址,而不是绝对地址。- 构建文件写入绝对路径时请用
os.path.join
方法,因为不同系统文件夹分隔符不一样。写入文件前一个良好的习惯是使用os.path.exists
检查目标文件夹是否存在,如果不存在先创建文件夹,再写入。
上传表单模板upload_form.html
代码如下:
#file_upload/templates/upload_form.html
{% extends "file_upload/base.html" %}
{% block content %}
{% if heading %}
<h3>{{ heading }}</h3>
{% endif %}
<form action="" method="post" enctype="multipart/form-data" >
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-info form-control " type="submit" value="submit">Upload</button>
</form>
{% endblock %}
显示文件清单模板file_list.html
代码如下所示:
# file_upload/templates/file_list.html
{% extends "file_upload/base.html" %}
{% block content %}
<h3>File List</h3>
<p> <a href="/file/upload1/">RegularFormUpload</a> | <a href="/file/upload2/">ModelFormUpload</a>
| <a href="/file/upload3/">AjaxUpload</a></p>
{% if files %}
<table class="table table-striped">
<tbody>
<tr>
<td>Filename & URL</td>
<td>Filesize</td>
<td>Upload Method</td>
</tr>
{% for file in files %}
<tr>
<td><a href="{{ file.file.url }}">{{ file.file.url }}</a></td>
<td>{{ file.file.size | filesizeformat }}</td>
<td>{{ file.upload_method }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>No files uploaded yet. Please click <a href="{% url 'file_upload:file_upload' %}">here</a>
to upload files.</p>
{% endif %}
{% endblock %}
注意:
-
对于上传的文件我们可以调用
file.url
,file.name
和file.size
来查看上传文件的链接,地址和大小。 -
上传文件的大小默认是以B显示的,数字非常大。使用Django模板过滤器
filesizeformat
可以将文件大小显示为人们可读的方式,如MB,KB。 -
使用ModelForm上传文件
使用ModelForm
上传是推荐的上传方式,前提是你已经在模型中通过upload_to
选项自定义了用户上传文件存储地址,并对文件进行了重命名。
首先要自定义自己的FileUploadModelForm
,由File模型重建的。
代码如下所示:
#file_upload/forms.py
from django import forms
from .models import File
# Model form
class FileUploadModelForm(forms.ModelForm):
class Meta:
model = File
fields = ('file', 'upload_method',)
widgets = {
'upload_method': forms.TextInput(attrs={'class': 'form-control'}),
'file': forms.ClearableFileInput(attrs={'class': 'form-control'}),
}
def clean_file(self):
file = self.cleaned_data['file']
ext = file.name.split('.')[-1].lower()
if ext not in ["jpg", "pdf", "xlsx"]:
raise forms.ValidationError("Only jpg, pdf and xlsx files are allowed.")
# return cleaned data is very important.
return file
使用ModelForm
处理文件上传的视图model_form_upload
方法非常简单,只需调用form.save()
即可,无需再手动编写代码写入文件。
#file_upload/views.py
from django.shortcuts import render, redirect
from .models import File
from .forms import FileUploadForm, FileUploadModelForm
import os
import uuid
from django.http import JsonResponse
from django.template.defaultfilters import filesizeformat
# Create your views here.
# Upload File with ModelForm
def model_form_upload(request):
if request.method == "POST":
form = FileUploadModelForm(request.POST, request.FILES)
if form.is_valid():
form.save() # 一句话足以
return redirect("/file/")
else:
form = FileUploadModelForm()
return render(request, 'file_upload/upload_form.html',
{'form': form,'heading': 'Upload files with ModelForm'}
)
Django配置文件
- BASE_DIR
默认值BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
。
这个是Django项目文件夹所在目录得绝对路径,一般不要修改。
- DEBUG
默认值是True
。在本地开发测试环境下设置DEBUG=True可以显示bug信息,便于开发者找出代码错误所在。当你在部署项目在生产环境时,请切记设置DEBUG=False。这是因为生产环境下打开Debug一旦发生错误或异常会暴露很多敏感设置信息。
注意: 当你设置DEBUG=False
, 你一定要设置ALLOWED_HOSTS
选项, 否则会抛出异常。
- ALLOWED_HOSTS
默认值为空[]
。设置ALLOWED_HOSTS是为了限定用户请求中的host值,以防止黑客构造包来进行头部攻击。该选项正确设置方式如下:
-
DEBUG=True: ALLOWED_HOSTS可以为空,也可设置为[‘127.0.0.01’, ‘localhost’]
-
在正式部署项目时,请尽量不要设置
ALLOWED_HOSTS=['*']
。 -
SECRET_KEY
SECRET_KEY是Django根据自己算法生成的一大串随机数,本质是个加密盐,用于防止CSRF(Cross-site request forgery)跨站请求伪造攻击。
当部署Django项目到生产环境中时,Django文档建议不直接在settings.py
里输入字符串,而是采取下面几种方法读取SECRET_KEY。
方法一: 从环境变量中读取SECRET_KEY
import os
SECRET_KEY = os.environ['SECRET_KEY']
方法二: 从服务器上Django项目文件价外的某个文件读取
with open('/etc/secret_key.txt') as f:
SECRET_KEY = f.read().strip()
- INSTALLED_APPS
这个设置比较简单,也比较常用,用于增删一个项目(Project)所包含的应用(APP)。
只有对列入此项的APP, Django才会生成相应的数据表。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'polls', # 自定义的APP
]
- AUTH_USER_MODEL
默认为auth.user
。也可以为自定义用户模型, 如users.user
。
- STATIC_ROOT和STATIC_URL
这两个选项是关于静态文件(如CSS, JS,和图片)的最重要的设置。
一般设置如下。STATIC_URL是静态文件URL,设置后可以通过使用{% static 'assets/imges/xxx.jpg' %}
方式直接访问/static/文件夹里的静态文件。
如果你设置了STATIC_ROOT, 当你运行python manage.py collectstatic
命令的时候,Django会将各app下所有名为static的文件夹及其子目录复制收集到STATIC_ROOT。把静态文件集中一起的目的是为了更方便地通过Apache或Nginx部署。
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
一般情况下我们会尽量把静态文件只放在static文件夹或它的子目录下,所以上述两个设置对于一般项目是够的。
那么问题来了,如果你还有一些文件夹中也有静态文件,可是文件夹并不是以static命名也不在static子目录里,此时你也希望搜集使用那些静态文件,你该怎么办呢?
这时我们就要设置静态文件目录STATICFILES_DIRS
值了。
- STATICFILES_DIRS
默认值为空。当你设置该选项后,python manage.py collectstatic
命令会把static文件夹及静态文件目录STATICFILES_DIRS
里的静态文件都复制到一份到STATIC_ROOT
。
比如下例中Django会将下面两个文件夹内容也复制到STATIC_ROOT
。注意里面的路径必需是绝对路径哦。
STATICFILES_DIRS = [
"/home/user/pictures",
"/opt/webfiles/myfiles",
]
- MEDIA_ROOT和MEDIA_URL
media文件价一般用于放置用户上传的文件。
对于此文件夹的权限设置异常重要,因为用户可能会上传可执行的文件,影响网站和服务器的安全。
对于此文件夹权限,建议使用sudo chmod 755 media
命令设置成755,而不要使用777(可读、可写、可执行)。
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
- 国际化(语言与时间)
USE_TZ = True # 默认值True。
TIME_ZONE = 'Asia/Shanghai' # 设置时区
USE_I18N = True # 默认为True,是否启用自动翻译系统
USE_L10N = True # 默认False,以本地化格式显示数字和时间
- 邮箱服务配置
EMAIL_HOST = 'smtp.qq.com' # 发送者邮箱服务器
EMAIL_PORT = 25 # 端口
EMAIL_HOST_USER = '' # 发送者用户名(邮箱地址)
EMAIL_HOST_PASSWORD = '' # 发送者密码
EMAIL_USE_SSL = True
DEFAULT_FROM_EMAIL = 'xxx@qq.com'
- 模板设置
如果你在根目录下建立了一个templates
文件夹,专门用于存放属于项目的模板文件,你还需要在settings.py
中显示地将模板目录设置为BASE_DIR
目录下的templates
文件夹。
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], # 设置项目模板目录
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
- 中间件设置
如果你希望在Django项目中使用自定义的中间件,你需要在 MIDDLEWARE
选项里注册。
注意:中间件的添加顺序很重要。
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',
'myapp.middleware.CustomMiddleware1', # 新增自定义中间件。
]
- 数据库设置
Django默认使用轻量级的SQLite数据库,无需进行任何设置开箱即用。
当然小编我并不建议在settings.py
直接写入数据库密码,而是采取读取外部配置文件的方式,更安全。
下面以MYSQL为例介绍了基本配置方式。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'mydb', # 你要存储数据的库名,事先要创建之
'USER': 'xxs', # 数据库用户名
'PASSWORD': 'xxxx', # 密码
'HOST': 'localhost', # 主机
'PORT': '3306', # 数据库使用的端口
}
}
- 缓存设置
Django中提供了多种缓存方式,如果要使用缓存,需要先在settings.py
中进行配置,然后应用。
根据缓存介质的不同,你需要设置不同的缓存后台Backend。
比如如果你希望使用redis做缓存后台,你需要先安装redis
和django_redis
, 然后再修改settings.py
中的缓存配置。
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://your_host_ip:6379', # redis所在服务器或容器ip地址
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "your_pwd", # 你设置的密码
},
},
}
- Session相关设置
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,
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 = 60 * 30 # Session的cookie失效日期(30min)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = True # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = True # 是否每次请求都保存Session,默认修改之后才保存