Django5入门与进阶

作者:user 发布日期: 浏览量:7

一、Django5 介绍及安装

1、Django 的介绍

  • Django 官方文档:https://www.djangoproject.com/
  • Django 是一个高级的 Python Web 框架,可以快速开发安全和可维护的网站。由经验丰富的开发者构建,Django
    负责处理网站开发中麻烦的部分,开发者可以专注于编写应用程序,而无需重新开发。
  • Django 是免费和开源的,有活跃的社区,丰富的文档,以及很多免费和付费的解决方案。
  • Django 目前最新版本:v5.1.2

2、Django 的安装

  • pip 安装
pip install Django==5.1.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
  • 安装成功后终端会出现如下图所示信息
  • 在虚拟环境的 Scripts 目录下出现 django-admin.exe 的项目创建工具。同时在 Lib 下的 site-packages 目录中,也会出现一个
    django 目录,这个是后面我们开发项目会用到的 django 开发包

二、Django5 项目创建与项目配置

1、Django 项目创建

用命令的方式:

 django-admin startproject 项目名称

如果 django-admin.exe 安装的位置是当前项目的虚拟环境中,则需要将 django-admin.exe 在环境变量中进行配置,
或者使用相对路径(或绝对路径)的方式找到 django-admin.exe 再执行项目创建命令

用pyCharm工具的方式:

  • 除了在终端使用命令的方式创建之外,还可以在 PyCharm 中创建项目。 PyCharm 必须为专业版才能创建与调试 Django 项目
    ,社区版是不支持此功能的
  • 打开 PyCharm 并在左上方单击 File -> New Project,选择第一个 Django,并创建新项目
  • 使用 PyCharm 创建的项目比使用命令的方式多了一个 templates 目录(用于放 HTML 模板文件),以及 settings.py 文件多了
    BASE_DIR / ‘tempaltes’


项目文件介绍:

django_test
  |-- django_test
    |-- __init__.py
    |-- asgi.py
    |-- settings.py
    |-- urls.py
    |-- wsgi.py
  |-- manage.py
  • manage.py:项目管理命令行工具,内置多种方式与项目进行交互,包括启动项目、创建app、数据管理等。在终端窗口中,将路径切换到
    django_test 项目并输入 python manage.py help 可以查看该工具的指令信息
  • init.py:初始化文件,一般情况下无须修改
  • asgi.py:开启一个 ASGI 服务,ASGI是异步网关协议接口
  • settings.py:项目的配置文件,项目的所有功能都需要在该文件中进行配置
  • urls:项目的路由设置,设置网站的具体网址内容
  • wsgi.py:全称为 python Web Server Gateway Interface,即 Python 服务网关接口,是 Python 应用与 Web 服务器之间的接口,用于
    Django 项目在服务器上的部署和上线

Django5 操作指令介绍:
| 指令 | 说明 |
|------|------|
| changepassword | 修改内置用户的用户密码 |
| createsuperuser | 为内置用户表创建超级管理员账号 |
| remove_stale_contenttypes | 删除数据库中已不再使用的数据库 |
| check | 检测整个项目是否存在异常问题 |
| compilemessages | 编译语言文件,用于项目的区域语言设置 |
| createcachetable | 创建缓存数据表,为内置的缓存机制提供存储功能 |
| dbshell | 进入Django配置的数据库,可以执行数据库的SQL语句 |
| diffsettings | 显示当前settings.py的配置信息与默认配置的差异 |
| dumpdata | 导出数据表的数据并以JSON格式存储,如 python manage.py dumpdata index>
data.json,这是index的模型所对应的数据导出,并保存在data.json文件中 |
| flush | 清空数据表的数据信息 |
| inspectdb | 获取项目所有模型的定义过程 |
| loaddata | 将数据文件导入数据表 |
| makemessages | 创建语言文件,用于项目的区域语言设置 |
| makemigrations | 从模型对象创建数据迁移文件并保存在App的migrations文件夹 |
| migrate | 根据迁移文件的内容,在数据库里生成相应的数据表 |
| optimizemigration | 允许优化迁移操作 |
| sendtestemail | 向指定的收件人发生测试的电子邮件 |
| shell | 进入Django的Shell模式,用于调试项目功能 |
| showmigrations | 查看当前项目的所有迁移文件 |
| sqlflush | 查看清空数据库的SQL语句脚本 |
| sqlmigrate | 根据迁移文件内容输出相应的SQL语句 |
| sqlsequencereset | 重置数据表递增字段的索引值 |
| squashmigrations | 对迁移文件进行压缩处理 |
| startapp | 创建项目应用App |
| startproject | 创建新的Django项目 |
| test | 运行App里面的测试内容 |
| testserver | 新建测试数据库并使用该数据库运行项目 |
| clearsessions | 清除会话Session数据 |
| collectstatic | 手机所有的静态文件 |
| findstatic | 查找静态文件的路径信息 |
| runserver | 在本地计算机启动Django项目 |

2、开发第一个Django5应用

  • 前面我们创建的是一个项目,一个项目是由一个或者多个应用组成(一般开发时,一个项目里就创建一个应用即可),项目里的每一个应用都是独立的,可以拥有独立的数据库,模板代码,业务代码
  • 多个应用的情形,如:一个网站可以有前台用户应用和后台管理员应用。复杂的电商项目,后台甚至可以拆分为用户应用,商品应用,订单应用,支付应用,积分应用,优惠券应用等

项目的创建与功能演示都是在项目虚拟环境中
下列使用的命令是在配置环境变量后使用的命令,而在项目虚拟环境的命令方式如图

  • 创建项目
# [] 表示可选参数
startapp 应用名称 [destination]

  • 创建后的应用如图

应用目录介绍:

app01
  |-- migrations
    |-- __init__.py
  |-- __init__.py
  |-- admin.py
  |-- apps.py
  |-- models.py
  |-- tests.py
  |-- views.py
  • migrations目录:用于存放数据库迁移历史文件
  • init.py:说明目录是一个python模块
  • admin.py:默认提供了 admin 后台管理,用作网站的后台管理站点配置相关
  • apps.py:应用配置文件
  • models.py:用于应用操作数据库模型
  • tests.py:做单元测试
  • views.py:用于编写Web应用视图,接收数据,处理数据,与Model(模型)、Template(模板)进行交互,返回应答

Django 的 MTV 模型:

  • Model(模型):业务对象与数据库的对象(ORM)
  • Template(模板):负责如何把页面展示给用户
  • View(视图):负责业务逻辑,并在适当的时候调用Model和Template
  • 此外,Django还有一个 urls 分发器,它的作用是将一个URI的页面请求分发给不同的View处理,View在调用相应的Model和Template

3、创建第一个应用

  • 第一步:执行命令创建应用
 python .\manage.py startapp app01
  • 第二步:注册应用到项目的 settings.py
  • 把 app01 应用的 apps.py 里的 App01Config 类注册到 settings.py 里
  • 第三步:编写模板网页代码 index.html
  • 在 templates 目录下,新建 index.html 文件
  • 第四步:编写视图处理请求层代码
  • 在应用的 views.py 中编写应用的 index 方法,request 是客户端请求对象,render 是渲染方法,可以携带数据渲染到指定页面
def index(request):
    return render(request, 'index.html')

  • 第五步:编写请求映射函数配置
  • 在项目的 urls.py 中编写应用的 index/ 请求,执行上面应用定义的请求处理代码,也就是写一个映射关系代码
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', app01.views.index)
]

img_16.png

  • 第六步:运行项目
runserver [port]

img_17.png

4、Django5项目配置之 settings.py 文件

  • Django5 的配置文件 settings.py 用于配置整个网站的环境和功能,核心配置必须有项目路径、密钥配置、域名访问权限、App列表、中间件、资源文件、模板配置、数据库的连接方式。
  • 资源文件配置分为静态资源文件和媒体资源文件,静态资源文件的配置方式由配置属性 STATIC_URL、STATICFILES_DIRS 和 STATIC_ROOT
    进行设置;媒体资源的配置方式由配置属性 MEDIA_URL 和 MEDIA_ROOT 决定。

基本配置:

from pathlib import Path

"""
##### 项目路径 #####

BASE_DIR项目路径:主要通过 os 模块读取当前项目在计算机系统的具体路径,该代码在创建时自动生成,一般情况下无须修改。
"""
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/

"""
##### 密钥配置 #####

SECRET_KEY密钥配置:这是一个随机值,在项目创建的时候自动生成,一般情况下无须修改,主要用于重要数据的加密处理,提高项目的安全性,避免遭到攻击者恶意破坏。
密钥主要用于用户密码、CSRF机制和Session会话等数据加密。
-- 用户密码:Django内置一套 Auth 认证系统,该系统具有用户认证和存储用户信息等功能,在创建用户时,将用户密码通过密钥进行加密处理,保证用户的安全性
-- CSRF机制:该机制主要用于表单提交,防止窃取网站的用户信息来制造恶意请求
-- Session会话:Session的信息存放在Cookie中,以一串随机的字符串表示,用于标识当前访问网站的用户身份,记录相关用户信息
"""
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-s$bp2cm^46+kgpphe-tg=mfi7gb1y-*b%dlev&%(ql5mi=h5eq'

"""
##### 调试配置 #####

DEBUG调试模式:该值为布尔类型,如果在开发调试阶段,值应该设置为 True,在开发过程中会自动检测代码是否发生更改,根据检测结果执行是否刷新重启系统;如果项目部署上线,值应该为 False,否则会泄漏项目的相关信息。
"""
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

"""
##### 域名访问权限 #####

ALLOWED_HOSTS域名访问权限:设置可访问的域名,默认值为空列表。
-- 当 DEBUG 值为 True 且 ALLOWED_HOSTS 为空列表时,项目只允许以 localhost 或 127.0.0.1 在浏览器访问;
-- 当 DEBUG 值为 False 时,ALLOWED_HOSTS 为必填项,否则程序无法启动;
-- 如果想允许所有域名都可以访问,可设置 ALLOWED_HOSTS = ['*']。
"""
ALLOWED_HOSTS = []

"""
##### App列表 #####

INSTALLED_APPS应用列表:用于告诉Django有哪些应用App,在项目创建时已有:admin、auth 和 sessions 等配置信息,这些都是Django内置的应用功能,各个功能说明如下:
-- admin:内置的后台管理系统
-- auth:内置的用户认证系统
-- contenttypes:记录项目中所有model元数据(Django 的 ORM 框架)
-- sessions:Session会话功能,用于标识当前访问网站的用户身份,记录相关用户信息
-- messages:消息提示功能
-- staticfiles:查找静态文件资源路径
如果在项目中创建了应用App,就必须在App列表INSTALLED_APPS添加App类。
"""
# Application definition
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'helloWorld.apps.HelloworldConfig'
]

静态资源配置 STATIC_URL:

  • 在 App 中创建一个 static 目录,并放置静态资源
  • 可以直接通过:IP + Port + /static/ + 静态资源文件 的方式进行访问static目录下的静态资源文件
"""
##### 静态资源配置 #####

静态资源指的是网站中不回改变的文件。在一般的应用中,静态资源包括 CSS文件、JavaScript文件以及图片等资源文件。
默认配置,App下的 static 目录为静态资源,可以直接访问,其它目录不可以。
"""
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/

STATIC_URL = 'static/'

静态资源集合配置 STATICFILES_DIRS:

  • 由于 STATIC_URL 的特殊性,在开发中会造成诸多不便,比如静态文件夹存放在项目根目录以及定义多个静态文件夹等。因此可以通过配置
    STATICFILES_DIRS 实现多个目录下的静态资源可以访问。
"""
##### 静态资源配置 #####

静态资源指的是网站中不回改变的文件。在一般的应用中,静态资源包括 CSS文件、JavaScript文件以及图片等资源文件。
默认配置,App下的 static 目录为静态资源,可以直接访问,其它目录不可以。
"""
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/

STATIC_URL = 'static/'

# 设置静态资源文件集合
STATICFILES_DIRS = [BASE_DIR / 'static', BASE_DIR / 'MarkDown']
  • 都是通过直接访问:IP + Port + /static/ + 静态资源文件 的方式进行访问
    img_18.png
    img_19.png
    img_20.png

静态资源部署配置 STATIC_ROOT:

  • 静态资源配置还有 STATIC_ROOT,其作用是在服务器上部署项目,实现服务器和项目之间的映射。STATIC_ROOT
    主要收集整个项目的静态资源并存放在一个新的文件夹,然后由该文件夹与服务器之间构建映射关系。
  • 当项目的配置属性 DEBUG 值为 Ture 时,Django 会自动提供静态文件代理服务,此时整个项目处于开发阶段,因此无须使用
    STATIC_ROOT;当配置属性 DEBUG 值为 False 时,项目处于生产环境,Django 不再提供静态文件代理服务,此时需要在项目的配置文件中设置
    STATIC_ROOT。
  • 设置 STATIC_ROOT 需要使用 Django 操作指令 collectstatic 来收集所有静态资源,这些资源都会保存在 STATIC_ROOT 所设置的文件夹里。
"""
##### 静态资源配置 #####

静态资源指的是网站中不回改变的文件。在一般的应用中,静态资源包括 CSS文件、JavaScript文件以及图片等资源文件。
默认配置,App下的 static 目录为静态资源,可以直接访问,其它目录不可以。
"""
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/

STATIC_URL = 'static/'

# 设置静态资源文件集合
STATICFILES_DIRS = [BASE_DIR / 'static', BASE_DIR / 'MarkDown']

# 静态资源部署配置
STATIC_ROOT = BASE_DIR / 'static'

媒体资源配置 MEDIA

  • 一般情况下,STATIC_URL 是设置静态文件的路由地址,如 CSS样式文件、JavaScript文件以及常用图片等。对于一些经常变动的资源,通常将其存放在媒体资源文件夹,如用户头像、歌曲文件等。
  • 媒体资源文件和静态资源文件是可以同时存在的,而且可以独立运行,互不影响。
  • 媒体资源只有配置属性 MEDIA_URL 和 MEDIA_ROOT。 MEDIA_URL 用于设置媒体资源的路由地址,MEDIA_ROOT 用于获取 media
    文件夹在计算机系统的完整路径信息。
"""
##### 媒体资源配置 #####

媒体资源只有配置属性 MEDIA_URL 和 MEDIA_ROOT。 MEDIA_URL 用于设置媒体资源的路由地址,MEDIA_ROOT 用于获取 media 文件夹在计算机系统的完整路径信息。
"""
# 设置媒体路由
MEDIA_URL = 'media/'

