Python视频学习(十二、Django)

目录

  • 0 背景
    • 0.1 MVC框架
    • 0.2 Django
  • 1. 虚拟环境
  • 2. ★第一个Django案例
    • 2.1 创建项目
      • a. 创建项目
        • 项目目录说明
      • b. 创建app
        • 目录说明
      • c. 安装应用
      • d. 运行测试服务器
    • 2.2 ORM操作
      • a. 定义模型类——继承models.Model
        • `on_delete`
      • b. 数据迁移—— `manage.py makemigrations, migrate`
        • 默认数据库
        • 生成字段名
        • 生成表名——app名_类名
      • c. ★操作数据—— `manage.py shell`
        • 操作关系字段
    • 2.3 后台管理操作——`admin`
      • a 界面本地化
      • b 创建管理员—— `manage.py createsuperuser`
      • c. 注册模型类
      • d. 自定义管理页面
    • 2.4 视图初级(MVC中的C)
      • a. 定义视图函数
      • b. ★配置url
      • 页面url地址设置
    • 2.5 模板初级(MVC中的V)
      • a. 创建模板目录和文件
      • b. 配置模板路径
      • c. 模板文件内代码书写
      • d. 视图中调用模板
        • ★使用快捷方式
  • 3. ★模型高级——ORM
    • 3.1 配置MYSQL数据库
      • a. 配置数据库信息
      • b. 安装mysql数据库的包
      • c. 添加install mysql
    • 3.2 常用字段和参数
      • a. 属性命名限制
      • b. 字段类型
      • c. 常用参数
    • 3.3 ★条件查询
      • a. 查看MySQL日志文件
      • a. 查询函数
      • b. ★条件运算符
      • c. 排序——order_by
      • d. ★Q对象——逻辑关系
      • e. ★F对象——属性之间的比较
      • f. 聚合函数——aggregate、count
    • 3.4 ★查询集QuerySet
      • a. 返回查询集的方法
      • b. 查看查询集是否有数据——`exist`
      • c. 查询集的特点
      • d. 查询集切片
    • 3.5 模型类关系——一对一、多对多、一对多
    • 3.6 ★关联查询
      • a. 查询关联对象
      • b. ★关联查询写法
      • c. 自关联查询 模型定义
    • 3.7 ★管理器——models.Manager
      • a. 自定义管理器类的应用场景
      • b. 自定义管理器对象
    • 3.8 元选项——指定表名
  • 4. 视图高级
    • 4.1 URL匹配过程
    • 4.2 获取url参数
      • a. 位置参数
      • b. 关键字参数
    • 4.3 ★错误视图
    • 4.4 ★HttpRequest对象
      • a. ★QueryDict对象
    • 4.5 HttpResponse对象
      • JsonResponse
      • HttpResponseRedirect
  • 5. ★cookie和session
    • 5.1 cookie
      • a.cookie的特点
      • b. 设置cookie—— `response.set_cookie`
      • c. 获取cookie —— `request.COOKIES`
    • 5.2 session
      • a. session的特点
      • b. 启用Session
      • c. 存储方式
      • d. ★使用session —— `request.session`
      • e. 配置Redis的Session
  • 6. 模板高级
    • 6.1 模板的原始使用
    • 6.2 模板文件加载顺序
    • 6.3 ★模板变量
    • 6.4 ★模板标签 templatetag
      • for 循环
      • if 判断
    • 6.5 ★过滤器
      • 内建过滤器
      • 自定义过滤器
    • 6.6 模板注释
    • 6.7 模板继承
    • 6.8 HTML转义
      • 关闭模板文中的转义
    • 6.9 ★装饰器来显示必须登录才能访问
    • 6.10 CSRF 攻击
      • a. 攻击原理
      • b. django防止CSRF的方法
      • c. django的CSRF防御原理
    • 6.10 验证码
      • 看不清,换一个图片
    • 6.11 ★URL反向解析
      • a. 模板超链接使用 URL反向解析
      • b. 视图中使用重定向反向解析url
  • 7. 其他常用技术
    • 7.1 使用静态文件
      • a. ★动态获取静态文件路径
      • b. 静态文件查找顺序
    • 7.2 ★中间件
      • a. 调用流程
        • 案例——返回 请求的IP地址
      • b. ★使用中间件
    • 7.3 Admin后台管理
      • a. 注册模型管理类
      • b. 列表页选项
      • b. 编辑页选项
      • c. 综合代码
      • d. 修改admin页面模板
    • 7.4 上传文件
      • a. 后台上传图片
      • b. 客户端上传图片
      • c. 解决无迁移问题
    • 11.5 分页

0 背景

0.1 MVC框架

软件框架是由其中的各个模块组成的,每个模块负责特定的功能,模块与模块之间相互协作来完成软件开发。软件框架的设计,也是针对某一类软件问题而设计的,其目的主要是提高软件开发效率。

MVC框架的核心思想是:解耦,让不同的代码块之间降低耦合,增强代码的可扩展性和可移植性,实现向后兼容。

  • M 全拼为Model,主要封装对数据库层的访问,对数据库中的数据进行增、删、改、查操作。
  • V 全拼为View,用于封装结果,生成页面展示的html内容。
  • C 全拼为Controller,用于接收请求,处理业务逻辑,与Model和View交互,返回结果。

Python视频学习(十二、Django)_第1张图片

0.2 Django

Django的主要目的是简便、快速的开发数据库驱动的网站。它强调代码复用,多个组件可以很方便的以"插件"形式服务于整个框架,Django有许多功能强大的第三方插件,你甚至可以很方便的开发出自己的工具包。这使得Django具有很强的可扩展性。它还强调快速开发和DRY(DoNotRepeatYourself)原则。

django官方网站
django源码

Django框架遵循MVC设计,并且有一个专有名词:MVT

  • M:全拼为Model,与MVC中的M功能相同,负责和数据库交互,进行数据处理。
  • V:全拼为View,与MVC中的C功能相同,接收请求,进行业务处理,返回应答。
  • T:全拼为Template,与MVC中的V功能相同,负责封装构造要返回的html。

Python视频学习(十二、Django)_第2张图片

1. 虚拟环境

虚拟环境是真实python环境的复制版本。
在虚拟环境中使用的python是复制的python,安装python包也是安装在复制的python中。

安装好virtualenv后在.bashrc中添加下面2行:

export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh
命令 含义
sudo pip install virtualenv 安装虚拟环境
sudo pip install virtualenvwrapper 安装虚拟环境扩展包
export WORKON_HOME=$HOME/.virtualenvs 在配置文件中设置 WORKON_HOME
mkvirtualenv 虚拟环境名 创建虚拟环境
mkvirtualenv -p python3 环境名 创建指定python版本的虚拟环境
workon 虚拟环境名 进入虚拟环境
workon tab-tab 查看所有环境
deactivate 退出当前虚拟环境
rmvirtualenv 虚拟环境名 删除虚拟环境:
pip install 包名 在当前虚拟环境中安装包
注意不能加sudo,否则就是给系统环境安装
pip install django==1.8.2 指定版本安装包
pip freeze > requirements.txt 将当前python环境的包信息导出,为了让别的环境安装依赖时方便

注意:在虚拟环境中不可使用sudo pip install 包名称 来安装python包,这样安装的包实际是安装在了真实的主机环境上。

2. ★第一个Django案例

安装django环境
创建django项目
设计模型类并利用模型类和数据库进行交互
使用django后台管理数据
编写视图函数,进行URL配置
模板的使用
图书-英雄案例完成

2.1 创建项目

在Django中,一个程序包含多个app,每个app负责不同的功能模块:

Python视频学习(十二、Django)_第3张图片

步骤:

  1. 创建项目
  2. 创建app
  3. 注册app
  4. 运行服务器

a. 创建项目

django-admin startproject 项目名称

项目目录说明

Python视频学习(十二、Django)_第4张图片

目录/文件 作用
manage.py 是项目管理文件,通过它管理项目。
与项目同名的目录,此处为test1。 主模块
__init__.py 是一个空文件,作用是这个目录test1可以被当作包使用。
settings.py 是项目的整体配置文件。
urls.py 是项目的URL配置文件。
wsgi.py 是项目与WSGI兼容的Web服务器入口,详细内容会在布署中讲到。

b. 创建app

python manage.py startapp booktest

目录说明

Python视频学习(十二、Django)_第5张图片

