简介

Django 是一个开放源代码的 Web 应用框架,由 Python 写成。Django 采用了 MVT 的软件设计模式,即模型(Model),视图(View)和模板(Template)。MVC 模式使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。
MVC 优势:

  • 低耦合
  • 开发快捷
  • 部署方便
  • 可重用性高
  • 维护成本低

Python 加 Django 是快速开发、设计、部署网站的最佳组合。

MVC 与 MTV模型

MVC 模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
MVC 以一种插件式的、松耦合的方式连接在一起。

  • 模型(M)- 编写程序应有的功能,负责业务对象与数据库的映射(ORM)。
  • 视图(V)- 图形界面,负责与用户的交互(页面)。
  • 控制器(C)- 负责转发请求,对请求进行处理。

简易图:
image.png
用户操作流程图:
image.png

MTV 模型

Django 的 MTV 模式本质上和 MVC 是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django 的 MTV 分别是指:

  • M 表示模型(Model):编写程序应有的功能,负责业务对象与数据库的映射(ORM)。
  • T 表示模板 (Template):负责如何把页面(html)展示给用户。
  • V 表示视图(View):负责业务逻辑,并在适当时候调用 Model和 Template。

除了以上三层之外,还需要一个 URL 分发器,它的作用是将一个个 URL 的页面请求分发给不同的 View 处理,View 再调用相应的 Model 和 Template,MTV 的响应模式如下所示:
image.png
说明:
用户通过浏览器向我们的服务器发起一个请求(request),这个请求会去访问视图函数:

  • a.如果不涉及到数据调用,那么这个时候视图函数直接返回一个模板也就是一个网页给用户。
  • b.如果涉及到数据调用,那么视图函数调用模型,模型去数据库查找数据,然后逐级返回。

视图函数把返回的数据填充到模板中空格中,最后返回网页给用户。

Django的快速入门

1.创建基础项目

django-admin startproject HelloWorld

image.png
目录说明:

  • HelloWorld: 项目的容器。
  • manage.py: 一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互。
  • HelloWorld/init.py: 一个空文件,告诉 Python 该目录是一个 Python 包。
  • HelloWorld/asgi.py: 一个 ASGI 兼容的 Web 服务器的入口,以便运行你的项目。
  • HelloWorld/settings.py: 该 Django 项目的设置/配置。
  • HelloWorld/urls.py: 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站”目录”。
  • HelloWorld/wsgi.py: 一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目。

接下来我们进入 HelloWorld 目录输入以下命令,启动服务器:

python manage.py runserver 0.0.0.0:8000

image.png

2.视图和 URL 配置

新建视图文件:

  1. from django.http import HttpResponse
  2. def hello(request):
  3. return HttpResponse("Hello world ! ")

配置url内容 修改urls.py

  1. from django.conf.urls import url
  2. from . import views
  3. urlpatterns = [
  4. url(r'^$', views.hello),
  5. ]

image.png
可以通过路径匹配:

  1. from django.conf.urls import url
  2. from django.urls import path
  3. from . import views
  4. urlpatterns = [
  5. url(r'^$', views.hello),
  6. path('hello/', views.hello),
  7. ]

image.png
注意:项目中如果代码有改动,服务器会自动监测代码的改动并自动重新载入,所以如果你已经启动了服务器则不需手动重启。
path() 函数
Django path() 可以接收四个参数,分别是两个必选参数:route、view 和两个可选参数:kwargs、name。
语法格式:
path(route, view, kwargs=None, name=None)

  • route: 字符串,表示 URL 规则,与之匹配的 URL 会执行对应的第二个参数 view。
  • view: 用于执行与正则表达式匹配的 URL 请求。
  • kwargs: 视图使用的字典类型的参数。
  • name: 用来反向获取 URL。(用于重定向)

    Django构成介绍

    Django 模板

    视图展示文件。
    第一步:在 HelloWorld 目录底下创建 templates 目录并建立 hello.html文件
    image.png
    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>Title</title>
    6. </head>
    7. <body>
    8. <h1>{{ hello }}</h1>
    9. </body>
    10. </html>
    变量使用了双括号.
    第二步:修改配置文件(settings.py)。指定模板路径
    修改 TEMPLATES 中的 DIRS 为 [os.path.join(BASE_DIR, ‘templates’)]
    1. TEMPLATES = [
    2. {
    3. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
    4. 'DIRS': [os.path.join(BASE_DIR, 'templates')],#修改
    5. 'APP_DIRS': True,
    6. 'OPTIONS': {
    7. 'context_processors': [
    8. 'django.template.context_processors.debug',
    9. 'django.template.context_processors.request',
    10. 'django.contrib.auth.context_processors.auth',
    11. 'django.contrib.messages.context_processors.messages',
    12. ],
    13. },
    14. },
    15. ]
    第三步:修改 views.py,增加一个新的方法,用于向模板提交数据 ```python from django.shortcuts import render

def hello2(request): context = {} context[‘hello’] = ‘Hello World!’ return render(request, ‘hello.html’, context) #render 将数据渲染到模板文件上

  1. 第四步:添加url映射(修改urls.py文件)。
  2. ```python
  3. path('hello2/', views.hello2),

image.png

Django 模板标签

变量

模板语法:

view:{"HTML变量名" : "views变量名"}#字典 键值对的形式
HTML:{{变量名}}

列表

templates 中的 hello.html中,可以用 . 索引下标取出对应的元素。

def hello3(request):
    context = {}
    context['helloList'] = ["列表1","列表2","列表3"]
    return render(request, 'hello.html', context) #render 将数据渲染到模板文件上
<h1>{{ helloList }}</h1>
<h1>{{ helloList.0 }}</h1>

字典

templates 中的 hello.html中,可以用 .键 取出对应的值。

def hello4(request):
    views_dict = {"name":"字典"}
    return render(request, "hello.html", {"views_dict": views_dict})
<h1>{{ views_dict.name }}</h1>

过滤器

模板语法:

{{ 变量名 | 过滤器:可选参数 }}

1.模板过滤器可以在变量被显示前修改它,过滤器使用管道字符

{{ name|lower }}

{{ name }} 变量被过滤器 lower 处理后,文档大写转换文本为小写。
2.过滤管道可以被 套接 ,既是说,一个过滤器管道的输出又可以作为下一个管道的输入

{{ name|first|upper }}

3.length
返回对象的长度,适用于字符串和列表。
字典返回的是键值对的数量,集合返回的是去重后的长度

{{ name|length}}

4.date
根据给定格式对一个日期变量进行格式化。
格式 Y-m-d H:i:s返回 年-月-日 小时:分钟:秒 的格式时间。

def hello5(request):
    now = datetime.datetime.now()  # 获取当前时间
    return render(request, "hello.html", {"time": now})
<h1>{{ time|date:"Y-m-d" }}</h1>

5.truncatechars
如果字符串包含的字符总个数多于指定的字符数量,那么会被截断掉后面的部分。
截断的字符串将以 结尾。
6.safe
将字符串标记为安全,不需要转义。
要保证 views.py 传过来的数据绝对安全,才能用 safe。
和后端 views.py 的 mark_safe 效果相同。
Django 会自动对 views.py 传到HTML文件中的标签语法进行转义,令其语义失效。加 safe 过滤器是告诉 Django 该数据是安全的,不必对其进行转义,可以让该数据语义生效。

def hello6(request):
    views_str = "<a href='https://www.baidu.com/'>点击跳转</a>"
    return render(request, "hello.html", {"views_str": views_str})
<h1>{{ views_str|safe }}</h1>

for 标签

{% for %} 允许我们在一个序列上迭代。
与 Python 的 for 语句的情形类似,循环语法是 for X in Y ,Y 是要迭代的序列而 X 是在每一个特定的循环中使用的变量名称。
每一次循环中,模板系统会渲染在 {% for %}{% endfor %} 之间的所有内容。

def hello7(request):
    views_list = ["列表1","列表2","列表3","列表4",]
    return render(request, "hello.html", {"views_list": views_list})
<p>
    {% for foo in views_list %}
        {{ foo }}
    {% empty %}
        空空如也~
    {% endfor %}
</p>
<p>
    {% for foo in views_list reversed %}
        {{ foo }}
    {% empty %}
        空空如也~
    {% endfor %}
</p>

reversed:反转。
image.png

ifequal/ifnotequal 标签

{% ifequal %} 标签比较两个值,当他们相等时,显示在 {% ifequal %} 和 {% endifequal %} 之中所有的值。

{% ifequal user currentuser %}
    <h1>Welcome!</h1>
{% endifequal %}

和 {% if %} 类似, {% ifequal %} 支持可选的 {% else%} 标签

{% ifequal section 'sitenews' %}
    <h1>Site News</h1>
{% else %}
    <h1>No News Here</h1>
{% endifequal %}

注释标签

