目录
一、Django框架简介与环境搭建
Python的WEB框架有Django、Tornado、Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM、模型绑定、模板引擎、缓存、Session等诸多功能。
本文使用的版本为1.8.4
1、MVC模型
先来了解一下另一种设计模式MVC,其核心思想是分工、解耦,让不同的代码之间降低耦合,增强代码的可扩展性和可移植性,实现向后兼容。
MVC模式的图解
-
M为Model,主要是对数据库的交互,对数据库中的数据进行增删改查操作
-
V为View,主要是用于封装,来生成展示给浏览器的html用户
-
C为Controll,主要是用于接受请求,处理业务逻辑,返回结果,与Model和View交互调度
2、Django使用的是MVT设计模式
Django也是一个MVC框架。但是在Django中,控制器接受用户输入的部分由框架自行处理,所以 Django 里更关注的是模型(Model)、模板(Template)和视图(Views),称为 MTV模式:
- M为Model,与MVC中的M功能相同
- V为View,与MVC中的C的功能相同
- T为Template,与MVC中的V功能相同
3、虚拟环境
虚拟环境是真实python环境的复制版本,当项目在进行迭代时,后续迭代使用的包版本已经升级,如果前期迭代出现问题,需要使用到虚拟环境安装指定版本的包。
1)安装虚拟环境需要的安装包。
pip install virtualenv
pip install virtualenvwrapper
2)创建虚拟环境
创建虚拟环境:mkvirtualenv virtual_env1(虚拟环境名称)
创建python3虚拟环境:mkvirtualenv -p python3 virtual_env1(虚拟环境名称) 仅在同时部署了python2和python3
进入虚拟工作环境:workon 虚拟环境名称
进入虚拟环境后输入: pip3 install django==1.8.4 安装完成后输入pip3 list查看一下是否存在django1.8.4
查看机器上有多少个虚拟环境,workon 空格 + 两下tab键
退出虚拟环境:deactivate
删除虚拟环境 :rmvirtualenv
3)linux 环境中添加环境变量
①cd到根目录下,如:cd /root
②vim .bashrc
③在文件末尾添加:export WORKON_HOME=$HOME/.virtualenvs和source /usr/local/bin/virtualenvwrapper.sh
④保存退出后执行:source .bashrc
4、创建项目
1)创建项目
命令行执行:django-admin startproject 项目名称,如:django-admin startproject test11
C:\python\Django>django-admin startproject test11
一个项目有很多应用,每个应用完成特定的功能。 Django开发中,一个功能模块用一个应用来实现
2)创建应用
命令行执行:python manage.py startapp 应用名,如:python manage.py startapp booktest
C:\python\Django\test11>python manage.py startapp booktest
注意:创建应用时应先进入项目目录
3)注册应用
建立应用和项目关系,注册应用 ,在settings文件中添加应用名称,具体为:INSTALLED_APPS 元组内添加创建的应用名称
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'booktest', # 添加新创建的应用名称
)
4)启动项目
python manage.py runserver
自定义ip和端口、或端口被占用时启动:python manage.py runserver 127.0.0.1:8001
启动成功后访问结果如下图:
二、Django的基本使用
1、应用目录文件
- migrations 用于存放数据库迁移历史文件
- admin.py 网站的后台管理站点的配置文件
- models.py 用于保存数据库模型类
- tests.py 用于开发测试的
- views.py 编写Web应用视图函数
2、模型类操作
1)模型类设计:
在应用的models.py中设计模型类,设计类必须继承models.Model。
from django.db import models
# Create your models here.
class BookInfo(models.Model):
"""一类:图书模型类"""
b_title = models.CharField(max_length=20) # 图书名称,CharField说明是一个字符串,max_length指定最大长度
b_pubdate = models.DateField() # 图书出版日期,DateField说明是一个日期类型
class HeroInfo(models.Model):
"""多类:英雄任务模型"""
h_name = models.CharField(max_length=20) # 英雄名称
h_gender = models.BooleanField(default=False) # 英雄性别 布尔类型 default指定默认值为False:男
h_comment = models.CharField(max_length=256) # 备注
h_book = models.ForeignKey('BookInfo', on_delete=True) # 关系属性h_book,建立图书类和英雄类之间的一对多关系
models.ForeignKey可以建立两个模型类之间一对多的关系,Django在生成表的时候,就会在多的表中创建一列作为外键,建立两个表之间一对多的关系
2)模型类设计完成后,执行命令生成设计类对应的表。
①生成迁移文件,命令行执行:python manage.py makemigrations
②生成表:python manage.py migrate
3)通过模型类操作数据表
python manage.py shell 进入shell模式,通过模型类操作数据表:
>>> from booktest.models import BookInfo
>>> from booktest.models import HeroInfo
>>> from datetime import date
>>> b = BookInfo()
>>> b.btitle = '天龙八部'
>>> b.bpub_date = date(1999,9,9)
>>> b.save()
>>> h = HeroInfo()
>>> h.hname = '乔峰'
>>> h.hgender = False
>>> h.hcomment = '降龙十八掌'
>>> h.hbook = b
>>> h.save()
>>> h.hname = '段誉'
>>> h.hgender = False
>>> h.hcomment = '妹杀'
>>> h.hbook = b
>>> h.save()
>>> h1 = HeroInfo.objects.get(hname='段誉')
>>> h1.hname
'段誉'
>>> h.delete()
>>> exit()
-------重要-------:
由一查多: b.heroinfo_set.all() 查询图书关联的英雄
由多查一: 查询图书表里面的所有内容:BookInfo.objects.all()
3、后台管理
1)admin本地化
settings文件中修改配置为中文显示:LANGUAGE_CODE = 'zh-hans' 和 TIME_ZONE = 'Asia/Shanghai'
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'
注册超级用户:python manage.py createsuperuser 输入用户及密码,例:admin/Test@2019
C:\python\Django\test7>python manage.py createsuperuser
Username (leave blank to use 'python'): admin
Email address: Test@123.com
Password:
Password (again):
Superuser created successfully.
启动服务:python manage.py runserver,浏览器输入:127.0.0.1:8000/admin,输入注册用户密码进入Django管理页面。
2)自定义模型管理类
在管理页面对模型类进行增删改查,需要在应用的admin.py文件中创建自定义模型管理类,并将创建的图书类和英雄类注册到Django管理后台:
from django.contrib import admin
from booktest.models import BookInfo, HeroInfo
# Register your models here.
class BookInfoAdmin(admin.ModelAdmin):
"""图书模型管理类"""
list_display = ['id', 'b_title', 'b_pubdate'] # list_display参数表示显示到页面的数据形式,参数与设计类时的参数需要保持一致,否则会报错
class HeroInfoAdmin(admin.ModelAdmin):
""""英雄模型管理类"""
list_display = ['id', 'h_name', 'h_gender', 'h_comment', 'h_book']
admin.site.register(BookInfo, BookInfoAdmin)
admin.site.register(HeroInfo, HeroInfoAdmin)
刷新管理页面,展示注册到管理页面的模型类信息。
在管理页面新增英雄类信息,点击首页的Hero infos进入新增页面
点击增加 hero info,会跳转到新增页面:
输入英雄名称、备注,选择关联的书,点击保存就新增成功一个英雄信息了。
这里看到选择的关联书显示为2个BookInfo object,需要修改模型类__str__方法。
from django.db import models
# Create your models here.
class BookInfo(models.Model):
"""一类:图书模型类"""
b_title = models.CharField(max_length=20) # 图书名称,CharField说明是一个字符串,max_length指定最大长度
b_pubdate = models.DateField() # 图书出版日期,DateField说明是一个日期类型
def __str__(self):
# 定义模型类返回对象值
return self.b_title
class HeroInfo(models.Model):
"""多类:英雄任务模型"""
h_name = models.CharField(max_length=20) # 英雄名称
h_gender = models.BooleanField(default=False) # 英雄性别 布尔类型 default指定默认值为False:男
h_comment = models.CharField(max_length=256) # 备注
h_book = models.ForeignKey('BookInfo', on_delete=True) # 关系属性h_book,建立图书类和英雄类之间的一对多关系
def __str__(self):
# 定义模型类返回对象值
return self.h_name
在管理页面还可以进行修改和删除操作,这里就不一一贴图了。
4、视图
1)定义视图函数
Django中通过定义视图函数,建立视图函数和请求url的联系,在请求对应的url时通过视图函数返回对应的HTML页面。
在应用的views文件中定义视图函数,函数格式如下:
def index(request):
"""显示图书信息"""
books = BookInfo.objects.all()
return render(request, 'booktest/index.html', {'books': books})
视图函数必须有一个请求参数request,进行处理之后,需要返回一个HTTPResponse的类对象,就是返回给浏览器显示的内容。
2)进行url配置
定义好视图函数之后,需要建立视图函数和请求url的联系。
首先是在项目的urls.py文件中,urlpatterns列表内添加应用的urls文件: url(r'^', include('booktest.urls'))
url配置的目的是建立视图函数和url的对应关系,url配置项定义在urlpatterns的列表中,每一个配置项都调用url函数。
url函数有两个参数,第一个是正则表达式,第二个是对应的处理动作。
配置url时,有两种语法格式:
- url(正则表达式, 函数视图名)
- url(正则表达式, include(应用中的urls文件))
实际项目在配置url时,首先在项目的urls文件中添加配置项时,并不写具体的url和视图函数之间的对应关系,而是包含具体应用的urls文件,在应用的urls文件中写url和视图函数的对应关系。
注:新建的应用中并没有urls文件,复制一份项目urls文件到应用目录下就好了。
# 项目配置url
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^', include('booktest.urls')) # 配置应用url
]
应用配置url
urlpatterns = [
url(r'^index$', views.index), # 首页
]
3)url匹配的过程:
在项目的urls.py文件中包含具体应用的urls文件,应用的urls文件中写url和视图的对应关系。
当用户输入:127.0.0.1:8001/index时,去除域名和最前面的/,剩下的index,拿index字符串到项目的urls文件中进行匹配,匹配成功后执行视图函数index,index视图函数返回内容给浏览器。
5、模板
1)创建模板目录
在应用的同级目录下创建templates目录,并在目录下创建和应用名称相同的目录,用于存放每个应用的模板文件。
2)配置模板目录
在settings中添加创建的templates目录路径,具体为settings中TEMPLATES列表中'DIRS': [os.path.join(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',
],
},
},
]
3)创建模板文件
在模板目录下和应用名称相同的目录下创建模板文件:index.html
内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>{{ book.title }}</h1>
书信息如下:<br/>
<ul>
{% for book in books %}
<li>
{{ book.b_title }} -- {{ book.b_pubdate }}
</li>
{% endfor %}
</ul>
</body>
</html>
启动服务:python manage.py runserver,浏览器输入:127.0.0.1:8000/index,访问页面如下
4)使用模板文件
a)加载模板文件
去模板目录下获取html文件的内容,得到一个模板对象
b)定义模板上下文
向模板文件传递数据
c)模板渲染
得到一个标准的html内容
d)返回给浏览器
使用模板文件可以在应用的views.py中自定义分封装一个函数,如下
from django.shortcuts import render
from django.template import loader, RequestContext
from booktest.models import BookInfo, HeroInfo
# Create your views here.
def my_render(request):
# 1、加载模板文件
temp = loader.get_template('booktest/index.html')
# 2、定义模板上下文,给模板文件传递数据
context = RequestContext(request, {})
# 3、模板渲染,产生一个替换后的html内容
res = temp.render(context)
# 4、返回html内容给客户端
return res
def index(request):
"""显示图书信息"""
books = BookInfo.objects.all()
return render(request, 'booktest/index.html', {'books': books})
上面封装的函数index_no_render,相当于Django自带的render函数: render(request, 'booktest/show_books.html', {})
5)给模板文件传递数据
模板变量使用:{{ 模板变量名 }}
模板代码段: {% 代码段 %}
如 for循环:
{% for i in list %}
{{ i }}
{% endfor %}
三、Model属性及后端配置
1、配置mysql数据库
1)配置mysql
在项目的settings文件中配置如下
# settings中配置mysql:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'test7',
'USER': 'root',
'PASSWORD': '123456',
'HOST': '127.0.0.1',
'PORT': 3306
}
}
注:需要先在本地创建数据库:test7
配置完成后启动服务,会报错:
django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module: No module named 'MySQLdb'
需要安装操作mysql数据库的包,python3安装pymysql
安装完成后在项目的__init__中添加如下代码:
import pymysql
pymysql.install_as_MySQLdb()
这时启动服务成功,数据库中会生成对应表
2)重新进行迁移
要在数据库中生成模型类对应表,需要重新进行迁移。
a)删除应用下migrations目录下,之前生成的迁移文件
b)命令行执行命令
①生成迁移文件:python manage.py makemigrations
②生成表:python manage.py migrate
这时在数据库中会创建表如下:
2、案例
案例--在首页对图书类进行新增和删除操作。
具体效果图如下,点击新增时新增一本新的图书信息,点击删除时删除图书。新增和删除后都停留在当前页面。
1)创建新的模板文件:show_books.html,内容如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>图书信息展示</title>
</head>
<body>
<a href="/create">新增</a>
<ul>
{% for book in books %}
<li>
{{ book.b_title }} -- {{ book.b_pubdate }} -- <a href="/delete{{ book.id }}">删除</a>
</li>
{% endfor %}
</ul>
</body>
</html>
2)创建视图函数
首先创建一个新的视图函数show_books,展示图书信息。
点击页面的新增和删除,调用对应的新增和删除视图函数。具体函数内容如下:
def show_books(request):
# 展示图书信息
books = BookInfo.objects.all()
return render(request, 'booktest/show_books.html', {'books': books})
def create(request):
# 新增图书
b = BookInfo()
b.b_title = '流星蝴蝶剑'
b.b_pubdate = date(1999, 1, 1)
b.save()
return redirect("/show_books")
def delete(request, bid):
# 根据id删除图书
b = BookInfo.objects.get(id=bid)
b.delete()
return redirect("/show_books")
3)配置url
urlpatterns = [
url(r'^index$', views.index), # 首页
url(r'^show_books$', views.show_books), # 展示图书信息
url(r'^create$', views.create), # 新增图书信息
url(r'^delete(\d+)$', views.delete), # 删除图书信息,delete视图函数有一个参数bid,可以使用位置参数,匹配到数字后会当做参数传给delete
]
在页面即可新增和删除图书。
3、重定向
重定向,即在完成某个操作后,重定向到对应的页面。
重定向返回格式:return redirect('/index')
def delete(request, bid):
# 根据id删除图书
b = BookInfo.objects.get(id=bid)
b.delete()
return redirect("/show_books")
4、模型类的字段属性和选项
a)不能是python保留关键字;
b)不能有连续下划线,这是由Django的查询方式决定的;
c)定义属性时需要指定字段类型,通过字段类型的参数指定选项,语法如下:
属性名 = model.字段类型(选项)
字段类型中文文档:https://yiyibooks.cn/xx/django_182/ref/models/fields.html
5、查询
get BookInfo.objects.get(id=1) 返回且只能返回一条数据,返回值是一个对象,参数为查询条件
all BookInfo.objects.all() 返回模型类对应表的所有数据,返回值是一个QuerySet--查询集
filter 返回满足查询条件的数据,返回值是一个QuerySet--查询集
- exact BookInfo.objects.filter(id__exact=1)
- contains BookInfo.objects.filter(btitle__contains='天')
- endswith BookInfo.objects.filter(btitle__endswith='部')
- startswith BookInfo.objects.filter(btitle__startswith='天')
- isnull BookInfo.objects.filter(btitle__isnull=False)
- in BookInfo.objects.filter(id__in=[1,3,5])
- gt BookInfo.objects.filter(id__gt=1)
- lt BookInfo.objects.filter(id__lt=1)
- gte BookInfo.objects.filter(id__gte=1)
- lte BookInfo.objects.filter(id__lte=1)
- 日期查询__year __month __date(1991,1,1)
exclude 返回不满足查询条件的数据,返回值是一个QuerySet--查询集
- BookInfo.object.exclude(id=1)
order_by 对查询结果进行排序,返回值是一个QuerySet--查询集,参数中写排序的字段。默认升序,降序在参数前加一个负号:-
- 按照图书id进行排序,升序排序
- BookInfo.object.all().order_by('id')
- 按照图书阅读量进行降序排序
- BookInfo.objects.all().order_by('-bread')
聚合函数 aggregate 进行聚合操作,返回值是一个字典,进行聚合操作时需要先导入聚合函数
Q对象:from django.db.models import Q
- 查询id大于3且阅读量大于30的图书: BookInfo.objects.filter(id__gt=3, bread__gt=30) 等同:BookInfo.objects.filter(Q(id__gt=3)&Q(bread__gt=30))
- 查询id大于3或阅读量大于30的图书: BookInfo.objects.filter(Q(id__gt=3)|Q(bread__gt=30))
- 查询id不等于3的图书:BookInfo.objects.exclude(id=3) BookInfo.objects.filter(~Q(id=3))
F对象:from django.db.models import F
- 查询阅读量大于评论量的图书:BookInfo.objects.filter(bread__gt=F('bcomment'))
- 查询阅读量大于2倍评论量的图书: BookInfo.objects.filter(bread__gt=F('bcomment')*2)
- 聚合函数:from django.db.models import Sum, Count, Max, Min, Avg
- 查询所有图书的数量:BookInfo.objects.all().aggregate(Count('id')) # 返回一个字典{'id__count': 5}
- 查询所有图书的阅读量:BoonInfo.objects.all().aggregate(Sum('bread'))
count()函数 返回值是一个数字
- 统计所有图书数量:BookInfo.objects.all().count()
- 统计id大于3的图书数量:BoonInfo.objects.filter(id__gt=3).count()
对一个QuerySet继续调用上面的方法
C:\python\Django\test7>python manage.py shell
Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:54:40) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from booktest.models import BookInfo
>>> from booktest.models import HeroInfo
>>> BookInfo.objects.get(id=1)
<BookInfo: 天龙八部>
>>> BookInfo.objects.all()
[<BookInfo: 天龙八部>, <BookInfo: 神雕侠侣>, <BookInfo: 流星蝴蝶剑>]
>>> BookInfo.objects.filter(id__exact=1)
[<BookInfo: 天龙八部>]
>>> BookInfo.objects.filter(b_title__contains='天')
[<BookInfo: 天龙八部>]
>>> BookInfo.objects.filter(b_title__endswith='部')
[<BookInfo: 天龙八部>]
>>> BookInfo.objects.filter(b_title__startswith='流')
[<BookInfo: 流星蝴蝶剑>]
>>> BookInfo.objects.filter(b_title__isnull=False)
[<BookInfo: 天龙八部>, <BookInfo: 神雕侠侣>, <BookInfo: 流星蝴蝶剑>]
>>> HeroInfo.objects.filter(id__in=[1,3])
[<HeroInfo: 乔峰>, <HeroInfo: 小龙女>]
>>> HeroInfo.objects.filter(id__gt=1)
[<HeroInfo: 杨过>, <HeroInfo: 小龙女>, <HeroInfo: 段誉>, <HeroInfo: 扫地僧>]
>>> HeroInfo.objects.filter(id__lt=1)
[]
>>> HeroInfo.objects.filter(id__lt=2)
[<HeroInfo: 乔峰>]
>>> HeroInfo.objects.filter(id__lte=2)
[<HeroInfo: 乔峰>, <HeroInfo: 杨过>]
>>> HeroInfo.objects.filter(id__gte=2)
[<HeroInfo: 杨过>, <HeroInfo: 小龙女>, <HeroInfo: 段誉>, <HeroInfo: 扫地僧>]
>>> BookInfo.objects.filter(b_pubdate__year=1999)
[<BookInfo: 流星蝴蝶剑>]
>>> BookInfo.objects.filter(b_pubdate__month=1)
[<BookInfo: 天龙八部>, <BookInfo: 流星蝴蝶剑>]
>>> BookInfo.objects.filter(b_pubdate__month=2)
[]
>>> BookInfo.objects.filter(b_pubdate__day=2)
[]
>>> BookInfo.objects.filter(b_pubdate__day=1)
[<BookInfo: 天龙八部>, <BookInfo: 神雕侠侣>, <BookInfo: 流星蝴蝶剑>]
>>> BookInfo.objects.exclude(id=1)
[<BookInfo: 神雕侠侣>, <BookInfo: 流星蝴蝶剑>]
>>> BookInfo.objects.all().order_by('id')
[<BookInfo: 天龙八部>, <BookInfo: 神雕侠侣>, <BookInfo: 流星蝴蝶剑>]
>>> BookInfo.objects.all().order_by('-id')
[<BookInfo: 流星蝴蝶剑>, <BookInfo: 神雕侠侣>, <BookInfo: 天龙八部>]
查询集:
a)惰性查询 只有实际使用查询集中的数据的时候才会发生对数据库的真正查询
b)缓存 当第一次使用同一个查询集时,会发生实际数据库的查询,然后把结果缓存起来,之后再使用这个查询集时,使用的是缓存中的结果
查询集切片
对查询集切片返回一个新的查询集
取出查询集第一条数据的两种方式:
b[0] 如果b[0]不存在,会抛出IndexError异常
b[0:1].get() 如果b[0:1].get()不存在,会抛出DoseNotExist异常
>>> b = BookInfo.objects.all()
>>> b[0]
<BookInfo: 天龙八部>
>>> b[0:1].get()
<BookInfo: 天龙八部>
>>> b[3]
Traceback (most recent call last):
File "C:\Python36\lib\site-packages\django\core\management\commands\shell.py", line 69, in handle
self.run_shell(shell=options['interface'])
File "C:\Python36\lib\site-packages\django\core\management\commands\shell.py", line 61, in run_shell
raise ImportError
ImportError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "C:\Python36\lib\site-packages\django\db\models\query.py", line 201, in __getitem__
return list(qs)[0]
IndexError: list index out of range
>>> b[3:4].get()
Traceback (most recent call last):
File "C:\Python36\lib\site-packages\django\core\management\commands\shell.py", line 69, in handle
self.run_shell(shell=options['interface'])
File "C:\Python36\lib\site-packages\django\core\management\commands\shell.py", line 61, in run_shell
raise ImportError
ImportError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "C:\Python36\lib\site-packages\django\db\models\query.py", line 334, in get
self.model._meta.object_name
booktest.models.DoesNotExist: BookInfo matching query does not exist.
>>>
6、配置mysql日志
在 MySQL的安装目录下的 my.ini 配置文件的 [mysqld] 下增加以下信息,日志路径可修改。
[mysqld]
port = 3306
basedir=C:\\software\\mysql-5.7.25-winx64
datadir=C:\\software\\mysql-5.7.25-winx64\\data
max_connections=200
character-set-server=utf8
default-storage-engine=INNODB
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
# 开启日志
log_bin=C:\\software\\mysql-5.7.25-winx64\\logs\\mysql-bin.log
log_error=C:\\software\\mysql-5.7.25-winx64\logs\\mysql.err
long_query_time=0.1
slow_query_log=ON
slow_query_log_file=C:\\software\\mysql-5.7.25-winx64\\logs\\mysql-slow.log
general_log=ON
general_log_file=C:\\software\\mysql-5.7.25-winx64\\logs\\logmysql.log
server-id=1
[mysql]
default-character-set=utf8
在执行查询操作时,mysql的日志文件中会对应显示Django对mysql的真实sql请求。
>>> HeroInfo.objects.filter(id__gte=3)
[<HeroInfo: 小龙女>, <HeroInfo: 段誉>, <HeroInfo: 扫地僧>]
对应日志:
SELECT `booktest_heroinfo`.`id`, `booktest_heroinfo`.`h_name`, `booktest_heroinfo`.`h_gender`, `booktest_heroinfo`.`h_comment`, `booktest_heroinfo`.`h_book_id` FROM `booktest_heroinfo` WHERE `booktest_heroinfo`.`id` >= 3 LIMIT 21
四、模型关系及相关操作
1、模型类关系
一对多 例:图书类-英雄类 models.ForeignKey() 定义在多类中
多对多 models.ManyToManyField('另一个类名') 定义在哪个类中都可以
一对一 models.OneToOneField('另一个类名') 定义在哪个类中都可以
2、关联查询(一对多)
由一类的对象查询多类的时候: 一类的对象.多类名小写_set.all(),例:b = BookInfo.objects.get(id=1) b.heroinfo_set.all()
由多类的对象查询一类的时候: 多类的对象.关联属性 # 查询多类的对象对应一类的对象 h = HeroInfo.objects.get(id=1) h.hbook
由多类的对象查询一类对象id的时候: 多类的对象.关联属性__id
在一对多关系中,一对应的类我们叫他一类,多对应的那个类叫多类,把多类中定义的建立关联的类属性叫: 关联属性
例:
查询id为1的图书关联的英雄信息(查询的是哪个类对应表的数据,就使用那个类进行查询): HeroInfo.objects.filter(hbook__id=1)
查询id为1的英雄关联的的图书信息: BookInfo.objects.filter(heroinfo__id=1)
查询图书信息,要求图书中英雄的描述包含“八”: BookInfo.objects.filter(heroinfo__hcomment__contains='八')
查询图书信息,要求图书中英雄的id大于3: BookInfo.objects.filter(heroinfo__id__gt=3)
查询书名为“天龙八部”的所有英雄: HeroInfo.objects.filter(hbook__btitle='天龙八部')
通过多类的条件查询一类的数据:
一类名.objects.filter(多类名小写__查询多类属性名称__条件名称)
通过一类的条件查询多类的数据:
多类名.objects.filter(关联属性__查询一类属性名称__条件名称)
>>> HeroInfo.objects.filter(h_book__id=1)
[<HeroInfo: 乔峰>, <HeroInfo: 段誉>, <HeroInfo: 扫地僧>]
>>> BookInfo.objects.filter(heroinfo__id=1)
[<BookInfo: 天龙八部>]
>>> BookInfo.objects.filter(heroinfo__h_comment__contains='龙')
[<BookInfo: 天龙八部>]
>>> BookInfo.objects.filter(heroinfo__id__gt=3)
[<BookInfo: 天龙八部>, <BookInfo: 天龙八部>]
>>> HeroInfo.objects.filter(h_book__b_title='天龙八部')
[<HeroInfo: 乔峰>, <HeroInfo: 段誉>, <HeroInfo: 扫地僧>]
>>>
查询相关函数总结:
get: 返回且只返回一条数据,返回值是一个对象,参数可以写查询条件
all: 返回模型类对应表的所有数据,返回值是一个QuerySet
filter: 返回满足查询条件的数据,返回值是一个QuerySet,参数可以写查询条件
exclude: 返回不满足查询条件的数据,返回值是一个QuerySet,参数可以写查询条件
order_by: 对返回结果进行排序,返回值是一个QuerySet,参数可以写查询条件
注意:
1、get、filter、exclude函数中可以写查询条件,如果传多个参数,参数之间代表且的关系
2、all、filter、exclude、order_by返回是一个查询集,可以继续对查询集进行查询
from django.db.models import F,Q,Sum,Count,Avg,Max,Min
F对象用于类属性之间的比较
Q对象用于条件之间的逻辑关系
aggregate:进行聚合操作,返回值是一个字典,进行聚合的时候需要先导入聚合类
count:返回结果集中数据的数量,返回值是一个数字
注意:
对一个QuerySet实例对象,可以继续调用上面的所有函数
通过模型类进行关联查询时,要查询哪个表中的数据,就用哪个表对应的类来查。
写关联查询条件的时候,如果类中没有关联属性,条件写对应类的小写名称,如果类中有关联属性,就写关联属性
3、插入、更新和删除
b = BookInfo()
b.btitle = '天龙八部'
b.save() # 插入和更新
b.delete() # 删除
4、自关联
自关联是一种特殊的一对多的关系。
案例:显示深圳市的上级地区和下级地区。
1)创建模型类
class AreaInfo(models.Model):
"""自关联-地区类"""
aname = models.CharField(max_length=56)
aparent = models.ForeignKey('self', null=True, blank=True)
- 生成迁移文件:python manage.py makemigrations
- 生成表:python manage.py migrate
2)创建模板文件 areas.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>地区案例</title>
</head>
<body>
<h1>深圳市上级地区</h1><br/>
<ul>
<li>
{{ area_parent.aname }} <br/>
</li>
</ul>
<h1>深圳市</h1><br/>
<ul>
<li>
{{ area_self.aname }} <br/>
</li>
</ul>
<h1>深圳市下级地区</h1><br/>
<ul>
{% for area in area_children %}
<li>
{{ area.aname }} <br/>
</li>
{% endfor %}
</ul>
</body>
</html>
3)定义视图函数
def areas(request):
# 显示深圳市的上级地区和下级地区
# 1、获取深圳市的信息
area_self = AreaInfo.objects.get(aname='深圳市')
# 2、获取深圳市上级地区
area_parent = area_self.aparent
# 3、获取深圳市下级地区
area_children = area_self.areainfo_set.all
return render(request, 'booktest/areas.html', {"area_self": area_self, "area_parent": area_parent, "area_children": area_children})
4)配置url
urlpatterns = [
url(r'^index$', views.index), # 首页
url(r'^show_books$', views.show_books), # 展示图书信息
url(r'^create$', views.create), # 新增图书信息
url(r'^delete(\d+)$', views.delete), # 删除图书信息,delete视图函数有一个参数bid,可以使用位置参数,匹配到数字后会当做参数传给delete
url(r'^areas$', views.areas), # 显示深圳市的上级地区和下级地区
]
5)启动服务,访问http://127.0.0.1:8000/areas,效果如下:
自关联查询:
area = AreaInfo.objects.get(aname='深圳市')
查询父级自关联:area.aparent
查询子级自关联:area.areainfo_set.all()
5、管理器对象
BookInfo.objects.all()中的objects是Django自动生成的管理器对象,通过这个管理器可以实现对数据的查询
objects是一个models.Manager类的一个对象。如果我们自己自定义一个管理器,Django不再自动生成默认的objects管理器。
1)自定义一个管理器类,这个类继承models.Manager
2)再在具体的模型类中定义一个自定义管理器的对象
自定义管理器的应用场景:
a)改变查询的结果集
比如调用BookInfo.objects.all() 返回没有删除的数据
b)添加额外的方法
管理器中定义一个方法帮我们操作模型类对应的数据表
self.model 就可以创建一个跟自定义管理器对应的模型类对象
class BookInfoManager(models.Manager):
"""图书管理器类"""
# 1、改变查询的结果集
def all(self):
# 1、调用父类的all查询所有数据
books = super().all()
# 2、对数据进行过滤
books = books.filter(isDeleted=False)
# 3、返回数据
return books
# 2、添加额外的方法
def create_book(self, b_title, b_pubdate):
# 获取self所在的模型类
model_class = self.model
book = model_class()
book.b_title= b_title
book.b_pubdate = b_pubdate
book.save()
return book
Django自动生成的管理器对象中也有一个create函数,可以直接调用:BookInfo.objects.create(b_title='test2', b_pubdate='1991-01-01')
BookInfo继承models.Model ,BookInfoManager继承 models.Manager ,objects = BookInfoManager(),objects可以调用BookInfoManager中定义的函数
6、元选项
Django默认生成模型类对应表名:应用名小写_模型类名小写 如:booktest_bookinfo
元选项:
需要在模型类中定义一个元类Meta,在类里面定义一个类属性db_table就可以指定表名。
class AreaInfo(models.Model):
"""自关联-地区类"""
aname = models.CharField(max_length=56)
aparent = models.ForeignKey('self', null=True, blank=True)
class Meta:
db_table = 'area_info' # 指定模型类对应的表名,模型类修改名称也不会改变表名称
五、登录案例
1、视图
视图的功能
接收请求,处理请求,与M和T进行交互,返回应答
返回html内容HTTPResponse,或者redirect,还可能是JsonResponse
2、视图函数的使用
1)定义视图函数
request参数必须有,request是一个HTTPRequest类型的对象。参数名可以变化,但不要更改。
2)配置url
建立url和视图函数之间的对应关系
a)在项目的urls文件中配置应用的urls文件,在应用的urls文件中配置url和视图函数的对应关系
b)url配置项定义在urlpatterns列表中,每个元素就是一个配置项,每一个配置项都调用一个视图函数
3)url匹配的过程
域名和参数都不参与匹配。例:http:127.0.0.1:8001/aindex?a=1&b=2,去掉域名和参数,剩下:aindex。 aindex先到项目的urls文件中进行匹配,匹配成功后再继续到对应的应用的urls文件中进行匹配,如果有匹配到,就调用对应的视图函数,给浏览器返回对应的html内容;如果匹配失败,返回404
4)错误视图
404:找不到请求页面。关闭调试模式后(settings 中 DEBUG=True修改为False),默认会显示一个标准的错误页面,如果要自定义错误页面,可以在templates下新建一个404.html
a)url没有配置
b)url配置错误
500:服务器端错误
a)视图错误
网站开发完成后需要关闭调试模式,在项目的settings文件中:
# DEBUG = True
# ALLOWED_HOSTS = []
DEBUG = False
ALLOWED_HOSTS = ['*']
3、捕获url参数
在进行url匹配时,把所需要捕获的部分设置成一个正则表达式组,这样Django框架会自动将匹配成功后的相应组的内容作为参数传递给视图函数。
1)位置参数:
参数名可以任意指定
urls中配置:
url(r'^delete(\d+)$', views.delete) # urls文件中,(\d+)匹配到的数字,当做参数传递给delete函数中的bid
视图函数:
def delete(request, bid):
# 根据id删除图书
b = BookInfo.objects.get(id=bid)
b.delete()
return redirect('/index')
2)关键字参数:
在位置参数的基础上给正则表达式组命名即可。
?P<组名>
urls中配置:
url(r'^delete(?P<bid>\d+)$', views.delete) # urls文件中,(\d+)匹配到的数字,当做参数传递给delete函数中的bid
视图函数:
def delete(request, bid):
b = BookInfo.objects.get(id=bid)
b.delete()
return redirect('/index')
关键字参数,视图函数的参数必须和正则表达式组名一致
4、登录案例
1)创建模板文件 login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form method="post" action="/login_check">
{% csrf_token %}
请输入用户名: <input type="text" name="account"><br/>
请输入密码: <input type="password" name="password"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
2)定义视图函数
定义一个登录的视图函数,返回登录页面;还需要定义一个登录后校验账号密码的函数。
def login(request):
# 登录页
return render(request, 'booktest/login.html')
def login_check(request):
# 登录校验
# 1、获取用户名和密码 request.POST保存post方式提交的参数 request.GET保存的get方式提交的参数
account = request.POST.get('account')
password = request.POST.get('password')
# print(type(request.POST))
# type(request.POST): <class 'django.http.request.QueryDict'>
# 2、判断用户名密码是否正确
if account == 'test' and password == '123':
# 3、正确跳转到首页,
return redirect('/index')
else:
# 错误返回登录页
return redirect('/login')
3)配置url
urlpatterns = [
url(r'^index$', views.index), # 首页
url(r'^show_books$', views.show_books), # 展示图书信息
url(r'^create$', views.create), # 新增图书信息
url(r'^delete(\d+)$', views.delete), # 删除图书信息,delete视图函数有一个参数bid,可以使用位置参数,匹配到数字后会当做参数传给delete
url(r'^areas$', views.areas), # 显示深圳市的上级地区和下级地区
url(r'^login$', views.login), # 登录页
url(r'^login_check$', views.login_check), # 登录校验
]
4)启动服务,访问:127.0.0.1:8000/login
5、ajax登录案例
异步的JavaScript,在不重新加载页面的情况下,对页面进行局部请求
<script src="/static/js/jquery-1.12.4.min.js"></script>
<script>
$(function () {
//绑定btnAjax的点击事件
$('#btnLogin').click(function () {
username = $('#username').val()
password = $('#password').val()
$.ajax({
'url': '/ajax_handle',
'type': 'post',
'dataType': 'json',
'data': {'username': username, 'password': password}
}).success(function (data) {
// 对data进行处理
if (data.res == 0){
$('#errmsg').show().html("用户名或密码错误")
}
else{
location.href = '/index'
}
})
})
}
)
</script>
六、cookie与session
1、cookie
cookie是由服务器生成,存储在浏览器的一小段文本信息。
cookie的特点:
- 以键值对的方式进行存储
- 通过浏览器访问一个网站时,会将浏览器存储的和网站相关的所有cookie信息发给该网站的服务器
- cookie是基于域名安全的
- cookie是有有效期的,如果不指定,关闭浏览器cookie就会过期
设置cookie记住用户名。
1)模板文件login.html添加一行:勾选记住用户名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form method="post" action="/login_check">
{% csrf_token %}
请输入用户名: <input type="text" name="account"><br/>
请输入密码: <input type="password" name="password"><br/>
<input type="checkbox" name="remember">记住用户名<br/>
<input type="submit" value="提交">
</form>
</body>
</html>
2)登录校验时增加判断
def login_check(request):
# 登录校验
# 1、获取用户名和密码 request.POST保存post方式提交的参数 request.GET保存的get方式提交的参数
account = request.POST.get('account')
password = request.POST.get('password')
remember = request.POST.get('remember')
print(remember)
# print(type(request.POST))
# type(request.POST): <class 'django.http.request.QueryDict'>
# 2、判断用户名密码是否正确
if account == 'test' and password == '123':
# 3、正确跳转到首页
response = redirect('/index')
if remember == 'on':
# 设置cookie account,过期时间为一周
response.set_cookie('account', account, max_age=7*24*3600)
return response
else:
# 错误返回登录页
return redirect('/login')
3)登录时输入正确账号和密码,勾选记住密码,设置cookie信息如下
4)再次访问登录页,显示账号信息
需要先获取cookie中的账号,将账号传递给模板文件显示
def login(request):
# 登录页
# 如果请求的页面中有我们之前设置的cookie信息,即account,获取account账号并传递给模板文件,否则返回空
if 'account' in request.COOKIES:
account = request.COOKIES.get('account')
else:
account = ''
return render(request, 'booktest/login.html', {"account": account})
模板文件接收并显示账号,对应模板文件中修改内容
请输入用户名: <input type="text" name="account" value="{{ account }}"><br/>
再次访问登录页,默认显示账号信息
2、session
session存储在服务器端。
session特点:
- session是以键值对的形式存储的
- session依赖于cookie,唯一的标识码保存在sessionid # Django中session存放的表为django_session,数据表中session_key,就是请求浏览器时返回cookie中的sessionid
- session过期时间默认为14天,设置时参数值为秒
记住用户状态。
1)登录校验视图函数,用户登录成功后添加一个session
def login_check(request):
# 登录校验
# 1、获取用户名和密码 request.POST保存post方式提交的参数 request.GET保存的get方式提交的参数
account = request.POST.get('account')
password = request.POST.get('password')
remember = request.POST.get('remember')
print(remember)
# print(type(request.POST))
# type(request.POST): <class 'django.http.request.QueryDict'>
# 2、判断用户名密码是否正确
if account == 'test' and password == '123':
# 3、正确跳转到首页
response = redirect('/index')
if remember == 'on':
# 设置cookie account,过期时间为一周
response.set_cookie('account', account, max_age=7*24*3600)
# 用户登录成功后,记住用户登录状态
request.session['isLogin'] = True
return response
else:
# 错误返回登录页
return redirect('/login')
2)登录视图中,判断用户是否已登录,即获取session中是否有我们设置的值,如果有,为登录状态,直接跳转到首页,否则执行之前的步骤。
def login(request):
# 登录页
# 判读用户是否登录
if request.session.has_key('isLogin'):
# 用户已登录,跳转到首页
return redirect('/index')
else:
# 用户未登录
# 如果请求的页面中有我们之前设置的cookie信息,即account,获取account账号并传递给模板文件,否则返回空
if 'account' in request.COOKIES:
account = request.COOKIES.get('account')
else:
account = ''
return render(request, 'booktest/login.html', {"account": account})
3)刷新登录页,输入正确用户名密码,登录成功后,再次访问登录页,会直接跳转到首页。
3、对象及方法
cookie:
# cookie保存在request对象的COOKIE属性中 request.COOKIIE
def set_cookie(request):
# 设置cookie
response = HttpResponse('设置cookie')
response.set_cookie('num', 1, max_age=1 * 24 * 3600)
return response
def get_cookie(request):
# 获取cookie
num = request.COOKIES['num']
return HttpResponse(num)
session:
通过Httpresponse对象的request对象的session属性进行会话的操作。
request.session
1)以键值对的形式写值
request.session['键'] = 值
2)根据键读取值
request.session.get('键', 默认值)
值 = request.session['键']
3)删除session中键值对部分
request.session.clear()
4)删除整条session数据
request.session.flush()
5)删除session中指定的键对应的值
del request.session['键']
6)设置会话超时时间,默认为14天,参数值为秒,如:1*24*3600为一天86400秒
request.session.set_expiry(1*24*3600)
4、cookie和session的应用场景
cookie:记住用户名。用于安全性不高的地方
session:涉及到安全性比较高的数据。密码
七、模板 templates
1、模板的功能
模板文件不仅仅是一个html文件,模板文件包含两部分内容:
- 静态内容:css、js、html
- 动态内容:用于动态去产生一些网页内容,通过模板语言来生成。
2、模板文件的使用
通常是在视图函数中使用模板产生的html内容返回给客户端。
- 加载模板文件,获取模板文件的内容,产生一个模板对象
- 定义模板上下文,给模板文件产生数据
- 模板渲染产生html页面内容,用传递的数据替换相应的变量,产生一个替换后的标准的html内容
- 返回html内容给客户端
3、模板文件加载顺序
1)首先去配置的模板目录下面找模板文件,模板目录可以定义多个路径:'DIRS': [os.path.join(BASE_DIR, 'templates')]
2)去settings文件中的INSTALL_APP里面的每个应用去找模板文件,前提是应用中必须有templates文件夹。其中admin和auth是有templates文件夹的
4、模板语言
模板语音简称为DTL(Django Template Language)
4.1 模板变量
模板变量名是由数字、字母、下划线和点组成的,不能以下划线开头。
在模板文件中使用模板变量:{{ 模板变量 }}
模板变量的解析顺序,例如:{{ book.btitle }}
1)首先把book当成一个字典,将btitle当做字典的键名,去获取值:book['btitle']
2)把book当成一个对象,把btitle当成属性,进行取值:book.btitle
3)把book当成一个对象,把btitle当成对象的方法,进行调用取值:book.btitle
例如:{{ book.0 }}
1)首先把book当成一个字典,将0当做字典的键名,去获取值:book['0']
2)把book当成一个列表,把0当成列表下标,进行取值:book[0]
如果获取失败,产生为空填充模板变量
4.2 模板标签
{% 代码段 %}
for循环:
{% for i in 列表 %}
# 列表不能为空
{% empty %} # 列表为空时执行
{% endfor %} 结束for循环
可以通过{{ forloop.counter }}得到for循环遍历到了了几次。
{% if 条件 %}
{% elif 条件 %}
{% else 条件 %}
{% endif 条件 %}
关系比较操作符:> < >= <= == != # 比较操作符进行比较操作时,比较操作符两边都必须要至少一个空格
逻辑运算:and or not
4.3 过滤器
过滤器用于带模板变量进行操作。
date:改变日期的显示格式 {{ book.bpub_date|date:'Y m d'}} # 格式化显示时间
length:求长度。字符串、列表、元组、字段 {{ book.btitle|length }} # 显示长度
default:设置模板变量默认值
格式:模板变量|过滤器:参数
内置标签和过滤器中文资料:https://yiyibooks.cn/xx/django_182/ref/templates/builtins.html
自定义过滤器:
# 1、自定义过滤器实际就是python函数
# 2、在应用目录下创建templatetags包,文件夹必须为templatetags
# 3、在templatetags下创建自定义过滤器文件名,文件名任意
# 4、导入所需类:from django.template import Library, 创建对象:register = Library()
# 5、定义自定义过滤器(python函数),使用对象装饰自定义函数,自定义过滤器最少一个参数,最多两个参数
# 自定义过滤器
# 过滤器就是python函数
from django.template import Library
# 创建一个Library类的对象
register = Library()
@register.filter
def mod(num):
# 自定义过滤器函数
# 判断num是否为偶数
if num % 2 == 0:
return num
# 6、在对应的模板文件中加载自定义的模板文件名 {% load filters %}
# 7、在模板文件中使用
<!DOCTYPE html>
<html lang="en">
{% load filters %}
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>{{ book.title }}</h1>
书信息如下:<br/>
<ul>
{% for book in books %}
<li>
{# 只显示书的id为偶数的信息 #}
{% if book.id|mod %}
{{ book.b_title }} -- {{ book.b_pubdate|date:'Y m d' }} {# 使用过滤器date,格式化显示日期 #}
{% endif %}
</li>
{% endfor %}
</ul>
</body>
</html>
4.4 模板注释
单行注释:{# 注释内容 #}
多行注释:
{% comment %}
注释内容
{% endcomment %}
模板注释的内容在浏览器是看不到的
4.5 模板继承
extends 继承模块
block 快名 在模板文件中预留给子模板的,子模板可以在预留块写自己的内容,也可以继承父模块的内容。
在父模块里可以定义块,使用标签:
{% block 块名 %}
块中间可以写内容,也可以不写
{% endblock %}
子模板继承父模板之后,可以重写父模板中块的内容。
继承格式:
{% extends 父模板文件路径 %}
{% block 块名 %}
{{ block.super }} # 获取父模板中块的内容
{% endblock %}
格式如下:
{% extends 'booktest/base.html' %}
{% block title %}child1标题{% endblock title %}
{% block b1 %}
<h1>child1 b11111</h1>
{% endblock b1 %}
{% block b2 %}
{{ block.super }}
<h1>child1 b222222222</h1>
{% endblock b2 %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}父模板标题{% endblock title %}</title>
</head>
<body>
<h1>顶部标题</h1>
{% block b1 %}
<p>这是父b1的内容</p>
{% endblock b1 %}
{% block b2 %}
<p>这是父b2的内容</p>
{% endblock b2 %}
<h1>底部标题</h1>
</body>
</html>
{% extends 'booktest/base.html' %}
{% block title %}child1标题{% endblock title %}
{% block b1 %}
<h1>child1 b11111</h1>
{% endblock b1 %}
{% block b2 %}
{{ block.super }}
<h1>child1 b222222222</h1>
{% endblock b2 %}
html转义:
在模板上下文中的HTML标记默认是会转义的。
大于号> 转义为>
小于号< 转义为<
单引号'' 转义为&39;
双引号"" 转义为"
与符号& 转义为&
关闭模板上下文字符串的转义,可以使用{{ 模板变量|safe }}
也可以使用,off关闭转义,on打开转义:
{% autoescape off %}
模板语音代码
{% endautoescape %}
模板硬编码中的字符串默认不会经过转义,如果需要转义,那需要手动进行转义
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
未转义的:<br/>
{{ content }} <br/>
转义的:<br/>
{{ content|safe }} <br/>
模板硬编码中的字符串默认不会经过转义 <br/>
{{ test|default:'<h1>hhhhhh</h1>' }} <br/>
手动进行转义 <br/>
{{ test|default:'<h1>hhhhhh</h1>' }} <br/>
</body>
</html>
实际显示页面内容
八、基于模板登录案例
1. csrf攻击
被攻击的条件:
1)登录正常网站之后,你的浏览器保存sessionId,你没有退出
2)你访问了其他页面,并点击其他页面上的按钮
Django防csrf攻击:
1)默认打开csrf中间件
2)表单post提交数据时,加上% {% csrf_token %}
防御原理:
1)渲染模板文件时,会在页面生成一个name='csrfmiddlewaretoken'的input隐藏标签:
<input type='hidden' name='csrfmiddlewaretoken' value='XNbxSNTv3Cs8ax29GxShq2cqvY50K5yd' />
2)服务器交给浏览器保存一个名字为csrftoken的cookie信息
3)提交表单时,两个值都会发给服务器,服务器进行对比,值相同则csrf验证通过,否则不通过。
2. 验证码
from PIL import Image, ImageDraw, ImageFont, ImageFilter
from django.utils.six import BytesIO
import random
def verify_code(request):
# 定义变量,用于画面的背景色、宽、高
bgcolor = (random.randrange(20, 100), random.randrange(20, 100), 255)
width = 100
height = 25
# 创建画面对象
im = Image.new('RGB', (width, height), bgcolor)
# 创建画笔对象
draw = ImageDraw.Draw(im)
# 调用画笔的point()函数绘制噪点
for i in range(0, 100):
xy = (random.randrange(0, width), random.randrange(0, height))
fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
draw.point(xy, fill)
# 定义验证码的备选值
verify_value_str = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0'
# 随机选取4个值作为验证码
random_str = ''
for i in range(4):
random_str += verify_value_str[random.randrange(len(verify_value_str))]
# 构造字体对象
font_ = ImageFont.truetype(r'C:\Windows\Fonts\Arial.ttf', 20)
fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
# 绘制4个字
draw.text((5, 2), random_str[0], font=font_, fill=fontcolor)
draw.text((25, 2), random_str[1], font=font_, fill=fontcolor)
draw.text((50, 2), random_str[2], font=font_, fill=fontcolor)
draw.text((75, 2), random_str[3], font=font_, fill=fontcolor)
# 释放画笔
del draw
# 存入session,用于验证
request.session['verifycode'] = random_str
# 内存文件操作
buf = BytesIO()
# 将图片保存到内存中,保存类型为png
im.save(buf, 'png')
# 将内存中的图片数据返回给客户端,MIME类型为图片png
return HttpResponse(buf.getvalue(), 'image/png')
3. 反向解析
当某一个url配置的地址发生变化时,页面上使用反向解析生成地址的位置不需要重新进行配置。
根据url正则表达式的配置动态生成url。
在项目的urls文件中设置应用的urls时指定namespace
项目urls配置:
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^', include('booktest.urls', namespace='booktest')), # 设置命名空间 namespace
]
应用urls配置:
urlpatterns = [
url(r'^index1$', views.index, name='index'), # 反向解析
url(r'^show_args/(\d+)/(\d+)$', views.show_args, name='show_args'), # 反向解析,位置参数
url(r'^show_kwargs/(?P<c>\d+)/(?P<d>\d+)$', views.show_kwargs, name='show_kwargs'), # 反向解析,关键字参数
]
在模板文件中使用时,格式如下:
{% 'namespace名: name名' %},如:<a href="{% url 'booktest:index' %}">可点击</a>
带位置参数:
{% 'namespace名: name名' 参数 %},如:<a href="{% url 'booktest:show_args' 1 2 %}">动态生成位置参数:</a><br/>
关键字参数:
{% 'namespace名: name名' 关键字参数 %},如:<a href="{% url 'booktest:show_kwargs' c=3 d=4 %}">动态生成位置参数:</a><br/>
在重定向的时候使用反向解析:
from django.core.urlresolvers import reverse
无参数:
reverse('namespace名称: name名')
有位置参数:
reverse('namespace名称: name名', args=位置参数元组)
有关键字参数:
reverse('namespace名称: name名', kwargs=关键字参数字典)
def test_url_redirect(request):
# 重定向到index
# url = reverse('booktest:index')
# 重定向到show_args/1/2
# url = reverse('booktest:show_args', args=(1, 2))
# 重定向到show_kwargs/3/4
url = reverse('booktest:show_kwargs', kwargs={'c': 3, 'd': 4})
return redirect(url)
九、django功能-静态文件、中间件、后台
1、静态文件
在网页上使用的css、js和图片叫做静态文件。
1)新建静态文件夹
在项目目录下,即应用同级目录下创建static目录,目录下创建css、js、images目录
2)配置静态文件所在的物理目录,项目settings配置文件:
# 设置访问静态文件对应的url地址
STATIC_URL = '/static/'
# 设置静态文件存放的物理路径
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
settings.STATICFILES_FINDERS
('django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder')
3)动态获取STATIC_URL,拼接静态文件url路径:
{% load staticfiles %}
<img src="{% static 'images/meinv.jpg' %}">
2、中间件
中间件函数是Django框架给我们预留的函数接口,让我们可以干预请求和应答的过程。
2.1 获取浏览器端的ip地址
使用request对象的META属性,request.META["REMOTE_ADDR"]
2.2 使用中间件
1)在应用目录下创建middlerware.py文件
2)定义中间件类
from django.http import HttpResponse
class BlockIPsMiddleWare(object):
"""中间件类"""
BlockIPs = ['10.11.18.139']
def process_view(self, request, view_func, *view_args, **view_kwargs):
"""中间件函数"""
if request.META['REMOTE_ADDR'] in BlockIPsMiddleWare.BlockIPs:
print(request.META)
return HttpResponse("<h1>Forbidden</h1>")
class TestMiddleWare(object):
def __init__(self):
"""服务器重启之后,接收第一个请求时调用一次"""
print("---init---")
def process_request(self, request):
"""
1、产生request对象后,url匹配之前调用
2、如果这些函数都没有返回值,即返回值为None,继续往下调用函数;任意函数有返回值,直接调用process_response,其他函数都不再调用
"""
print('---process_request---')
return HttpResponse("终止后续函数调用")
def process_view(self, view_func, *view_args, **view_kwargs):
"""url匹配后,视图函数调用之前调用"""
print('---process_view---')
def process_response(self, request, response):
"""视图函数调用之后,内容返回浏览器之前"""
print('------process_response------')
return response
class ExceptionTest1MiddleWare(object):
"""视图函数发生异常时调用"""
def process_exception(self, request, exception):
print('----process_exception1----')
class ExceptionTest2MiddleWare(object):
"""视图函数发生异常时调用"""
def process_exception(self, request, exception):
print('----process_exception2----')
print(exception)
在类中定义中间件预留函数:
__init__:服务器重启后,第一个请求时调用一次
process_request:产生request对象后,url匹配之前调用
process_view:url匹配后,视图函数调用之前调用
process_response:视图函数调用后,返回给浏览器内容前调用
process_exception:视图函数发生异常时调用。如果注册的多个中间件函数中包好process_exception,调用的顺序和注册的顺序是相反的。
3)注册中间件类
在项目的settings文件中注册中间件类。
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
# 'booktest.middleware.TestMiddleWare',
'booktest.middleware.ExceptionTest1MiddleWare',
'booktest.middleware.ExceptionTest2MiddleWare',
)
3.后台管理
3.1 使用
1)本地化,在settings中设置语音和时区
2)创建超级用户
3)注册模型类
4)自定义管理页面
3.2 模型管理类相关属性
列表相关的选项
页大小
每页中显示多少条数据,默认每页显示100,属性如下:
list_per_page = 100
1)打开booktest/admin文件,修改AreaInfoAdmin如下:
class AreaInfoAdmin(admin.ModelAdmin):
"""地区模型管理类"""
list_display = ['id', 'aname', 'aparent']
# list_per_page = 100 # 默认每页显示100
list_per_page = 2
访问admin管理页,查看地区管理信息如下。
2)在浏览器查看列表页面,会显示每页数据
“操作选项”的位置
a)顶部显示的属性,设置为True在顶部显示,设置为False不在顶部显示,默认为True
b)底部显示的属性,设置为True在底部显示,设置为False不在底部显示,默认为False
class AreaInfoAdmin(admin.ModelAdmin):
"""地区模型管理类"""
list_display = ['id', 'aname', 'aparent']
# list_per_page = 100 # 默认每页显示100
list_per_page = 2
actions_on_top = False # 顶部显示的属性,默认为True:顶部显示,设置为False不在顶部显示
actions_on_bottom = True # 底部显示的属性,默认为False:不在底部显示,设置为True在底部显示
页面显示:
3)方法列是不能排序的,如果需要排序需要为方法列指定排序依据。
模型类中定义的方法也可以显示在管理页列表中,如在AreaInfo类添加一个方法如下:
class AreaInfo(models.Model):
"""自关联-地区类"""
aname = models.CharField(max_length=56)
aparent = models.ForeignKey('self', null=True, blank=True)
def __str__(self):
return self.aname
def parent_area(self):
# 添加方法,显示父级地区信息
return self.aparent
在admin管理模型类中添加方法名
class AreaInfoAdmin(admin.ModelAdmin):
"""地区模型管理类"""
list_display = ['id', 'aname', 'aparent', 'parent_area'] # 添加方法名
# list_per_page = 100 # 默认每页显示100
list_per_page = 2
actions_on_top = False # 顶部显示的属性,默认为True:顶部显示,设置为False不在顶部显示
actions_on_bottom = True # 底部显示的属性,默认为False:不在底部显示,设置为True在底部显示
添加方法在页面显示如下:
方法列排序
class AreaInfo(models.Model):
"""自关联-地区类"""
aname = models.CharField(max_length=56)
aparent = models.ForeignKey('self', null=True, blank=True)
def __str__(self):
return self.aname
def parent_area(self):
# 显示父级地区信息
return self.aparent
parent_area.admin_order_field = 'aname' # 方法列排序,指定排序依据为aname
添加如上方法,点击方法列即可进行排序。
4)列标题默认为属性或方法的名称,可以通过属性设置。需要先将模型字段封装成方法,再对这个方法使用这个属性,模型字段不能直接使用这个属性:short_description = '列标题'
class AreaInfo(models.Model):
"""自关联-地区类"""
aname = models.CharField(max_length=56)
aparent = models.ForeignKey('self', null=True, blank=True)
def __str__(self):
return self.aname
def parent_area(self):
# 显示父级地区信息
return self.aparent
parent_area.admin_order_field = 'aname' # 方法列排序,指定排序依据为aname
parent_area.short_description = '父级地区' # 方法列设置列标题
管理页显示效果如下:
5)关联对象
无法直接访问关联对象的属性或方法,可以在模型类中封装方法,访问关联对象的成员。
a)打开booktest/model.py文件,修改HeroInfo类
class HeroInfo(models.Model):
"""多类:英雄任务模型"""
h_name = models.CharField(max_length=20) # 英雄名称
h_gender = models.BooleanField(default=False) # 英雄性别 布尔类型 default指定默认值为False:男
h_comment = models.CharField(max_length=256) # 备注
h_book = models.ForeignKey('BookInfo', on_delete=True) # 关系属性h_book,建立图书类和英雄类之间的一对多关系
def __str__(self):
# 定义模型类返回对象值
return self.h_name
def parent(self):
if self.h_book is None:
return ''
return self.h_book
parent.short_description = '关联书籍'
b)打开booktest/admin.py,修改HeroInfoAdmin类
class HeroInfoAdmin(admin.ModelAdmin):
""""英雄模型管理类"""
list_display = ['id', 'h_name', 'h_gender', 'h_comment', 'h_book', 'parent']
c)浏览器刷新显示新的效果页面
6)搜索框
用于对指定字段进行搜索,支持模糊查询。列表类型,表示在这些字段上进行搜索。
search_fields = []
a)打开booktest/admin.py,修改AreaInfoAdmin类
class AreaInfoAdmin(admin.ModelAdmin):
"""地区模型管理类"""
list_display = ['id', 'aname', 'aparent', 'parent_area']
# list_per_page = 100 # 默认每页显示100
list_per_page = 2
actions_on_top = False # 顶部显示的属性,默认为True:顶部显示,设置为False不在顶部显示
actions_on_bottom = True # 底部显示的属性,默认为False:不在底部显示,设置为True在底部显示
search_fields = ['aname'] # 根据名称查询
b)打开浏览器,刷新会出现搜索框
7)中文标题
a)打开booktest/model.py文件,修改模型类,为属性指定verbose_name属性参数。
class AreaInfo(models.Model):
"""自关联-地区类"""
aname = models.CharField(max_length=56, verbose_name='地区名称')
aparent = models.ForeignKey('self', null=True, blank=True, verbose_name='父级地区名称')
def __str__(self):
return self.aname
def parent_area(self):
# 显示父级地区信息
return self.aparent
parent_area.admin_order_field = 'aname' # 方法列排序,指定排序依据为aname
parent_area.short_description = '父级地区' # 方法列设置列标题
b)浏览器刷新看最新效果
编辑页选项
显示字段顺序,属性:fields = []
1)英雄列表页,点击列表中任意id,跳转到英雄编辑页,默认显示为:'h_name', 'h_gender', 'h_comment', 'h_book'
2)打开booktest/admin.py,修改HeroInfoAdmin类
class HeroInfoAdmin(admin.ModelAdmin):
""""英雄模型管理类"""
list_display = ['id', 'h_name', 'h_gender', 'h_comment', 'h_book', 'parent']
fields = ['h_book', 'h_name', 'h_gender', 'h_comment'] # 修改编辑页英雄显示信息的顺序
修改前:
修改后:
分组显示
1)打开booktest/admin.py,修改HeroInfoAdmin类
class HeroInfoAdmin(admin.ModelAdmin):
""""英雄模型管理类"""
list_display = ['id', 'h_name', 'h_gender', 'h_comment', 'h_book', 'parent']
# fields = ['h_name', 'h_gender', 'h_comment', 'h_book'] # 修改编辑页英雄显示信息的顺序
fieldsets = (
('组1标题', {'fields': ('h_book', 'h_name')}),
('组2标题', {'fields': ('h_gender', 'h_comment')})
) # fields和fieldsets只能使用其中一个
2)刷新页面查看最新效果
关联对象
在一对多的关系中,可以在一类的编辑页面编辑多类的对象,嵌入多端对象的方式包括表格、块两种。
InlineModelAdmin:表示在模型的编辑页嵌入关联的模型类对象。
子类TabularInline:表示以表格的形式嵌入
子类StackedInline,以块的形式嵌入
以块的形式嵌入
1)打开booktest/admin.py,创建BookStackedInline
class BookStackedInline(admin.StackedInline):
model = HeroInfo # 关联子对象
# extra = 2 # 额外编辑2个子对象
2)打开booktest/admin.py,修改BookInfoAdmin如下:
class BookStackedInline(admin.StackedInline):
model = HeroInfo # 关联子对象
# extra = 2 # 额外编辑2个子对象
class BookInfoAdmin(admin.ModelAdmin):
"""图书模型管理类"""
list_display = ['id', 'b_title', 'b_pubdate'] # list_display参数表示显示到页面的数据形式,参数与设计类时的参数需要保持一致,否则会报错
inlines = [BookStackedInline] # 类BookStackedInline需要在BookInfoAdmin之前定义,不然找不到
3)书类的编辑页,刷新浏览器查看最新效果。
以表格的形式嵌入
1)打开booktest/admin.py,创建BookStackedInline
class BookTabularInline(admin.TabularInline):
model = HeroInfo # 关联子对象
# extra = 2 # 额外编辑2个子对象
2)打开booktest/admin.py,修改BookInfoAdmin如下:
class BookTabularInline(admin.TabularInline):
model = HeroInfo # 关联子对象
# extra = 2 # 额外编辑2个子对象
class BookInfoAdmin(admin.ModelAdmin):
"""图书模型管理类"""
list_display = ['id', 'b_title', 'b_pubdate'] # list_display参数表示显示到页面的数据形式,参数与设计类时的参数需要保持一致,否则会报错
inlines = [BookTabularInline] # 类BookStackedInline需要在BookInfoAdmin之前定义,不然找不到
3)书类的编辑页,刷新浏览器查看最新效果。
4. 重写模板
复制本地base_site.html到项目的templates文件夹下的admin文件夹下
本地base_site.html路径: C:\Python36\Lib\site-packages\django\contrib\admin\templates\admin\base_site.html
重写base_site.html文件
十、图片上传及分页
1、图片上传
1.1. 配置上传文件保存目录
1)新建上传文件保存目录
在项目下static文件下创建,文件名任意,如:media
2)项目配置文件中配置上传文件保存路径:settings中配置:MEDIA_ROOT = os.path.join(BASE_DIR, 'static/media')
1.2. 后台管理页面上传文件
1)设计模型类
class PicTest(models.Model):
"""
1、FileField 上传文件字段
2、ImageField 继承FileField,对上传的内容进行校验,确保是有效的图片。
"""
goods_pic = models.ImageField(upload_to='booktest') # upload_to 定义上传到那个目录,为配置上传文件保存路径下再新建的应用名称文件
2)迁移生成表:
①python manage.py makemigrations
②生成表:python manage.py migrate
迁移时如果报错,No migrations to apply 需要去查看生成的迁移文件,如:0001_initial.py,文件中创建的表在数据库是否已存在,如果已存在需要将已存在的删除,只创建新的
3)应用的admin文件中注册模型类
admin.site.register(PicTest)
页面效果如下:
1.3. 用户自定义页面上传图片
1)定义用户自定义上传图片页面,自定义一个表单,提交方式必须为:post,enctype必须为:multipart/form-data
新建模板文件,upload_pic.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" enctype="multipart/form-data" action="upload_handle">
{% csrf_token %}
<input type="file" name="pic"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
2)定义视图函数
def upload_pic(request):
# 用户上传图片
return render(request, 'booktest/upload_pic.html')
def upload_handle(request):
# 1、获取request中的图片对象
pic = request.FILES['pic'] # upload_pic.html页面上传图片标签的name属性值 <input type="file" name="pic"><br/>
# print(type(pic))
# pic类型为:
# <class 'django.core.files.uploadedfile.InMemoryUploadedFile'> 小于2.5M
# <class 'django.core.files.uploadedfile.TemporaryUploadedFile'> 大于2.5M
# 2、创建一个文件
save_name = '%s/booktest/%s' % (settings.MEDIA_ROOT, pic.name)
# pic_name = pic.name()
with open(save_name, 'wb') as f:
# 3、获取上传文件的内容并写到创建的文件中
for content in pic.chunks():
f.write(content)
# 4、将图片保存到数据库
PicTest.objects.create(goods_pic='booktest/%s' % pic.name)
# 5、返回结果
return HttpResponse('ok')
3)配置url
urlpatterns = [
url(r'^upload_pic$', views.upload_pic), # 用户上传图片页面
url(r'^upload_handle', views.upload_handle), # 上传图片处理
]
官方文档:https://yiyibooks.cn/xx/django_182/topics/http/file-uploads.html
上传文件操作:https://yiyibooks.cn/xx/django_182/ref/files/uploads.html#django.core.files.uploadedfile.UploadedFile
2、分页
案例,查询省级地区的信息,显示在页面上。
1)定义视图函数
def show_area(request, pindex):
# 1、查询所有省级地区
areas = AreaInfo.objects.filter(aparent__isnull=True)
# 2、创建分页类实例对象,第一个参数为分页的查询集,第二个参数为每页数量
paginator = Paginator(areas, 10)
# 3、获取第1页的内容
# page是Page的实例对象,返回第number页的Page类实例对象
if pindex == '':
# 如果没有传页数,设置为第一页
pindex = 1
else:
# 否则转换为int类型
pindex = int(pindex)
# 返回第pindex页的实例对象
page = paginator.page(pindex)
# 2、使用模板
return render(request, 'booktest/show_area.html', {"page": page})
def areas(request):
return render(request, 'booktest/areas.html')
2)创建就模板文件show_area.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>分页</title>
</head>
<body>
<ul>
{% for area in page.object_list %}
<li>
{{ area.aname }}
</li>
{% endfor %}
</ul>
{% if page.has_previous %}
<a href="show_area{{ page.previous_page_number }}"><上一页</a>
{% endif %}
{% for pindex in page.paginator.page_range %}
{% if pindex == page.number %}
{{ pindex }}
{% else %}
<a href="show_area{{ pindex }}">{{ pindex }}</a>
{% endif %}
{% endfor %}
{% if page.has_next %}
<a href="show_area{{ page.next_page_number }}">下一页></a>
{% endif %}
</body>
</html>
3)配置url
urlpatterns = [
url(r'^show_area(?P<pindex>\d*)$', views.show_area), # 分页案例
]
4)访问页面
分页中文文档:https://yiyibooks.cn/xx/django_182/topics/pagination.html