目录/文件 作用
admin.py 文件跟网站的后台管理相关。
__init.py__ 是一个空文件,表示当前目录booktest可以当作一个python包使用。
migrations文件夹 数据库迁移相关
models.py 文件跟数据库操作相关。
tests.py 文件用于开发测试用例,在实际开发中会有专门的测试人员,这个事情不需要我们来做。
views.py 跟接收浏览器请求,进行处理,返回页面相关。相当于controller

c. 安装应用

应用创建成功后,需要安装才可以使用,也就是建立应用和项目之间的关联。

在主模块的 settings.pyINSTALLED_APPS下添加应用的名称就可以完成安装。

Python视频学习(十二、Django)_第6张图片

d. 运行测试服务器

在开发阶段,为了能够快速预览到开发的效果,django提供了一个纯python编写的轻量级web服务器,仅在开发阶段使用。

python manage.py runserver ip:端口
例:
python manage.py runserver

可以不写IP和端口,默认IP是127.0.0.1,默认端口为8000。

Python视频学习(十二、Django)_第7张图片

如果增加、修改、删除文件,服务器会自动重启;

Python视频学习(十二、Django)_第8张图片

2.2 ORM操作

O是object,也就类对象的意思,R是relation,翻译成中文是关系,也就是关系数据库中数据表的意思,M是mapping,是映射的意思。在ORM框架中,它帮我们把类和数据表进行了一个映射,可以让我们通过类和类对象就能操作它所对应的表格中的数据。ORM框架还有一个功能,它可以根据我们设计的类自动帮我们生成数据库中的表格,省去了我们自己建表的过程。

django中内嵌了ORM框架,不需要直接面向数据库编程,而是定义模型类,通过模型类和对象完成数据表的增删改查操作。

步骤:

  1. 在models.py中定义模型类
  2. 执行数据迁移
  3. 通过类和对象完成数据增删改查操作

a. 定义模型类——继承models.Model

在每个appmodel.py中定义模型类,继承自models.Model

说明:不需要定义主键列,在生成时会自动添加,并且值为自动增长。

from django.db import models

# Create your models here.
class BookInfo(models.Model):
    btitle = models.CharField(max_length= 20)
    bpub_date = models.DateField()

class HeroInfo(models.Model):
    hname = models.CharField(max_length=20)
    hgender = models.BooleanField()
    hcomment = models.CharField(max_length=100)
    hbook = models.ForeignKey('BookInfo',on_delete = models.CASCADE)

on_delete

Django2以上,ForeignKey字段一定要写on_delete参数,可选的如下(都在django.db.models内):

参数 说明
CASCADE 级联删除
PROTECT 阻止引用对象删除,会抛出异常ProtectedError
SET_NULL 设置为NULL, 只有当null参数为True时才可以
SET_DEFAULT 设置成默认值,所以一定要传递参数default
SET on_delete=models.SET(get_sentinel_user),

b. 数据迁移—— manage.py makemigrations, migrate

迁移由两步完成:

  1. 生成迁移文件:根据模型类生成创建表的迁移文件。
    python manage.py makemigrations

  2. 执行迁移:根据第一步生成的迁移文件在数据库中创建表。
    python manage.py migrate

Django框架根据我们设计的模型类生成了迁移文件,在迁移文件中我们可以看到fields列表中每一个元素跟BookInfo类属性名以及属性的类型是一致的。同时我们发现多了一个id项,这一项是Django框架帮我们自动生成的,在创建表的时候id就会作为对应表的主键列,并且主键列自动增长

Python视频学习(十二、Django)_第9张图片

Python视频学习(十二、Django)_第10张图片

默认数据库

Django默认采用sqlite3数据库,上图中的db.sqlite3就是Django框架帮我们自动生成的数据库文件。

sudo apt-get install sqliteman

数据库的设置在settings.py中:

Python视频学习(十二、Django)_第11张图片

生成字段名

生成的列名和自己定义的列名相同,并且多了一个id项,这一项是Django框架帮我们自动生成的,在创建表的时候id就会作为对应表的主键列,并且主键列自动增长。

关系属性生成的字段名为: 关系属性名_id

生成表名——app名_类名

应用名_model名
_

c. ★操作数据—— manage.py shell

python manage.py shell

完成数据表的迁移之后,下面就可以通过进入项目的shell,进行简单的API操作。如果需要退出项目,可以使用ctrl+d快捷键或输入quit()。

基本查询命令 说明
BookInfo.objects.all() 查询所有图书
图书对象.save() 保存当前图书信息到数据库/ 修改图书信息
b=BookInfo.objects.get(id=1) 根据主键查询图书
图书对象.delete() 删除图书信息
关联对象查询命令 说明
b.heroinfo_set.all() 返回该图书关联的所有英雄信息
h.hbook_id 获取外键值
h.hbook 获取外键引用对象

多的一方可以直接使用属性值来访问一 的一方, 而一的一方获取多的一方时,需要使用 xxx_set

from booktest.models import BookInfo,HeroInfo  # 引入类
from datetime import date

b1 = BookInfo()
b1.btitle= "金庸新著"
b1.bpub_date = date(1991,1,1)
b1.save()  # 保存

b2 = BookInfo()
b2.btitle= "屠龙宝刀"
b2.bpub_date = date(1995,5,6)
b2.save()  # 保存

BookInfo.objects.all() # 获取所有
BookInfo.objects.get(id=1) # 根据id获取
BookInfo.objects.get(pk=1) # 根据主键获取

b1.btitle="点击就送"
b1.save()  # 修改

b.delete()  # 删除

操作关系字段

b1 = BookInfo.objects.get(id=1)

h = HeroInfo()
h.hname = "张无忌"
h.hgender = True
h.hbook = b1

h.save()

h.hbook_id  # 获取外键值
h.hbook    # 获取引用对象

b1.heroinfo_set.all()  # 反向获取对象

Python视频学习(十二、Django)_第12张图片

2.3 后台管理操作——admin

步骤:

  1. 管理界面本地化
  2. 创建管理员
  3. 注册模型类
  4. 自定义管理页面

a 界面本地化

在主模块的settings.py中,设置:

LANGUAGE_CODE = 'zh-hans' #使用中国语言
TIME_ZONE = 'Asia/Shanghai' #使用中国上海时间

b 创建管理员—— manage.py createsuperuser

python manage.py createsuperuser

在这里插入图片描述

然后启动:

python manage.py runserver

进入URL:

http://127.0.0.1:8000/admin/

c. 注册模型类

要让自己创建的模型类,可以被admin页面管理,需要对他们进行注册:

进入主模块的admin.py:

from django.contrib import admin
from booktest.models import BookInfo,HeroInfo
# 后台管理相关文件
# Register your models here.

# 注册模型类
admin.site.register(BookInfo)
admin.site.register(HeroInfo)

再刷新

d. 自定义管理页面

Django提供了自定义管理页面的功能,比如列表页要显示哪些值。

进入主模块的admin.py,创建自己的ModelAdmin类,然后在里面的list_display类属性决定了显示哪些 属性:

from django.contrib import admin
from booktest.models import BookInfo,HeroInfo
# 后台管理相关文件
# Register your models here.
# 自定义模型管理类
class BookInfoAdmin(admin.ModelAdmin):
    '''图书模型管理类'''
    list_display = ['id', 'btitle', 'bpub_date']


class HeroInfoAdmin(admin.ModelAdmin):
    '''英雄人物模型管理类'''
    list_display = ['id', 'hname', 'hcomment']

# 注册模型类
# admin.site.register(BookInfo)
admin.site.register(BookInfo, BookInfoAdmin)
admin.site.register(HeroInfo, HeroInfoAdmin)

2.4 视图初级(MVC中的C)

步骤:

  1. 定义视图函数
  2. 配置URLconf

a. 定义视图函数

  • 视图函数就是普通的Python函数
  • 定义在每个app中的views.py
  • 函数接收一个参数request
  • 函数返回django.http.HttpResponse对象
  • django.shortcuts中有快捷方式render、 redirect
from django.shortcuts import render
from django.http import HttpResponse
import time
# Create your views here.
def index(request):
    currenttime = str(time.localtime().tm_min) +":"+ str(time.localtime().tm_sec)
    return HttpResponse(currenttime )

b. ★配置url

https://docs.djangoproject.com/en/2.1/topics/http/urls/

django查找URL的顺序是这样子的:

  1. Django查看root URLconf module. 默认的,会查看settings.py中的ROOT_URLCONF, 但是如果发送来的HttpRequest object中有 urlconf 属性(set by middleware), 那么这个值就会覆盖ROOT_URLCONF设置
  2. Django 读取root URLconf module这个模块中的变量名为urlpatterns的内容,它应该是一个list,并且都是django.urls.path()或者django.urls.re_path()的实例
  3. Django 遍历所有URL pattern, 找到第一个匹配的URL
  4. 一旦匹配成功,Django就会引入对应的view来调用,which is a simple Python function (or a class-based view). The view gets passed the following arguments:
    • An instance of HttpRequest.
    • If the matched URL pattern returned no named groups, then the matches from the regular expression are provided as positional arguments.
    • The keyword arguments are made up of any named parts matched by the path expression, overridden by any arguments specified in the optional kwargs argument to django.urls.path() or django.urls.re_path().
  5. 如果没有匹配成功,或者匹配过程有任何错误,那么Django invokes an appropriate error-handling view. See Error handling below.

主模块的urls.py中:

from django.contrib import admin
from django.urls import path
from django.urls import include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('',  include('booktest.urls'))
]

booktest应用中的urls.py(需要自己创建):

from  django.conf.urls import url
from django.urls import re_path
from .views import index

urlpatterns = [
    re_path(r'.*', index),
    # url(r'index', index),
    # url('^',index)
]

页面url地址设置

习惯统一以/开头,这样就会从host开始拼接,如果不写/,则会当成相对地址

<a href="/create">新增</a>

2.5 模板初级(MVC中的V)

步骤:

  1. 创建模板文件夹和模板文件
  2. settings.py中配置模板路径
  3. view中加载模板文件对象
  4. 给模板传递数据,生成上下文
  5. 渲染上下文,得到html
  6. 返回html

a. 创建模板目录和文件

直接在项目名底下建立目录templates, 然后在该目录中,为每个app建立同名文件夹:

Python视频学习(十二、Django)_第13张图片

b. 配置模板路径

settings.py中,找到TEMPLATESDIR, 加上模板路径:

'DIRS': [os.path.join(BASE_DIR, 'templates')],

Python视频学习(十二、Django)_第14张图片

c. 模板文件内代码书写

{{变量名}}

{%代码段%}
比如:
{% for i in data %}
	循环内部代码
{% empty %}
	如果循环列表长度为0则运行这部分
{% endfor %}

d. 视图中调用模板

  1. 找到模板
  2. 定义上下文
  3. 渲染模板
from django.http import HttpResponse
from django.template import loader,RequestContext

def index(request):
    '''使用模板文件'''
    # 使用模板文件
    # 1.加载模板文件, 模板对象
    temp = loader.get_template('booktest/index.html')
    # 2.定义模板上下文:给模板文件传递数据
    context = RequestContext(request, {} )
    # 3.模板渲染:产生标准的html内容
    res_html = temp.render(context)
    # 4.返回给浏览器
    return HttpResponse(res_html)

★使用快捷方式

def index(request):
    # 进行处理,和M和T进行交互。。。
    # return HttpResponse('老铁,没毛病')
    # return my_render(request, 'booktest/index.html')
	return render(request, 'booktest/index.html', {'content':'hello world', 'list':list(range(1,10))})

3. ★模型高级——ORM

对象-关系映射ORM系统一般以中间件的形式存在

Python视频学习(十二、Django)_第15张图片

3.1 配置MYSQL数据库

a. 配置数据库信息

settings.py中:

DATABASES = {
    'default': {
        # 'ENGINE': 'django.db.backends.sqlite3',
        'ENGINE': 'django.db.backends.mysql',
        # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        'NAME': 'test2', #数据库名字,
        'USER': 'root', #数据库登录用户名
        'PASSWORD': 'mysql', #数据库登录密码
        'HOST': 'localhost', #数据库所在主机
        'PORT': '3306', #数据库端口
    }
}

注意:数据库test2 Django框架不会自动生成,需要我们自己进入mysql数据库去创建。

b. 安装mysql数据库的包

pip install pymysql

c. 添加install mysql

在主模块的__init__.py内写入:

import pymysql
pymysql.install_as_MySQLdb()

3.2 常用字段和参数

Django根据属性的类型确定以下信息:

  1. 当前选择的数据库支持字段的类型
  2. 渲染管理表单时使用的默认html控件
  3. 在管理站点最低限度的验证

django会为表创建自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后django不会再创建自动增长的主键列

a. 属性命名限制

  • 不能是python的保留关键字。
  • 不允许使用连续的下划线,这是由django的查询方式决定的
  • 定义属性时需要指定字段类型,通过字段类型的参数指定选项

属性=models.字段类型(选项)

b. 字段类型

字段类型都在django.db.models

字段 描述 自己的属性及其作用
AutoField 自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性。
BooleanField 布尔字段,值为True或False
NullBooleanField 可空布尔型,支持Null、True、False三种值
CharField 字符串 参数max_length表示最大字符个数
TextField 大文本字段,一般超过4000个字符时使用。
IntegerField 整数
DecimalField 十进制浮点数 (max_digits=None, decimal_places=None)
参数max_digits表示总位数。
参数decimal_places表示小数位数。
FloatField 浮点数
DateField 日期 [auto_now=False, auto_now_add=False]
参数auto_now表示每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false。
参数auto_now_add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false。
参数auto_now_add和auto_now是相互排斥的,组合将会发生错误。
TimeField 时间 参数同DateField。
DateTimeField 日期时间 参数同DateField。
FileField 上传文件字段。
ImageField 继承于FileField,对上传的内容进行校验,确保是有效的图片。

c. 常用参数

参数名 含义
default 默认值。设置默认值。
primary_key 若为True,则该字段会成为模型的主键字段,默认值是False,一般作为AutoField的选项使用。
unique 如果为True, 这个字段在表中必须有唯一值,默认值是False。
db_index 若值为True, 则在表中会为此字段创建索引,默认值是False。
db_column 字段的名称,如果未指定,则使用属性的名称。
null 如果为True,表示允许为空,默认值是False。
blank 如果为True,则该字段允许为空白,默认值是False。
  • null是数据库范畴的概念,blank是后台管理页面表单验证范畴的。
  • 默认值不是数据库的默认值,而是在创建django对象时的默认值
  • 当修改模型类之后,如果添加的选项不影响表的结构,则不需要重新做迁移,商品的选项中defaultblank不影响表结构。

3.3 ★条件查询

a. 查看MySQL日志文件

默认情况下mysql的日志文件没有产生,需要修改mysql的配置文件

  1. 修改配置文件
    sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf

  2. 将下面2行的注释去掉
    Python视频学习(十二、Django)_第16张图片

  3. 重启mysql
    sudo service mysql restart

  4. 查看日志文件
    sudo tail -f /var/log/mysql/mysql.log #可以实时查看数据库的日志内容

a. 查询函数

所有的查询都是使用的 对象.objects里面的方法:

函数名 功能 返回值 说明
get 返回表中满足条件的一条且只能有一条数据 返回值是一个模型类对象。 参数中写查询条件。
1) 如果查到多条数据,则抛异常MultipleObjectsReturned
2)查询不到数据,则抛异常:DoesNotExist
all 返回模型类对应表格中的所有数据。 返回值是QuerySet类型 查询集
filter 返回满足条件的数据。 返回值是QuerySet类型 参数写查询条件。
exclude 返回不满足条件的数据。 返回值是QuerySet类型 参数写查询条件。
order_by 对查询结果进行排序。 返回值是QuerySet类型 参数中写根据哪些字段进行排序。

b. ★条件运算符

条件格式:模型类属性名__条件名=值

条件运算 说明 例子
exact 是否全等, 区分大小写 .filter(id__exact=1)
contains 是否包含,区分大小写 filter(btitle__contains='传')
endswith, startswith 是否以xx开始、结束,区分大小写 .filter(btitle__endswith='部')
isnull 是否为空 .filter(btitle__isnull=False)
in 是否在范围内 .filter(id__in=[1, 3, 5])
gt、gte、lt、lte 大于小于等等 .filter(id__gt=3)
日期类型比较… .filter(bpub_date__year=1980)
.filter(bpub_date__gt=date(1990, 1, 1))
  1. 判断是否相等:exact 或者直接写 =
BookInfo.objects.get(id=1)

BookInfo.objects.filter(id__exact=1)
  1. 模糊查询: contains, endswith, startswith
BookInfo.objects.filter(btitle__contains='传')
BookInfo.objects.filter(btitle__endswith='部')

以上运算符都区分大小写,在这些运算符前加上i表示不区分大小写,如iexact、icontains、istartswith、iendswith.

  1. 空查询 isnull
BookInfo.objects.filter(btitle__isnull=False)
  1. 范围查询in
BookInfo.objects.filter(id__in = [1,3,5])
  1. 比较查询:gt,lt,gte, lte
BookInfo.objects.filter(id__gt=3)
  1. 日期查询:年月日属性
BookInfo.objects.filter(bpub_date__year=1980)

from datetime import date
BookInfo.objects.filter(bpub_date__gt=date(1980,1,1))

c. 排序——order_by

order方法内加上 字段名,如果多个字段,使用,隔开; 默认是从小到大排序,如果逆序,需要在字段名前加上-

# 查询所有图书的信息,按照id从小到大进行排序。
BookInfo.objects.all().order_by('id')
# 查询所有图书的信息,按照id从大到小进行排序。
BookInfo.objects.all().order_by('-id')
# 把id大于3的图书信息按阅读量从大到小排序显示。
BookInfo.objects.filter(id__gt=3).order_by('-bread')

d. ★Q对象——逻辑关系

filter等函数可以传入多个参数,用于指定多个条件,不过这些条件都是 and方式:

BookInfo.objects.filter(btitle__exact="abc", bread__gt = 10)

而Q对象可以用来与或非:&|~

from django.db.models import Q

BookInfo.objects.filter(Q(id__gt=3)&Q(bread__gt=30))
BookInfo.objects.filter(Q(id__gt=3)|Q(bread__gt=30))
BookInfo.objects.filter(~Q(id=3))

e. ★F对象——属性之间的比较

用于类属性之间的比较

from django.db.models import F

BookInfo.objects.filter(bread__gt=F('bcomment'))  # 阅读量大于评论
BookInfo.objects.filter(bread__gt=F('bcomment')*2) # 还可以进行直接计算

f. 聚合函数——aggregate、count

对查询结果进行聚合操作

2个方法

方法 含义
aggregate 聚合的函数,配合聚合类来使用,返回字典
count 统计返回的数据个数,返回的是数字

from django.db.models import Sum,Count,Max,Min,Avg

聚合类
Sum
Count
Max
Min
Avg
BookInfo.objects.all().aggregate(Count('id'))
# {'id__count': 5}
BookInfo.objects.aggregate(Sum('bread'))  # all()可以省略
# {'bread__sum': 126}


BookInfo.objects.all().count()
BookInfo.objects.count()
BookInfo.objects.filter(id__gt=3).count()

aggregate返回值是一个字典类型:

  {'聚合类小写__属性名':值}
  如:{'sum__bread':3}

3.4 ★查询集QuerySet

a. 返回查询集的方法

返回查询集的过滤器:

  • all():返回所有数据。
  • filter():返回满足条件的数据。
  • exclude():返回满足条件之外的数据,相当于sql语句中where部分的not关键字。
  • order_by():对结果进行排序。

返回单个值的过滤器如下:

  • get():返回单个满足条件的对象
    • 如果未找到会引发"模型类.DoesNotExist"异常。
    • 如果多条被返回,会引发"模型类.MultipleObjectsReturned"异常。
  • count():返回当前查询结果的总条数。
  • aggregate():聚合,返回一个字典。

b. 查看查询集是否有数据——exist

查询集对象.exists()返回值为True/False

c. 查询集的特点

  1. 惰性查询:只有在实际使用查询集中的数据的时候才会发生对数据库的真正查询。
    调用数据的情况包括迭代、序列化、与if合用。
  2. 缓存:当使用的是同一个查询集时,第一次使用的时候会发生实际数据库的查询,然后把结果缓存起来,之后再使用这个查询集时,使用的是缓存中的结果。

每个查询集都包含一个缓存来最小化对数据库的访问。在新建的查询集中,缓存为空,首次对查询集求值时,会发生数据库查询,django会将查询的结果存在查询集的缓存中,并返回请求的结果,接下来对查询集求值将重用缓存中的结果。

d. 查询集切片

  • 可以对一个查询集进行取下标或者切片操作来限制查询集的结果。
  • 对一个查询集进行切片操作会产生一个新的查询集,下标 不允许 为负数
  • 返回的新查询集也由缓存,只要不用数据,就不会去数据库查询
  • 取出查询集第一条数据的两种方式:
方式 说明
b[0] 如果b[0]不存在,会抛出IndexError异常
b[0:1].get() 如果b[0:1].get()不存在,会抛出DoesNotExist异常。

3.5 模型类关系——一对一、多对多、一对多

  1. 一对多关系
    例:图书类-英雄类
    models.ForeignKey() 定义在多的类中。
  2. 多对多关系
    例:新闻类-新闻类型类 体育新闻 国际新闻
    models.ManyToManyField 定义在哪个类中都可以。
  3. 一对一关系
    例:员工基本信息类-员工详细信息类. 员工工号
    models.OneToOneField定义在哪个类中都可以

3.6 ★关联查询

a. 查询关联对象

  • 由一类的对象查询多类的时候:
    一类的对象.多类名小写_set.all() #查询所用数据
    比如: b.heroinfo_set.all()

  • 由多类的对象查询一类的时候:
    多类的对象.关联属性 #查询多类的对象对应的一类的对象
    比如: h.hbook

  • 由多类的对象查询一类对象的id时候:
    多类的对象. 关联属性_id
    比如: h.hbook_id

b. ★关联查询写法

  • 通过多类的条件查询一类的数据:
    一类名.objects.filter(多类名小写__多类属性名__条件)
  • 通过一类的条件查询多类的数据:
    多类名.objects.filter(关联属性__一类属性名__条件)

Python视频学习(十二、Django)_第17张图片

例:查询图书信息,要求图书关联的英雄的描述包含'八'。
BookInfo.objects.filter(heroinfo__hcomment__contains='八')
例:查询图书信息,要求图书中的英雄的id大于3.
BookInfo.objects.filter(heroinfo__id__gt=3)
例:查询书名为“天龙八部”的所有英雄。
HeroInfo.objects.filter(hbook__btitle='天龙八部')

c. 自关联查询 模型定义

也是使用ForeignKey,只不过里面参数是写self

class CityInfo(models.Model):
    cname = models.CharField(max_length=50)
    cparent = models.ForeignKey(to="self", on_delete=models.CASCADE, blank=True, null=True)

3.7 ★管理器——models.Manager

每个模型类 都以一个类属性叫做objects,它的类型是django.db.models.Manager。 它是Django帮我自动生成的管理器对象,通过这个管理器可以实现对数据的查询。

Python视频学习(十二、Django)_第18张图片

a. 自定义管理器类的应用场景

  1. 改变查询的结果集。
    比如调用BookInfo.books.all()返回的是没有删除的图书的数据。
  2. 添加额外的方法。——比如增删改查的方法
    管理器类中定义一个方法帮我们操作模型类对应的数据表。

可以在模型类中自己增加一个Manager字段,如果自己增加了,那么默认的就失效了:

class BookInfo(models.Model):
	book = models.Manager()

b. 自定义管理器对象

小提示:

  1. 自定义管理器对象中使用super().xxx()调用原来的方法
  2. 自定义管理器对象中可以使用self.model()创建对应模型类的对象

自己定义一个类,继承自 models.Manager ,可以就写在models.py中:

在管理器对象中使用self.model()就可以创建一个跟自定义管理器对应的模型类对象。

from django.db import models

# Create your models here.
class BookInfoManager(models.Manager):
    '''图书模型管理器类'''
    # 1.改变原有查询的结果集
    def all(self):
        # 1.调用父类的all方法,获取所有数据
        books = super().all() # QuerySet
        # 2.对books中的数据进行过滤
        books = books.filter(isDelete=False)
        # 返回books
        return books

    # 2.封装方法,操作模型类对应的数据表(增删改查)
    def create_book(self, btitle, bpub_date):
        '''添加一本图书'''
        # 1.创建一个图书对象
        # 获取self所在的模型类
        model_class = self.model
        book = model_class()
        # book = BookInfo()
        book.btitle = btitle
        book.bpub_date = bpub_date
        # 2.添加进数据库
        book.save()
        # 3.返回book
        return book
class BookInfo(models.Model):
	...
	objects = BookInfoManager() # 自定义一个BookInfoManager类的对象

默认的objects管理器对象中已经有create方法,传参的时候必须使用关键字参数来指定列名

3.8 元选项——指定表名

因为django默认生成的表名为appname_modelname的小写名字, 如果app的名字改变了,那么表名也会失效。 为了防止这种情况,可以自定义表名:

  • 元选项:
    需要在模型类中定义一个元类Meta,在里面定义一个类属性db_table就可以指定表名。