Django 注释使用 {# #}。

{# 这是一个注释 #}

include 标签

{% include %} 标签允许在模板中包含其它的模板的内容。
下面这个例子都包含了 nav.html 模板:

{% include “nav.html” %}

csrf_token

csrf_token 用于form表单中,作用是跨站请求伪造保护。
如果不用{% csrf_token %}标签,在用 form 表单时,要再次跳转页面会报403权限错误。用了{% csrf_token %}标签,在 form 表单提交数据时,才会成功。
解析:
首先,向服务器发送请求,获取登录页面,此时中间件 csrf 会自动生成一个隐藏input标签,该标签里的 value 属性的值是一个随机的字符串,用户获取到登录页面的同时也获取到了这个隐藏的input标签。
然后,等用户需要用到form表单提交数据的时候,会携带这个 input 标签一起提交给中间件 csrf,原因是 form 表单提交数据时,会包括所有的 input 标签,中间件 csrf 接收到数据时,会判断,这个随机字符串是不是第一次它发给用户的那个,如果是,则数据提交成功,如果不是,则返回403权限错误。

配置静态文件

1、在项目根目录下创建 statics 目录。
image.png
2.配置
settings 文件的最下方配置添加以下配置

STATIC_URL = '/static/' # 别名
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "statics"),
]

3、在 statics 目录下创建 css 目录,js 目录,images 目录,plugins 目录, 分别放 css文件,js文件,图片,插件。
image.png
4、在 HTML 文件的 head 标签中引入 css。
注意:此时引用路径中的要用配置文件中的别名 static,而不是目录 statics。

<link rel="stylesheet" href="/static/index.css">

5.在模板中使用需要加入 {% load static %} 代码

{% load static %}
{{hello}}<img id="imgId" src="{% static "imgs/aa.jpg" %}">

image.png

模板继承

模板可以用继承的方式来实现复用,减少冗余内容。
网页的头部和尾部内容一般都是一致的,我们就可以通过模板继承来实现复用。
父模板用于放置可重复利用的内容,子模板继承父模板的内容,并放置自己的内容。

父模板

标签 block…endblock: 父模板中的预留区域,该区域留给子模板填充差异性的内容,不同预留区域名字不能相同。

{% block 名称 %} 
预留给子模板的区域,可以设置设置默认内容
{% endblock 名称 %}

子模板

子模板使用标签 extends 继承父模板:

{% extends “父模板路径”%}

子模板如果没有设置父模板预留区域的内容,则使用在父模板设置的默认内容,当然也可以都不设置,就为空。
子模板设置父模板预留区域的内容:

{ % block 名称 % }
内容 
{% endblock 名称 %}

例子:
base.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>父模板</title>
</head>
<body>
    <h1>Hello World!</h1>
    {# mainbody 可以被子模版替换掉 #}
    {% block mainbody %}
       <p>original</p>
    {% endblock %}
</body>
</html>

children.html

{% extends "base.html" %} 
{% block mainbody %}
    <p>继承了 base.html 文件</p>
{% endblock %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>子模板</title>
</head>
<body>
<P>{{ name }}</P>
</body>
</html>
def hello8(request):
    name = "模板继承"
    return render(request, "children.html", {"name": name})
 path('hello8/', views.hello8),

image.png

Django 模型(数据库)

Django 对各种数据库提供了很好的支持,包括:PostgreSQL、MySQL、SQLite、Oracle。
安装数据库

pip install pymysql

Django ORM

Django 模型使用自带的 ORM。
对象关系映射(Object Relational Mapping,简称 ORM )用于实现面向对象编程语言里不同类型系统的数据之间的转换。
ORM 在业务逻辑层和数据库层之间充当了桥梁的作用。
ORM 是通过使用描述对象和数据库之间的映射的元数据,将程序中的对象自动持久化到数据库中。
image.png
使用 ORM 的好处:

  • 提高开发效率。
  • 不同数据库可以平滑切换。

使用 ORM 的缺点:

  • ORM 代码转换为 SQL 语句时,需要花费一定的时间,执行效率会有所降低。
  • 长期写 ORM 代码,会降低编写 SQL 语句的能力。

ORM 解析过程:

  • 1、ORM 会将 Python 代码转成为 SQL 语句。
  • 2、SQL 语句通过 pymysql 传送到数据库服务端。
  • 3、在数据库中执行 SQL 语句并将结果返回。

ORM 对应关系表:
image.png

数据库配置

默认情况下,配置使用SQLite。若不使用SQLite作为数据库,则需要额外的设置,在settings.py 文件中找到 DATABASES 配置项,将其信息修改为

DATABASES = {
    'default':
    {
        'ENGINE': 'django.db.backends.mysql',    # 数据库引擎
        'NAME': 'db_course', # 数据库名称
        'HOST': '192.168.9.1', # 数据库地址,本机 ip 地址 127.0.0.1
        'PORT': 3306, # 端口
        'USER': 'root',  # 数据库用户名
        'PASSWORD': 'java', # 数据库密码
    }
}

注册数据库
在与 settings.py 同级目录下的 init.py 中引入模块和进行配置

import pymysql
pymysql.install_as_MySQLdb()

定义模型

创建 APP

Django 规定,如果要使用模型,必须要创建一个 app。我们使用以下命令创建一个 HelloWordTestModel的 app:

django-admin.py startapp HelloWordTestModel

image.png
修改 HelloWordTestModel/models.py 文件

from django.db import models


class Stu(models.Model):
    '''自定义Stu表对应的Model类'''
    # 定义属性:默认主键自增id字段可不写
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=16)
    age = models.SmallIntegerField()
    sex = models.CharField(max_length=1)
    classid = models.CharField(max_length=8)

    # 定义默认输出格式
    def __str__(self):
        return "%d:%s:%d:%s:%s" % (self.id, self.name, self.age, self.sex, self.classid)

    # 自定义对应的表名
    class Meta:
        db_table = "stu"

激活模型

要将该应用程序包括在我们的项目中,我们需要在设置中添加对其配置类的引用INSTALLED_APPS。
编辑HelloWorld/settings.py文件,并将该虚线路径添加到该INSTALLED_APPS设置。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'HelloWordTestModel'  # 添加此项
]

初始化

python manage.py migrate   # 创建表结构

python manage.py makemigrations HelloWordTestModel  # 让 Django 知道我们在我们的模型有一些变更
python manage.py migrate HelloWordTestModel   # 创建表结构

image.png
表名默认组成结构为:应用名_类名。
注意:尽管我们没有在 models 给表设置主键,但是 Django 会自动添加一个 id 作为主键。

数据库操作

在 HelloWorld 目录中添加 testdb.py 文件(下面介绍),并修改 urls.py

path(‘testdb/‘, testdb.testdb),
path(‘findStuList/‘, testdb.findStuList),
path(‘updateStu/‘, testdb.updateStu),
path(‘delStuData/‘, testdb.delStuData),

# -*- coding: utf-8 -*-

from django.http import HttpResponse

from HelloWordTestModel.models import Stu


# 数据库操作
def testdb(request):
    stu = Stu(name='zhw',age=18,sex='m',classid=1)
    stu.save()
    return HttpResponse("<p>数据添加成功!</p>")



#查询
def findStuList(request):
    # 初始化
    response = ""
    result = ""
    # 通过objects这个模型管理器的all()获得所有数据行,相当于SQL中的SELECT * FROM
    list = Stu.objects.all()
    # filter相当于SQL中的WHERE,可设置条件过滤结果
    response2 = Stu.objects.filter(id=1)
    # 获取单个对象
    response3 = Stu.objects.get(id=1)
    # 限制返回的数据 相当于 SQL 中的 OFFSET 0 LIMIT 2;
    Stu.objects.order_by('name')[0:2]
    # 数据排序
    Stu.objects.order_by("id")
    # 上面的方法可以连锁使用
    Stu.objects.filter(name="zhw").order_by("id")
    # 输出所有数据
    for var in list:
        result += var.name + " "
    response = result
    return HttpResponse("<p>" + response + "</p>")


# 数据库操作
def updateStu(request):
    # 修改其中一个id=1的name字段,再save,相当于SQL中的UPDATE
    stu = Stu.objects.get(id=1)
    stu.name = 'Google'
    stu.save()
    # 另外一种方式
    # Stu.objects.filter(id=1).update(name='Google')
    # 修改所有的列
    # Stu.objects.all().update(name='Google')
    return HttpResponse("<p>修改成功</p>")


# 数据库操作
def delStuData(request):
    # 删除id=1的数据
    stu = Stu.objects.get(id=1)
    stu.delete()
    # 另外一种方式
    # Stu.objects.filter(id=1).delete()
    # 删除所有数据
    # Stu.objects.all().delete()
    return HttpResponse("<p>删除成功</p>")

image.png
image.png

ORM - 单表

创建应用:

django-admin.py startapp app

主-HelloWorld中settings.py配置
image.png