# 设置 media 目录的完整路径
MEDIA_ROOT = BASE_DIR / 'helloWorld/media/'
  • 配置属性设置后,还需要将 media 文件夹注册到 Django 里,让 Django 知道如何找到媒体文件,否则无法在浏览器上访问该文件夹的文件信息。打开
    urls.py 文件,为媒体文件夹 media 添加相应的路由地址。
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path

import helloWorld.views

urlpatterns = [
                  path('admin/', admin.site.urls),
                  path('index/', helloWorld.views.index),
              ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

img_21.png

模板配置:

  • 在Web开发中,模板是一种较为特殊的HTML文档。这个HTML文档嵌入了一些能够让Django识别的变量和指令,然后由Django的模板引擎解析这些变量和指令,生成完整的HTML网页并返回给用户浏览。
    模板是Django里面的MTV框架模式的 T 部分,配置模板路径是告诉Django在解析模板时,如何找到模板所在的位置。
"""
##### 模板配置 #####

模板配置是以列表格式呈现的,每个元素具有不同的含义,其含义说明如下:
-- BACKEND:定义模板引擎,用于识别模板里面的变量和指令。内置的模板引擎有 DjangoTemplates 和 jinja2.Jinja2,每个模板引擎都有自己的变量和指令语法
-- DIRS:设置模板所在路径,告诉Django在哪个地方查找模板的位置,默认为空列表
-- APP_DIRS:是否在App里查找模板文件
-- OPTIONS:用于填充在 RequestContext 的上下文(模板里面的变量和指令),一般情况下不做任何修改
"""
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

如果应用里的模板和项目里的模板名字一样,Django会按照
‘DIRS’: [BASE_DIR / ‘helloWorld/templates’, BASE_DIR / ‘templates’] 配置的顺序进行渲染。
例如按照当前的配置,应用模板在前,项目模板在后,则优先渲染应用模板。

  • 创建应用里的模板
    img_22.png
  • 在 views.py 修改要展示的模板文件
    img_23.png
  • 设置模板所在路径
    img_24.png

数据库配置:

  • 数据库配置是选择项目所使用的数据库的类型,不同的数据库需要设置不同的数据库引擎,数据库引擎用于实现项目与数据库的连接。
  • 项目创建时默认使用 Sqlite3 数据库,这是一款轻型的数据库,常用于嵌入式系统开发,而且占用的资源非常少。
"""
##### 数据库配置 #####

Django提供4种数据库模板引擎:
-- 'django.db.backends.postgresql'
-- 'django.db.backends.mysql'
-- 'django.db.backends.sqlite3'
-- 'django.db.backends.oracle'
"""
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}
  • 如果要把默认的 Sqlite3 数据库改为 MySQL 数据库,首先需要安装MySQL连接模块 mysqlclient:
pip install mysqlclient -i https://pypi.tuna.tsinghua.edu.cn/simple
  • mysqlclient 模块安装好后,在项目配置文件 settings.py 中配置 MySQL 数据库连接信息:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': '数据库名称',
        'USER': '用户名',
        'PASSWORD': '数据库密码',
        'HOST': '地址',
        'PORT': '端口'
    }
}

Django5 至少需要 MySQL 8.0.11 及以上版本

  • Django 支持多数据库。例如下面定义了两个数据库:MySQL 和 SQLite,每个键值对代表 Django 连接了某个数据库。
  • 若项目中连接了多个数据库,则数据库之间的使用需要遵循一定的规则和设置。比如项目中定义了多个模型,每个模型所对应的数据表可以选择在某个数据库中生成,如果模型没有指向某个数据库,模型就会在
    key 为 default 的数据库中生成。
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': '数据库名称',
        'USER': '用户名',
        'PASSWORD': '数据库密码',
        'HOST': '地址',
        'PORT': '端口'
    },
    'sqlite3': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}
  • 创建好数据库,配置好连接之后,使用 Django5 manage.py 提供的内置命令 migrate 来创建 Django 内置功能的数据表
migrate

img_26.png
img_25.png

如果使用 mysqlclient 执行 migrate 指令不成功,如下图。
可以安装 PyMySQL 进行替换(或者使用 MySQLDB 等进行替换)
img_27.png

pip install PyMySQL -i https://pypi.tuna.tsinghua.edu.cn/simple
# 在 settings.py 中引入并使用 pymysql
import pymysql

pymysql.install_as_MySQLdb()

img_29.png
img_28.png
img_30.png

中间件配置:

  • 中间件(Middleware)是一个用来处理 Django 的请求(Request)和响应(Response)的框架级别的钩子,它是一个轻量、低级别的插件系统,用于在全局范围内改变
    Django 的输入和输出。
  • 当用户在网站中进行某个操作时,从请求到响应的过程中,当 Django 接收到用户请求时,首先经过中间件处理请求信息,执行相关的处理,然后将处理结果返回给用户。
"""
##### 中间件配置 #####

Django 默认配置的中间件,其含义说明如下:
-- SecurityMiddleware:内置的安全机制,保护用户与网站的通信安全
-- SessionMiddleware:Session会话功能
-- CommonMiddleware:处理请求信息,规范化请求内容
-- CsrfViewMiddleware:开启CSRF防护功能
-- AuthenticationMiddleware:开启内置的用户认证系统
-- MessageMiddleware:开启内置的信息提示功能
-- XFrameOptionsMiddleware:防止恶意程序单击劫持
-- LocaleMiddleware:国际化和本地化功能
"""
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',
]

img_31.png

  • 中间件也可以自定义,在自定义中间件时,必须继承 MiddlewareMixin。中间件可以定义五个方法(主要使用 process_request 和
    process_response),分别是:
    process_request(self, request):请求 views 方法之前会执行
    process_view(self, callback, callback_args, callback_kwargs):Django会在调用视图函数之前调用该方法
    process_template_response(self, request, response):该方法对视图函数返回值有要求,必须是一个含有 render
    方法类的对象,才会执行此方法
    process_exception(self, request, exception):这个方法只有在视图函数中出现异常了才会执行
    process_response(self, request, response):请求执行完成,返回页面前会执行

img_32.png
img_33.png

其它配置:

  • 还有一些其他 settings.py 配置,其说明如下:
"""
##### ROOT_URLCONF 配置 #####

指定了当前项目的根 URL,是 Django 路由系统的入口
"""
ROOT_URLCONF = 'django_pro.urls'
"""
##### WSGI_APPLICATION 配置 #####

项目部署时,Django 的内置服务器将使用的 WSGI 应用程序对象的完整 Python 路径
"""
WSGI_APPLICATION = 'django_pro.wsgi.application'
"""
##### AUTH_PASSWORD_VALIDATORS 配置 #####

这是一个支持插拔的密码验证器,且可以一次性配置多个,Django 通过这些内置组件来避免用户设置的密码等级不足的问题
"""
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]
"""
##### LANGUAGE_CODE、TIME_ZONE 配置 #####

分别代表语言配置项和当前服务段时区的配置项,常用的配置项如下所示:
-- LANGUAGE_CODE:取值是英文 'en-us' 或中文 'zh-Hans'
-- TIME_ZONE:取世界时区 'UTC' 或中国时区 'Asia/Shanghai'
"""
LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'
"""
##### USE_I18N 配置 #####

USE_I18N = True:项目开发完成后,可以选择不同国家的用户提供服务,那么就需要支持国际化和本地化
"""
USE_I18N = True
"""
##### USE_TZ 配置 #####

USE_TZ = True:是指对时区的处理方式,当设置为 True 时,存储到数据库的时间是世界时间 'UTC'
"""
USE_TZ = True
"""
##### DEFAULT_AUTO_FIELD 配置 #####

默认逐渐自增类型
"""
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

三、Django5 路由的定义与使用

1、Django 路由的定义

  • 一个完整的路由包含:路由地址、视图函数(或者视图类)、可选变量和路由命名。
  • 路由称为URL(Uniform Resource Locator,统一资源定位符),是对可以从互联网上得到的资源位置和访问方法的一种简介的表示,是互联网上标准资源的地址。
    img_34.png

2、Django 路由变量

  • 在开发中,有时一个路由可以代表多个不同的页面,如果多个页面都写路由,维护起来比较麻烦。因此可以通过路由变量来实现一个路由代表多个页面。
  • 路由变量类型有:字符类型、整型、slug、uuid等,最为常用的是字符类型和整型。各个类型说明如下:
    字符类型:匹配任何非空字符串,当不含斜杠。如果不指定类型,就默认使用字符类型。
    整型:匹配0和正整数。
    slug:可理解为注释、后缀或附属等概念,常作为路由的解释性字符。可匹配任何ASCII字符以及连接符和下划线,能使路由更加清晰易懂。例如网页的标题是“15岁的孩子”,齐鲁有地址可以设置为“15-sui-de-hai-zi”
    uuid:匹配一个uuid格式的对象。
  • 当然也可以带多个路由变量。
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', helloWorld.views.index),
    path('article/<int:categoryId>/<int:articleId>', helloWorld.views.article)
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

3、Django 正则路由

  • 有时候为了更好的进行路由匹配,可以用正则表达式。
  • 正则 urls 匹配,必须使用 re_path 方法。
  • 正则表达式 ?P 开头是固定格式。
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', helloWorld.views.index),
    path('article/<int:categoryId>/<int:articleId>', helloWorld.views.article),
    re_path('blog/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/',helloWorld.views.blog)
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

4、Django 路由重定向

  • 重定向称为HTTP协议重定向,也可以称为网页跳转,它对应的HTTP状态码为301、303、307、308.简单来说,网页重定向就是在浏览器访问某个网页时,这个网页不提供响应,而是自动跳转到其它网址,由其它网址来生成响应内容。
  • Django的网页重定向有两种方式:路哟重定向和自定义视图的重定向。两种重定向方式各有优点,路由重定向是使用Django内置的视图类
    RedirectView 实现的,默认支持HTTP的GET请求;自定义视图重定向是在自定义视图的响应状态设置重定向,额能让开发者实现多方面的开发需求。

路由重定向:

  • 路由重定向方式,用 RedirectView 实现,在 urls.py 里再加一个路由代码:
# 在页面上请求 redirectTo 路由时会重定向到 index
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', helloWorld.views.index),
    path('redirectTo', RedirectView.as_view(url='index/')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

自定义视图重定向:

  • 在开发时,更多使用的是自定义视图重定向,视图代码里,通过 redirect() 方法来实现具体的页面重定向。

img_35.png

5、Django 命名空间 namespace

  • 当网站项目规模越来越大,子项目越来越多的时候,为了方便管理路由地址,我们可以采用命名空间namespace来对路由地址根据子项目分类。
  • 首先,先新建两个项目 user 和 order
    img_36.png
    img_37.png
  • 然后,分别在需要使用命名空间的 user 和 order 项目中的 views.py 文件编写展示的内容
    img_38.png
    img_39.png
  • 第三,分别在需要使用命名空间的 user 和 order 项目目录下创建 urls.py 文件,并编写路由
    img_40.png
    img_41.png
  • 第四,在主项目路由文件 urls.py 中添加子项目根路由,命名空间。命名空间值一般为项目名,作用是为当前路由进行标注。
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include

import helloWorld.views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', helloWorld.views.index),
    path('article/<int:categoryId>/<int:articleId>', helloWorld.views.article),
    path('user/', include(('user.urls', 'user'), namespace='user')),
    path('order/', include(('order.urls', 'order'), namespace='order')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

img_42.png

6、Django 路由命名与反向解析 reverse 与 resolve

  • 我们在 urls.py 文件里定义的路由信息,有时候需要动态获取得到,然后进行一些处理、统计、日志等操作。因此,我们引入路由方向解析
    reverse 与 resolve 方法。 在使用这两个方法前,还需要给路由取名,否则无法找到需要的路由信息。
  • reverse 方法根据路由名称获取路由地址;resolve 方法根据路由地址得到路由所有信息。
    img_43.png
  • resolve 返回对象属性介绍:
    | 函数方法 | 说明 |
    |--------|--------|
    | func | 路由的视图函数或视图类对象 |
    | args | 以元组格式获取路由的变量信息 |
    | kwargs | 以字典格式获取路由的变量信息 |
    | url_name | 获取路由命名 name |
    | app_names | 与 app name 功能一致,但以列表格式表示 |
    | namespaces | 与namespace功能一致,但以列表格式表示 |
    | route | 获取整个路由的名称 |

四、Django5 视图定义与使用

1、Django 视图的定义

  • 视图(Views)是 Django 的 MTV 架构模式的 V 部分,主要负责处理用户请求和生成相应的响应内容,然后在页面或其它类型文档中显示

2、Django5 设置视图响应状态

  • 客户端请求后端服务,在 views.py 视图层方面最终 return 返回视图响应。Python内置了响应类型来实现返回不同的 HTTP 状态码:
    | 响应类型 | 解释说明 |
    |---------|--------|
    | HttpResponse(‘Hello World’) | 状态码 200,请求已成功被服务器接收 |
    | HttpResponseRedirect(‘/’) | 状态码 302,重定向首页地址 |
    | HttpResponsePermanentRedirect(‘/’) | 状态码 301,永久重定向首页地址 |
    | HttpResponseBadRequest(‘400’) | 状态码 400,访问的页面不存在或请求错误 |
    | HttpResponseNotFound(‘404’) | 状态码404,网页不存在或网页的URL失效 |
    | HttpResponseForbidden(‘403’) | 状态码 403,没有访问权限 |
    | HttpResponseNotAllowed(‘405’) | 状态码 405,不允许使用该请求 |
    | HttpResponseServerError(‘500’) | 状态码 500,服务器内容出错 |
    | JsonResponse({‘foo’:’bar’}) | 默认状态码 200,响应内容为JSON数据 |
    | StreamingHttpResponse() | 默认状态码 200,响应内容以流式输出 |
    img_44.png
  • 对于简单的网页,我们可以直接响应到页面,但是复杂的网页,就会增加视图函数的代码量。因此引入模板,通过 Django 提供的 render
    方法渲染数据到模板,然后再响应到页面。
  • 使用 render 方法渲染,request 和 template_name 是必须传的参数,其它参数可选。
    request:浏览器向服务器发送的请求对象,包含用户信息、请求内容和请求方式等
    template_name:设置模板文件名,用于生成网页内容
    context:对模板上下文(模板变量)赋值,以字典格式表示,默认情况下是一个空字典
    context_type:响应内容的数据格式,一般情况下使用默认值即可
    status:HTTP状态码,默认 200
    using:设置模板引擎(默认django的模板引擎),用于解析模板文件,生成网页内容
  • 模板里取值语法,使用双花括号 {{ 字典的key值 }}
    img_45.png
    img_46.png

3、Django5 设置重定向响应

  • 前面在 urls.py 文件里使用 RedirectView 实现 Django5 重定向,这种方式简单但是不灵活,日常开发中为了实现业务上重定向跳转,更多需要使用
    redirect() 方法
  • 重定向的状态码分为 301 和 302,301 是永久性跳转,302
    是临时跳转,两者的区别在于搜索引擎的网页抓取。301重定向是永久的重定向,搜索引擎在抓取新内容的同时会将就的网址替换为重定向后的网址;302跳转是暂时的跳转,搜索引擎抓取新内容而保留旧的网址。
  • Django内置提供了重定向类 HttpResponseRedirect 和 HttpResponsePermanentRedirect 分别代表HTTP状态码302和301。
    img_47.png
  • redirect() 第一个参数是页面路径,permanent=False 用于判断是否永久重定向,默认 False
    img_48.png

4、Django5 二进制文件下载响应

  • 响应内容除了返回网页信息外,还可以实现文件下载功能,是网站常用的功能之一。
  • Django 提供三种方式实现文件下载功能,分别是:HttpResponse、StreamingHttpResponse 和 FileResponse。三者的说明如下:
    HttpResponse:是所有响应过程的核心类,它的底层功能类是 HTTPResponseBase;
    StreamingResponse:是在 HttpResponseBase
    的基础上进行继承与重写的,它实现流式响应输出(流式响应输出是使用python的迭代器将数据进行分段处理并传输的),适用于大规模数据响应和文件传输响应;
    FileResponse:是在 StreamingHttpResponse 的基础上进行重写的,它实现文件的流式响应输出,只适用于文件传输响应。
# HttpResponse 示例

# 定义文件路径,这里指向的是本地的一个二进制文件
file_path = 'D:\\test.exe'

def download_file1(request):
    file = open(file_path, "rb")  # 打开文件
    response = HttpResponse(file)  # 创建HttpRersponse对象
    response['Content_Type'] = 'application/octet-stream'
    response['Content_Disposition'] = 'attachment;filename=' + 'test.exe'
    return response

def download_file2(request):
    file = open(file_path, "rb")  # 打开文件
    response = StreamingResponse(file)  # 创建StreamingResponse对象
    response['Content_Type'] = 'application/octet-stream'
    response['Content_Disposition'] = 'attachment;filename=' + 'test2.exe'
    return response


def download_file3(request):
    file = open(file_path, "rb")  # 打开文件
    response = FileResponse(file)  # 创建FileResponse对象
    response['Content_Type'] = 'application/octet-stream'
    response['Content_Disposition'] = 'attachment;filename=' + 'test3.exe'
    return response

5、Django5 Http请求&HttpRequest请求类

  • HTTP(Hypertext Transfer Protocol,超文本传输协议)是一个简单的 请求-响应 协议。
  • HTTP请求分为8种请求方式,每种请求方式说明如下:
    | 请求方式 | 说明 |
    |---------|--------|
    | OPTIONS | 返回服务器针对特定资源所支持的请求方法 |
    | GET | 向特定资源发出请求 |
    | POST | 向指定资源提交数据处理请求 |
    | PUT | 向指定资源位置上传数据内容 |
    | DELETE | 请求服务器删除 request-URL所标示的资源 |
    | HEAD | 与GET请求类似,返回的响应中没有具体内容,用于获取报头 |
    | TRACE | 回复和显示服务器收到的请求,用于测试和诊断 |
    | CONNECT | HTTP/1.0协议中能够将连接改为管道方式的代理服务器 |
  • 在上述的HTTP请求方式里,最基本的是 GET 和 POST 请求。GET 和 POST 请求是可以设置请求参数的,两者的设置方式如下:
    GET请求的请求参数是在路由地址后面添加 “?” 和参数内容,参数内容以 key=value
    形式表示,等号前面的是参数名,后面是参数值;如果涉及多个参数,每个参数之间使用 “&” 隔开,如:127.0.0.1:
    8000/getInfo?name=’张三’&age=12
    POST请求的请求参数设置在请求体里。
  • 在 Django5 中,HTTP请求信息都被封装到了 HttpRequest 类中。

HttpRequest类的常用属性如下:

  • COOKIE:获取客户端(浏览器)的 Cookie 信息,以字典形式表示,并且键值对都是字符串类型
  • FILES:djando.http.request.QueryDict 对象,包含所有的文件上传信息
  • GET:获取GET请求的请求参数,它是 django.http.request.QueryDict对象,操作起来类似于字典
  • POST:获取POSt请求的请求参数,它是 django.http.request.QueryDict对象,操作起来类似于字典
  • META:获取客户端的请求头信息,以字典形式存储。
  • method:获取当前请求的请求方式(GET或POST)
  • path:获取当前请求的路由地址
  • session:一个类似于字典的对象,用来操作服务器的会话信息,可临时存放用户信息
  • user:当 Django 启用 AuthenticationMiddleware 中间件时才可用。它的值是内置数据模型 User
    对象,表示当前登录的用户;如果当前用户没有登录,那么user将设为 django.contrib.auth.models.AnonymousUser 的一个实例

HttpRequest类的常用方法如下:

  • is_secure():是否采用 HTTPS 协议
  • get_host():获取服务器的域名。如果在访问的时候设有端口,就会加上端口号
  • get_full_path():返回路由地址。如果该请求为GET请求并且设有请求参数,返回路由地址就会将请求参数返回
def get_test(request):
    # 常用属性
    print(request.content_type)
    print(request.content_params)
    print(request.COOKIES)
    print(request.scheme)
    # 常用方法
    print(request.is_secure())
    print(request.get_host())
    print(request.get_full_path())
    # 获取参数
    # /getInfo?name="张三"&age=12
    print(request.GET.get('name')) # 张三
    print(request.GET.get('age')) # 12
    print(request.GET.get('abc')) # None
    print(request.GET.get('abc', 'default value')) # default value

def post_test(request):
    print(request.method) # POST
    # 参数在请求体
    print(request.POST.get('name')) # 张三
    print(request.POST.get('age')) # 12
    print(request.POST.get('abc')) # None
    print(request.POST.get('abc', 'default value')) # default value

6、Django 会话管理

  • HTTP是一种无状态协议,每次客户访问web页面时,客户端打开一个单独的浏览器窗口连接到web服务器,由于服务器不回自动保存之前客户端请求的相关信息,所以无法识别一个HTTP请求是否为第一次访问。
    这就引进了web客户端和服务器之间的会话,这就是会话管理。
  • 常用的会话跟踪技术是 Cookie 和 Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确认用户身份。

关于 Cookie:

  • Cookie是某些网站为了辨别用户身份,进行Session跟踪而存储在用户本地端上的数据(通常经过加密),由用户客户端计算机暂时或永久保存。
  • Cookie定义了一些HTTP请求头和HTTP响应头,通过这些HTTP头信息使服务器可以与客户进行状态交互。
  • 客户端请求服务器后,如果服务器需要记录用户状态,服务器会在响应信息中包含一个 Set-Cookie
    的响应头,客户端会根据这个响应头存储Cookie信息。再次请求服务器时,客户端会在请求信息中包含一个Cookie请求头,而服务器会根据这个请求头进行用户身份、状态等校验。

img_49.png

关于 Session:

  • Session是一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上,这就是Session。
    客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。

  • 当程序需要为某个客户端的请求创建一个Session的时候,服务器首先检查这个客户端的请求里是否已包含了一个Session标识,称为sessionid,
    如果已包含一个sessionid则说明之前已经为该客户创建过session,服务器就按sessionid把这个session检索出来(如果检索不到可能会创建一个新的),
    如果客户端请求不包含sessionid,则为此客户创建一个session并生成一个与此session相关联的sessionid,sessionid的值应该时一个既不会重复,又不容易被找到规律以仿造的字符串,这个sessionid将被在本次响应中返回给客户端保存。

img_50.png

Cookie和Session的区别:

  • 数据存储位置:cookie数据存放在客户端浏览器上,session数据存放在服务器上
  • 安全性:cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session
  • 服务器性能:session会在一定时间内保存在服务器上,当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie
  • 数据大小:单个cookie保存的数据不能超过4k,很多浏览器都有限制一个站点最多保存20个cookie
  • 信息重要程度:可以考虑将用户信息等重要信息存放为session,其它信息如果需要保留,可以放在cookie

操作 Cookie:

### 获取 Cookie ###

# 获取指定键名的 cookie
request.COOKIES['key']
request.COOKIES.get('key')

request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
default: 默认值
salt: 加密盐
max_age: 后台控制过期时间,单位为秒
expires: 专门针对IE浏览器设置的超时时间

### 设置 Cookie ###

# 获取 HttpResponse对象
# res = HttpResponse(...)
res = render(request, ...)

# 设置 cookie
res.set_cookie(key, value, ...)
res.set_signed_cookie(key, value, salt='加密盐', max_age=None, ...)
key: 键
value: 值
max_age: 过期时间
expires: 过期时间(IE浏览器)
path='/': Cookie 生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问
domain=None: Cookie 生效的域名
secure=False: 是否https传输
httponly=False: 只能http协议传输,无法被JavaScript获取(并不绝对,底层抓包可以获取到也可以被覆盖)

### 删除 Cookie ###

# 获取 HttpResponse对象
# res = HttpResponse(...)
res = render(request, ...)

# 删除 cookie
res.delete_cookie(key) # 此方法会删除用户浏览器上之前设置的cookie值

操作 Session:

### 获取、设置、删除 Session中数据

request.session['key1'] # 没有值会报错
request.session.get('key1', None) # 可以获取多组
request.session['key2'] = 'value2' # 可以设置多组
request.session.setdefault('key1', 123) # 存在则不设置
del request.session['key1'] # 删除session

### 所有键、值、键值对
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()

### 会话 session 的 key
request.session.session_key

### 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired()

### 检查会话session的key在服务器中是否存在
request.session.exists('session_key')

### 删除当前会话所有session数据(只删除客户端)
request.session.delete()

### 删除当前的会话数据并删除会话的cookie
request.session.flush() # 服务器、客户端都删除,这用于确保前面的会话数据不可以再次被用户的浏览器访问

### 设置会话session和cookie的过期时间
# django 默认的 session 过期时间是14天   
request.session.set_expiry(value)
# 如果value是个整数,session会在该秒数后失效
# 如果value是个datetime或timedelta,session就会在这个时间后失效
# 如果value是0,用户关闭浏览器就会失效
# 如果value是None,session会依赖全局session失效策略

7、Django 文件上传

  • 文件上传功能是网站开发常见的功能之一,例如图片、视频、音乐。无论上传的文件是什么格式,其上传原理都是将文件以二进制的数据格式读取并写入网站或业务系统的目录里。
### upload.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <input type="file" name="myfile">
    <br/>
    <input type="submit" value="文件上传">
</form>
</body>
</html>
### urls.py
path('toUpload/', helloWorld.views.to_upload),
path('upload', helloWorld.views.upload),
### views.py
def to_upload(request):
    """
    文件上传页面
    :param request:
    :return:
    """
    return render(request, 'upload.html')


def upload(request):
    """
    文件上传
    :param request:
    :return:
    """
    # 获取上传的文件,如果没有文件,则默认返回None
    file = request.FILES.get('myfile', None)

    if file:
        # 打开特定的文件进行二进制写操作
        f = open(os.path.join("D:\\myfile", file.name), 'wb+')
        # 分块写入文件
        for chunk in file.chunks():
            f.write(chunk)
        f.close()
        return HttpResponse('文件上传成功')
    else:
        return HttpResponse('没找到文件')

文件对象 file 提供一下属性来获取文件信息:

  • myfile.name: 获取上传文件的文件名,包含文件后缀名
  • myfile.size: 获取上传文件的文件大小
  • myfile.content_type: 获取文件类型,通过后缀名判断文件类型

从文件对象 myfile 获取文件内容,Django提供了以下读取方式,每种方式说明如下:

  • myfile.read(): 从文件对象里读取整个文件上传的数据,这个只适合小文件
  • myfile.chunks(): 按流式响应方式读取文件,在 for 循环中进行迭代,将大文件分块写入服务器所指定的保存位置
  • myfile.multiple_chunks(): 判断文件对象的文件大小,返回True或者False,当文件大于2.5MB(默认值为2.5MB)时,该方法返回True,否则返回False。因此,可以根据该方法来选择用
    read() 方法读取还是采用 chunks() 方法。

8、Django5 列表视图 ListView

  • 为了实现快速开发,Django提供视图类功能,封装了视图开发常用的代码,这种基于类实现的响应与请求称为 CBV(Class Base Views)。
  • 列表视图类 ListView:该视图类可以将数据库表的数据以列表的形式显示到页面,常用于数据的查询和展示。
  • 首先,为了得到数据库数据,先定义模型来映射数据库表。models.py 里定义 StudentInfo 类:
from django.db import models

class StudentInfo(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    age = models.IntegerField()

    class Meta:
        db_table = 't_student'
"""
settings.py

##### 数据库配置 #####
"""
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_pro',
        'USER': 'admin',
        'PASSWORD': 'Your Password',
        'HOST': '192.168.31.123',
        'PORT': '3306'
    },
    'sqlite3': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    },
}
  • 然后执行 makemigrations 和 migrate 指令
    img_51.png

要使用 ListView,需要继承它并设置一些属性。以下是一些常用的属性:

  • model: 指定要使用的模型
  • template_name: 指定要使用的模板名称
  • context_object_name: 指定上下文变量名称,默认为 object_list
  • paginate_by: 指定分页大小
  • extra_context: 设置模型外的数据

在 views.py 里,我们可以定义 StudentListView 类:

class StudentListView(ListView):
    # 设置模板文件
    template_name = 'student/list.html'
    # 设置模型外的数据
    extra_context = {'title': '学生信息列表'}
    # 查询结果集
    queryset = StudentInfo.objects.all().order_by('id')
    # 每页展示5条数据
    paginate_by = 5
    # 设置上下文对象名称
    context_object_name = 'student_list'

    # def get_queryset(self):
    #     # 查询结果集
    #     queryset = StudentInfo.objects.all().order_by('id')
    #     # print("queryet =>", queryset.count())
    #     return queryset
    # 
    # def get_context_data(self, **kwargs):
    #     context = super().get_context_data(**kwargs)
    #     # print("context =>", context)  # Debugging line
    #     # for ctx in context:
    #     #     print(ctx)
    #     return context

除了设置属性之外,还可以重写 ListView 中的方法以进行自定义。以下是一些常见的方法:

  • get_queryset(): 返回要在视图中使用的查询集合,这里可以对查询集合进行筛选、排序等操作
  • get_context_data(): 返回要在模板上下文中使用的变量。这里可以添加额外的变量,如表单、过滤器等

在 urls.py 里,定义映射:

path('student/list/', helloWorld.views.StudentListView.as_view()),

在模板页面,Django 提供了分页的功能:Paginator 和 Page 类都是用来做分页的。

## Paginator 常用属性和方法
count: 总共有多少条数据
num_pages: 总共有多少页面
page_range: 页面的区间。例如有 3 页,那么range是(1, 4)

## Page常用的属性和方法
has_next: 是否还有下一页
has_previous: 是否还有上一页
next_page_number: 下一页的页码
previous_page_number: 上一页的页码
number: 当前页
start_index: 当前页的第一条数据的索引值
end_index: 当前页的最后一条数据的索引值

在 student/list.html 中使用:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
</head>
<body>
<h1>{{ title }}</h1>
<ul>
    {% for student in student_list %}
    <li>ID: {{ student.id }} - Name: {{ student.name }} - Age: {{ student.age }}</li>

    {% empty %}
    <li>没有学生记录。</li>
    {% endfor %}
</ul>

<div class="pagination">
    <span class="step-links">
        {% if page_obj.has_previous %}
            <a href="?page=1">&laquo; 第一页</a>
            <a href="?page={{ page_obj.previous_page_number }}">前一页</a>
        {% endif %}

        <span class="current">
            第 {{ page_obj.number }} 页,共 {{ page_obj.paginator.num_pages }} 页
        </span>

        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}">下一页</a>
            <a href="?page={{ page_obj.paginator.num_pages }}">最后一页 &raquo;</a>
        {% endif %}
    </span>
</div>
</body>
</html>

9、Django5 详细视图 DetailView

  • DetailView 多用于展示某一个具体数据对象的详细信息的页面。使用
    DetailView,只需要指定要使用的模型和对象的唯一标识符,并可以自定义其他一些属性,例如模型名称、模板名称、上下文数据等。

以下是 DetailView 的一些常见属性和方法:

  • model: 指定要使用的模型
  • queryset: 指定要使用的查询集,用于获取对象。如果未指定,则将使用模型的默认查询集
  • pk_url_kwarg: 指定URL中用于获取对象唯一标识的参数名称,默认为 ‘pk’
  • context_object_name: 指定将对象传递给模板时的上下文比变量名称,默认为 ‘model’
  • template_name: 指定要使用的模板的名称
  • get_object(queryset=None): 获取要展示的对象。可以重写这个方法来自定义获取对象的逻辑
  • get_context_data(kwargs): 返回要传递给模板的上下文数据。你可以重写这个方法来自定义上下文数据
  • get(): 处理GET请求的方法,根据配置的对象获取规则执行对象获取和展示逻辑
  • dispatch(request, args, *kwargs): 处理请求的入口方法,根据请求的不同方法(GET、POST等)执行相应的处理逻辑

在 urls.py 里,定义映射:

# 默认使用的是 pk
path('student/<int:pk>', helloWorld.views.StudentDetailView.as_view()),

# 可以使用 pk_url_kwarg进行重新指定
# 指定URL中用于获取对象的唯一标识符的参数名称,默认 pk
pk_url_kwarg = 'id'

# 在 urls.py 中就可以使用的指定的 id 
path('student/<int:id>', helloWorld.views.StudentDetailView.as_view()),

在 views.py 文件

class StudentDetailView(DetailView):
    # 设置模板名称
    template_name = 'student/detail.html'
    # 设置模型外的数据
    extra_context = {'title': '学生详情信息'}
    # 设置上下文对象名称
    context_object_name = 'student_detail'
    # 设置查询模型
    model = StudentInfo
    # 指定URL中用于获取对象的唯一标识符的参数名称,默认 pk
    pk_url_kwarg = 'id'

10、Django5 新增视图 CreateView

  • 创建类 CreateView 是对模型新增数据的视图类,它是在表单视图类 FormView 的基础上加以封装的,简单来说,就是在视图类 FormView
    的基础上加入数据新增的功能。
  • 所有涉及到表单视图的功能开发,都需要定义 form 表单类。

创建的 Form 类:

from django import forms
from django.forms import ModelForm

from helloWorld.models import StudentInfo


class StudentForms(ModelForm):
    class Meta:  # 配置中心
        model = StudentInfo  # 导入的 model
        # fields = '__all__'  # 代表所有字段
        fields = ['name', 'age']  # 指定展示的字段

        widgets = {  # 使用组件,这里使用的 forms 来源于 django(from django import forms)
            'name': forms.TextInput(attrs={'id': 'name', 'class': 'inputClass'}),
            'age': forms.NumberInput(attrs={'id': 'age'})
        }

        labels = {  # 指定标签
            'name': '姓名',
            'age': '年龄'
        }

在 views.py 文件

class StudentCreateView(CreateView):
    # 设置模板名称
    template_name = 'student/add.html'
    # 设置模型外的数据
    extra_context = {'title': '学生信息添加'}
    # 指定Form表单
    form_class = StudentForms
    # 执行完成后跳转地址
    success_url = '/student/list'

在 urls.py 里,定义映射:

path('student/create', helloWorld.views.StudentCreateView.as_view())

在 tempaltes/student/add.html 文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{title}}</title>
    <style>
        .inputClass {
            width: 200px;
        }
    </style>
</head>
<body>
<h1>{{ title }}</h1>
<form action="" method="post">
    {% csrf_token %}
    {{form.as_p}}
    <input type="submit" value="提交"/>
</form>
</body>
</html>

11、Django5 修改视图 UpdateView

  • 视图类 UpdateView 是在视图类 FormView 和视图 DetailView 的基础上实现的,首先使用视图类 DetailView 的功能(功能核心类是
    SingleObjectMixin),通过路由变量查询数据表某条数据并显示在网页上,然后在视图类 FormView 的基础上,通过表单实现数据修改。

在 views.py 文件里新建 UpdateView 类:

class StudentUpdateView(UpdateView):
    # 设置模板名称
    template_name = 'student/edit.html'
    # 设置模型外的数据
    extra_context = {'title': '学生信息编辑'}
    # 设置查询类型
    model = StudentInfo
    # 指定Form表单
    form_class = StudentForms
    # 执行完成后跳转地址
    success_url = '/student/list'

在 urls.py 里,定义映射:

path('student/update/<int:pk>', helloWorld.views.StudentUpdateView.as_view()),

在 tempaltes/student/update.html 文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{title}}</title>
    <style>
        .inputClass {
            width: 200px;
        }
    </style>
</head>
<body>
<h1>{{ title }}</h1>
<form action="" method="post">
    {% csrf_token %}
    {{form.as_p}}
    <input type="submit" value="确定"/>
</form>
</body>
</html>

12、Django5 删除视图 DeleteView

  • 视图类 DeleteView 的使用方法与视图类 UpdateView 类似,视图类 DeleteView
    只能删除单条数据,路由变量为模型主键提供查询范围,因为模型主键具有唯一性,所以通过主键查询能够精准到某条数据。查询出来的数据通过
    POST 请求实现数据删除。

在 views.py 文件里新建 DeleteView 类:

class StudentDeleteView(DeleteView):
    # 设置模板名称
    template_name = 'student/delete.html'
    # 设置模型外的数据
    extra_context = {'title': '删除学生信息'}
    # 设置查询类型
    model = StudentInfo
    # 设置上下文对象名称
    context_object_name = 'student'
    # 执行完成后跳转地址
    success_url = '/student/list'

在 urls.py 里,定义映射:

path('student/delete/<int:pk>', helloWorld.views.StudentDeleteView.as_view()),

在 tempaltes/student/update.html 文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{title}}</title>
</head>
<body>
<h1>{{ title }}</h1>
<form action="" method="post">
    {% csrf_token %}
    您确定要删除id:{{student.id}},姓名:{{student.name}},年龄: {{student.age}}的数据吗?
    <input type="submit" value="确定"/>
</form>
</body>
</html>

五、Django5 模板引擎

  • Django 作为 Web 框架,需要一种便利的方法动态地生成HTML网页,因此有了模板这个概念。模板包含所需HTML的部分代码以及一些特殊语法,特殊语法用于描述如何将视图传递的数据动态插入HTML网页中。
  • Django 可以配置一个或多个模板引擎(如果前后端分离,Django值提供API接口,无须使用模板引擎),模板引擎有Django模板语言(Django
    Template language,DTL)和 Jinja3。Django模板语言是Django内置的功能之一,Jinja3是当前Python流行的模板语言。

1、Django5 内置模板引擎

Django内置的模板引擎包含模板上下文(亦可称为模板变量)、标签和过滤器,各个功能说明如下:

  • 模板上下文是以变量的形式写入模板文件里面,变量值由视图函数或视图类传递所得
  • 标签是对模板上下文进行控制输出,比如模板上下文的判断和循环控制等
  • 模板继承隶属于标签,它是将每个模板文件重复的代码抽取出来并写在一个共用的模板文件中,其他模板文件通过继承共用的模板文件来实现完整的网页输出
  • 过滤器是对模板上下文进行操作处理,比如模板上下文的内容截取、替换或格式转换等

1-1 模板上下文

  • 模板上下文是模板中基本的组成单位,上下文的数据由视图函数或视图类传递。它以{{ variable }}
    表示,variable是上下文的名称,它支持Python所有的数据类型,如字典、列表、元组、字符串、整型或实例化对象等。上下文的数据格式不同,在模板里的使用方式也有所差异。

使用变量的一些注意点如下:

当模板引擎遇到一个变量,将计算这个变量,然后输出结果
变量名可以由字母、数字、下划线、点组成,不能以数字和下划线开头
当模板引擎遇到 “.” 的时候,按以下顺序进行解析:a.按照 dict 解析 var[key] b.按照对象的属性或方法解析 var.var/func
c.按照索引解析 var[index]
如果变量不存在,不会引发异常,模板会插入空字符串
在模板中使用变量或方法时,不能出现 ()、[]、{}
调用方法时,不能传递参数

在 views.py 中的 index() 方法使用:

# 定义类
class Person():
    # 属性 姓名
    name = None
    # 属性 年龄
    age = None

    def __init__(self, name, age):
        self.name = name
        self.age = age

def index(request):
    my_str = '字符串类型模板变量'
    my_dict = { 'addr': '字典类型的模板变量', 'phone': '123123123'}
    my_obj = Person('张三', 23)
    my_list = ['java', 'python', 'javascript']
    my_tuple = ('java', 'python', 'javascript')

    content_value = {
        "str": my_str,
        "dict": my_dict,
        "obj": my_obj,
        "list": my_list,
        "tuple": my_tuple
    }

    return render(request, 'index.html', context=content_value)

在 templates 下 index.html 中使用:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
字符串:{{str}}<br/> # 字符串类型模板变量
字典类型:{{dict.addr}}-{{dict.phone}}<br/> # 字典类型的模板变量-123123123
对象类型:{{obj.name}}-{{obj.age}}<br/> # 张三-23
列表:{{list.0}}-{{list.1}}-{{list.2}}<br/> # java-python-javascript # 通过”.“进行调用,不能通过[0]、[1]、[2]的方式,会报错
元组:{{tuple.0}}-{{tuple.5}}-{{tuple.2}}<br/> # java--javascript # 对于索引不存在的,显示空字符串
</body>
</html>

1-2 模板标签

  • 标签是对模板上下文进行控制输出,它是以 {% tag %} 表示的,其中tag是标签的名称,Django内置了许多模板标签,比如 {% if
    %}(判断标签)、{% for %}(循环标签)、{% url %}(路由标签)等。

常用的内置标签:
| 标签 | 描述 |
|-------------|-------------|
| {% for … in … %} {% endfor %} | 遍历输出上下文的内容 |
| {% if %} {% elif %} {% endif %} | 对上下文进行条件判断 |
| {% csrf_token %} | 生成csrf_token的标签,用于防护跨站请求伪造攻击 |
| {% url %} | 引用路由配置的地址,生成相应的路由地址 |
| {% with %} | 将上下文名重新命名 |
| {% load %} | 加载导入 Django 的标签库 |
| {% static %} | 读取静态资源的文件内容 |
| {% extend xxx %} | 模板继承,xxx为模板文件名,使当前模板继承xxx模板 |
| {% block %} | 重写父类模板的代码 |

在 for 标签中,模板还提供一些特殊的变量来获取 for 标签的循环信息,变量说明如下:
| 变量 | 描述 |
|-------------|-------------|
| forloop.counter | 获取当前循环的索引,从1开始计算 |
| forloop.counter() | 获取当前循环的索引,从0开始计算 |
| forloop.revcounter | 索引从最大数开始递减,直到索引到1的位置 |
| forloop.revcounter() | 索引从最大数开始递减,直到索引到0的位置 |
| forloop.first | 当遍历的元素为第一项时为真 |
| forloop.last | 当遍历的元素为最后一项时为真 |
| forloop.parentloop | 在嵌套的for循环中,获取上层for循环的forloop |

在 templates 下 index.html 中使用:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
字符串:{{str}}<br/> # 字符串类型模板变量
字典类型:{{dict.addr}}-{{dict.phone}}<br/> # 字典类型的模板变量-123123123
对象类型:{{obj.name}}-{{obj.age}}<br/> # 张三-23
列表:{{list.0}}-{{list.1}}-{{list.2}}<br/> # java-python-javascript # 通过”.“进行调用,不能通过[0]、[1]、[2]的方式,会报错
元组:{{tuple.0}}-{{tuple.5}}-{{tuple.2}}<br/> # java--javascript # 对于索引不存在的,显示空字符串

<h1>模板标签</h1>
<h3>遍历标签 for</h3>
{% for item in list%}
    <p>这是第{{forloop.counter}}次遍历</p>
    {% if forloop.first %}
        <p>这是第一项,{{item}}</p>
    {% elif forloop.last %}
        <p>这是最后一项,{{item}}</p>
    {% endif %}
{% endfor %}

<h3>判断标签 if</h3>
{% if str == '字符串类型模板变量' %}
    <p>字符串类型模板变量</p>
{% elif str == '字符串类型模板变量2' %}
    <p>字符串类型模板变量2</p>
{% endif %}

<h3>url标签</h3>
<a href="{% url 'index' %}" target="_blank">请求index</a>

<h3>变量重命名with标签</h3>
{% with info=str %}
    { {info}}
{% endwith %}
</b  ody>
</hml>

1-3 模板继承