class BookInfo(models.Model):
	...
	class Meta:
        db_table = 'bookinfo' # 指定模型类对应表名

4. 视图高级

4.1 URL匹配过程

  1. 在项目的urls文件中包含具体应用的urls文件,在具体应用的urls文件中包含具体url和视图的对应关系。
  2. url配置项是定义在一个名叫urlpatterns的列表中,其中的每一个元素就是一个配置项,每一个配置项都调用path或者re_path函数。
  3. urlpatterns中的每个正则表达式在第一次访问它们时被编译,这使得运行很快。
  4. 不能在开始加反斜杠,推荐在结束加反斜杠。
    正确:index/
    正确:index
    错误:/index
    错误:/index/
    
  5. URL匹配的时候不包括域名、GET参数

Python视频学习(十二、Django)_第19张图片

4.2 获取url参数

进行url匹配时,把所需要的捕获的部分设置成一个正则表达式组,这样django框架就会自动把匹配成功后相应组的内容作为参数传递给视图函数。

注意:两种参数的方式不要混合使用,在一个正则表达式中只能使用一种参数方式。

a. 位置参数

位置参数,参数名可以随意指定

url(r'^delete(\d+)/$',views.show_arg),

b. 关键字参数

在位置参数的基础上给正则表达式组命名即可。

?P<组名>
关键字参数,视图中参数名必须和正则表达式组名一致.

re_path(r'index/(?P\d+)'

def index(request, num):
	pass

4.3 ★错误视图

网站开发完成后,要修改settings.py配置:

  • DEBUG=False ——— 关闭开发者模式
  • ALLOWED_HOST=[ '*'] ——— 配置可以访问的IP

如果此时出现错误,Django自己会返回标准的错误页面。 如果需要修改,则需要的templates目录下面自定义一个404.html文件, 或者 500.html 等等。

DEBUG模式下看不到错误视图,只能看到调试信息

  • 404.html会收到一个参数叫做request_path


    


找不到了

{{request_path}}

4.4 ★HttpRequest对象

属性 说明
request.POST 保存的是POST提交的参数(QueryDict类型)
request.GET 保存的是GET提交的参数(QueryDict类型)
request.method 返回请求方式字符串
request,path 返回请求路径字符串,不包含域名和url参数
request.encoding 请求方式,如果返回None,代表是浏览器默认encoding,一般是UTF-8
这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值
request.FILES 一个类似于字典的对象,包含所有的上传文件。
request.COOKIES 一个标准的Python字典,包含所有的cookie,键和值都为字符串。
request.session 一个既可读又可写的类似于字典的对象,表示当前的会话,只有当Django 启用会话的支持时才可用,详细内容见"状态保持"。

a. ★QueryDict对象

django.http.QueryDict类似于字典,和一般字典不同的是,它可以处理一个key带有多个value的情况。

方法 说明
对象[key] 没有值就报错,一个键同时拥有多个值将获取最后一个值
get(key) 没有值就返回None,一个键同时拥有多个值将获取最后一个值
getlist(key) 返回List
from django.http.request import QueryDict
q = QueryDict('a=123&b=456`)
q['a'] # 获取值,没有值就报错
q.get('a')  # 获取值,没有值就返回None
q.get('a', 'default')  # 获取值,没有对应的key就返回默认值
q.getlist('a') # 获取一个key的多个值,返回类型为list

取消django防止跨域访问的保护:
settings.py中的'django.middleware.csrf.CsrfViewMiddleware',注释掉

4.5 HttpResponse对象

django.http.HttpResponse

视图在接收请求并处理后,必须返回HttpResponse对象或子对象

属性 说明
content 表示返回的内容。
charset 表示response采用的编码字符集,默认为utf-8。
status_code 返回的HTTP响应状态码。
content-type 指定返回数据的的MIME类型,默认为’text/html’。
方法 说明
__init__ 创建HttpResponse对象后完成返回内容的初始化。
set_cookie 设置Cookie信息。
set_cookie(key, value='', max_age=None, expires=None)
delete_cookie(key) 删除指定的key的Cookie,如果key不存在则什么也不发生。
write 向响应体中写数据。

JsonResponse

JsonResponse继承自HttpResponse,它的 content-type为 application/json,创建对象时接收字典作为参数

return JsonResponse({'h1':'hello','h2':'world'})

HttpResponseRedirect

继承自 HttpResponse,状态码为302

return HttpResponseRedirect('/')

5. ★cookie和session

实现状态保持主要有两种方式:

  • 在客户端存储信息使用Cookie。
  • 在服务器端存储信息使用Session。

cookie和session的应用场景

  • cookie:记住用户名。安全性要求不高。
  • session:涉及到安全性要求比较高的数据。银行卡账户,密码

5.1 cookie

Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器

Cookies最典型记住用户名。

Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用

a.cookie的特点

  • Cookie以键值对的格式进行信息的存储。
  • Cookie基于域名安全,不同域名的Cookie是不能互相访问的,如访问itcast.cn时向浏览器中写了Cookie信息,使用同一浏览器访问baidu.com时,无法访问到itcast.cn写的Cookie信息。
  • 当浏览器请求某网站时,会将浏览器存储的跟网站相关的所有Cookie信息提交给网站服务器。
  • cookie是有过期时间的,如果不指定,默认是浏览器关闭后就没了。

b. 设置cookie—— response.set_cookie

设置cookie是使用一个HttpResponse对象,或者它的子类对象, 里面的set_cookie方法,是以键值对保存的

response.set_cookie(key,value, max_age, expires)
`max_age`是一个整数,表示在指定秒数后过期。
`expires`是一个datetime或timedelta对象,会话将在这个指定的日期/时间过期。
max_age与expires二选一。
如果不指定过期时间,在关闭浏览器时cookie会过期。

例子:

response = HttpResponse('这是响应')
response.set_cookie('mykey1', 1123415)
return response


response.set_cookie( 'num', 1, max_age = 秒数) #

c. 获取cookie —— request.COOKIES

def cookie_get(request):
    response = HttpResponse("读取Cookie,数据如下:
"
) if 'h1' in request.COOKIES: response.write('

' + request.COOKIES['h1'] + '

'
) return response

5.2 session

  • session就像是去健身房办卡,办好卡后,健身房那边保存了卡的信息和对应用户的信息,而用户只需要记住卡号就可以使用了。

  • 在服务器内,session就是一张表, 里面是键值对的方式来存储: session_key和session_data

  • django的框架中,session叫做 django_session,保存在 request.session中,设置也是使用这个属性,类似于使用字典

  • django会在cookie中让客户端保存key为sessionid的cookie,用来维护session

cookie无论保存的是什么类型的数据,取出来的时候都是字符串,而 session数据因为是保存在服务器中,所以取出来的类型不变

a. session的特点

  1. session是以键值对进行存储的。
  2. session依赖于cookie。唯一的标识码保存在**sessionid** cookie中。
  3. session也是有过期时间,如果不指定,默认两周就会过期。
  4. 存储Session时,键与Cookie中的sessionid相同,值是开发人员设置的键值对信息,进行了base64编码,过期时间由开发人员设置。

b. 启用Session

默认是启用的。打开test3/settings.py文件,在项MIDDLEWARE_CLASSES中启用Session中间件。

Python视频学习(十二、Django)_第20张图片

c. 存储方式

settings.py文件,设置SESSION_ENGINE项指定Session数据存储的方式,可以存储在数据库、缓存、Redis等。

  1. 存储在缓存中:存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快。
    SESSION_ENGINE='django.contrib.sessions.backends.cache'

  2. 存储在数据库中,如下设置可以写,也可以不写,这是默认存储方式。
    SESSION_ENGINE='django.contrib.sessions.backends.db'

    • 如果存储在数据库中,需要在项INSTALLED_APPS中安装Session应用。
      Python视频学习(十二、Django)_第21张图片

    • 迁移后会在数据库中创建出存储Session的表。
      Python视频学习(十二、Django)_第22张图片

    • 表结构如下
      Python视频学习(十二、Django)_第23张图片

  3. 混合存储:优先从本机内存中存取,如果没有则从数据库中存取。
    SESSION_ENGINE='django.contrib.sessions.backends.cached_db'

d. ★使用session —— request.session

使用 说明
request.session['键']=值 以键值对的格式写session。
request.session['键'] 获取session
request.session.get('键',默认值) 根据键读取值。
request.session.clear() 清除所有session,在存储中删除值部分。
request.session.flush() 清除session数据,在存储中删除session的整条数据。
del request.session['键'] 删除session中的指定键及值,在存储中只删除某个键及对应的值。
request.session.set_expiry(value) 设置会话的超时时间,如果没有指定过期时间则两个星期后过期。
  • 如果value是一个整数,会话将在value秒没有活动后过期。
  • 如果value为0,那么用户会话的Cookie将在用户的浏览器关闭时过期。
  • 如果value为None,那么会话永不过期。

session保存的值是有类型的

e. 配置Redis的Session

  1. 先安装包
pip install django-redis-sessions
  1. 配置settings.py
SESSION_ENGINE = 'redis_sessions.session'
SESSION_REDIS_HOST = 'localhost'
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 2
SESSION_REDIS_PASSWORD = ''
SESSION_REDIS_PREFIX = 'session'

6. 模板高级

模板文件包含两部分内容:

  1. 静态内容:css,js,html。
  2. 动态内容:用于动态去产生一些网页内容。通过模板语言来产生。

模板语言的使用都在django.template中,settings.py内定义了配置:

Python视频学习(十二、Django)_第24张图片

Django处理模板分为两个阶段:

  1. 加载:根据给定的路径找到模板文件,编译后放在内存中。
  2. 渲染:使用上下文数据对模板插值并返回生成的字符串。

6.1 模板的原始使用

  1. 加载模板文件,获取一个模板对象
  2. 定义模板上下文,给模板文件传数据
  3. 模板渲染,返回替换后的html
  4. 返回响应
# 1.加载模板文件,获取一个模板对象
temp = loader.get_template(template_path)
# 2.定义模板上下文,给模板文件传数据
context = RequestContext(request, context)
# 3.模板渲染,产生一个替换后的html内容
res_html = temp.render(context)
# 4.返回应答
return HttpResponse(res_html)

6.2 模板文件加载顺序

  1. 首先去配置的模板目录下面去找模板文件。
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',
            ],
        },
    },
]
  1. 去INSTALLED_APPS下面的每个应用的templates文件夹去找模板文件,前提是应用中必须有templates文件夹
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'booktest',
)

6.3 ★模板变量

模板语言包括4种类型,分别是:
* 变量
* 标签
* 过滤器
* 注释

  • 模板变量名是由数字,字母,下划线和点组成的,不能以下划线开头。
  • 使用模板变量:{{模板变量名}}
  • 模板变量的解析顺序:
    1. {{ book.btitle }}
      1. 首先把book当成一个字典,把btitle当成键名,进行取值book[‘btitle’]
      2. 把book当成一个对象,把btitle当成属性,进行取值book.btitle
      3. 把book当成一个对象,把btitle当成对象的方法,进行取值book.btitle
    2. {{book.0}}
      1. 首先把book当成一个字典,把0当成键名,进行取值book[0]
      2. 把book当成一个列表,把0当成下标,进行取值book[0]

★如果解析失败,则产生内容时用空字符串填充模板变量。

6.4 ★模板标签 templatetag

内建标签

for 循环

{% 代码段 %}
	for循环:
{% for x in 列表 %}
	# 列表不为空时执行
{% empty %}
	# 列表为空时执行
{% endfor %}

可以使用{{ forloop.counter }} 得到循环遍历了第几次

if 判断

{% if 条件 %}
	代码
{% elif 条件 %}
	代码
{% else %}
	代码
{% endif %}
  • 可以使用的条件判断符号> < >= <= == !=
  • 可以使用的逻辑运算and or not
  • 注意:进行比较操作时,比较操作符两边必须有空格
{% for book in books %}
    {% if book.id <= 2 %}
        <li class="red">{{ forloop.counter }}--{{ book.btitle }}</li>
    {% elif book.id <= 5 %}
        <li class="yellow">{{ forloop.counter }}--{{ book.btitle }}</li>
    {% else %}
        <li class="green">{{ forloop.counter }}--{{ book.btitle }}</li>
    {% endif %}
{% endfor %}

6.5 ★过滤器

语法如下:

  • 使用管道符号|来应用过滤器,用于进行计算、转换操作,可以使用在变量、标签中。
  • 如果过滤器需要参数,则使用冒号:传递参数。
变量|过滤器:参数

内建过滤器

官方文档

  • 过滤器用于对模板变量进行操作。
  • 举例
    • length:求长度。字符串,列表.
    • default:设置模板变量的默认值。
    • date:改变日期的显示格式。
      • Y表示年,格式为4位,y表示两位的年。
      • m表示月,格式为01,02,12等。
      • d表示日, 格式为01,02等。
      • j表示日,格式为1,2等。
      • H表示时,24进制,h表示12进制的时。
      • i表示分,为0-59。
      • s表示秒,为0-59。
  • 格式:模板变量|过滤器:参数
book | length

data|default:'默认值'

value|date:"Y年m月j日  H时i分s秒"

自定义过滤器

自定义过滤器其实就是一个python的函数

官方文档

比如模板代码中不能使用%取余,所以需要自定义过滤器:

步骤:

  1. 在项目根目录下创建目录templatetags, 里面要有__init__.py
  2. 在里面创建py文件,里面写方法 (文件名和方法名都不一定)
  3. 按照下面写
from django.template import library

register = Library()   # 自定义过滤器需要使用它

@register.filter   # 这个装饰必须要
def mod(num):   # 方法名字不一定,参数就是过滤器可以接收的参数( | 之前)
	return num % 2 == 0

@register.filter
def mod_val(num, val):   # 定义一个带参数的过滤器
	pass
  1. 页面中先加载这个定义了 过滤器的文件,然后就可以使用了
# 页面中
{{ load.filters }}  # filters 是那个py文件的名字
...
{{ if book.id | mod_val : 2 %}}

自定义过滤器的参数至少有一个,最多2个

templatetags也可以创建在app目录下

6.6 模板注释

django的注释代码不会被编译,不会输出到客户端

  • 单行注释: {# {% if book.id|mod %}#}
  • 多行注释:
{% comment %}
		...
{% endcomment %}

6.7 模板继承

  • 父模块中:

为了更好的可读性,建议给endblock标签也写上名字,这个名字与对应的block名字相同。父模板中也可以使用上下文中传递过来的数据。

{% block 块名 %}
预留区域,可以编写默认内容,也可以没有默认内容
{% endblock 块名%}
子模板去继承父模板之后,可以重写父模板中的某一块的内容。
  • 子模块中:

子模版不用填充父模版中的所有预留区域,如果子模版没有填充,则使用父模版定义的默认值。

首行写继承格式:{% extends 父模板文件路径%}

{% extends 父模板文件路径%}

...
{% block 块名 %}
	{{ block.super}} #获取父模板中块的默认内容
重写的内容
{% endblock 块名%}

Python视频学习(十二、Django)_第25张图片

base.html


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}父模板文件{% endblock title %}title>
head>
<body>
<h1>导航条h1>
{% block b1 %}
    <h1>这是父模板b1块中的内容h1>
{% endblock b1 %}
{% block b2 %}
    <h1>这是父模板b2块中的内容h1>
{% endblock b2 %}
<h1>版权信息h1>
body>
html>

child.html

{% extends 'booktest/base.html' %}
{% block title %}子模板文件{% endblock title %}
{% block b1 %}
    {{ block.super }}
    <h1>这是子模板b1块中的内容h1>
{% endblock b1 %}

{% block b2 %}
    {{ block.super }}
    <h1>这是子模板b2块中的内容h1>
{% endblock b2 %}

6.8 HTML转义

  • 在模板上下文中的html标记默认是会被转义的。即内容都会被当成文本显示,而不会转换成HTML标签

一般的变量使用,默认是{{t1|escape}} ,所以 escape可以不写。

关闭模板文中的转义

  1. {{ 模板变量|safe}} ——只关闭这一个
  2. 关闭一个代码段内的:
{% autoescape off %}
	模板语言代码
{% endautoescape %}

6.9 ★装饰器来显示必须登录才能访问

def login_required(view_func):
    '''登录判断装饰器'''
    def wrapper(request, *view_args, **view_kwargs):
        # 判断用户是否登录
        if request.session.has_key('islogin'):
            # 用户已登录,调用对应的视图
            return view_func(request, *view_args, **view_kwargs)
        else:
            # 用户未登录,跳转到登录页
            return redirect('/login')
    return wrapper

6.10 CSRF 攻击

CSRF全拼为Cross Site Request Forgery,译为跨站请求伪造。CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账…造成的问题包括:个人隐私泄露以及财产安全。