创建模型

在项目中的 models.py 中添加以下类:

class Book(models.Model):
    id = models.AutoField(primary_key=True) # id 会自动创建,可以手动写入
    title = models.CharField(max_length=32) # 书籍名称
    price = models.DecimalField(max_digits=5, decimal_places=2) # 书籍价格
    publish = models.CharField(max_length=32) # 出版社名称
    pub_date = models.DateField() # 出版时间
$ python manage.py migrate   # 创建表结构

$ python manage.py makemigrations app0  # 让 Django 知道我们在我们的模型有一些变更
$ python manage.py migrate app0   # 创建表结构

数据库操作

# -*- coding: utf-8 -*-
from __future__ import unicode_literals


# Create your views here.
from django.shortcuts import render,HttpResponse
from app import models

# Create your views here.

def hello(request):
    return HttpResponse("这个是app应用")



def add_book(request):
    books = models.Book.objects.create(title="如来神掌",price=200,publish="功夫出版社",pub_date="2010-10-10")
    print(books, type(books)) # Book object (18)
    return HttpResponse("<p>数据添加成功!</p>")


def find_book(request):
    books = models.Book.objects.all()
    print(books,type(books)) # QuerySet类型,类似于list,访问 url 时数据显示在命令行窗口中。
    return HttpResponse("<p>查找成功!</p>")

def order_book(request):
    books = models.Book.objects.order_by("price") # 查询所有,按照价格升序排列
    booksOrder = models.Book.objects.order_by("-price") # 查询所有,按照价格降序排列
    print(books)
    print("\n")
    print(booksOrder)
    return HttpResponse("<p>查找成功!</p>")

def reverse_book(request):
    # 按照价格升序排列:降序再反转
    books = models.Book.objects.order_by("-price").reverse()
    return HttpResponse("<p>查找成功!</p>")

def count_book(request):
    books = models.Book.objects.count() # 查询所有数据的数量
    books = models.Book.objects.filter(price=200).count() # 查询符合条件数据的数量
    return HttpResponse("<p>查找成功!</p>")

def exists_book(request):
    books = models.Book.objects.exists()
    # 报错,判断的数据类型只能为QuerySet类型数据,不能为整型
    books = models.Book.objects.count().exists()
    # 报错,判断的数据类型只能为QuerySet类型数据,不能为模型类对象
    books = models.Book.objects.first().exists()
    return HttpResponse("<p>查找成功!</p>")

#values() 方法用于查询部分字段的数据。
def values_book(request):
    # 查询所有的id字段和price字段的数据
    books = models.Book.objects.values("pk","price")
    print(books[0]["price"],type(books)) # 得到的是第一条记录的price字段的数据
    return HttpResponse("<p>查找成功!</p>")

def exclude_book(request):
    books = models.Book.objects.exclude(pk=5)
    print(books)
    print("//////////////////////////////////////")
    books = models.Book.objects.exclude(publish='天空八部', price=300)
    print(books, type(books))  # QuerySet类型,类似于list。
    return HttpResponse("<p>查找成功!</p>")

def last_book(request):
    books = models.Book.objects.last() # 返回所有数据的最后一条数据
    return HttpResponse("<p>查找成功!</p>")

def first_book(request):
    books = models.Book.objects.first() # 返回所有数据的最后一条数据
    return HttpResponse("<p>查找成功!</p>")


def values_list_book(request):
    # 查询所有的price字段和publish字段的数据
    books = models.Book.objects.values_list("price","publish")
    print(books)
    print(books[0][0],type(books)) # 得到的是第一条记录的price字段的数据
    return HttpResponse("<p>查找成功!</p>")


def filter_book(request):
    # 查询价格为200或者300的数据
    books = models.Book.objects.filter(price__in=[200,300])
    # 查询价格大于200的数据
    books = models.Book.objects.filter(price__gt=200)
    #查询价格大于等于200的数据
    books = models.Book.objects.filter(price__gte=200)
    # 查询价格小于300的数据
    books = models.Book.objects.filter(price__lt=300)
    # 查询价格小于等于300的数据
    books = models.Book.objects.filter(price__lte=300)
    #查询200 - 300 之间
    books = models.Book.objects.filter(price__range=[200, 300])
    #区分大小写的包含
    books = models.Book.objects.filter(title__contains="如来")
    #不区分大小写的包含
    books = models.Book.objects.filter(title__icontains="python")  # 不区分大小写
    #以指定字符开头,= 号后面为字符串
    books = models.Book.objects.filter(title__startswith="如")
    #__endswith 以指定字符结尾,= 号后面为字符串。
    books = models.Book.objects.filter(title__endswith="如")
    #ateField 数据类型的年份
    books = models.Book.objects.filter(pub_date__year=2008)
    #DateField 数据类型的月份
    books = models.Book.objects.filter(pub_date__month=10)
    #DateField 数据类型的天数
    books = models.Book.objects.filter(pub_date__day=0o1)
    return HttpResponse("<p>查找成功!</p>")

#删除
def del_book(request):
    books = models.Book.objects.filter(pk=8).first().delete()
    books = models.Book.objects.filter(pk__in=[1, 2]).delet
    books = models.Book.objects.delete() # 报错
    books = models.Book.objects.all().delete() # 删除成功
    return HttpResponse("<p>查找成功!</p>")

#更新
def update_book(request):
    #模型类的对象.属性 = 更改的属性值 模型类的对象.save()
    books = models.Book.objects.filter(pk=7).first()
    books.price = 400
    books.save()
    #方式二:QuerySet 类型数据.update(字段名=更改的数据)(推荐)
    books = models.Book.objects.filter(pk__in=[7, 8]).update(price=888)
    return HttpResponse("<p>查找成功!</p>")

app中创建urls.py,配置

# -*- coding: utf-8 -*-
from django.conf.urls import url
from django.urls import path

from . import views

urlpatterns = [
    url(r'^$', views.hello),
    path('add_book/', views.add_book),
    path('find_book/', views.find_book),
    path('order_book/', views.order_book),
    path('reverse_book/', views.reverse_book),
    path('count_book/', views.count_book),
    path('exists_book/', views.exists_book),
    path('exclude_book/', views.exclude_book),
    path('last_book/', views.last_book),
    path('first_book/', views.first_book),
    path('values_book/', views.values_book),
    path('values_list_book/', views.values_list_book),
    path('filter_book/', views.filter_book),
    path('del_book/', views.del_book),
    path('update_book/', views.update_book),
]

查询
使用 all() 方法来查询所有内容。返回的是 QuerySet 类型数据,类似于 list,里面放的是一个个模型类的对象,可用索引下标取出模型类的对象。
filter() 方法用于查询符合条件的数据。返回的是 QuerySet 类型数据,类似于 list,里面放的是满足条件的模型类的对象,可用索引下标取出模型类的对象。pk=3 的意思是主键 primary key=3,相当于 id=3。因为 id 在 pycharm 里有特殊含义,是看内存地址的内置函数 id(),因此用 pk。
exclude() 方法用于查询不符合条件的数据。返回的是 QuerySet 类型数据,类似于 list,里面放的是不满足条件的模型类的对象,可用索引下标取出模型类的对象。
get() 方法用于查询符合条件的返回模型类的对象符合条件的对象只能为一个,如果符合筛选条件的对象超过了一个或者没有一个都会抛出错误。
order_by() 方法用于对查询结果进行排序。
返回的是 QuerySet类型数据,类似于list,里面放的是排序后的模型类的对象,可用索引下标取出模型类的对象。
注意:

  • a、参数的字段名要加引号。
  • b、降序为在字段前面加个负号 -

reverse() 方法用于对查询结果进行反转。返回的是 QuerySe t类型数据,类似于 list,里面放的是反转后的模型类的对象,可用索引下标取出模型类的对象。
count() 方法用于查询数据的数量返回的数据是整数。
first() 方法返回第一条数据返回的数据是模型类的对象也可以用索引下标 [0]
last() 方法返回最后一条数据返回的数据是模型类的对象不能用索引下标 [-1],ORM 没有逆序索引。
exists() 方法用于判断查询的结果 QuerySet 列表里是否有数据。返回的数据类型是布尔,有为 true,没有为 false。
注意:判断的数据类型只能为 QuerySet 类型数据,不能为整型和模型类的对象。
values() 方法用于查询部分字段的数据。
返回的是 QuerySet 类型数据,类似于 list,里面不是模型类的对象,而是一个可迭代的字典序列,字典里的键是字段,值是数据。
注意:

  • 参数的字段名要加引号
  • 想要字段名和数据用 values

values_list() 方法用于查询部分字段的数据。
返回的是 QuerySet 类型数据,类似于 list,里面不是模型类的对象,而是一个个元组,元组里放的是查询字段对应的数据。
注意:

  • 参数的字段名要加引号
  • 只想要数据用 values_list