模板继承是Django模板语言中最强大的部分。模板继承使你可以构建基础的“骨架”模板,将通用的功能或者属性写在基础模板中,也叫基类模板或者父模板。子模版可以继承父类模板,子模版继承后将自动拥有父类中的属性和方法,我们还可以在子模版中对父模板进行重写,即重写父模板的属性和方法,从而实现子模版的定制。

  • 在继承模板中最常用的标签就是{% block %} 与 {% extends %}标签,其中 {% block %}标签与 {% endblock %}标签成对出现。

在 templates 中创建基础模板 base.html:

# templates/base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        {% block title %}
        base标题
        {% endblock %}
    </title>
    {% load static %}
</head>
<body>
<div id="head">
    <!--    <img src="http://127.0.0.1:8000/static/test.png" alt="">-->
    <img src="{% static 'test.png' %}" alt="">
</div>
<div id="content">
    {% block content %}
    默认content内容
    {% endblock %}
</div>
<div id="footer">
    版权所有 www.lgk.pub
</div>
</body>
</html>

在 templates 中创建子模板 course.html 继承基础模板:

# templates/course.html

{% extends 'base.html' %}

<!-- 重写title -->
{% block title %}
课程标题
{% endblock %}

<!-- 重写content -->
{% block content %}
课程相关的内容:Django学习
{% endblock %}

在 views.py 使用:

def to_course(request):
    """
    跳转课程页面
    :param request:
    :return:
    """
    return render(request, 'course.html')

在 urls.py 里,定义映射:

path('toCourse/', helloWorld.views.to_course),

1-4 过滤器

  • Django过滤器是一种用于在Django模板中处理数据的技术,过滤器的作用是可以对模板做的变量进行加工、过滤或格式化,返回一个新的值供模板使用。
  • 过滤器作用是在变量输出时,对输出的变量值做进一步的处理。
  • 过滤器的语法格式如下:{{ 变量 | 过滤器1:参数值1 | 过滤器2:参数值2 … }}

常用的过滤器如下:
| 过滤器 | 说明 |
|--------|--------|
| add | 加法 |
| addslashes | 添加斜杠 |
| capfirst | 首字母大写 |
| center | 文本居中 |
| cut | 切除字符 |
| date | 日期格式化 |
| default | 设置默认值 |
| default_if_none | 为None设置默认值 |
| dictsort | 字典排序 |
| dictsortreversed | 字典反向排序 |
| divisibleby | 整除判断 |
| escape | 转义 |
| escapejs | 转义js代码 |
| filesizeformat | 文件尺寸人性化显示 |
| first | 第一个元素 |
| floatformat | 浮点数格式化 |
| force_escape | 强制离开转义 |
| get_digit | 获取数字 |
| iriencode | 转换IRI |
| join | 字符列表链接 |
| last | 最后一个 |
| length | 长度 |
| length_is | 长度等于 |
| linebreaks | 行转换 |
| linebreaksbr | 行转换 |
| linenumbers | 行号 |
| ljust | 左对齐 |
| lower | 小写 |
| make_list | 分割成字符串列表 |
| phone2numeric | 电话号码 |
| pluralize | 复数形式 |
| pprint | 调试 |
| random | 随机获取 |
| rjust | 右对齐 |
| safe | 安全确认 |
| safeseq | 列表安全确认 |
| slice | 切片 |
| slugify | 转换成ASCII |
| stringformat | 字符串格式化 |
| striptags | 去除HTML中的标签 |
| time | 时间格式化 |
| timesince | 从何时开始 |
| timeuntil | 到何时多久 |
| title | 所有单词首字母大写 |
| truncatechars | 截断字符 |
| truncatechars_html | 截断字符 |
| truncatewords | 截断单词 |
| truncatewords_html | 截断单词 |
| unordered_list | 无序列表 |
| upper | 大写 |
| urlencode | 转义url |
| urlize | url转成可点击的链接 |
| urlizetrunc | urlize的截断方式 |
| wordcount | 单词计数 |
| wordwrap | 单词包裹 |
| yesno | 将True、False和None,映射成字符串 ‘yes’、’no’、’maybe’ |
| … | … |

# 过滤器使用
首字母大写:{{ msg | capfirst }} <br/> # Hello
获取长度:{{ msg | length }} <br/> # 5
时间格式化:{{ date | date: 'Y-m-d H-i-s'}} # 2025-01-07 11:50:21

2、Jinja3 模板引擎

  • Jinja3 是 Python 里被广泛应用的模板引擎,它的设计思想来源于Django的模板引擎,并扩展了其语法和一系列强大的功能,其中最显著的是增加了沙箱执行功能和可选的自动转义功能,这对大多数应用的安全性来说非常重要。
  • 官方文档:https://jinja.palletsprojects.com/en/stable/

Jinja3 具备的特性:

  • 沙箱执行模式:模板的每个部分都在引擎的监督之下执行,模板将会被明确地标记在白名单或黑名单内,这样对那些不信任的模板也剋执行
  • 强大的自动HTML转义系统,可以有效地阻止跨站脚本攻击。
  • 模板继承机制:此机制可以使得所有模板具有相似一致得布局,也方便开发人员对模板进行修改和管理。
  • 高效得执行效率:Jinja3引擎模板第一次加载是就把源码转换成Python字节码,加快模板执行时间。
  • 调试系统融合了标准的Python的 TrackBack 功能,使得模板编译和运行期间的错误能及时被发现和调试。
  • 语法配置,可以重新配置Jinja3,使得它更好地适应LaTeX或JavaScript的输出。

2-1 jinja3 安装与配置

  • pip 安装 Jinja3
pip install Jinja2 -i  https://pypi.tuna.tsinghua.edu.cn/simple

- 配置使用Jinja3

由于Django的内置功能是使用Django的模板引擎,如果将整个项目都改为Jinja3模板引擎,就会导致内置功能无法正常使用,在这种情况下,纪要保证内置功能能够正常使用,又要使用Jinja3模板引擎,只能将两个模板引擎共同存在一个项目里。

在 settings.py 配置使用 jinja3模板引擎:

"""
##### 模板配置 #####

模板配置是以列表格式呈现的,每个元素具有不同的含义,其含义说明如下:
-- BACKEND:定义模板引擎,用于识别模板里面的变量和指令。内置的模板引擎有 DjangoTemplates 和 jinja2.Jinja2,每个模板引擎都有自己的变量和指令语法
-- DIRS:设置模板所在路径,告诉Django在哪个地方查找模板的位置,默认为空列表
-- APP_DIRS:是否在App里查找模板文件
-- OPTIONS:用于填充在 RequestContext 的上下文(模板里面的变量和指令),一般情况下不做任何修改
"""
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates', BASE_DIR / 'helloWorld/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',
            ],
        },
    },
    {
        'BACKEND': 'django.template.backends.jinja2.Jinja2',  # 固定写法
        'DIRS': [BASE_DIR / 'templates'],  # 使用 Jinja3 模板引擎的路径
        'APP_DIRS': True,
        'OPTIONS': {
            'environment': 'helloWorld.Jinja3.environment',  # 指定Jinja3环境配置文件的路径
        },
    },
]

在 helloWorld 项目库里新建 Jinja3.py,用来定义环境参数:

# helloWorld/Jinja3.py

from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse
from jinja2 import Environment


def environment(**options):
    env = Environment(**options)
    env.globals.update(
        {
            'static': staticfiles_storage.url,
            'url': reverse
        }
    )
    return env

2-2 jinja3 模板语法

  • 尽管Jinja的设计思想来源于Django的模板引擎,但在功能上和使用上,Jinja3比Django的模板引擎更为完善,而且Jinja3的模板语法在使用上与Django模板引擎存在一定的差异
  • 由于Jinja3有模板设计人员帮助手册。并且官方文档对模板语法的使用说明较为详细,因此这里值讲述Jinja3与Django模板语言的使用差异。
  • 常用中主要是 for、url、load 的使用有差异

Jinja3语法文档:https://jinja.palletsprojects.com/en/stable/templates/#variables

2-3 jinja3 过滤器

- Jinja3的过滤器的用法与 Django内置过滤器的使用方法相似,也是由管道符“|”连接模板上下文和过滤器,但是两者的过滤器名称是不同的,而且过滤器的参数设置方式也不同。

Jinja3过滤器文档:https://jinja.palletsprojects.com/en/stable/templates/#filters

Jinja3常用过滤器如下:
| 过滤器 | 说明 |
|-----------|--------------------|
| abs | 设置数值的绝对值 |
| default | 设置默认值 |
| escape | 转义字符,转成HTML的语法 |
| first | 获取上下文的第一个元素 |
| last | 获取上下文的最后一个元素 |
| length | 获取上下文的长度 |
| join | 功能与Python的join语法一致 |
| safe | 将上下文转义处理 |
| int | 将上下文转换为int类型 |
| float | 将上下文转换为float类型 |
| lower | 将字符串转换为小写 |
| upper | 将字符串转换为大写 |
| replace | 字符串的替换 |
| truncate | 字符串的截断 |
| striptags | 删除字符串中所有的HTML标签 |
| trim | 截取字符串前面和后面的空白字符 |
| string | 将上下文转换成字符串 |
| wordcount | 计算长字符串的单词个数 |

六、Django5 模型定义与使用

  • Django5 对各种数据库提供了很好的支持,包括 PostgreSQL、MySQL、SQLite 和
    Oracle,而且为这些数据库提供了统一的API方法,这些API统称为ORM框架。通过使用Django5内置的ORM框架可以实现数据库的连接和读写操作。

1、Django5 模型定义

  • ORM框架是一种用于实现面向对象编程语言中不同类型系统的数据之间转换的技术。它创建了一个可在编程语言中使用的“虚拟对象数据库”,通过对虚拟对象数据库的操作从而实现对目标数据库的操作,虚拟对象数据库与目标数据库是相互对应。
  • 在 Django5 中,虚拟对象数据库也称为模型,通过模型实现对目标数据库的读写操作,实现方法如下:
    • 配置目标数据库,在 settings.py 中设置配置属性
    • 构建虚拟对象数据库,在 APP 的 models.py 文件中以类的形式定义模型
    • 通过模型在目标数据库中创建相应的数据表
    • 在其他模块(如视图函数)里使用模型来实现目标数据库的读写操作

模型字段类型如下:

  • AutoField: 自增长类型,数据表的字段类型为整数,长度为11位。
  • BigAutoField: 自增长类型,数据表的字段类型为 bigint,长度为20位。
  • CharField: 字符类型。
  • BooleanField: 布尔类型。
  • CommaSeparatedIntegerField: 用逗号分割的整数类型。
  • DateField: 日期(Date)类型。
  • DateTimeField: 日期时间(Datetime)类型。
  • Decimal: 十进制小数类型
  • EmailField: 字符类型,存储邮箱格式的字符串
  • FloatField: 浮点数类型,数据表的字段类型变成 Double 类型
  • IntegerField: 整数类型,数据表的字段类型为11位的整数
  • BigIntegerField: 长整数类型
  • IPAddressField: 字符类型,存储IPV4地址的字符串
  • GenericIPAddressField: 字符类型,存储IPV4和IPV6地址的字符串
  • NullBooleanField: 允许为空的布尔类型
  • PositiveIntegerField: 正整数的整数类型
  • PositiveSmallIntegerField: 小正整数类型,取值范围为 0~32767
  • SlugField: 字符类型,包含字母、数字、下划线和连字符的字符串
  • SmallIntegerField: 小整数类型,取值范围为 -32768 ~ 32767
  • TextField: 长文本类型
  • TimeField: 时间类型,显示时分秒 HH:MM[:ss[.uuuuuu]]
  • URLField: 字符类型,存储路由格式的字符串
  • BinaryField:二进制数据类型
  • FileField: 字符类型,存储文件路径的字符串
  • ImageField: 字符类型,存储图片路径的字符串
  • FilePathField: 字符类型,从特定的文件目录选择某个文件

模型字段参数如下:

  • verbose_name: 默认为 None,在 Admin 站点管理设置字段的显示名称
  • primary_key: 默认为 False,若为 True,则将字段设置为主键
  • max_length: 默认为 None,设置字段的最大长度
  • unique: 默认为 False,若为 True,则设置字段的唯一属性
  • blank: 默认为 False,若为 True,则字段允许值为空值,数据库将存储空字符串
  • null: 默认为 False,若为 True,则字段允许为空值,数据库表现为NULL
  • db_index: 默认为 False,若为 True,则以此字段来创建数据库索引
  • default: 默认为 NOT_PROVIDED 对象,设置字段的默认值
  • editable: 默认为 True,允许字段可编辑,用于设置 Admin 的新增数据的字段
  • serialize: 默认为 True,允许字段序列化,可见数据转换为JSON格式
  • unique_for_date: 默认为 None,设置日期字段的唯一性
  • unique_for_month: 默认为 None,设置日期字段月份的唯一性
  • unique_for_year: 默认为 None,设置日期字段年份的唯一值
  • choices: 默认为空列表,设置字段的可选值
  • help_text: 默认为空字符串,用于设置表单的提示信息
  • db_column: 默认为 None,设置数据表的列名称,若不设置,则将该字段名作为数据表的列名
  • db_tablespace: 默认为 None,如果字段已经创建索引,那么数据库的表空间名称将作为该字段的索引名。注意:部分数据库不支持表空间
  • auto_created: 默认为 False,若为 True,则自动创建字段,用于一对一的关系模型
  • validators: 默认为空列表,设置字段内容的验证函数
  • error_message: 默认为 None,设置错误提示

ForeignKey 方法参数如下:
| 参数名 | 参数说明 |
|---------|---------|
| to | 指定关联的目标模型类。可以使用字符串表示模型类的路径,也可以直接使用模型类的引用 |
| on_delete | 指定当关联对象被删除的行为。CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()、DO_NOTHING |
| related_name | 指定反向关联的名称,默认为“模型类名_set” |
| to_field | 指定关联的目标模型类中用于关联的字段名称。默认为主键名称 |
| db_index | 如果为 True,则在目标模型的关联字段上创建索引 |
| null | 指定关联字段是否可以为空,如果 null=True,则数据库中该字段将允许 NULL 值 |
| blank | 指定关联字段是否可以为空,如果blank=True,则表单中该字段可以为空 |
| limit_choices_to | 指定关联对象的过滤条件,可以是一个字典、一个 QuerySet或者一个函数 |
| verbose_name | 用于在 Django Admin 后台显示字段名称 |
| help_text | 用于在 Django Admin 后台显示帮助文本|