a. 攻击原理

登录了的网站,保存了sessionid, 然后登录一个钓鱼网站的时候,它骗你点击一个地方,然后从它向你已登录的网站发送一个请求,用来装作是你来操作。

Python视频学习(十二、Django)_第26张图片

b. django防止CSRF的方法

Python视频学习(十二、Django)_第27张图片

  1. Django框架配置的中间件内要打开CSRF防护。
  2. CSRF防护只防止POST请求,对GET是不起作用的
  3. 自己的表单为了能够通过CSRF防护,需要在表单内加一句
{% csrf_token %}

c. django的CSRF防御原理

  1. 渲染模板文件时,会把{% csrf_token %}生成为一个名字叫做csrfmiddlewaretoken的隐藏域(input:hidden)。
  2. 当启用中间件并加入标签csrf_token后, 服务器交给浏览器保存一个名字为csrftoken的cookie信息, 它的值和上面的隐藏域的值相同。
  3. 当提交请求的时候,django会检查这两个值是否都有,并且是否匹配;如果对比失败则返回403页面,而不会进行后续的处理。

6.10 验证码

将验证码的内容保存在session内,然后生成一张图片发送给用户,让它输入,之后和服务器session的内容对比。

代码不用自己背,网上有很多:
pip install Pillow==3.4.1

  • 提示1:随机生成字符串后存入session中,用于后续判断。
  • 提示2:视图返回mime-type为image/png。
from PIL import Image, ImageDraw, ImageFont
from django.utils.six import BytesIO
...
def verify_code(request):
    #引入随机函数模块
    import random
    #定义变量,用于画面的背景色、宽、高
    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=fill)
    #定义验证码的备选值
    str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0'
    #随机选取4个值作为验证码
    rand_str = ''
    for i in range(0, 4):
        rand_str += str1[random.randrange(0, len(str1))]
    #构造字体对象,ubuntu的字体路径为“/usr/share/fonts/truetype/freefont”
    font = ImageFont.truetype('FreeMono.ttf', 23)
    #构造字体颜色
    fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
    #绘制4个字
    draw.text((5, 2), rand_str[0], font=font, fill=fontcolor)
    draw.text((25, 2), rand_str[1], font=font, fill=fontcolor)
    draw.text((50, 2), rand_str[2], font=font, fill=fontcolor)
    draw.text((75, 2), rand_str[3], font=font, fill=fontcolor)
    #释放画笔
    del draw
    #存入session,用于做进一步验证
    request.session['verifycode'] = rand_str
    #内存文件操作
    buf = BytesIO()
    #将图片保存在内存中,文件类型为png
    im.save(buf, 'png')
    #将内存中的图片数据返回给客户端,MIME类型为图片png
    return HttpResponse(buf.getvalue(), 'image/png')

看不清,换一个图片

<script type="text/javascript" src="/static/jquery-1.12.4.min.js">script>
<script type="text/javascript">
    $(function(){
        $('#change').css('cursor','pointer').click(function() {
            $('#yzm').attr('src',$('#yzm').attr('src')+1)
        });
    });
script>
...
<img id="yzm" src="/verify_code/?1"/>
<span id="change">看不清,换一个span>

6.11 ★URL反向解析

给一个自己配置的route URL命名,这样当URL改变时,其他跳转的连接如果是名字引用,那么就不需要在每个HTML页面中修改跳转地址。

  • 反向解析应用在两个地方:
    1. 模板中的超链接
    2. 视图中的重定向。

步骤:

  1. url配置的时候,在include中指定参数namespace

在这里插入图片描述

  1. 在每个匹配view的路由中指定name参数

在这里插入图片描述

在模板中使用url标签
在视图中使用reverse函数

a. 模板超链接使用 URL反向解析

在模板文件中跳转的时候,书写:

<a href="{% url 'namespace:name'   %}"> 跳转a>

普通链接:<a href="/fan2/">fan2a>
<hr>
反向解析:<a href="{%url 'booktest:fan2'%}">fan2a>
  • 如果跳转的视图带有位置参数,那么传递参数的方式为—— 用空格隔开:
url(r'^show_args/(\d+)/(\d+)$', views.show_args, name='show_args'), # 捕获位置参数
 跳转
  • 如果跳转的视图带有关键字参数,那么传递方式为:—— 用空格隔开
url(r'^show_kwargs/(?P\d+)/(?P\d+)$', views.show_kwargs, name='show_kwargs'),  # 捕获关键字参数
 跳转

b. 视图中使用重定向反向解析url

from django.core.urlresolvers import reverse

...
def index(request):
	url = reverse('booktest:index')
	url = reverse('booktest:index', args=(1,2)) # 传递位置参数
	url = reverse('booktest:index', kwargs={'c':3, 'd':4}) # 传递关键字参数
	return redirect(url)
	# return redirect(reverse('booktest:fan2'))
  • 传递位置参数
return redirect(reverse('booktest:fan2', args=(2,3)))
  • 传递关键字参数
return redirect(reverse('booktest:fan2', kwargs={'id':100,'age':18}))

7. 其他常用技术

7.1 使用静态文件

项目中的CSS、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中,以方便管理

静态文件可以放在项目根目录下,也可以放在应用的目录下,由于有些静态文件在项目中是通用的,所以推荐放在项目的根目录下,方便管理。

步骤:

  1. 在项目根目录下创建文件夹static,下面分别创建css, js, images
    在这里插入图片描述

  2. 在配置文件中配置使用静态文件,并且指定路径:

在这里插入图片描述

  • STATIC_URL设置访问静态文件对应的url。
  • STATICFILES_DIRS设置静态文件所在的物理目录。
  1. 在模板文件中引用
<script src="/static/img/a.jpg ">script>

a. ★动态获取静态文件路径

如果每次都在模板文件中写固定的路径,那么STATIC_URL一改,维护起来就很麻烦。

步骤:

  1. 在模板文件中写上

{% load staticfiles %}
或者
{% load static from staticfiles %}

...
下面使用动态的方式获取静态文件的路径
 拿到  /static/images/a.jpg

说明:这种方案可以隐藏真实的静态文件路径,但是结合Nginx布署时,会将所有的静态文件都交给Nginx处理,而不用转到Django部分,所以这项配置就无效了。

b. 静态文件查找顺序

  1. 先去配置的 STATICFILES_DIRS中查找
  2. 然后再去项目app中查找static文件夹
from django.conf import settings

print(settings.STATICFILES_FINDERS)
# ('django.contrib.staticfiles.finders.FileSystemFinder',    这个负责在配置的静态目录下找
#   'django.contrib.staticfiles.finders.AppDirectoriesFinder')   这个负责在app下的static文件夹找

7.2 ★中间件

  • 中间件函数是django框架给我们预留的函数接口,让我们可以干预请求和应答的过程。
  • 中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。
  • Django在中间件中预置了五个方法,这五个方法的区别在于不同的阶段执行,对输入或输出进行干预
中间件方法 作用 参数 返回值
__init__ :服务器启动之后接收第一个请求的时候调用。 self
process_request 是对每个请求产生request对象之后,进行url匹配之前调用 self, request None或者 HttpResponse
process_view 是在url匹配之后,调用视图函数之前 self,request,view_func, *view_args, **view_kwargs None或者 HttpResponse
process_response 视图函数调用之后,内容返回给浏览器之前,要将response返回 self, request, response HttpResponse
process_exception 视图函数出现异常,会调用这个函数。 self, request, exception HttpResponse

★注:

  1. 如果在process_request中返回了Response的话,那么之后的url匹配还有view就不会被调用了,直接调用process_response返回结果
  2. 如果在process_view中返回了Response的话,那么之后的view就不会调用了,直接调用process_response返回结果
  3. 如果注册了多个包含process_exception的中间件类,那么会先调用 后注册的异常处理

a. 调用流程

Python视频学习(十二、Django)_第28张图片

案例——返回 请求的IP地址

视图函数中

request.META['REMOTE_ADDR']

b. ★使用中间件

步骤:

  1. app中创建一个文件——middleware.py(文件名不是固定的,但是一般叫这个)
    Python视频学习(十二、Django)_第29张图片

  2. 在文件中定义一个中间件类, 在内部定义中间件方法

class TestMiddleware(object):
    '''中间件类'''
    def __init__(self):
        '''服务器重启之后,接收第一个请求时调用'''
        print('----init----')

    def process_request(self, request):
        '''产生request对象之后,url匹配之前调用'''
        print('----process_request----')
        # return HttpResponse('process_request')

    def process_view(self, request, view_func, *view_args, **view_kwargs):
        '''url匹配之后,视图函数调用之前调用'''
        print('----process_view----')
        return HttpResponse('process_view')

    def process_response(self, request, response):
        '''视图函数调用之后,内容返回浏览器之前'''
        print('----process_response----')
        return response

  1. 注册中间件类
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.BlockedIPSMiddleware', # 注册中间件类
    # 'booktest.middleware.TestMiddleware',
    'booktest.middleware.ExceptionTest1Middleware',
    'booktest.middleware.ExceptionTest2Middleware',
)

7.3 Admin后台管理

管理类可以定义在admin.py中, 继承自models.AdminModel

管理类属性 含义
list_per_page = 10 控制每页显示多少条
list_display = [] 控制显示哪些列属性/方法
actions_on_bottom = True
actions_on_top = False
list_filter = [‘atitle’] # 列表页右侧过滤栏
search_fields = [‘atitle’] # 列表页上方的搜索框

a. 注册模型管理类

管理类有两种使用方式:

  1. 注册参数
class AreaAdmin(admin.ModelAdmin):
    pass

...

admin.site.register(AreaInfo,AreaAdmin)
  1. 装饰器
@admin.register(AreaInfo)
class AreaAdmin(admin.ModelAdmin):
    pass

b. 列表页选项

class AreaAdmin(admin.ModelAdmin):
    list_per_page = 10    # 每页显示多少条
    actions_on_top=True   # 操作选项的位置
    actions_on_bottom=False
	list_display=[模型字段1,模型字段2,...]   # 显示的列字段
    list_display = ['id','atitle','title']   # 还能将 方法作为列
    list_filter=['atitle']  # 右侧过滤栏
    search_fields=['atitle']   # 搜索框




class AreaInfo(models.Model):
    ...
    def title(self):
        return self.atitle
    # 方法列是不能排序的,如果需要排序需要为方法指定排序依据。
    title.admin_order_field='atitle'
	# 指定列标题
    title.short_description='区域名称'
	# 访问关联对象
	# 无法直接访问关联对象的属性或方法,可以在模型类中封装方法,访问关联对象的成员。
	 def parent(self):
        if self.aParent is None:
          return ''
        return self.aParent.atitle
    parent.short_description='父级区域名称'	

b. 编辑页选项

class AreaAdmin(admin.ModelAdmin):
    ...
    fields=['aParent','atitle']  # 可编辑字段

	fieldset=(   # 分组显示, 和fields二选一使用
	    ('组1标题',{'fields':('字段1','字段2')}),
	    ('组2标题',{'fields':('字段3','字段4')}),
	)
    inlines = [AreaStackedInline]   # 关联对象
    # inlines = [AreaTabularInline]



class AreaStackedInline(admin.StackedInline):
    model = AreaInfo #关联子对象
    extra = 2#额外编辑2个子对象

class AreaTabularInline(admin.TabularInline):
    model = AreaInfo#关联子对象
    extra = 2#额外编辑2个子对象

c. 综合代码

from django.db import models

class AreaInfo(models.Model):
    '''地址模型类'''
    # 地区名称
    atitle = models.CharField(verbose_name='标题', max_length=20)
    # 自关联属性
    aParent = models.ForeignKey('self', null=True, blank=True)

    def __str__(self):
        return self.atitle

    def title(self):
        return self.atitle
    title.admin_order_field = 'atitle'   # 控制这个方法,是按照哪个属性排序
    title.short_description = '地区名称'  # 控制这个方法显示对应列名

    def parent(self):
        if self.aParent is None:
            return ''
        return self.aParent.atitle
    parent.short_description = '父级地区名称'


class PicTest(models.Model):
    '''上传图片'''
    goods_pic = models.ImageField(upload_to='booktest')

from django.contrib import admin
from booktest.models import AreaInfo,PicTest
# Register your models here.

class AreaStackedInline(admin.StackedInline):
    # 写多类的名字
    model = AreaInfo
    extra = 2

class AreaTabularInline(admin.TabularInline):
    model = AreaInfo
    extra = 2

class AreaInfoAdmin(admin.ModelAdmin):
    '''地区模型管理类'''
    list_per_page = 10 # 指定每页显示10条数据
    list_display = ['id', 'atitle', 'title', 'parent']
    actions_on_bottom = True
    actions_on_top = False
    list_filter = ['atitle'] # 列表页右侧过滤栏
    search_fields = ['atitle'] # 列表页上方的搜索框

    # fields = ['aParent', 'atitle']  指定详细页面中字段顺序
    fieldsets = (   # 分组显示, 这个字段和  fields只能二选一使用
        ('基本', {'fields':['atitle']}),
        ('高级', {'fields':['aParent']})
    )

    # inlines = [AreaStackedInline]
    inlines = [AreaTabularInline]

admin.site.register(AreaInfo, AreaInfoAdmin)

admin.site.register(PicTest)

d. 修改admin页面模板

步骤:

  1. templates文件夹下创建admin文件夹,里面创建base_site.html
  2. 在虚拟环境中找到原来的django模板文件:
/home/python/.virtualenvs/py_django/lib/python3.5/site-packages/django/contrib/admin/templates/admin
  1. 直接复制过来,在里面修改就可以了

7.4 上传文件

步骤:

1. 配置上传文件的保存目录
可以创建在静态文件目录下,也可以不。 创建一个media文件夹

Python视频学习(十二、Django)_第30张图片

2. 配置上传文件保存目录

在这里插入图片描述

a. 后台上传图片

  1. 创建模型类

其中创建一个 ImageField的字段,upload_to属性指定的是文件夹名称, 这个名称是 配置的上传文件保存目录下的文件夹名称

在这里插入图片描述

  1. 迁移生成这个模型类对应的表格 —— 内部的这个ImageField字段就i变成一个列,用于存放图片保存到的地址信息,而不是完整图片内容。

  2. 注册模型类,使得后台可以访问

b. 客户端上传图片

http://python.usyiyi.cn/documents/django_182/topics/http/file-uploads.html
http://python.usyiyi.cn/documents/django_182/ref/files/uploads.html#django.core.files.uploadedfile.UploadedFile

  1. 创建表单和文件上传控件

Python视频学习(十二、Django)_第31张图片

  1. 定义接收上传文件的视图函数,在内部来将图片写入文件
  • request.FILES[name属性]可以访问到对应的上传文件处理器
  • 文件处理器有2种:
    • —— 文件小于2.5M时,保存在内存中
    • —— 文件大于2.5M时,保存在临时文件中
  • 调用处理器的chunks方法,来遍历获取文件内容,然后自行保存在文件内
def upload_handle(request):
    '''上传图片处理'''
    # 1.获取上传文件的处理对象
    pic = request.FILES['pic']

    # 2.创建一个文件
    save_path = '%s/booktest/%s'%(settings.MEDIA_ROOT,pic.name)
    with open(save_path, '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')
  1. 再创建一个对应的模型类对象,将图片存储的位置写入,并且保存到数据库

c. 解决无迁移问题

如果之前在别的项目中使用的是同一个数据库,那么在执行迁移的时候,django是根据app名字+迁移文件名来执行迁移的,那么在之前的app+迁移文件名都相等的情况下,之后项目的迁移不会被执行。

解决办法:

  1. 删除对应数据库中管理迁移表格中,对应的条目
  2. 修改迁移文件名

11.5 分页

Paginator类对象的属性

属性名 说明
count 返回对象总数。
num_pages 返回分页之后的总页数
page_range 返回分页后页码的列表

Paginator类对象的方法:

方法名 说明
__init__(obj_list, int) 返回分页对象,第一个参数为列表数据,第二个参数为每页数据的条数
page(self, number) 返回第number页的Page类实例对象

Page类对象的属性

属性名 说明
number 返回当前页的页码
object_list 返回包含当前页的数据的查询
paginator 返回对应的Paginator类对象

Page类对象的方法

属性名 说明
has_previous 判断当前页是否有前一页
has_next 判断当前页是否有下一页
previous_page_number 返回前一页的页码
next_page_number 返回下一页的页码
from django.core.paginator import Paginator  # 分页对象
paginator = Paginator(areas, 10) #按每页10条数据进行分页
currentPage = paginator.page( currentPageNumber)

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>分页title>
head>
<body>
<ul>
    {# 遍历获取每一条数据 #}
    {#  {% for area in page.object_list %}#}
    {% for area in page %}
        <li>{{ area.atitle }}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>

你可能感兴趣的:(Python)