distinct() 方法用于对数据进行去重。
返回的是 QuerySet 类型数据。
注意:

  • 对模型类的对象去重没有意义,因为每个对象都是一个不一样的存在。
  • distinct() 一般是联合 values 或者 values_list 使用。

filter() 方法基于双下划线的模糊查询(exclude 同理)。
注意:filter 中运算符号只能使用等于号 = ,不能使用大于号 > ,小于号 < ,等等其他符号。
in 用于读取区间,= 号后面为列表 。
**
gt 大于号 ,= 号后面为数字。
gte 大于等于,= 号后面为数字。
lt 小于,=号后面为数字。
lte 小于等于,= 号后面为数字。
range 在 … 之间,左闭右闭区间,= 号后面为两个元素的列表。
contains 包含,= 号后面为字符串。
icontains 不区分大小写的包含,= 号后面为字符串。
startswith 以指定字符开头,= 号后面为字符串。
endswith 以指定字符结尾,= 号后面为字符串。
year 是 DateField 数据类型的年份,= 号后面为数字。
month 是DateField 数据类型的月份,= 号后面为数字。
__day 是DateField 数据类型的天数,= 号后面为数字。
删除
方式一:使用模型类的 对象.delete()
返回值:元组,第一个元素为受影响的行数。
方式二:使用 QuerySet 类型数据.delete()(推荐)
返回值:元组,第一个元素为受影响的行数。
注意:**

  • a. Django 删除数据时,会模仿 SQL约束 ON DELETE CASCADE 的行为,也就是删除一个对象时也会删除与它相关联的外键对象。
  • b. delete() 方法是 QuerySet 数据类型的方法,但并不适用于 Manager 本身。也就是想要删除所有数据,不能不写 all。

修改
方式一:
模型类的对象.属性 = 更改的属性值
模型类的对象.save()
方式二:QuerySet 类型数据.update(字段名=更改的数据)(推荐)
返回值:整数,受影响的行数

ORM - 多表(sql方式)

表与表之间的关系可分为以下三种:

  • 一对一: 一个人对应一个身份证号码,数据字段设置 unique。
  • 一对多: 一个家庭有多个人,一般通过外键来实现。
  • 多对多: 一个学生有多门课程,一个课程有很多学生,一般通过第三个表来实现关联。

image.png

# 书籍表 Book:title 、 price 、 pub_date 、 publish(外键,多对一) 、 authors(多对多)
class BookData(models.Model):
    id = models.AutoField(primary_key=True)  # id 会自动创建,可以手动写入
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    pub_date = models.DateField()
    # 外键 外键在一对多的多中设置:models.ForeignKey("关联类名", on_delete=models.CASCADE)。
    # Django1.1 版本不需要联级删除:on_delete=models.CASCADE,Django2.2 需要。
    publish = models.ForeignKey("Publish", on_delete=models.CASCADE)
    # 多对多
    authors = models.ManyToManyField("Author")

    def __str__(self):
        return "%d:%s:%d:%s:%s" % (self.id, self.title, self.price, self.publish, self.pub_date)

    # 自定义对应的表名
    class Meta:
        db_table = "pc_book_data"
        verbose_name = '书籍信息'
        verbose_name_plural = '书籍信息管理'


# 出版社表 Publish:name 、 city 、 email
class Publish(models.Model):
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=64)
    email = models.EmailField()

    def __str__(self):
        return "%d:%s:%s:%s" % (self.id, self.name, self.city, self.email)

    class Meta:
        db_table = "pc_publish"


# 作者表 Author:name 、 age 、 au_detail(一对一)
class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.SmallIntegerField()
    # 一对一 OneToOneField = ForeignKey(...,unique=True)设置一对一。
    au_detail = models.OneToOneField("AuthorDetail", on_delete=models.CASCADE)

    def __str__(self):
        return "%d:%s:%s:%s" % (self.id, self.name, self.age, self.au_detail)

    class Meta:
        db_table = "pc_author"


# 作者详情表 AuthorDetail:gender 、 tel 、 addr 、 birthday
class AuthorDetail(models.Model):
    gender_choices = (
        (0, "女"),
        (1, "男"),
        (2, "保密"),
    )
    gender = models.SmallIntegerField(choices=gender_choices)
    tel = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)
    birthday = models.DateField()

    def __str__(self):
        return "%d:%s:%s:%s:%s" % (self.id, self.gender, self.tel, self.addr, self.birthday)

    class Meta:
        db_table = "pc_authorDetail"
# -*- coding: utf-8 -*-
from django.http import HttpResponse

from app import models

from django.db import connection

"""
一对多(外键 ForeignKey)
方式一: 传对象的形式,返回值的数据类型是对象,书籍对象。
步骤:
    a. 获取出版社对象
    b. 给书籍的出版社属性 pulish 传出版社对象
"""


def add_book(request):
    #  获取出版社对象
    pub_obj = models.Publish.objects.filter(pk=1).first()
    #  给书籍的出版社属性publish传出版社对象
    book = models.BookData.objects.create(title="Python教程", price=1, pub_date="2010-10-10", publish=pub_obj)
    print(book, type(book))
    return HttpResponse(book)


"""
方式二: 传对象 id 的形式(由于传过来的数据一般是 id,所以传对象 id 是常用的)。
一对多中,设置外键属性的类(多的表)中,MySQL 中显示的字段名是:外键属性名_id。
返回值的数据类型是对象,书籍对象。
步骤:
    a. 获取出版社对象的 id
    b. 给书籍的关联出版社字段 pulish_id 传出版社对象的 id
"""


def add_book_One(request):
    #  获取出版社对象
    pub_obj = models.Publish.objects.filter(pk=1).first()
    #  给书籍的出版社属性publish传出版社对象
    book = models.BookData.objects.create(title="Python教程", price=200, pub_date="2010-10-10", publish=pub_obj.id)
    print(book, type(book))
    return HttpResponse(book)


"""
多对多(ManyToManyField)
    方式一: 传对象形式,无返回值。
    步骤:
        a. 获取作者对象
        b. 获取书籍对象
        c. 给书籍对象的 authors 属性用 add 方法传作者对象
"""


def manyToMany_add_book(request):
    #  获取作者对象
    chong = models.Author.objects.filter(name="令狐冲").first()
    ying = models.Author.objects.filter(name="任盈盈").first()
    #  获取书籍对象
    book = models.BookData.objects.filter(title="Python教程").first()
    #  给书籍对象的 authors 属性用 add 方法传作者对象
    book.authors.add(chong, ying)
    return HttpResponse(book)


"""
方式二: 传对象id形式,无返回值。
步骤:
    a. 获取作者对象的 id
    b. 获取书籍对象
    c. 给书籍对象的 authors 属性用 add 方法传作者对象的 id
"""


def manyToMany_add_book_one(request):
    #  获取作者对象
    chong = models.Author.objects.filter(name="令狐冲").first()
    #  获取作者对象的id
    pk = chong.pk
    #  获取书籍对象
    book = models.Book.objects.filter(title="冲灵剑法").first()
    #  给书籍对象的 authors 属性用 add 方法传作者对象的id
    book.authors.add(pk)

#原生sql
def findBookDetail(requests):
    cursor = connection.cursor()
    # 插入操作
    cursor.execute("insert into pc_book_data(title,price,pub_date,publish_id) values('传说中的申小五',1,'2021-11-11',1)")
    cursor.execute("insert into pc_book_data(title,price,pub_date,publish_id) values('传说中的申小五',1,'2021-11-11',1)")
    # 更新操作
    cursor.execute("update pc_book_data set title='传说中的申小五1' where title='传说中的申小五'")
    # 删除操作
    cursor.execute("delete from pc_book_data where title='传说中的申小五1'")
    # 查询操作
    cursor.execute("select * from pc_book_data")
    #raw = cursor.fetchone()  # 返回结果行游标直读向前,读取一条
    result=cursor.fetchall()  # 读取所有
    return HttpResponse(result)
    path('manyToMany_add_book_one/', testOperateDb.manyToMany_add_book_one),
    path('manyToMany_add_book/', testOperateDb.manyToMany_add_book),
    path('testOperateDb_add_book_One/', testOperateDb.add_book_One),
    path('testOperateDb_add_book/', testOperateDb.add_book),
    path('findBookDetail/', testOperateDb.findBookDetail),

image.pngORM - 聚合与分组查询

聚合查询函数是对一组值执行计算,并返回单个值。
Django 使用聚合查询前要先从 django.db.models 引入 Avg、Max、Min、Count、Sum(首字母大写)。

from django.db.models import Avg,Max,Min,Count,Sum  #   引入函数

聚合查询返回值的数据类型是字典。
聚合函数 aggregate() 是 QuerySet 的一个终止子句, 生成的一个汇总值,相当于 count()。
使用 aggregate() 后,数据类型就变为字典,不能再使用 QuerySet 数据类型的一些 API 了。
日期数据类型(DateField)可以用 Max 和 Min。
返回的字典中:键的名称默认是(属性名称加上__聚合函数名),值是计算出来的聚合值。
如果要自定义返回字典的键的名称,可以起别名:

aggregate(别名 = 聚合函数名("属性名称"))
# -*- coding: utf-8 -*-
from django.db.models import Avg, Max, Min, Count, Sum  # 引入函数

from app import models


# 计算所有图书的平均价格:
def avgData(requests):
    res = models.BookData.objects.aggregate(Avg("price"))
    print(res, type(res))


# 计算所有图书的数量、最贵价格和最便宜价格:

def countData(requests):
    res = models.Book.objects.aggregate(c=Count("id"), max=Max("price"), min=Min("price"))
    print(res, type(res))

建议:
模型查询方式,底层需要转换成sql查询,性能相对原生sql低,单表操作简单,多表操作复杂,可维护性不高,因而建议,开发中使用原生sql编写。

Django 表单

HTTP 请求

HTTP协议以”请求-回复”的方式工作。客户发送请求时,可以在请求中附加数据。服务器通过解析请求,就可以获得客户传来的数据,并根据URL来提供特定的服务。

GET 方法

search.py

# -*- coding: utf-8
from django.http import HttpResponse
from django.shortcuts import render


# 表单
def search_form(request):
    return render(request, 'search_form.html')


# 接收请求数据
def search(request):
    request.encoding = 'utf-8'
    if 'keyword' in request.GET and request.GET['keyword']:
        message = '你搜索的内容为: ' + request.GET['keyword']
    else:
        message = '你提交了空表单'
    return HttpResponse(message)

配置路径

url(r'^search-form/$', search.search_form),
url(r'^search/$', search.search),

search_form.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/search/" method="get">
        <input type="text" name="keyword">
        <input type="submit" value="搜索">
    </form>
</body>
</html>

image.png
image.png

POST 方法

# 接收POST请求数据
def search_post(request):
    ctx = {}
    if request.POST:
        ctx['rlt'] = request.POST['keyword']
    return render(request, "search_post.html", ctx)
url(r'^search_post/$', search.search_post),

search_post.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/search-post/" method="post">
        {% csrf_token %}
        <input type="text" name="keyword">
        <input type="submit" value="搜索">
    </form>

    <p>{{ rlt }}</p>
</body>
</html>

注意:
表格后面还有一个{% csrf_token %}的标签。csrf 全称是 Cross Site Request Forgery。这是Django提供的防止伪装提交请求的功能。POST 方法提交的表格,必须有此标签。
image.png

Django 视图

一个视图函数,简称视图,是一个简单的 Python 函数,它接受 Web 请求并且返回 Web 响应。响应可以是一个 HTML 页面、一个 404 错误页面、重定向页面、XML 文档、或者一张图片…无论视图本身包含什么逻辑,都要返回响应。代码写在哪里都可以,只要在 Python 目录下面,一般放在项目的 views.py 文件中。每个视图函数都负责返回一个 HttpResponse 对象,对象中包含生成的响应。视图层中有两个重要的对象:请求对象(request)与响应对象(HttpResponse)。

HttpRequest 对象

1、GET

数据类型是 QueryDict,一个类似于字典的对象,包含 HTTP GET 的所有参数。
有相同的键,就把所有的值放到对应的列表里。取值格式:对象.方法

2、POST

数据类型是 QueryDict,一个类似于字典的对象,包含 HTTP POST 的所有参数。常用于 form 表单,form 表单里的标签 name 属性对应参数的键,value 属性对应参数的值。取值格式: 对象.方法get():返回字符串,如果该键对应有多个值,取出该键的最后一个值。
注意:
post 请求返回 403
1. 解决:
导入模块:

from django.views.decorators.csrf import csrf_exempt

在函数前面添加修饰器:
@csrf_exempt

@csrf_exempt
def postRequestData(request):
    name = request.POST.get("name")

2. 原因:
当采用客户端象 django 的服务器提交 post 请求时,会得到403,权限异常。

3、body

数据类型是二进制字节流,是原生请求体里的参数内容,在 HTTP 中用于 POST,因为 GET 没有请求体。
在 HTTP 中不常用,而在处理非 HTTP 形式的报文时非常有用,例如:二进制图片、XML、Json 等。

4、path

获取 URL 中的路径部分,数据类型是字符串。 name = request.path

HttpResponse 对象

响应对象主要有三种形式:HttpResponse()、render()、redirect()。
HttpResponse(): 返回文本,参数为字符串,字符串中写文本内容。如果参数为字符串里含有 html 标签,也可以渲染。
render(): 返回文本,第一个参数为 request,第二个参数为字符串(页面名称),第三个参数为字典(可选参数,向页面传递的参数:键为页面参数名,值为views参数名)。
redirect():重定向,跳转新页面。参数为字符串,字符串中填写页面路径。一般用于 form 表单提交后,跳转到新页面。

Django 路由

路由简单的来说就是根据用户请求的 URL 链接来判断对应的处理程序,并返回处理结果,也就是 URL 与 Django 的视图建立映射关系。
Django 路由在 urls.py 配置,urls.py 中的每一条配置对应相应的处理方法。
当一个url请求过来之后
1、先到项目主目录下的urls内。
2、由这个url做处理分发给其他app内的urls。
如:
一级路由:主目录urls内引入include

  url(r'^testModel/',include("HelloWordTestModel.urls")),

HelloWordTestModel下创建urls文件

from django.conf.urls import url,include
from django.urls import path,re_path

from . import views

urlpatterns = [
    url(r'^$', views.hello),
    path('childrenHello/', views.childrenHello)

]

创建视图 HelloWordTestModel下修改views文件。

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.shortcuts import render

# Create your views here.

from django.http import HttpResponse

def childrenHello(request):
    return HttpResponse("业务-childrenHello")

def hello(request):
    return HttpResponse("业务-hello")

image.png

正则路径中的有名分组

语法:
(?P<组名>正则表达式)

re_path(“^index/(?P[0-9]{4})/(?P[0-9]{2})/$”, views.index),

正则路径中的有名分组

路由分发(include)

存在问题:Django 项目里多个app目录共用一个 urls 容易造成混淆,后期维护也不方便。
解决:使用路由分发(include),让每个app目录都单独拥有自己的 urls。
步骤:

  • 1、在每个 app 目录里都创建一个 urls.py 文件。
  • 2、在项目名称目录下的 urls 文件里,统一将路径分发给各个 app 目录。
    path("app01/", include("app01.urls")),
    path("app02/", include("app02.urls")),
    

    路径参数

    path('articles/<int:year>/<int:month>/', views.requestParamData),#两个参数
    path('articles/data<int:year>/', views.requestParamData),#一个参数 另一个默认惨呼
    path('articles/<int:year>/', views.requestParamDataOne),#一个参数
    
    ```python def requestParamData(request,year,month=18): return HttpResponse(str(year)+”————-“+str(month))

def requestParamDataOne(request,year): return HttpResponse(str(year))

<a name="FaDml"></a>
### URL的反向解析

- 解决:在做链接时,通过指向urlconf的名称,动态生成链接地址
- 视图:使用django.urls.reverse()函数
- 模板:使用url模板标签
```python
path('articles/<int:year>/', views.requestParamDataOne,name='news-year-archive'),#一个参数
path('redirectYear/', views.redirect_to_year),
path('redirectIndex/', views.index),
from django.http import HttpResponseRedirect
from django.urls import reverse

def redirect_to_year(request):
    # ...
    year = 2019
    # ...
    return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))

from django.shortcuts import redirect

def index(request):
    year = 2019
    return redirect(reverse('news-year-archive',args=(year,)))

404错误

  • 404的错误页面,在模板目录中创建一个404.html的页面
  • 在配置文件中 settings.py 配置 DEBUG = False
  • 在配置文件中 settings.py 配置 TEMPLATES = [{‘DIRS’: [os.path.join(BASE_DIR,’templates’)] }]
  • 同时需要在项目的根目录下创建文件夹templates,并且在此目录下创建一个404.html文件
  • 在出现404的情况时,自动寻找404页面。
  • 也可以在视图函数中 手动报出404错误,带提醒信息

在视图函数中也可以指定返回一个404
注意 Http404需要在django.http的模块中引入

响应404 raise Http404(‘异常’)

模板中 404.html

<!DOCTYPE html>
<html>
<head>
    <title>404</title>
</head>
<body>
    <center>
        <h2>404 not found</h2>
        <h3>{ {   exception   } }</h3>
    </center>
</body>
</html>

Django Admin 管理工具

介绍

Django 提供了基于 web 的管理工具。
Django 自动管理工具是 django.contrib 的一部分。你可以在项目的 settings.py 中的 INSTALLED_APPS 看到它

INSTALLED_APPS = [
    'django.contrib.admin',#管理网站。你会很快使用它。
    'django.contrib.auth',#认证系统
    'django.contrib.contenttypes',#内容类型的框架。
    'django.contrib.sessions',#会话框架
    'django.contrib.messages',#消息框架。
    'django.contrib.staticfiles',#管理静态文件的框架。
    'HelloWordTestModel' #自定义引用
]

引入第三方组件也需要在此处进行配置。

激活管理工具

通常我们在生成项目时会在 urls.py 中自动设置好,我们只需去掉注释即可。
主引用里面:

# urls.py
from django.conf.urls import url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
]

可以通过命令 python manage.py createsuperuser 来创建超级用户。

   python manage.py createsuperuser

    # 输入您所需的用户名,然后按Enter键。
    Username: admin

    # 然后将提示您输入所需的电子邮件地址:
    Email address: admin@example.com

    # 最后一步是输入你的密码(>=8位)。您将被要求输入密码两次,第二次作为第一次的确认
    Password: **********
    Password (again): *********
    Superuser created successfully.

设置时区和语言

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

自定义的应用程序的加入到后台管理

打开HelloWordTestModel/admin.py 文件

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.contrib import admin

# Register your models here.
from django.contrib import admin
from HelloWordTestModel.models import Stu

# Register your models here.
admin.site.register(Stu)

image.png
自定义设计后台管理
修改HelloWordTestModel/admin.py

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.contrib import admin

# Register your models here.
from django.contrib import admin
from HelloWordTestModel.models import Stu

# 第一种
# admin.site.register(Stu)

#Stu模型的管理器(装饰器写法)
@admin.register(Stu)
class StuAdmin(admin.ModelAdmin):
    #listdisplay设置要显示在列表中的字段(id字段是Django模型的默认主键)
    list_display = ('id','name','age','sex','classid')

    #设置哪些字段可以点击进入编辑界面
    list_display_links = ('id','name')

    #list_per_page设置每页显示多少条记录,默认是100条
    list_per_page = 10

    #ordering设置默认排序字段,负号表示降序排序
    ordering = ('id',)  #-id降序

    #list_editable 设置默认可编辑字段
    #list_editable = ['age','sex','classid']

    #其他请详见手册文档说明

image.png

Django cookie 与 session

Cookies

一个 Web 服务器可以分配一个唯一的 session 会话 ID 作为每个 Web 客户端的 cookie,对于客户端的后续请求可以使用接收到的 cookie 来识别。
在Web开发中,使用 session 来完成会话跟踪,session 底层依赖 Cookie 技术。
image.png
Django 中 Cookie 的语法
设置 cookie:

rep.set_cookie(key,value,...) 
rep.set_signed_cookie(key,value,salt='加密盐',...)

获取 cookie:

request.COOKIES.get(key)

删除 cookie:

rep =HttpResponse || render || redirect 
rep.delete_cookie(key)

案例:

#登录
def login(request):
    if request.method == "GET":
        return render(request, "login.html")
    username = request.POST.get("username")
    password = request.POST.get("pwd")
    user_obj = models.UserInfo.objects.filter(username=username, password=password).first()
    print(user_obj.username)
    if not user_obj:
        return redirect("/testModel/login/")
    else:
        rep = redirect("/testModel/index/")
        rep.set_cookie("is_login", True)
        return rep

#注册
def register(request):
    if request.method == "GET":
        return render(request, "register.html")
    username = request.POST.get("username")
    password = request.POST.get("pwd")
    user_obj = models.UserInfo.objects.filter(username=username, password=password).first()
    if not user_obj:
        userInfo = models.UserInfo(username=username, password=password)
        userInfo.save()
        return HttpResponse("注册成功")
    else:
        rep = redirect("/testModel/login/")
        return rep


def index(request):
    print(request.COOKIES.get('is_login'))
    status = request.COOKIES.get('is_login')  # 收到浏览器的再次请求,判断浏览器携带的cookie是不是登录成功的时候响应的 cookie
    if not status:
        return redirect('/testModel/login/')
    return render(request, "index.html")


def logout(request):
    rep = redirect('/testModel/login/')
    rep.delete_cookie("is_login")
    return rep  # 点击注销后执行,删除cookie,不再保存用户状态,并弹到登录页面


def order(request):
    print(request.COOKIES.get('is_login'))
    status = request.COOKIES.get('is_login')
    if not status:
        return redirect('/testModel/login/')
    return render(request, "order.html")

HelloWordTestModel.urls.py

path('login/', views.login),
path('register/', views.register),
path('index/', views.index),
path('logout/', views.logout),
path('order/', views.order)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>


<h2>index 页面。。。</h2>


<a href="/logout/">注销</a>

</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<h3>用户登录</h3>
<form action="" method="post">
    {% csrf_token %}
    <p>用户名: <input type="text" name="username"></p>
    <p>密码: <input type="password" name="pwd"></p>
    <input type="submit">
</form>
</body>
</html>

order.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>


<h2>order 页面。。。</h2>
<a href="/logout/">注销</a>

</body>
</html>

register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册</title>
</head>
<body>
<h3>用户注册</h3>
<form action="" method="post">
    {% csrf_token %}
    <p>用户名: <input type="text" name="username"></p>
    <p>密码: <input type="password" name="pwd"></p>
    <input type="submit">
</form>
</body>
</html>

image.png

Session

服务器在运行时可以为每一个用户的浏览器创建一个其独享的 session 对象,由于 session 为用户浏览器独享,所以用户在访问服务器的 web 资源时,可以把各自的数据放在各自的 session 中,当用户再去访问该服务器中的其它 web 资源时,其它 web 资源再从用户各自的 session 中取出数据为用户服务。
image.png

工作原理

  • a. 浏览器第一次请求获取登录页面 login。
  • b. 浏览器输入账号密码第二次请求,若输入正确,服务器响应浏览器一个 index 页面和一个键为 sessionid,值为随机字符串的 cookie,即 set_cookie (“sessionid”,随机字符串)。
  • c. 服务器内部在 django.session 表中记录一条数据。django.session 表中有三个字段。
    • session_key:存的是随机字符串,即响应给浏览器的 cookie 的 sessionid 键对应的值。
    • session_data:存的是用户的信息,即多个 request.session[“key”]=value,且是密文。
    • expire_date:存的是该条记录的过期时间(默认14天)
  • d. 浏览器第三次请求其他资源时,携带 cookie :{sessionid:随机字符串},服务器从 django.session 表中根据该随机字符串取出该用户的数据,供其使用(即保存状态)。

注意: django.session 表中保存的是浏览器的信息,而不是每一个用户的信息。 因此, 同一浏览器多个用户请求只保存一条记录(后面覆盖前面),多个浏览器请求才保存多条记录。
cookie 弥补了 http 无状态的不足,让服务器知道来的人是”谁”,但是 cookie 以文本的形式保存在浏览器端,安全性较差,且最大只支持 4096 字节,所以只通过 cookie 识别不同的用户,然后,在对应的 session 里保存私密的信息以及超过 4096 字节的文本。
session 设置:

request.session[“key”] = value

执行步骤:

  • a. 生成随机字符串
  • b. 把随机字符串和设置的键值对保存到 django_session 表的 session_key 和 session_data 里
  • c. 设置 cookie:set_cookie(“sessionid”,随机字符串) 响应给浏览器

session 获取:

request.session.get(‘key’)

执行步骤:

  • a. 从 cookie 中获取 sessionid 键的值,即随机字符串。
  • b. 根据随机字符串从 django_session 表过滤出记录。
  • c. 取出 session_data 字段的数据。

session 删除,删除整条记录(包括 session_key、session_data、expire_date 三个字段):

request.session.flush()

删除 session_data 里的其中一组键值对:

del request.session[“key”]

执行步骤:

  • a. 从 cookie 中获取 sessionid 键的值,即随机字符串
  • b. 根据随机字符串从 django_session 表过滤出记录
  • c. 删除过滤出来的记录

image.png
案例:
HelloWordTestModel.session_views.py

from django.shortcuts import render, redirect

from HelloWordTestModel import models


def login(request):
    if request.method == "GET":
        return render(request, "login.html")
    username = request.POST.get("username")
    password = request.POST.get("pwd")

    user_obj = models.UserInfo.objects.filter(username=username, password=password).first()
    print(user_obj.username)

    if not user_obj:
        return redirect("/testModel/session_login/")
    else:
        request.session['is_login'] = True
        request.session['user1'] = username
        return redirect("/testModel/s_index/")


def s_index(request):
    status = request.session.get('is_login')
    if not status:
        return redirect('/testModel/session_login/')
    return render(request, "s_index.html")


def s_logout(request):
   # del request.session["is_login"] # 删除session_data里的一组键值对
    request.session.flush() # 删除一条记录包括(session_key session_data expire_date)三个字段
    return redirect('/testModel/session_login/')

HelloWordTestModel.urls.py

path('session_login/', session_views.login),
path('s_index/', session_views.s_index),
path('s_logout/', session_views.s_logout),

s_index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>session_index 页面。。。{{ request.session.user1 }}</h2>
<a href="/s_logout/">注销</a>
</body>
</html>

image.png

image.png
注销:
image.png
image.png

Django Form 组件

Django Form 组件用于对页面进行初始化,生成 HTML 标签,此外还可以对用户提交对数据进行校验(显示错误信息)。
报错信息显示顺序:

  • 先显示字段属性中的错误信息,然后再显示局部钩子的错误信息。
  • 若显示了字段属性的错误信息,就不会显示局部钩子的错误信息。
  • 若有全局钩子,则全局钩子是等所有的数据都校验完,才开始进行校验,并且全局钩子的错误信息一定会显示。

使用 Form 组件,需要先导入 forms:
创建自定义form验证

from django import forms
from django.core.exceptions import ValidationError
from HelloWordTestModel import models

class EmpForm(forms.Form):
    name = forms.CharField(min_length=5, label="姓名", error_messages={"required": "该字段不能为空!","min_length": "用户名太短。"})
    age = forms.IntegerField(label="年龄")
    salary = forms.DecimalField(max_digits=5, decimal_places=2, label="工资")
    r_salary = forms.DecimalField(max_digits=5, decimal_places=2, label="请再输入工资")

    def clean_name(self):  # 局部钩子
        val = self.cleaned_data.get("name")
        if val.isdigit():
            raise ValidationError("用户名不能是纯数字")
        elif models.Emp.objects.filter(name=val):
            raise ValidationError("用户名已存在!")
        else:
            return val

    def clean(self):  # 全局钩子 确认两次输入的工资是否一致。
        val = self.cleaned_data.get("salary")
        r_val = self.cleaned_data.get("r_salary")
        if val == r_val:
            return self.cleaned_data
        else:
            raise ValidationError("请确认工资是否一致。")
def add_emp(request):
    if request.method == "GET":
        form = EmpForm()  # 初始化form对象
        return render(request, "add_emp.html", {"form":form})
    else:
        form = EmpForm(request.POST)  # 将数据传给form对象
        if form.is_valid():  # 进行校验
            data = form.cleaned_data
            data.pop("r_salary")
            models.Emp.objects.create(**data)
            return redirect("/testModel/index/")
        else:  # 校验失败
            clear_errors = form.errors.get("__all__")  # 获取全局钩子错误信息
            return render(request, "add_emp.html", {"form": form, "clear_errors": clear_errors})
urls.py
path('add_emp/', views.add_emp)
models.py
class Emp(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.SmallIntegerField()
    salary = models.SmallIntegerField()
    class Meta:
        db_table = "py_emp"

image.png

Django 中间件

Django 中间件是修改 Django request 或者 response 对象的钩子,可以理解为是介于 HttpRequest 与 HttpResponse 处理之间的一道处理过程。相当于拦截器和处理器。
Django 中间件作用:

  • 修改请求,即传送到 view 中的 HttpRequest 对象。
  • 修改响应,即 view 返回的 HttpResponse 对象。

中间件组件配置在 settings.py 文件的 MIDDLEWARE 选项列表中。
配置中的每个字符串选项都是一个类,也就是一个中间件。
Django 默认的中间件配置:

#默认的中间件
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

自定义中间件

中间件可以定义四个方法,分别是:

process_request(self,request) #请求
process_view(self, request, view_func, view_args, view_kwargs)#视图
process_exception(self, request, exception)#异常
process_response(self, request, response)#响应

自定义中间的步骤:
在 HelloWordTestModel目录下新建一个 py 文件,名字自定义,并在该 py 文件中导入 MiddlewareMixin,加入上述四种方法。

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("md1  process_request 方法。", id(request))  # 在视图之前执行

    def process_response(self, request, response):
        # 基于请求响应
        print("md1  process_response 方法!", id(request))  # 在视图之后
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("md1  process_view 方法!")  # 在视图之前执行 顺序执行
        # return view_func(request)

    def process_exception(self, request, exception):  # 引发错误 才会触发这个方法
        print("md1  process_exception 方法!")
        # return HttpResponse(exception) #返回错误信息

process_request 方法

process_request 方法有一个参数 request,这个 request 和视图函数中的 request 是一样的。
process_request 方法的返回值可以是 None 也可以是 HttpResponse 对象。

  • 返回值是 None 的话,按正常流程继续走,交给下一个中间件处理。
  • 返回值是 HttpResponse 对象,Django 将不执行后续视图函数之前执行的方法以及视图函数,直接以该中间件为起点,倒序执行中间件,且执行的是视图函数之后执行的方法。

process_request 方法是在视图函数之前执行的。
当配置多个中间件时,会按照 MIDDLEWARE中 的注册顺序,也就是列表的索引值,顺序执行。
不同中间件之间传递的 request 参数都是同一个请求对象。

process_response

process_response 方法有两个参数,一个是 request,一个是 response,request 是请求对象,response 是视图函数返回的 HttpResponse 对象,该方法必须要有返回值,且必须是response。
process_response 方法是在视图函数之后执行的。
当配置多个中间件时,会按照 MIDDLEWARE 中的注册顺序,也就是列表的索引值,倒序执行。

process_view

process_view 方法有四个参数:

  • request 是 HttpRequest 对象。
  • view_func 是 Django 即将使用的视图函数。
  • view_args 是将传递给视图的位置参数的列表。
  • view_kwargs 是将传递给视图的关键字参数的字典。

view_args 和 view_kwargs 都不包含第一个视图参数(request)。
process_view 方法是在视图函数之前,process_request 方法之后执行的。
返回值可以是 None、view_func(request) 或 HttpResponse 对象。

  • 返回值是 None 的话,按正常流程继续走,交给下一个中间件处理。
  • 返回值是 HttpResponse 对象,Django 将不执行后续视图函数之前执行的方法以及视图函数,直接以该中间件为起点,倒序执行中间件,且执行的是视图函数之后执行的方法。
  • c.返回值是 view_func(request),Django 将不执行后续视图函数之前执行的方法,提前执行视图函数,然后再倒序执行视图函数之后执行的方法。
  • 当最后一个中间件的 process_request 到达路由关系映射之后,返回到第一个中间件 process_view,然后依次往下,到达视图函数。

    process_exception

    process_exception(request, exception)

参数说明:

  • request 是 HttpRequest 对象。
  • exception 是视图函数异常产生的 Exception 对象。

process_exception 方法只有在视图函数中出现异常了才执行,按照 settings 的注册倒序执行。
在视图函数之后,在 process_response 方法之前执行。
process_exception 方法的返回值可以是一个 None 也可以是一个 HttpResponse 对象。返回值是 None,页面会报 500 状态码错误,视图函数不会执行。
process_exception 方法倒序执行,然后再倒序执行 process_response 方法。返回值是 HttpResponse 对象,页面不会报错,返回状态码为 200。
视图函数不执行,该中间件后续的 process_exception 方法也不执行,直接从最后一个中间件的 process_response 方法倒序开始执行。
若是 process_view 方法返回视图函数,提前执行了视图函数,且视图函数报错,则无论 process_exception 方法的返回值是什么,页面都会报错, 且视图函数和 process_exception 方法都不执行。
直接从最后一个中间件的 process_response 方法开始倒序执行:
settings.py中MIDDLEWARE配置自定义中间件:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'HelloWordTestModel.middlewares.MD1' #自定义中间件
]

image.png

Django 视图- (函数的视图) 与 (类的视图)

FBV(function base views) 基于函数的视图,就是在视图里使用函数处理请求。
CBV(class base views) 基于类的视图,就是在视图里使用类处理请求。

FBV

基于函数的视图其实我们前面章节一直在使用,就是使用了函数来处理用户的请求,查看以下实例:
路由配置:

#urls.py 文件
urlpatterns = [
    path('hello8/', views.hello8),
]
def hello8(request):
    name = "模板继承"
    return render(request, "children.html", {"name": name})

CBV

基于类的视图,就是使用了类来处理用户的请求,不同的请求我们可以在类中使用不同方法来处理,这样大大的提高了代码的可读性。
定义的类要继承父类 View,所以需要先引入库:

from django.views import View

执行对应请求的方法前会优先执行 dispatch 方法(在get/post/put…方法前执行),dispatch() 方法会根据请求的不同调用相应的方法来处理。
其实,在我们都知道 Django 的 url 是将一个请求分配给可调用的函数的,而不是一个类,那是如何实现基于类的视图的呢? 主要还是通过父类 View 提供的一个静态方法 as_view()as_view 方法是基于类的外部接口, 他返回一个视图函数,调用后请求会传递给 dispatch 方法,dispatch 方法再根据不同请求来处理不同的方法。
路由配置:
urls.py 文件

urlpatterns = [
    path('hello9/', views.Login.as_view()),
]
class Login(View):

    def get(self, request):
        return HttpResponse("GET 方法")

    def post(self, request):
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")
        if user == "zhw" and pwd == "123456":
            return HttpResponse("POST 方法")
        else:
            return HttpResponse("POST 方法 1")
http://127.0.0.1:8000/hello9/

image.png

Django-验证码

  • 在用户注册、登录页面,为了防止暴力请求,可以加入验证码功能,如果验证码错误,则不需要继续处理,可以减轻一些服务器的压力
  • 使用验证码也是一种有效的防止crsf的方法
  • 需要安装扩展:pip install pillow

验证码视图

  • 新建viewsUtil.py,定义函数verifycode
  • Image表示画布对象
  • ImageDraw表示画笔对象
  • ImageFont表示字体对象,ubuntu的字体路径为“/usr/share/fonts/truetype/freefont” ```python

    -*- coding: utf-8

    import io

from django.http import HttpResponse

def verifycode(request):

# 引入绘图模块
from PIL import Image, ImageDraw, ImageFont
# 引入随机函数模块
import random
# 定义变量,用于画面的背景色、宽、高
bgcolor = (random.randrange(20, 100), random.randrange(
    20, 100), random.randrange(20, 100))
width = 100
height = 50
# 创建画面对象
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)
# 定义验证码的备选值
str = '1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
# 随机选取4个值作为验证码
rand_str = ''
for i in range(0, 4):
    rand_str += str[random.randrange(0, len(str))]
# 构造字体对象,按照目录找自己电脑的字体,关于后缀,otf或者TTF
font = ImageFont.truetype(r'C:\Windows\Fonts\Arial.TTF', 40)
# 构造字体颜色
fontcolor1 = (255, random.randrange(0, 255), random.randrange(0, 255))
fontcolor2 = (255, random.randrange(0, 255), random.randrange(0, 255))
fontcolor3 = (255, random.randrange(0, 255), random.randrange(0, 255))
fontcolor4 = (255, random.randrange(0, 255), random.randrange(0, 255))
# 绘制4个字
draw.text((5, 2), rand_str[0], font=font, fill=fontcolor1)
draw.text((25, 2), rand_str[1], font=font, fill=fontcolor2)
draw.text((50, 2), rand_str[2], font=font, fill=fontcolor3)
draw.text((75, 2), rand_str[3], font=font, fill=fontcolor4)
# 释放画笔
del draw
# 存入session,用于做进一步验证
request.session['verifycode'] = rand_str
# 内存文件操作
buf = io.BytesIO()
# 将图片保存在内存中,文件类型为png
im.save(buf, 'png')
# 将内存中的图片数据返回给客户端,MIME类型为图片png
return HttpResponse(buf.getvalue(), 'img/png')
<a name="yiJUT"></a>
### 配置url
```python
url(r'^verifycode/$',viewsUtil.verifycode),#验证码
path('verifycodeValid/', views.verifycodeValid), #验证验证码
<div>
    <form method='post' action='/verifycodeValid/'>
         {% csrf_token %}
        <input type="text" name="vc">
        <img id='verifycode' src="/verifycode/?1" alt="CheckCode"/>
        <span id='verifycodeChange'>看不清,换一个</span>
        <br>
        <input type="submit" value="提交">
    </form>
</div>

<script type="text/javascript" src="/static/js/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
    $(function(){
        $('#verifycodeChange').css('cursor','pointer').click(function() {
            $('#verifycode').attr('src',$('#verifycode').attr('src')+1)
        });
    });
</script>

image.png

Django-文件上传

  • 当Django在处理文件上传的时候,文件数据被保存在request.FILES

FILES中的每个键为中的name
注意:FILES只有在请求的方法为POST 且提交的

带有enctype=”multipart/form-data” 的情况下才会包含数据。

  • 否则,FILES 将为一个空的类似于字典的对象

    (1) 自定义上传的模板代码

    ```python from PIL import Image import time,os

def upload(request): if request.method == “GET”: return render(request, “upload.html”) ‘’’执行图片的上传’’’ myfile = request.FILES.get(“myfile”,None) if not myfile: return HttpResponse(“没有上传文件信息”) filename = str(time.time())+”.”+myfile.name.split(‘.’).pop()

## 打开特定的文件进行二进制的写操作
destination = open("./statics/pics/"+filename,"wb+")
for chunk in myfile.chunks():      # 分块写入文件
    destination.write(chunk)
destination.close()

# 执行图片缩放
im = Image.open("./statics/pics/"+filename)
# 缩放到75*75(缩放后的宽高比例不变):
im.thumbnail((75, 75))
# 把缩放后的图像用jpeg格式保存:
im.save("./statics/pics/s_"+filename,None)
#执行图片删除
#os.remove("./static/pics/"+filename)
return HttpResponse("上传成功!图片:"+filename)
```python
<html>
<head>
    <title>文件上传</title>
</head>
<body>
    <form method="post" action="" enctype="multipart/form-data">
        {% csrf_token %}
        <input type="text" name="title"><br>
        <input type="file" name="myfile"/><br>
        <input type="submit" value="上传">
    </form>
</body>
</html>
 path('upload/', views.upload),

image.png

(2) 使用模型处理上传文件:

将属性定义成models.ImageField类型

class ArticleUpload(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    pic = models.ImageField(upload_to='cars/')  # 必须新建一个cars文件夹

注意:如果属性类型为ImageField获使用图片处理,那么就需要安装包Pillow

pip install Pillow

  • 图片存储路径
    • 在项目根目录下创建media文件夹
    • 图片上传后,会被保存到“/static/media/cars/图片文件”
    • 打开settings.py文件,增加media_root项

      MEDIA_ROOT=os.path.join(BASE_DIR,”statics/media”)

urls.py

path('testupload/', views.testupload),#使用模型处理上传文件
def testupload(request):
    if request.method == "GET":
        return render(request, "test_upload_paper.html")
    elif request.method == "POST":
        title = request.POST.get("title", "")
        content = request.POST.get("content", "")
        pic = request.FILES.get("pic", "")
        models.ArticleUpload.objects.create(title=title, content=content, pic=pic)
        return HttpResponse("Success!")
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="post" action="" enctype="multipart/form-data">
        {% csrf_token %}
        <input type="text" name="title"><br>
        <input type="content" name="content"><br>
        <input type="file" name="pic"/><br>
        <input type="submit" value="上传">
    </form>
</body>
</html>

image.pngimage.png

Django-分页操作

Django提供了一些类实现管理数据分页,这些类位于django/core/paginator.py中

Paginator对象

  • Paginator(列表,int):返回分页对象,参数为列表数据,每面数据的条数

    属性

  • count:对象总数

  • num_pages:页面总数
  • page_range:页码列表,从1开始,例如[1, 2, 3, 4]

    方法

  • page(num):下标以1开始,如果提供的页码不存在,抛出InvalidPage异常

    异常exception

  • InvalidPage:当向page()传入一个无效的页码时抛出

  • PageNotAnInteger:当向page()传入一个不是整数的值时抛出
  • EmptyPage:当向page()提供一个有效值,但是那个页面上没有任何对象时抛出

    Page对象

    创建对象

  • Paginator对象的page()方法返回Page对象,不需要手动构造

    属性

  • object_list:当前页上所有对象的列表

  • number:当前页的序号,从1开始
  • paginator:当前page对象相关的Paginator对象

    方法

  • has_next():如果有下一页返回True

  • has_previous():如果有上一页返回True
  • has_other_pages():如果有上一页或下一页返回True
  • next_page_number():返回下一页的页码,如果下一页不存在,抛出InvalidPage异常
  • previous_page_number():返回上一页的页码,如果上一页不存在,抛出InvalidPage异常
  • len():返回当前页面对象的个数
  • 迭代页面对象:访问当前页面中的每个对象

例子:


from django.core.paginator import Paginator


def pagTest(request, pIndex):
    list1 = models.Book.objects.all()
    p = Paginator(list1, 10)
    if pIndex == '':
        pIndex = '1'
    pIndex = int(pIndex)
    list2 = p.page(pIndex)
    plist = p.page_range
    return render(request, 'pagTest.html', {'list': list2, 'plist': plist, 'pIndex': pIndex})
<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
<ul>
    {% for book in list %}
        <li>{{ book.id }}--{{ book.title }}</li>
    {% endfor %}
</ul>

{% for pindex in plist %}
    {% if pIndex == pindex %}
        {{ pindex }}&nbsp;&nbsp;
    {% else %}
        <a href="/app/pag{{ pindex }}/">{{ pindex }}</a>&nbsp;&nbsp;
    {% endif %}
{% endfor %}
</body>
</html>

https://gitee.com/zhw123/python-hello-word.git

 path('pag<int:pIndex>/', views.pagTest, name='pagTest'),#分页

image.png
代码