on_delete 的 models 属性有下面设置选项:

  • CASCADE: 默认选项,级联删除,无需显示指定
  • PROTECT: 保护模式,如果采用该选项,删除的时候,会抛出 ProtectedError 错误
  • SET_NULL: 置空模式,删除的时候,外键字段被置为空,亲啊提示 blank=True,null=True,定义该字段的时候,允许为空
  • SET_DEFAULT: 默认值模式,删除的时候,外键字段设置为默认值,所以定义外键的时候注意加上一个默认值
  • SET(): 自定义一个值,该值当然只能是对应的实体了
  • DO_NOTHING:

在 settings.py 中配置 MySQL数据库如下:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_pro',
        'USER': 'admin',
        'PASSWORD': '123456',
        'HOST': '192.168.31.123',
        'PORT': '3306'
    },
    'sqlite3': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    },
}

案例:图书信息

  • 首先在 models.py 中定义两个模型
class BookTypeInfo(models.Model):
    id = models.AutoField(primary_key=True)
    bookTypeName = models.CharField(max_length=20)

    class Meta:
        db_table = 't_bookType'
        verbose_name = "图书类别信息"


class BookInfo(models.Model):
    id = models.AutoField(primary_key=True)
    bookName = models.CharField(max_length=20)
    price = models.FloatField()
    publishDate = models.DateField()
    bookType = models.ForeignKey(BookTypeInfo, on_delete=models.PROTECT)

    class Meta:
        db_table = 't_book'
        verbose_name = "图书信息"

2、Django5 数据迁移

  • 首先执行如下命令生成数据库迁移文件。所谓的迁移文件,是类似模型类的迁移类,主要描述了数据表结构的类文件
python manage.py makemigrations
  • 这个生成的迁移文件在 migrations 目录下,每执行一次,都会生成一个新文件
    img_52.png

-再次执行如下命令执行迁移文件,同步到数据库中

python manage.py migrate

案例:图书信息

  • 然后执行数据迁移命令将数据同步到数据库中
# 生成迁移文件
python manage.py makemigrations
# 在数据创建新表
python manage.py migrate
  • 新增一些数据
insert into t_bookType(1, 'computer'),(2, 'math');
insert into t_book(1, 'java', 100, '2004-03-16', 1),(2, 'design', 88, '2024-03-05', 1),(1, 'enjoy math', 50, '2019-03-05', 2);

3、Django5 模型数据查询

  • 数据库设有多种数据查询方式,如单表查询、多表查询、子查询和联合查询等,而 Django 的 ORM 框架对不同的查询方式定义了相应的
    API 方法。
  • all(): 可以通过外键关联配置,将级联信息也查询出来
  • get(): 可以返回满足条件的单个数据
  • filter(): 可以返回满足条件的数据
    • SQL的 or 查询,需要引入Q,from django.db.models import Q
    • 语法格式:Q(field=value) | Q(field=value) 多个Q之间用“|”隔开
    • SQL的不等于查询,在Q中使用“~”即可,相当于 select * from t_book where not (id=2)
  • exclude(): 实现不等于查询,返回不满足条件的数据
  • count(): 返回满足查询条件后的数据量
  • distinct(): 对返回的数据进行去重
  • order_by(): 对结果进行排序。默认是升序,如果需要降序,只需要在字段前面加“-”即可
  • annotate(): 方法用于实现聚合查询,比如数值求和,求平均值等

案例:图书信息

  • 实现图书信息的查询,通过外键关联配置,把图书类别信息也级联查询出来。通过 all() 方法将所有信息查询出来
def bookListView(request):
    """
    图书列表查询
    :param request:
    :return:
    """
    bookList = BookInfo.objects.all()
    # print("bookList =>", serializers.serialize('json', bookList))

    # 对返回的数据进行截取
    # bookList = BookInfo.objects.all()[:2]

    # 根据传入的字段返回对应的数据
    # booklist = bookList.values()
    # print("raw =>", booklist)

    # 获取满足条件的单个对象,返回类似是字典类型
    # booklist = BookInfo.objects.get(bookName='java')
    # print("booklist =>", booklist.bookName)

    # 返回满足id等于2的数据,返回类型是列表
    # booklist = BookInfo.objects.filter(id=2)
    # 可以传入多个
    # booklist = BookInfo.objects.filter(id=1, price=100)
    # 可以接收字典类型
    # my_dict = dict(id=1, price=100)
    # booklist = BookInfo.objects.filter(**my_dict)

    # SQL的 or 查询,需要引入Q,from django.db.models import Q
    # 语法格式:Q(field=value) | Q(field=value) 多个Q之间用“|”隔开
    # booklist = BookInfo.objects.filter(Q(id=2) | Q(price=100) | Q(bookName='enjoy math'))
    # SQL的不等于查询,在Q中使用“~”即可,相当于 select * from t_book where not (id=2)
    # booklist = BookInfo.objects.filter(~Q(id=2))

    # exclude() 返回不满足条件的数据
    # booklist = BookInfo.objects.exclude(id=1)
    # print("booklist =>", serializers.serialize('json', booklist))

    # count() 返回满足查询条件后的数据量
    # booklist = BookInfo.objects.exclude(id=1).count()
    # print("booklist =>", booklist)

    # distinct() 返回去重后的数据
    # booklist = BookInfo.objects.values('bookName').distinct()
    # print("booklist =>", booklist)

    # order_by() 对结果进行排序。默认是升序,如果需要降序,只需要在字段前面加“-”即可
    # booklist = BookInfo.objects.order_by('price')  # 升序
    # booklist = BookInfo.objects.order_by('-price')  # 降序
    # print("booklist =>", serializers.serialize('json', booklist))

    # annotate() 方法用于实现聚合查询,比如数值求和,求平均值等
    # annotate类似SQL里的 group by
    # booklist = BookInfo.objects.values('bookType').annotate(Sum('price'))
    # booklist = BookInfo.objects.values('bookType').annotate(Avg('price'))
    # print("booklist =>", booklist)

    context_value = {
        "title": '图书列表',
        "bookList": bookList
    }
    return render(request, 'book/list.html', context=context_value)

Django5 模型数据分页查询:

  • 在 Django 中实现分页通常使用 Paginator 类。以下是一个简单的示例,展示了如何在 Django 视图中实现分页
  • Paginator来源于: from django.core.paginator import Paginator
def bookListView(request):
    """
    图书列表查询
    :param request:
    :return:
    """
    bookList = BookInfo.objects.all()
    # Paginator(object_list, per_page)
    # object_list: 结果集/列表
    # per_page: 每页多少条记录
    # page(page_index): 获取第几页数据
    booklist = Paginator(bookList, 2).page(2)
    print("总记录数:", BookInfo.objects.count())
    print("booklist =>", booklist)

    context_value = {
        "title": '图书列表',
        "bookList": booklist
    }
    return render(request, 'book/list.html', context=context_value)

4、Django5 高级查询匹配符

  • 上面讲述了开发中常用的数据查询方法,但是有时需要设置不同的查询条件来满足多方面的查询需求。上述的查询条件 filter() 和
    get() 是使用等值的方法来匹配结果,若想使用大于、不等于或模糊查询的匹配方法,则可在查询条件 filter 和 get 里使用下表的匹配符实现。
  • 使用 get() 和 filter() 实现查询时存在一些差异:
    • 查询条件 get:查询字段必须是主键或唯一约束字段,并且查询的数据必须存在,如果查询的字段有重复或者查询的数据不存在,程序就会抛出异常信息
    • 查询条件 filter:查询字段没有限制,只要该字段是数据表的某一个字段即可。查询结果以列表的形式返回,如果查询结果为空,就返回空列表

高级查询匹配符:
| 匹配符 | 使用 | 说明 |
|--------|---------|---------|
| __exact | filter(job__exact=’开发) | 等价于SQL的 like ‘开发’ |
| __iexact | filter(job__iexact=’开发’) | 等价于SQL的 like ‘开发’ 并忽略大小写 |
| __contains | filter(job__contains=’开发’) | 模糊匹配,等价于SQL的 like ‘%开发%’ |
| __icontains | filter(job__icontains=’开发’) | 模糊匹配,等价于SQL的 like ‘%开发%’ 并忽略大小写 |
| __gt | filter(job__gt=5) | 大于5 |
| __gte | filter(job__gte=5) | 大于等于5 |
| __lt | filter(job__lt=5) | 小于5 |
| __lte | filter(job__lte=5) | 小于等于5 |
| __in | filter(job__in=[1,2,3]) | 判断是否在列表内 |
| __startswith | filter(job__startswith=’开发’) | 以“开发”开头 |
| __istartswith | filter(job__istartswith=’开发’) | 以“开发”开头,并忽略大小写 |
| __endwith | filter(job__endwith=’开发’) | 以“开发”结尾 |
| __iendwith | filter(job__iendwith=’开发’) | 以“开发”结尾,并忽略大小写 |
| __range | filter(job__range=’开发’) | 在“开发”范围内 |
| __year | filter(job__year=‘2025’) | 日期字段的年份 |
| __month | filter(job__month=‘12’) | 日期字段的月份 |
| __day | filter(job__day=30) | 日期字段的天数 |
| __isnull | filter(job__isnull=True/False) | 判断是否为空 |

def bookListView(request):
    """
    图书列表查询
    :param request:
    :return:
    """
    # 模糊匹配
    # bookList = BookInfo.objects.filter(bookName__contains='编程')
    # 小于等于
    bookList = BookInfo.objects.filter(price__lte=88)
    print("booklist =>", serializers.serialize('json', bookList))

    context_value = {
        "title": '图书列表',
        "bookList": booklist
    }
    return render(request, 'book/list.html', context=context_value)

5、Django5 模型多表查询

  • 在日常开发者中,常常需要对多表数据进行查询。多表查询需要在数据表之间建立关系表才能够实现。一对多或一对一的表关系是通过外键实现关联的,而多表查询分为正向查询和反向查询。
  • 一模型 BookInfo 和 BookTypeInfo 为例,如果查询主题的是 BookInfo,通过外键 bookType_id 去查询 BookTypeInfo
    的关联数据,该查询称为正向查询;如果查询对象的主题是 BookTypeinfo,要查询它与模型 BookInfo 的关联数据,那么该查询称为反向查询。
def bookListView(request):
    """
    多表查询
    :param request:
    :return:
    """
    # 正向查询
    book: BookInfo = BookInfo.objects.filter(id=1).first()
    print("book =>", book.bookName, book.bookType.bookTypeName)

    # 反向查询
    bookType: BookTypeInfo = BookTypeInfo.objects.filter(id=1).first()
    print("bookType =>", bookType.bookinfo_set.first().bookName)

    context_value = {
        "title": '图书列表',
        "bookList": bookType.bookinfo_set.all()
    }
    return render(request, 'book/list.html', context=context_value)

6、Django5 模型数据的新增

  • Django 对数据库的数据进行增、删、改操作是借助内置 ORM 框架所提供的 API 方法实现的。简单来说,它在模型基础类 Model
    里定义数据操作方法,通过类继承将这些操作方法传给开发者自定义的模型对象,再由模型对象调用即可实现数据操作。
  • 模型数据的新增通过模型的 save() 方法实现,添加后可以返回主键 id 值

第一: 先创建一个获取类型的方法,用于填充到下拉框:

def preAdd(request):
    """
    预处理:获取图书类型
    :param request:
    :return:
    """
    bookTypeList = BookTypeInfo.objects.all()

    context_value = {
        "title": '图书添加',
        "bookTypeList": bookTypeList
    }

    return render(request, 'book/add.html', context=context_value)

第二:在原来 book/list.html 中添加跳转的链接:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{title}}</title>
</head>
<body>
<h2>{{title}}</h2><br>
<div><a href="/book/preAdd">添加</a></div>
<br>
<table border="1">
    <tr>
        <th>编号</th>
        <th>图书名称</th>
        <th>图书价格</th>
        <th>出版日期</th>
        <th>图书类别</th>
    </tr>
    {% for book in bookList %}
    <tr>
        <td>{{book.id}}</td>
        <td>{{book.bookName}}</td>
        <td>{{book.price}}</td>
        <td>{{book.publishDate | date:'Y-m-d'}}</td>
        <td>{{book.bookType.bookTypeName}}</td>
    </tr>
    {% endfor %}
</table>
</body>
</html>

第三:创建 book/add.html 页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{title}}</title>
</head>
<body>
<h2>{{title}}</h2><br>
<div><a href="/book/preAdd">添加</a></div>
<br>
<table border="1">
    <tr>
        <th>编号</th>
        <th>图书名称</th>
        <th>图书价格</th>
        <th>出版日期</th>
        <th>图书类别</th>
    </tr>
    {% for book in bookList %}
    <tr>
        <td>{{book.id}}</td>
        <td>{{book.bookName}}</td>
        <td>{{book.price}}</td>
        <td>{{book.publishDate | date:'Y-m-d'}}</td>
        <td>{{book.bookType.bookTypeName}}</td>
    </tr>
    {% endfor %}
</table>
</body>
</html>

第四:在 urls.py 创建映射:

path('book/list/', helloWorld.views.bookListView),
path('book/preAdd/', helloWorld.views.preAdd),
path('book/add/', helloWorld.views.addBookInfo),

第五: 在 views.py 进行新增实现:

def addBookInfo(request):
    """
    图书添加
    :param request:
    :return:
    """
    book = BookInfo()
    book.bookName = request.POST.get('bookName')
    book.publishDate = request.POST.get('publishDate')
    book.bookType_id = request.POST.get('bookType_id')
    book.price = request.POST.get('price')
    print(book.bookName, book.publishDate, book.bookType_id, book.price)

    # 添加数据
    book.save()

    # 添加完成后返回新增的id
    id = book.id
    print("id => ", id)

    return redirect('/book/list/') # 这里的 bookListView 是列表的处理函数

7、Django5 模型数据的修改

  • 模型数据的添加和修改都是使用 save() 方法

第一: 在 views.py 定义 preUpdate 方法,用于根据 id 获取图书信息:

def preUpdate(request, id):
    """
    预处理: 获取图书信息
    :param request:
    :return:
    """
    book = BookInfo.objects.get(id=id)
    bookTypeList = BookTypeInfo.objects.all()

    content_value = {
        'title': '图书修改',
        'bookTypeList': bookTypeList,
        'book': book
    }

    return render(request, 'book/edit.html', context=content_value)

第二: 在 urls.py 定义映射:

path('book/preUpdate/<int:id>', helloWorld.views.preUpdate),
path('book/update', helloWorld.views.updateBookInfo),

