pip install Django==5.1.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
用命令的方式:
django-admin startproject 项目名称
如果 django-admin.exe 安装的位置是当前项目的虚拟环境中,则需要将 django-admin.exe 在环境变量中进行配置,
或者使用相对路径(或绝对路径)的方式找到 django-admin.exe 再执行项目创建命令
用pyCharm工具的方式:
项目文件介绍:
django_test
|-- django_test
|-- __init__.py
|-- asgi.py
|-- settings.py
|-- urls.py
|-- wsgi.py
|-- manage.py
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项目 |
项目的创建与功能演示都是在项目虚拟环境中
下列使用的命令是在配置环境变量后使用的命令,而在项目虚拟环境的命令方式如图
# [] 表示可选参数
startapp 应用名称 [destination]
应用目录介绍:
app01
|-- migrations
|-- __init__.py
|-- __init__.py
|-- admin.py
|-- apps.py
|-- models.py
|-- tests.py
|-- views.py
Django 的 MTV 模型:
python .\manage.py startapp app01
def index(request):
return render(request, 'index.html')
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', app01.views.index)
]
runserver [port]
基本配置:
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:
"""
##### 静态资源配置 #####
静态资源指的是网站中不回改变的文件。在一般的应用中,静态资源包括 CSS文件、JavaScript文件以及图片等资源文件。
默认配置,App下的 static 目录为静态资源,可以直接访问,其它目录不可以。
"""
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/
STATIC_URL = 'static/'
静态资源集合配置 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']
静态资源部署配置 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:
"""
##### 媒体资源配置 #####
媒体资源只有配置属性 MEDIA_URL 和 MEDIA_ROOT。 MEDIA_URL 用于设置媒体资源的路由地址,MEDIA_ROOT 用于获取 media 文件夹在计算机系统的完整路径信息。
"""
# 设置媒体路由
MEDIA_URL = 'media/'
# 设置 media 目录的完整路径
MEDIA_ROOT = BASE_DIR / 'helloWorld/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)
模板配置:
"""
##### 模板配置 #####
模板配置是以列表格式呈现的,每个元素具有不同的含义,其含义说明如下:
-- 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’] 配置的顺序进行渲染。
例如按照当前的配置,应用模板在前,项目模板在后,则优先渲染应用模板。
数据库配置:
"""
##### 数据库配置 #####
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',
}
}
pip install mysqlclient -i https://pypi.tuna.tsinghua.edu.cn/simple
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': '数据库名称',
'USER': '用户名',
'PASSWORD': '数据库密码',
'HOST': '地址',
'PORT': '端口'
}
}
Django5 至少需要 MySQL 8.0.11 及以上版本
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': '数据库名称',
'USER': '用户名',
'PASSWORD': '数据库密码',
'HOST': '地址',
'PORT': '端口'
},
'sqlite3': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
migrate
如果使用 mysqlclient 执行 migrate 指令不成功,如下图。
可以安装 PyMySQL 进行替换(或者使用 MySQLDB 等进行替换)
pip install PyMySQL -i https://pypi.tuna.tsinghua.edu.cn/simple
# 在 settings.py 中引入并使用 pymysql
import pymysql
pymysql.install_as_MySQLdb()
中间件配置:
"""
##### 中间件配置 #####
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',
]
其它配置:
"""
##### 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'
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)
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)
路由重定向:
# 在页面上请求 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)
自定义视图重定向:
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)
# 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
HttpRequest类的常用属性如下:
HttpRequest类的常用方法如下:
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
关于 Cookie:
关于 Session:
Session是一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上,这就是Session。
客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。
当程序需要为某个客户端的请求创建一个Session的时候,服务器首先检查这个客户端的请求里是否已包含了一个Session标识,称为sessionid,
如果已包含一个sessionid则说明之前已经为该客户创建过session,服务器就按sessionid把这个session检索出来(如果检索不到可能会创建一个新的),
如果客户端请求不包含sessionid,则为此客户创建一个session并生成一个与此session相关联的sessionid,sessionid的值应该时一个既不会重复,又不容易被找到规律以仿造的字符串,这个sessionid将被在本次响应中返回给客户端保存。
Cookie和Session的区别:
操作 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失效策略
### 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 获取文件内容,Django提供了以下读取方式,每种方式说明如下:
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',
},
}
要使用 ListView,需要继承它并设置一些属性。以下是一些常用的属性:
在 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 中的方法以进行自定义。以下是一些常见的方法:
在 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">« 第一页</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 }}">最后一页 »</a>
{% endif %}
</span>
</div>
</body>
</html>
以下是 DetailView 的一些常见属性和方法:
在 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'
创建的 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>
在 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>
在 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>
Django内置的模板引擎包含模板上下文(亦可称为模板变量)、标签和过滤器,各个功能说明如下:
使用变量的一些注意点如下:
当模板引擎遇到一个变量,将计算这个变量,然后输出结果
变量名可以由字母、数字、下划线、点组成,不能以数字和下划线开头
当模板引擎遇到 “.” 的时候,按以下顺序进行解析: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>
常用的内置标签:
| 标签 | 描述 |
|-------------|-------------|
| {% 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>
模板继承是Django模板语言中最强大的部分。模板继承使你可以构建基础的“骨架”模板,将通用的功能或者属性写在基础模板中,也叫基类模板或者父模板。子模版可以继承父类模板,子模版继承后将自动拥有父类中的属性和方法,我们还可以在子模版中对父模板进行重写,即重写父模板的属性和方法,从而实现子模版的定制。
在 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),
常用的过滤器如下:
| 过滤器 | 说明 |
|--------|--------|
| 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
Jinja3 具备的特性:
pip install Jinja2 -i https://pypi.tuna.tsinghua.edu.cn/simple
由于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
Jinja3语法文档:https://jinja.palletsprojects.com/en/stable/templates/#variables
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 | 计算长字符串的单词个数 |
模型字段类型如下:
模型字段参数如下:
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 属性有下面设置选项:
在 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',
},
}
案例:图书信息
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 = "图书信息"
python manage.py makemigrations
-再次执行如下命令执行迁移文件,同步到数据库中
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);
案例:图书信息
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 模型数据分页查询:
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)
高级查询匹配符:
| 匹配符 | 使用 | 说明 |
|--------|---------|---------|
| __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)
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)
第一: 先创建一个获取类型的方法,用于填充到下拉框:
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 是列表的处理函数
第一: 在 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/')
# 删除所有数据
BookInfo.objects.all().delete()
# 删除指定id数据
BookInfo.objects.get(id=1).delete()
# 根据条件删除数据
BookInfo.objects.filter(price__lte=50).delete()
**extra()
适用于ORM难以实现的查询条件,将查询条件使用原生SQL语法实现,此方法需要依靠模型对象,在某程度上可防止SQL注入。它一共定义了6个参数,每个参数说明如下:
**
bookList = BookInfo.objects.extra(where=['price>%s'], params=[90])
raw()只能实现数据查询操作,并且也要依靠模型对象,它一共定义了4个参数,每个参数说明如下:
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)
Django5主要有4个事务方法:
在 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文件,在没有使用事务时:
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')
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="图书类别")
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 的常用属性和方法如下: