Django框架

目录

一、Django框架简介与环境搭建

1、MVC模型

2、Django使用的是MVT设计模式

3、虚拟环境

4、创建项目

二、Django的基本使用

1、应用目录文件

2、模型类操作

3、后台管理

4、视图

5、模板

三、Model属性及后端配置

1、配置mysql数据库

2、案例

3、重定向

4、模型类的字段属性和选项

5、查询

6、配置mysql日志

四、模型关系及相关操作

1、模型类关系

2、关联查询(一对多)

3、插入、更新和删除

4、自关联

5、管理器对象

6、元选项

五、登录案例

1、视图

2、视图函数的使用

3、捕获url参数

4、登录案例

5、ajax登录案例

六、cookie与session

1、cookie

2、session

3、对象及方法

4、cookie和session的应用场景

七、模板 templates

1、模板的功能

2、模板文件的使用

3、模板文件加载顺序

4、模板语言

八、基于模板登录案例

1. csrf攻击

2. 验证码

3. 反向解析

九、django功能-静态文件、中间件、后台

1、静态文件

2、中间件

3.后台管理

4. 重写模板

十、图片上传及分页

1、图片上传

2、分页


一、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时,有两种语法格式:

  1. url(正则表达式, 函数视图名)
  2. 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)
  1. 生成迁移文件:python manage.py makemigrations
  2. 生成表: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的特点:

  1. 以键值对的方式进行存储
  2. 通过浏览器访问一个网站时,会将浏览器存储的和网站相关的所有cookie信息发给该网站的服务器
  3. cookie是基于域名安全的
  4. 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特点:

  1. session是以键值对的形式存储的
  2. session依赖于cookie,唯一的标识码保存在sessionid  # Django中session存放的表为django_session,数据表中session_key,就是请求浏览器时返回cookie中的sessionid
  3. 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文件,模板文件包含两部分内容:

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

2、模板文件的使用

通常是在视图函数中使用模板产生的html内容返回给客户端。

  1. 加载模板文件,获取模板文件的内容,产生一个模板对象
  2. 定义模板上下文,给模板文件产生数据
  3. 模板渲染产生html页面内容,用传递的数据替换相应的变量,产生一个替换后的标准的html内容
  4. 返回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标记默认是会转义的。
            大于号> 转义为&gt;
            小于号< 转义为&lt;
            单引号'' 转义为&39;
            双引号"" 转义为&quot;
            与符号& 转义为&amp;

        关闭模板上下文字符串的转义,可以使用{{ 模板变量|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:'&lth1&gthhhhhh&lt/h1&gt' }} <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 }}">&lt;上一页</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 }}">下一页&gt;</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

 

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值