第三:修改 list.html,添加修改按钮:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{title}}</title>
</head>
<body>
<h2>{{title}}</h2><br>
<div><a href="/book/preAdd">添加</a></div>
<br>
<table border="1">
    <tr>
        <th>编号</th>
        <th>图书名称</th>
        <th>图书价格</th>
        <th>出版日期</th>
        <th>图书类别</th>
        <th>操作</th>
    </tr>
    {% for book in bookList %}
    <tr>
        <td>{{book.id}}</td>
        <td>{{book.bookName}}</td>
        <td>{{book.price}}</td>
        <td>{{book.publishDate | date:'Y-m-d'}}</td>
        <td>{{book.bookType.bookTypeName}}</td>
        <td>
            <a href="/book/preUpdate/{{book.id}}">修改</a>
        </td>
    </tr>
    {% endfor %}
</table>
</body>
</html>

第四:新建编辑页面 book/edit.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>{{title}}</h3>
<form action="/book/update" method="post">
    {% csrf_token %}
    <table>
        <tr>
            <td>图书名称:</td>
            <td>
                <input type="text" name="bookName" value="{{book.bookName}}">
            </td>
        </tr>
        <tr>
            <td>出版日期:</td>
            <td>
                <input type="text" name="publishDate" value="{{book.publishDate | date:'Y-m-d' }}">
            </td>
        </tr>
        <tr>
            <td>图书类型:</td>
            <td>
                <select name="bookType" id="bookTypeSelect">
                    {% for bookType in bookTypeList %}
                    <option value="{{ bookType.id }}" {% if bookType.id == book.bookType.id %}selected{% endif %}>
                        {{ bookType.bookTypeName }}
                    </option>
                    {% endfor %}
                </select>

            </td>
        </tr>
        <tr>
            <td>图书价格:</td>
            <td>
                <input type="text" name="price" value="{{book.price}}">
            </td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="hidden" name="id" value="{{book.id}}">
                <input type="submit" value="提交">
            </td>
        </tr>
    </table>
</form>
</body>
</html>

第五:在 views.py 中编写修改保存功能:

def updateBookInfo(request):
    """
    修改并保存图书信息
    :param request:
    :return:
    """
    book = BookInfo()
    book.id = request.POST.get('id')
    book.bookName = request.POST.get('bookName')
    book.publishDate = request.POST.get('publishDate')
    book.bookType_id = request.POST.get('bookType')
    book.price = request.POST.get('price')
    # print(book.bookName, book.publishDate, book.bookType_id, book.price)
    # 提交修改数据
    book.save()

    return redirect('/book/list/')

8、Django5 模型数据的删除

  • Django5 ORM 框架提供了 delete() 方法来实现数据的删除操作。
  • 常用的删除方式:删除所有数据、删除指定id数据、根据filter条件删除数据。
# 删除所有数据
BookInfo.objects.all().delete()

# 删除指定id数据
BookInfo.objects.get(id=1).delete()

# 根据条件删除数据
BookInfo.objects.filter(price__lte=50).delete()

9、Django5 ORM执行SQL语句

  • Django 在查询数据时,大多数查询都能使用ORM提供的API方法,但对于一些复杂的查询可能难以使用ORM的API方法实现,因此Django引入了SQL语句的执行方法。
  • Django执行SQL有3种实现方式:
    • extra(): 结果集修改器,一种提供额外查询参数的机制
    • raw(): 执行原始SQL并返回模型示例对象
    • execute(): 直接执行自定义SQL

**extra()
适用于ORM难以实现的查询条件,将查询条件使用原生SQL语法实现,此方法需要依靠模型对象,在某程度上可防止SQL注入。它一共定义了6个参数,每个参数说明如下:
**

  • select: 添加新的查询字段,即新增并定义模型之外的字段
  • where: 设置查询条件
  • params: 如果where设置了字符串格式化 %s,那么该参数为 where 提供数值
  • tables: 连接其他数据库,实现多表查询
  • order_by: 设置数据的排序方式
  • select_params: 如果select设置字符串格式化 %s,那么该参数为select提供数值
bookList = BookInfo.objects.extra(where=['price>%s'], params=[90])

raw()只能实现数据查询操作,并且也要依靠模型对象,它一共定义了4个参数,每个参数说明如下:

  • raw_query: SQL语句
  • params: 如果 raw_query 设置字符串格式化,那么该参数为 raw_query提供参数
  • translations: 为查询的字段设置别名
  • using: 数据库对象,即 Django 所连接的数据库
bookList = BookInfo.objects.raw("select * from t_book where price>%s", params=[90])

**execute()
执行SQL语句无须经过Django的ORM框架,Django连接数据库需要借助第三方模块实现,如MySQL的mysqlclient模块和SQLite的sqlite3模块等,这些模块连接数据库之后,可通过cursor的方式执行SQL语句,而execute就是使用这种方式执行SQL语句:
**

from django.db import connection
from django.db.backends.utils import CursorDebugWrapper

cursor: CursorDebugWrapper = connection.cursor()
cursor.execute("select * from t_book where price > 30")
rows = cursor.fetchall()
columns = [col[0] for col in cursor.description]
bookList = []
for row in rows:
    book_data = dict(zip(columns, row))
    book = BookInfo(**book_data)
    bookList.append((book))

# print("bookList =>", bookList)

10、Django5 ORM数据库事物

  • 事务是指作为单个逻辑执行的一系列操作,这些操作具有原子性,即这些操作要么完全执行,要么完全不执行。事务处理可以确保事务性成员的所有操作都成功完成,否则不回执行数据操作。
  • 事务应该具有4个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),这4个特性通常称为ACID特性。
    • 原子性:一个事务是不可分割的工作单位,事务中包括的操作要么都做,要么都不做
    • 一致性:事务必须是数据库从某个一致性状态变到另一个一致性状态,一致性与原子性是密切相关的
    • 隔离性:一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对其他事务是隔离的,各个事务之间不能互相干扰
    • 持久性:持久性也称为永久性(Permanence),指一个事务一旦提交,他对数据库中的数据的改变应该是永久性的,其他操作或故障不应该对其有任何影响

Django5主要有4个事务方法:

  • atomic(): 在视图函数或视图类里使用事务
  • savepoint(): 开启事务
  • savepoint_rollback(): 回滚事务
  • savepoint_commit(): 提交事务

在 models.py 中创建一个账户模型,并执行 makemigrations、migrate 在数据库创建表,并录入一些数据:

class AccountInfo(models.Model):
    id = models.AutoField(primary_key=True)
    user = models.CharField(max_length=20)
    account = models.FloatField()

    class Meta:
        db_table = "t_account"
        verbose_name = "用户账户信息"

python manage.py makemigrations
python manage.py migrate

在views.py文件,在没有使用事务时:

  • 张三成功加100,李四没有减100
def transfer(request):
    """
    模拟转账
    :param request:
    :return:
    """
    user1 = AccountInfo.objects.filter(user="张三")
    user1.update(account=F('account') + 100)

    user2 = AccountInfo.objects.filter(user="李四")
    user2.update(account=F('account') - 100 / 0)

    return HttpResponse('ok')

在views.py文件,使用事务时:

  • 由于事务执行过程有报错,于是事务进行回滚
from django.db import connection, transaction


# 使用事务
@transaction.atomic
def transfer(request):
    """
    模拟转账
    :param request:
    :return:
    """
    # 开启事务
    sid = transaction.savepoint()

    try:
        user1 = AccountInfo.objects.filter(user="张三")
        user1.update(account=F('account') + 100)

        user2 = AccountInfo.objects.filter(user="李四")
        user2.update(account=F('account') - 100 / 0)

        # 提交事务(如果不设置,当程序执行完成后,会自动提交事务)
        transaction.savepoint_commit(sid)
    except Exception as e:
        print('异常信息:', e)

        # 事务回滚
        transaction.savepoint_rollback(sid)

    return HttpResponse('ok')

七、Djangso5 表单定义与使用

  • 表单是搜集用户数据信息的各种表单元素的集合,其作用是实现网页上的数据交互。
  • 网页表单是Web开发的一项基本功能,Django5的表单功能由Form类实现,主要分为两种:django.forms.Form 和
    django.forms.ModelForm。前者是一个基础的表单功能,后者是在前者的基础上结合模型所生成的数据表单。

1、Django5 Form表单定义与使用

  • 首先在项目中创建 forms.py 文件,然后在 forms.py 里定义 BookInfoForm 类
class BookInfoForm(Form):
    """
    图书表单
    """
    bookName = forms.CharField(max_length=20, label="图书名称")
    price = forms.FloatField(label="图书价格")
    publishDate = forms.DateField(label="出版日期")
    # 获取图书类别列表
    bookTypeList = BookTypeInfo.objects.values()
    # 图书类别以下拉框形式显示,下拉框选项id是图书类别id,下拉框选项文本是图书类别名称
    choices = [(v['id'], v['bookTypeName']) for v, v in enumerate(bookTypeList)]
    bookType = forms.ChoiceField(required=False, choices=choices, label="图书类别")
  • 在 views.py 文件中使用表单:
def preAdd(request):
    """
    预处理:获取图书类型
    :param request:
    :return:
    """
    bookTypeList = BookTypeInfo.objects.all()

    context_value = {
        "title": '图书添加',
        "bookTypeList": bookTypeList
    }

    return render(request, 'book/add.html', context=context_value)


def preAdd2(request):
    """
    预处理:获取图书类型
    :param request:
    :return:
    """
    form = BookInfoForm()

    context_value = {
        "title": '图书添加2',
        "form": form
        # "bookTypeList": bookTypeList
    }

    return render(request, 'book/add2.html', context=context_value)
  • 创建新增表单页面
# book/add2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>{{title}}</h3>
<form action="/book/add/" method="post">
    {% csrf_token %}

    <table>
        {{ form.as_table }}
        <tr>
            <td colspan="2">
                <input type="submit" value="提交">
            </td>
        </tr>
    </table>
</form>
</body>
</html>

表单 Form 的常用属性和方法如下:

  • data: 默认值为 None,以字典形式表示,字典的键为表单字段,代表将数据绑定到对应的表单字段
  • auto_id: 默认值为 id_%s,以字符串格式化表示,若设置HTML元素控件的id属性,比如表单字段 job,则元素空间id属性为
    id_job,%s代表表单字段名称
  • prefix: 默认值为None,以字符串表示,设置表单的空间属性name和id属性值,如果一个网页里使用多个相同的表单,那么设置该属性可以区分每个表单
  • initial: 默认值为 None,以字典形式表示,在表单的示例化过程中设置初始值
  • label_suffix: 若参数值为None,则默认为冒号,以表单字段 job为例,其HTML空间含有label标签(
    ),其中label标签里的冒号由参数label_suffix设置
  • field_order: 默认值为None,则以表单字段定义的先后顺序进行排序,若要自定义排序,则将每个表单字段u你按先后顺序放置在列表里,并把列表作为该参数的值
  • use_required_attribute: 默认值为None(或为True),为表单字段所对应的HTML控件设置required属性,则该控件为必填项,数据不能为空,若设为False,则HTML空间为可填项
  • errors(): 验证表单的数据是否存在异常,若存在异常,则获取异常信息,异常信息可设为字典或JSON格式
  • is_valid(): 验证表单数据是否存在异常,若存在,则返回False,否则返回True
  • as_table(): 将表单字段以HTML的\标签生成网页表单
  • as_ul(): 将表单字段以HTML的\
      标签生成网页表单
    • as_p(): 将表单字段以HTML的\

      标签生成网页表单

    • has_changed(): 对比用于提交的数据表单与表单初始化数据是否发生变化

    表单自定义字段如下类型:

    • CharField: 文本框,参数 max_length 和 min_length 分别设置文本长度
    • IntegerField:数值框,参数 max_value 设置最大值,min_value 设置最小值
    • FloatField: 数值框,继承IntegerField,验证是否为浮点数
    • DecimalField: 数值框,继承IntegerField,验证数值是否设有小数点,参数max_digits设置最大位数,参数 decimal_places设置小数点最大位数
    • DateField: 文本框,继承BaseTemporalField,具有验证日期格式的功能,参数 input_formats 设置日期格式
    • TimeField: 文本框,继承BaseTemporalField,验证数据是否为datetime.time或特定时间格式的字符串
    • DateTimeField: 文本框,继承BaseTemporalField,验证数据是否为 datetime.datetime,datetime.date或特定日期格式的字符串
    • DurationField: 文本框,验证数据是否为一个有效的时间段
    • ...... 其他具体请参考文档

    2、Django5 ModelForm表单定义与使用

    • ModelForm 是在 Form 的基础上结合模型所生成的模型表单,模型表单时间模型字段转换成表单字段,有表单字段生成HTML空间,从而生成网页表单

    ModelForm 有9个属性,说明如下:

    • model: 必须属性,用于绑定Model对象
    • fields: 可选属性,设置模型内哪些字段转换成表单字段,默认值为None,代表所有的模型字段,也可以将属性值设为”all“,同样表示所有的模型字段。若只需要部分模型字段,则将模型字段写入一个列表或元组里,再把该列表或元组作为属性值
    • exclude: 可选属性,与 fields 相反,禁止模型字段转换成表单字段。属性值以列表或元组表示,若设置了该属性,则属性 fields 无须设置
    • labels: 可选属性,设置表单字段的参数label,属性以字典表示,字典的键为模型字段
    • widgets: 可选属性,设置表单字段的参数 widget,属性值以字典表示,字典的键为模型字段
    • localized_fields: 可选属性,将模型字段设为本地化的表单字段常用于日期类型的模型字段
    • field_classes: 可选属性,将模型字段重新定义,默认情况下,模型字段与表单字段遵从Django内置的转换规则
    • help_texts: 可选属性,设置表单字段的参数 help_text
    • error_messages: 可选属性,设置表单字段的参数 error_messages

    模型字段转换表单字段遵从Django内置的规则进行转换,两者的转换规则如下:
    | 模型字段类型 | 表单字段类型 |
    |-----------|-----------|
    | AutoField | 不能转换表单字段 |
    | BigAutoField | 不能转换表单字段 |
    | BigIntegerField | IntegerField |
    | BinaryField | CharField |
    | BooleanField | BooleanField或者NullBooleanField |
    | CharField | CharField |
    | DateField | DateField |
    | DateTimeField | DateTimeField |
    | DecimalField | DecimalField |
    | EmailField | EmailField |
    | FileField | FileField |
    | FilePathField | FilePathField |
    | ForeignKey | ModelChoiceField |
    | ImageField | ImageField |
    | IntegerField | IntegerField |
    | IPAddressField | IPAddressField |
    | GenericIPAddressField | GenericIPAddressField |
    | ManyToManyField | ModelMultipleChoiceField |
    | NullBooleanField | NullBooleanField |
    | PositiveIntegerField | IntegerField |
    | PositiveSmallIntegerField | IntegerField |
    | SlugField | SlugField |
    | SmallIntegerField | IntegerField |
    | TextField | CharField |
    | TimeField | TimeField |
    | URLField | URLField |

    在 forms.py 里创建 BookInfoModelForm 类,继承 ModelForm:

    class BookInfoModelForm(ModelForm):
        # 配置中心
        class Meta:
            model = BookInfo  # 导入模型
            fields = '__all__'  # 代表使用所有字段
            # fields = ['bookName', 'price'] # 指定字段
            widgets = {  # 定义控件
                'bookName': forms.TextInput(attrs={"placeholder": "请输入书名", "id": "bookName", "class": "inputClass"})
            }
            labels = {  # 指定标签
                "bookName": "图书名称",
                "price": "图书价格",
                "publishDate": "出版日期",
                "bookType": "图书类别"
            }
            help_texts = {
                "bookName": "请输入图书名称"
            }
    

    在 views.py 创建 preAdd3 函数:

    def preAdd3(request):
        """
        预处理:添加图书,使用 ModelForm 表单
        :param request:
        :return:
        """
        form = BookInfoModelForm()
    
        context_value = {
            "title": '图书添加3',
            "form": form
            # "bookTypeList": bookTypeList
        }
    
        return render(request, 'book/add2.html', context=context_value)
    

    八、Django5 内置 Admin 系统

    • Admin
      后台系统也称为网站后台管理系统,主要针对网站的信息进行股那里,如文字,图片、影音和其他日常使用的文件的发布、更新、删除等操作,也包括功能信息的统计和管理。简单来说,它是对网站数据库和文件进行快速操作和管理的系统,以使网页聂荣能够及时得到更新和调整。

    1、Django5 内置 Admin 系统初体验

    • Django 已内置 Admin 后台管理系统,在创建 Django 项目的时候,可以从配置文件 settings.py 中看到项目已默认启用 Admin 后台系统。
      img_53.png
    • urls.py 里定义了 Admin 系统的首页地址:
      img_54.png
    • 在浏览器输入 http://127.0.0.1:8000/admin/ 即可进入 Admin 系统首页,默认跳转到 Admin 系统登录页面。
      img_55.png

    Admin 后台管理系统汉化:

    • 打开Admin系统进入登录页,发现页面是英文,我们可以加一个中文本地化的中间件实现本地化中文
    • 在 settings.py 里使用如下中间件:
    """
    ##### 中间件配置 #####
    
    Django 默认配置的中间件,其含义说明如下:
    -- SecurityMiddleware:内置的安全机制,保护用户与网站的通信安全
    -- SessionMiddleware:Session会话功能
    -- CommonMiddleware:处理请求信息,规范化请求内容
    -- CsrfViewMiddleware:开启CSRF防护功能
    -- AuthenticationMiddleware:开启内置的用户认证系统
    -- MessageMiddleware:开启内置的信息提示功能
    -- XFrameOptionsMiddleware:防止恶意程序单击劫持
    -- LocaleMiddleware:国际化和本地化功能
    """
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        # 使用中文
        'django.middleware.locale.LocaleMiddleware',
        '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',
        # 'helloWorld.middleware.middleware.DefineMiddleware'
    ]
    

    创建管理员账户:

    • Admin 系统用户、权限、认证相关的表有如下6个,其中 auth_user 是用来存后台管理员信息的,默认里面是没有数据的
      img_56.png
    • 创建管理员用户可以通过python内置的 manage.py 的 createsuperuser 命令来创建超级管理员的账号和密码:
    python manage.py createsuperuser
    
    # 当前学习平台用户名:admin
    # 当前学习平台密码:Admin123
    

    img_57.png

    在Admin后台管理系统中可以看到,网页布局分为站点布局、认证和授权、用户和组,分别说明如下:

    • 站点管理是整个 Admin 后台的主题页面,整个项目的 App 所定义的模型都会在此页面显示
    • 认证和授权是 Django 内置的用户认证系统,包括用户信息、权限管理和用户组设置等功能
    • 用户和组是认证和授权所定义的模型,分别对应数据表 auth_user 和 auth_user_groups
      img_58.png

    2、Django5 注册模型到 Admin 系统

    • 在开发业务系统时,会定义很多的业务模型,我们可以把模型注册到Admin系统,让 Admin 系统帮我们维护这些模型。也就是在 Admin
      后台自动给模型实现增删改查功能

    注册模型到 Admin 系统有2种方式:

    • 方式一:直接将模型注册到 Admin 后台,以 BookTypeInfo 模型为例。打开 admin.py
    from django.contrib import admin
    
    from helloWorld.models import BookTypeInfo
    
    # Register your models here.
    # 方式一:直接将模型注册到 Admin 后台,以 BookTypeInfo 模型为例。
    admin.site.register(BookTypeInfo)
    

    img_59.png

    • 方式二:自定义类,继承 ModelAdmin,以 BookInfo 为例。打开 admin.py
    from django.contrib import admin
    
    from helloWorld.models import BookTypeInfo, BookInfo
    
    # Register your models here.
    # 方式一:直接将模型注册到 Admin 后台,以 BookTypeInfo 模型为例。
    admin.site.register(BookTypeInfo)
    
    
    # 方式二:自定义类,继承 ModelAdmin,以 BookInfo 模型为例。
    @admin.register(BookInfo)
    class BookInfoAdmin(admin.ModelAdmin):
        # 设置显示的字段
        list_display = ['id', 'bookName', 'price', 'publishDate', 'bookType']
        # 设置可搜索字段
        search_fields = ['bookName', 'publishDate']
    

    img_60.png

    3、Django5 内置 Admin 系统自定义设置

    • 在使用 Django 的内置 Admin 系统时会发现一些默认的设置,并不符合我们的业务要求,我们需要自定义设置
    • 比如模块项目管理标题,默认使用了模块项目名称。可以打开 helloWorld 项目的 apps.py,配置类里加 verbose_name= ‘网站图书管理’
      img_61.png
    # helloWorld/apps.py
    
    from django.apps import AppConfig
    
    
    class HelloworldConfig(AppConfig):
        default_auto_field = 'django.db.models.BigAutoField'
        name = 'helloWorld'
        verbose_name = '图书管理后台'
    
    • 设置网站标题和应用标题:在 admin.py 里配置
      img_62.png
    # helloWorld/admin.py
    
    from django.contrib import admin
    
    from helloWorld.models import BookTypeInfo, BookInfo
    
    # 设置网站标题和应用标题
    admin.site.site_title = '后台管理系统'
    admin.site.index_title = '图书管理模块'
    admin.site.site_header = 'XXX网站管理系统'
    
    # Register your models here.
    # 方式一:直接将模型注册到 Admin 后台,以 BookTypeInfo 模型为例。
    admin.site.register(BookTypeInfo)
    
    
    # 方式二:自定义类,继承 ModelAdmin,以 BookInfo 模型为例。
    @admin.register(BookInfo)
    class BookInfoAdmin(admin.ModelAdmin):
        # 设置显示的字段
        list_display = ['id', 'bookName', 'price', 'publishDate', 'bookType']
        # 设置可搜索字段
        search_fields = ['bookName', 'publishDate']
    

    img_64.png

    4、Django5 内置 Admin 系统二次开发

    创建一个普通管理员账户:

    • 首先在 Admin 后台管理系统新建一个普通管理员账号。在【认证和授权】的用户右侧点击【新增】,填写管理员信息,勾选可登录,分配用户权限
      img_65.png
    • 在 helloWorld/admin.py 重写 get_readonly_fields 方法,判断哪些字段对某个用户只读,例如下列除了超级管理员,对普通管理员
      bookName 属性只读,不可编辑
    # 方式二:自定义类,继承 ModelAdmin,以 BookInfo 模型为例。
    @admin.register(BookInfo)
    class BookInfoAdmin(admin.ModelAdmin):
        # 设置显示的字段
        list_display = ['id', 'bookName', 'price', 'publishDate', 'bookType']
        # 设置可搜索字段
        search_fields = ['bookName', 'publishDate']
    
        # 重新方法,设置只读字段
        def get_readonly_fields(self, request, obj=None):
            if request.user.is_superuser:
                self.readonly_fields = []
            else:
                self.readonly_fields = ['bookName']
    
            return self.readonly_fields
    
        # 还可以重写很多其他方法
        # 设置外键下拉框过滤筛选
        def formfield_for_foreignkey(self, db_field, request, **kwargs):
            pass
    
        # 添加或修改处理逻辑重写,可以增加一些日志等处理
        def save_model(self, request, obj, form, change):
            pass
    

    修改属性名称为中文:

    • 在模型定义字段时给字段添加 verbose_name 属性进行设置
      img_66.png
    # helloWorld/models.py
    
    class BookInfo(models.Model):
        id = models.AutoField(primary_key=True)
        bookName = models.CharField(max_length=20, verbose_name="名称")
        price = models.FloatField(verbose_name="价格")
        publishDate = models.DateField(verbose_name="出版日期")
        bookType = models.ForeignKey(BookTypeInfo, on_delete=models.PROTECT, verbose_name="图书类型")
    
        class Meta:
            db_table = 't_book'
            verbose_name = "图书信息"
    

    自定义模板:

    • Admin 后台管理系统的模板文件是 Django 框架内置提供,可以在源码路径: site-packages -> django -> contrib -> admin ->
      templates 下找到
    • 很多时候我们需要修改默认的模板,包括程序功能、样式等来达到业务需求。我们可以在项目的模板项目的 templates
      下(不建议在django源码里修改,会影响多个子应用),通过优先级来实现修改模板。
    • 具体方式如下:模块项目(比如在helloWorld)的templates下,新建 admin 目录,然后在该目录下创建需要覆盖的模板名称。

    九、Django5 内置 Auth 认证系统

    • 在开发一个网站的时候,无可避免的需要设计实现网站的用户系统。此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能。Django作为一个强大的框架,它内置了强大的用户认证系统Auth用于实现上述需求。
    • Auth系统默认使用 auth_user 表来存储用户数据。

    1、用户注册实现

    • 实现 Auth 认证系统里的用户注册的话,用的是 auth 模板 models.py 里定义的 User 模型。
    # helloWorld/urls.py
    
    # Auth 认证
    # 跳转注册页面
    path('toRegister', helloWorld.views.to_register),
    # 提交注册请求
    path('auth/register', helloWorld.views.register),
    
    # helloWorld/views.py
    
    def to_register(request):
        """
        跳转注册页面
        :param request:
        :return:
        """
        return render(request, 'auth/register.html')
    
    
    def register(request):
        """
        用户注册
        :param request:
        :return:
        """
        print(request.method)
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            # 校验用户是否存在
            result = User.objects.filter(username=username)
            # 注册用户
            if result:
                return render(request, 'auth/register.html',
                              context={"errorInfo": '当前用户已存在', "username": username, "password": password})
    
            User.objects.create_user(username=username, password=password)
            return render(request, 'auth/login.html')
    

    2、用户登录实现

    • 用户登录功能,后端验证主要通过 auth 模块提供的 authenticate() 校验方法,以及 login() 登录方法实现。
    • 通过 authenticate 校验用户是否已经存在。用户校验成功后,返回一个封装好的用户对象;校验错误则返回 None
    • 用户对象 is_active 方法判断用户是否激活
    • 通过调用 auth.login,用户登录成功之后,返回给客户端登录的凭证或者说是令牌、随机字符串,则不需要我们去操作 django_session
      表,会自动创建 session

    当执行完 auth.authenticate 和 auth.login 后,也就是登录成功后,我们就可以通过 request.user
    直接获取到当前登录的用户对象数据:登录成功的情况下,该方法获得的是登录用户对象;登录不成功的情况下,该方法获得的是匿名对象
    AnonymousUser

    # helloWorld/urls.py
    
    # Auth 认证
    # 跳转注册页面
    path('auth/toRegister', helloWorld.views.to_register),
    # 提交注册请求
    path('auth/register', helloWorld.views.register),
    # 跳转登录页面
    path('auth/toLogin', helloWorld.views.to_register),
    # 提交登录请求
    path('auth/login', helloWorld.views.register),
    
    # helloWorld/views.py
    
    def to_login(request):
        """
        跳转登录页面
        :param request:
        :return:
        """
        return render(request, 'auth/login.html')
    
    
    def login(request):
        """
        用户登录
        :param request:
        :return:
        """
        if request.method == 'POST':
            username = request.POST.get('username')
            password = request.POST.get('password')
            # 通过 auth 模块来检验加密后的密码,校验成功返回用户对象,否侧返回None
            user: User = auth.authenticate(request, username=username, password=password)
            # print(user, type(user))
            if user and user.is_active:
                # 用户登录成功之后,返回给客户端登录的凭证或者说是令牌、随机字符串
                auth.login(request, user)
                return render(request, 'auth/index.html')
            return render(request, '/auth/login.html', context={
                "errorInfo": "用户名或密码错误",
                "username": username,
                "password": password
            })
    

    3、用户修改密码实现

    • 用户修改密码主要通过 request.user 对象的 set_password 实现,当然校验原密码用 check_password,设置完成后,需要保存,调用
      save() 方法。
    # helloWorld/urls.py
    
    # 修改密码 get 请求直接跳转页面,post请求执行处理
    path('auth/setPwd', helloWorld.views.setPwd),
    
    # helloWorld/views.py
    
    def setPwd(request):
        """
        修改密码
        :param request:
        :return:
        """
        if request.method == 'POST':
            oldPwd = request.POST.get("oldPwd")
            newPwd = request.POST.get("newPwd")
            # 1、校验用户原密码 check_password
            isTrue = request.user.check_password(oldPwd)
    
            if not isTrue:
                return render(request, 'auth/setPwd.html', context={
                    "errorInfo": "原密码错误",
                    "oldPwd": oldPwd,
                    "newPwd": newPwd
                })
            else:
                # 2、设置新密码 set_password 实现加密
                request.user.set_password(newPwd)
                # 3、保存新密码
                request.user.save()
    
                return render(request, 'auth/index.html')
    
        else:
            return render(request, 'auth/setPwd.html')
    

    4、用户注销实现

    • 用户注销通过 auth.logout 方法实现。
    # helloWorld/urls.py
    
    # 注销登录
    path('auth/logout', helloWorld.views.logout),
    
    # helloWorld/views.py
    
    def logout(request):
        """
        注销登录
        :param request:
        :return:
        """
        # 用户注销
        auth.logout(request)
    
        return render(request, 'auth/index.html')
    

    十、Django5 内置其它高级功能