Python编程-后端开发之Django5应用请求处理与模板基础
最近写项目,刚好用到了Django,现在差不多闲下来,个人觉得单体项目来讲django确实舒服,故写此总结
模板语法了解即可,用到了再看,毕竟分离已经是主流操作了,ORM与DRF加紧梳理中。。。。
Django程序简介
Django是一个用于构建Web应用程序的高级Python Web框架。它鼓励快速开发和干净、实用的设计。Django的主要目标是使开发复杂的、数据库驱动的网站变得简单,同时也适用于更小的项目。以下是一些Django框架的主要特点:
-
MTV架构模式:Django采用了模型-模板-视图(Model-Template-View,MTV)的架构模式,将数据模型、用户界面和业务逻辑相分离,使得应用程序更易于维护和扩展。
-
强大的数据库支持:Django提供了内置的对象关系映射(Object-Relational Mapping,ORM)工具,支持多种数据库后端,包括MySQL、PostgreSQL、SQLite和Oracle等。
-
自动化的管理界面:Django自动生成管理界面,开发者无需编写额外的代码即可对数据库进行管理和操作,大大提高了开发效率。
-
安全性:Django内置了一系列安全功能,如防止SQL注入、跨站脚本攻击(XSS)和跨站请求伪造(CSRF)等,使得应用程序更加安全可靠。
-
灵活的URL路由:Django使用URLconf(URL配置)来将URL映射到视图函数,支持正则表达式和命名组,使URL配置更加灵活和可读性更高。
-
模板系统:Django提供了强大而灵活的模板系统,支持模板继承、自定义过滤器和标签等功能,使得页面设计更加简单和可维护。
-
多语言支持:Django提供了多语言支持功能,可以轻松地创建多语言网站,支持国际化和本地化。
-
易于扩展:Django提供了丰富的第三方应用程序和插件,可以轻松地扩展和定制功能,满足各种需求。
Django的MTV架构
在Django中,MVT(Model-View-Template)架构对应到了默认的目录层级,每个部分都有相应的位置和功能:
-
模型(Model):模型负责定义数据结构,通常对应数据库中的表。在Django中,模型位于应用程序的
models.py
文件中。每个应用程序都可以有自己的模型文件,用于描述该应用程序的数据模型。 -
视图(View):视图负责处理用户请求并返回相应的内容,可以是网页、JSON数据等。在Django中,视图通常位于应用程序的
views.py
文件中。视图函数接收请求,并根据需要从数据库中获取数据,然后渲染模板或返回JSON数据等。 -
模板(Template):模板负责生成最终呈现给用户的HTML页面。在Django中,模板通常位于应用程序的
templates
目录中(默认是不会创建的)。每个应用程序都可以有自己的模板目录,用于存放该应用程序的HTML模板文件。
除了上述三个部分外,Django还有一个项目级别的配置文件settings.py
,用于配置整个Django项目的设置,如数据库设置、静态文件设置等。此外,还有一个项目级别的URL配置文件urls.py
,用于定义项目中的URL路由。
Django的默认目录层级如下所示:
.
├── DjangoTestproject
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-310.pyc
│ │ └── settings.cpython-310.pyc
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
└── myfirstapp├── __init__.py├── admin.py├── apps.py├── migrations│ └── __init__.py├── models.py├── tests.py└── views.py
每个子应用程序都有自己的目录,其中包含模型、视图、模板等文件。项目级别的文件包括项目的配置、URL路由等。
文件/目录 | 功能描述 |
---|---|
admin.py | 网站后台管理站点配置相关 |
apps.py | 配置当前子应用的相关信息 |
migrations | 存放数据库迁移历史文件 |
models.py | 保存数据库模型类 |
tests.py | 开发测试用例,编写单元测试 |
views.py | 编写Web应用视图 |
MVC架构与MVT架构区别
MTV(Model-Template-View)与MVC(Model-View-Controller)是两种常见的软件架构模式,用于构建Web应用程序。它们之间的区别如下:
-
MVC 架构:
- Model(模型):负责处理应用程序的数据逻辑。它通常包含表示数据的类,以及与数据库交互的方法。
- View(视图):负责将模型的数据呈现给用户。视图通常是用户界面的一部分,例如网页或移动应用的页面。
- Controller(控制器):负责处理用户输入并更新模型和视图。控制器接收用户的请求,调用相应的模型逻辑来处理请求,然后更新视图以反映模型的变化。
MVC 将应用程序分为三个部分,分别处理数据、用户界面和用户输入。这种分离使得代码更易于管理和维护,同时也使得不同部分的代码可以独立开发和测试。
-
MTV 架构:
- Model(模型):与 MVC 中的模型类似,负责处理数据逻辑。
- Template(模板):与 MVC 中的视图类似,负责将模型的数据呈现给用户。模板通常包含 HTML、CSS 和一些简单的逻辑代码。
- View(视图):在 MTV 中,视图更多地指代控制器的功能。视图接收用户的请求,调用相应的模型逻辑来处理请求,然后将处理结果传递给模板进行渲染。
MTV 与 MVC 的主要区别在于视图的角色。在 MVC 中,视图负责将模型的数据呈现给用户,而在 MTV 中,视图更像是控制器,负责处理用户的请求和更新模型。
Django默认配置解读
- 指定django应用的根目录,因为django本身其实也是作为一个子应用嵌入进我们的web程序的
BASE_DIR = Path(__file__).resolve().parent.parent
- 密钥(Secret Key)是用于加密会话数据、CSRF 令牌等重要信息的关键。密钥是一个随机生成的字符串,用于保护 Django 站点的安全性。
SECRET_KEY = 'django-insecure-%jo-z*+2)e8osx#vnpwtilvpq14c0+sx*!z##6chez68jwvsa%'
- DEBUG调试模式开启状态,开启时访问页面将有详细输出与日志,并且可以直接访问静态资源
DEBUG = True
ALLOWED_HOSTS
用于指定哪些主机名(或 IP 地址)可以被允许访问此Django网站, 可以是*
表示允许所有主机访问,在DEBUG模式关闭状态下,必须进行设置
ALLOWED_HOSTS = []
- 子应用注册列表,用于将指定应用用于构建Django应用
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles',
]
- 中间件列表,以插件实现功能分离
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',
]
Web应用中间件是一种软件组件,它位于应用程序和底层操作系统之间,用于处理和管理在Web应用程序中发送和接收的网络请求和响应。中间件通常用于实现一些常见的功能,如路由请求、身份验证、日志记录、缓存、压缩和安全性。它们可以帮助开发人员简化应用程序的开发和维护,提高应用程序的性能和安全性,并允许开发人员集中精力处理应用程序的核心逻辑。
- 项目级路由配置位置
ROOT_URLCONF = 'DjangoProjectExample.urls'
- 模板文件配置项
TEMPLATES = [{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [],'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',],},},
]
- WSGI程序入口点指定,这个我们一般不进行修改,一般是WSGI程序通过
wsgi.application
来与django访问
WSGI_APPLICATION = 'DjangoProjectExample.wsgi.application'
WSGI(Web Server Gateway Interface)是Python Web应用程序和Web服务器之间的标准接口,它定义了Web服务器如何与Python Web应用程序进行通信的规范。在Django中,WSGI扮演着非常重要的角色,它使得Django应用程序能够在符合WSGI标准的任何WSGI兼容的Web服务器上运行,如Gunicorn、uWSGI等。
WSGI服务器通常是一个独立的软件,可以单独部署和运行,也可以作为Web服务器的一部分。常见的WSGI服务器包括:
- uWSGI:uWSGI是一个功能强大的WSGI服务器,支持多种协议和语言,可以与Nginx等Web服务器配合使用,广泛用于部署Python Web应用程序。
- Gunicorn:Gunicorn是一个简单而稳定的WSGI服务器,易于使用和部署,是Django和Flask等Python Web框架的推荐服务器。
- mod_wsgi:mod_wsgi是一个用于Apache HTTP服务器的模块,可以让Apache直接运行WSGI应用程序,是在Apache上部署Python Web应用程序的常用方式。
- Waitress:Waitress是一个轻量级的WSGI服务器,易于配置和部署,适合用于开发和测试环境。
这些WSGI服务器都符合WSGI标准,可以与任何符合WSGI标准的Python Web应用程序(如Django、Flask等)配合使用,实现Web应用程序的部署和运行。
- 数据库配置项,默认状态下为sqlite
DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3','NAME': BASE_DIR / 'db.sqlite3',}
}
MySQL配置指令:
# settings.py DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'dbname','USER': 'username','PASSWORD': 'password','HOST': '127.0.0.1','POST': '3306',} }
参考:https://docs.djangoproject.com/zh-hans/5.0/ref/databases/#mysql-notes
- 定义密码验证器(Password Validators),用于验证用户设置的密码是否符合安全要求。
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',},
]
在上面的配置中,包含了四个密码验证器:
- UserAttributeSimilarityValidator:检查密码与用户的属性(如用户名、邮箱等)的相似度,防止用户设置过于简单的密码。
- MinimumLengthValidator:检查密码的最小长度,确保密码长度符合要求。
- CommonPasswordValidator:检查密码是否为常见密码(如123456、password等),以提高密码的安全性。
- NumericPasswordValidator:检查密码是否仅由数字组成,以提高密码的复杂性。
- 指定了项目的默认语言
LANGUAGE_CODE = 'en-us'
'en-us’表示英语(美国)是项目的默认语言。Django会根据这个设置来选择适当的翻译文件,以便在需要时提供本地化的内容。
- 指定项目的默认时区
TIME_ZONE = 'UTC'
- 启用国际化功能状态,当这个设置为True时,Django会尝试根据LANGUAGE_CODE设置来翻译项目中的文本内容
USE_I18N = True
- 启用时区功能,当这个设置为True时,Django会使用TIME_ZONE设置来处理日期和时间
USE_TZ = True
STATIC_URL
指定了浏览器访问静态文件时的基础URL路径,相当于一条专属于静态资源的路由。
STATIC_URL = 'static/'
- 指定数据库的自增字段类型
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
这段代码是Django 3.2及以上版本中新增的设置,用于指定模型的默认自增字段(AutoField)。在之前的Django版本中,默认使用的是
AutoField
,它会根据数据库类型选择适合的自增字段类型(如整数或大整数)。但是,在Django 3.2中引入了BigAutoField
,它始终使用大整数类型来表示自增字段,以解决某些数据库(如PostgreSQL)中整数类型的自增字段可能不够大的问题。
Django的常见指令
-
安装 Django: 如果尚未安装 Django,请使用以下命令安装:
pip install django
-
创建 Django 项目: 在命令行中运行以下命令来创建 Django 项目:
django-admin startproject myproject
这将在当前目录中创建一个名为
myproject
的新目录,其中包含 Django 项目的初始文件。 -
创建应用:使用
python manage.py startapp
命令创建一个新的子应用。python manage.py startapp myapp
这将在项目中创建一个名为
myapp
的子应用。 -
初始化数据库: 在项目目录中运行以下命令来初始化数据库:
python manage.py migrate
这将创建默认的 SQLite 数据库文件,并为 Django 的内置应用程序创建必要的数据表。
-
运行开发服务器: 最后,运行以下命令启动 Django 的开发服务器:
python manage.py runserver
这将启动服务器,并在默认端口(通常为
8000
)上提供 Django 应用程序。
注册使用子应用
这里的子应用我们可以看作是当前应用的一个模块,每个模块都符合我们的MTV架构,在上面我运行以下指令创建了一个子应用(注意,创建在了我们django应用的同级目录下,其实只要指定了正确的目录都可以,但是这样更符合我们的开发规范):
python3 manage.py startapp myfirstapp
然后我们在django创建项目时生成的目录内可以找到setting.py
,我们在列表中加上我们需要注册的子应用的apps.py
中默认生成的类即可:
# 子应用生成的类
class MyfirstappConfig(AppConfig):default_auto_field = 'django.db.models.BigAutoField'name = 'myfirstapp'
# 修改后的setting中的应用数组INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','myfirstapp.apps.MyfirstappConfig'
]
setting.py.
中INSTALLED_APPS
中的默认字段:
django.contrib.admin
:Django 管理后台,用于管理网站的后台管理界面。django.contrib.auth
:提供用户认证和授权的功能,包括用户登录、注册、密码管理等。django.contrib.contenttypes
:提供了用于跟踪模型(Model)之间关系的框架,可以用于实现通用关系、内容类型等功能。django.contrib.sessions
:提供了会话管理功能,用于在客户端和服务器端之间存储会话数据。django.contrib.messages
:用于在视图之间传递消息的框架,包括成功消息、错误消息等。django.contrib.staticfiles
:用于管理静态文件(如 CSS、JavaScript、图片等)的框架,可以方便地在模板中引用静态文件。这些内置应用程序可以通过在
INSTALLED_APPS
设置中添加相应的应用名称来启用和使用。
视图创建使用
在 Django 中,视图(View)是处理用户请求并返回响应的函数或类。视图函数接收一个请求对象作为参数,然后根据请求的内容生成一个响应对象,通常这个响应对象是一个 HTML 页面。我们创建视图通常在子应用中的views.py
中。
创建简单视图
from django.http import HttpResponse
from django.http import HttpRequest def say_hello_world(request: HttpRequest) -> HttpResponse:return HttpResponse("Hello World! This is MyFirstDjangoApp!")
Django请求对象
在 Django 中,要导入请求对象(HttpRequest
),通常需要从 django.http
模块中导入。导入请求对象的方式如下:
from django.http import HttpRequest # 如果需要写类型提示 则需要导入
但实际上,在编写 Django 视图时,通常不需要手动导入 HttpRequest
。Django 会在调用视图函数时自动创建请求对象,并将其作为第一个参数传递给视图函数(所以视图函数的第一个传入参数必须定义,用于接收请求对象)。因此,可以直接在视图函数的参数列表中使用 HttpRequest
,而不需要显式地导入它。
请求对象的主要属性和方法包括:
属性/方法 | 描述 |
---|---|
request.method | 请求的方法,如 GET、POST 等。 |
request.GET | 一个类似字典的对象,包含了所有的 GET 参数。 |
request.POST | 一个类似字典的对象,包含了所有的 POST 参数。 |
request.FILES | 一个类似字典的对象,包含了所有上传的文件。 |
request.COOKIES | 一个包含所有 cookie 的字典。 |
request.session | 一个代表当前会话的对象,用于存储和访问会话数据。 |
request.path | 请求的路径部分,不包括域名和查询参数。 |
request.get_host() | 获取请求的主机部分,包括域名和端口。 |
request.is_secure() | 如果请求是通过 HTTPS 发送的,则返回 True,否则返回 False。 |
Django响应对象
在 Django 中,响应对象(HttpResponse
)代表了服务器返回给客户端的 HTTP 响应。它包含了要发送给客户端的内容以及其他相关的信息,如响应状态码、响应头等。当视图函数返回一个响应对象时,Django 将会把这个响应发送给客户端。
注意:在 Django 中,视图函数必须返回一个响应对象(HttpResponse
或其子类)。这是因为 Django 是一个基于请求和响应的 Web 框架,它需要将用户发来的请求映射到相应的视图函数,并将处理结果返回给客户端。如果视图函数没有返回响应对象,Django 将会抛出一个异常。因此,确保每个视图函数都正确返回一个响应对象是很重要的。如果视图函数不需要返回任何内容,可以返回一个空的响应对象。
响应对象的主要方法和属性包括:
方法/属性 | 描述 |
---|---|
HttpResponse(content, content_type='text/html', status=200) | 构造一个响应对象,content 是要返回的内容,content_type 是内容的类型,默认为 text/html ,status 是响应的状态码,默认为 200。 |
HttpResponseRedirect(redirect_to) | 构造一个重定向响应,将客户端重定向到指定的 URL。 |
JsonResponse(data, **kwargs) | 构造一个 JSON 格式的响应,data 是要返回的 JSON 数据。 |
response.content | 响应的内容,以字节形式表示。 |
response.status_code | 响应的状态码。 |
response['header'] | 获取指定的响应头。 |
视图路由管理
在 Django 中,路由(URLconf)用于将 URL 映射到视图函数,从而确定如何处理客户端发来的请求。Django 的路由系统基于 URL 模式(URL patterns)。路由模式通常使用正则表达式来匹配 URL。在路由配置中,我们可以使用 path()
函数或 re_path()
函数来定义 URL 模式。Django 的路由配置主要包括两个部分:
- 项目级别的路由配置:位于项目的
urls.py
文件中,用于指定项目级别的 URL 模式。这个文件通常位于项目的根目录下,例如myproject/urls.py
。 - 应用级别的路由配置:位于应用程序的
urls.py
文件中,用于指定应用程序级别的 URL 模式。每个应用程序都可以有自己的urls.py
文件,用于定义应用程序内部的 URL 映射关系。这个文件通常位于应用程序目录下,例如myapp/urls.py
。
项目级视图配置
在创建项目时生成的django子应用中的urls.py
文件中修改urlpatterns
列表:
from django.contrib import admin
from django.urls import path
from myfirstapp.views import say_hello_worldurlpatterns = [path('admin/', admin.site.urls),# 第一参数是路由路径 第二参数是视图函数 # 还有个name参数是模板和重定向相关 暂时不表path('hello_world/', say_hello_world),
]
path('admin/', admin.site.urls)
这个默认配置是将/admin/
路径映射到 Django 管理后台的 URL。当访问admin/
路径时,Django 会自动调用管理后台的视图函数,显示管理后台的登录页面(如果用户未登录),或者显示管理后台的主界面(如果用户已登录)。
使用python3 manage.py runserver
启动项目进行访问:
r123@localhost:~/DjamgoTestProject$ curl 'http://127.0.0.1:8000/hello_world/'
Hello World! This is MyFirstDjangoApp!
需要注意的是,django对于路由的管理默认是严格的,即访问
hello_world/
与hello_world
是不一样的,但是一旦我们在视图路径后加上/
,django将会在用户访问hello_world
时重定向到hello_world/
路由。为了确保加载正常,建议每个路由的路径都添加/
,这点同时也是其他web应用的约定。
应用级视图配置
对于项目级路由来讲,维护是及其不便的,所以我们有另一种方式来管理我们的视图路由,我们可以在子应用下建立一个新的urls.py
文件:
# myfirstapp/urls.py
from . import views
from django.urls import pathurlpatterns = [path('hello_world', views.say_hello_world),
]
然后在项目级的urls.py
中使用include
函数包含子应用中的urls.py
from django.contrib import admin
from django.urls import path, includeurlpatterns = [path('admin/', admin.site.urls),path('myfirstapp/', include('myfirstapp.urls'))
]
启动项目进行访问:
r123@localhost:~/DjamgoTestProject$ curl 'http://127.0.0.1:8000/myfirstapp/hello_world'
Hello World! This is MyFirstDjangoApp
路由解析顺序
urlpatterns = [re_path(r'^get', myfirstapp.views.get_index),re_path(r'^get_', myfirstapp.views.get_content)
]
需要注意的是,django的路由解析顺序是自上而下的顺序,如上述路由表,即使我们访问get_content
视图,我们也只会收到服务端get_index
的视图内容,因为正则匹配的路由在第一条中优先解析了我们的请求,造成了第二条路由被屏蔽了访问
路由属性NAME
name
属性在 Django 中用于给 URL 模式命名,使得我们可以在代码的其他地方引用这个 URL。这样做的好处是,当我们需要在模板中生成链接或在视图函数中重定向到某个 URL 时,可以直接使用这个 URL 的名称,而不必硬编码 URL 地址。
例如,在 urls.py
中定义了一个 URL 模式:
path('about/', views.about, name='about')
- 模板生成路由链接
我们可以在模板中这样使用这个命名的 URL(模板使用详见后文):
<a href="{% url 'about' %}">About Us</a>
这里的 {% url 'about' %}
将会被解析为 /about/
,这样就可以生成一个指向 “About Us” 页面的链接。如果后续需要修改 URL 地址,只需在 urls.py
中修改一处即可,而不必在模板中每处引用都修改。
reverse()
函数反向解析 URL
同样地,在视图函数中可以使用 reverse()
函数来反向解析 URL:
from django.urls import reverse
from django.http import HttpResponseRedirect, HttpResponsedef my_redirect_view(request) -> HttpResponse:# 重定向到 'about' 页面return HttpResponseRedirect(reverse('about'))
- 在
redirect()
函数中使用name
属性:
redirect()
函数可以直接接受URL的name
属性作为参数,用于重定向到特定的URL。
pythonCopy codefrom django.shortcuts import redirect
from django.http import HttpResponsedef my_redirect_view(request) -> HttpResponse:# 重定向到名为'about_page'的URLreturn redirect('about_page')
静态资源管理
在Django中,静态资源通常包括CSS样式表、JavaScript文件、图像文件等,这些文件需要被服务器直接提供给客户端,而不需要经过Django的处理。
静态资源目录列表
在项目的settings.py
文件中,定义静态文件目录的位置。可以使用STATICFILES_DIRS
设置静态文件目录列表,通常为了便于项目维护,我们会使用BASE_DIR配合指定静态资源目录路径
# 在settings.py增加以下内容STATICFILES_DIRS = [os.path.join(BASE_DIR, "static"),
]
这样设置后,在开启DEBUG模式下,我们可以直接在请求路径中使用static
路由,例如:
http://127.0.0.1:8000/static/example.png
静态资源搜集路径
可以使用STATIC_ROOT
设置收集静态文件的目录。不过,在部署应用程序之前,需要收集静态文件到STATIC_ROOT
目录中。可以使用collectstatic
管理命令来完成此操作。
# 在settings.py增加以下内容STATICFILES_DIRS = [os.path.join(BASE_DIR, "static"),os.path.join(BASE_DIR, "file"),
]# 在setting.py 设置静态文件收集目录
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
在部署项目时运行以下指令将会把所有的静态资源文件复制一份到STATIC_ROOT
路径中:
python manage.py collectstatic
在开发过程中,可以使用django.contrib.staticfiles
中的staticfiles_urlpatterns
来提供静态文件的URL模式。
# urls.pyfrom django.contrib.staticfiles.urls import staticfiles_urlpatterns
urlpatterns += staticfiles_urlpatterns()
说白了上述代码就是在路由表里增加了我们的静态资源路由规则,之所以选择复制一份到一个统一的目录下,原因是在在生产环境中,通常使用Web服务器(如Nginx或Apache)来提供静态文件,而不是由Django来处理。这样可以提高静态文件的访问速度和性能。
客户端请求处理
URL路径参数处理
在Django中,我们处理路径中的请求参数是依靠正则表达式的捕获分组来实现的,其处理方式也分为两种,一种是分组位置传递,一种是分组名传递,例如我们有个博客网站,其定位文章的路径通常是:
http://blog.example.com/userhash/article/123456
那我们就可以捕获路径中的/article/123456
来进行定位,我们在这里使用正则匹配,然后我去除了最后的/
,保证可以一次获取到请求的内容,而非重定向的响应:
urlpatterns = [re_path(r'^userhash/([a-z]+)/(\d{8})$', views.get_path_arguments),
]
得到结果:
(.venv) r123@localhost:~/DjamgoTestProject$ curl 'http://127.0.0.1:8000/myfirstapp/userhash/artclie/23333333'
Status: OK<br/>FirstArg: artclie<br/>SecondArg: 23333333<br/>
另一种方式则是借助正则的分组命名精确指定参数,修改路由规则:
urlpatterns = [re_path(r'^userhash/(?P<second_arg>[a-z]+)/(?P<first_arg>\d{8})$', views.get_path_arguments),
]
再次请求发现参数顺序发生改变:
r123@localhost:~/DjamgoTestProject$ curl 'http://127.0.0.1:8000/myfirstapp/userhash/artclie/23333333'
Status: OK<br/>FirstArg: 23333333<br/>SecondArg: artclie<br/
GET请求参数处理
对于get请求参数的处理,我们则是依靠request的GET属性来进行获取,使用get方法获取单个参数内容,使用getlist获取列表内容(处理多选框等一键多值的情况),并且在未获取到对应参数时将返回None或空列表
urlpatterns = [path('getargs', views.get_request_arguments),
]
对应的方法:
def get_request_arguments(request: HttpRequest) -> HttpResponse:arg1: str = request.GET.get('arg1')arg2: list[str] = request.GET.getlist('arg2')return HttpResponse(f"The Arg1: {arg1}<br/>"f"The Arg2: {arg2}<br/>")
我们依次测试访问:
(.venv) r123@localhost:~/DjamgoTestProject$ curl 'http://127.0.0.1:8000/myfirstapp/getargs?arg1=2333&arg2=233&arg2=2333333333'
The Arg1: 2333<br/>The Arg2: ['233', '2333333333']<br/>(.venv) r123@localhost:~/DjamgoTestProject$ curl 'http://127.0.0.1:8000/myfirstapp/getargs?arg1=2333&arg2=233'
The Arg1: 2333<br/>The Arg2: ['233']<br/>(.venv) r123@localhost:~/DjamgoTestProject$ curl 'http://127.0.0.1:8000/myfirstapp/getargs?arg1=&arg2=233'
The Arg1: <br/>The Arg2: ['233']<br/>(.venv) r123@localhost:~/DjamgoTestProject$ curl 'http://127.0.0.1:8000/myfirstapp/getargs?arg2=233'
The Arg1: None<br/>The Arg2: ['233']<br/>
POST请求参数处理
处理POST请求的方式与GET请求的方式是类似的,只不过我们获取的参数是POST的内容,但是我们在测试环境中测试时,需要关闭django的CRSF安全中间件,否则将会被拦截
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',
]
直接修改上面处理GET的视图函数来测试:
def get_request_arguments(request: HttpRequest) -> HttpResponse:arg1: str = request.POST.get('arg1')arg2: list[str] = request.POST.getlist('arg2')return HttpResponse(f"The Arg1: {arg1}<br/>"f"The Arg2: {arg2}<br/>")
与上面一致,我们直接进行测试即可(这里只展示一种):
(.venv) r123@localhost:~/DjamgoTestProject$ curl -X POST http://127.0.0.1:8000/myfirstapp/getargs -d "arg1=value1&arg2=value2"
The Arg1: value1<br/>The Arg2: ['value2']<br/>
JSON请求体处理
要处理JSON请求体,我们可以使用request的body内容来获取:
def get_request_body(request: HttpRequest) -> HttpResponse:json_data: bytes = request.bodyreturn HttpResponse(f"DataType: {type(json_data)}<br/>"f"DataContent: {json_data}<br/>")
测试结果如下:
(.venv) r123@localhost:~/DjamgoTestProject$ curl -X POST http://127.0.0.1:8000/myfirstapp/getbody -H "Content-Type: application/json" -d '{"key1": "value1", "key2": "value2"}'
DataType: <class 'bytes'><br/>DataContent: b'{"key1": "value1", "key2": "value2"}'<br/>
在使用json中的数据时需要先将请求体中的数据先解码后加载:
import json ...# 解码请求体中的JSON数据 json_data = request.body.decode('utf-8') # 将JSON字符串转换为Python对象 data = json.loads(json_data)
Headers请求头处理
在Django中request维护一个字典属性META用于保存请求头内容,常用的属性标识如下:
变量名 | 描述 |
---|---|
CONTENT_LENGTH | 请求正文的长度(作为字符串)。 |
CONTENT_TYPE | 请求正文的 MIME 类型。 |
HTTP_ACCEPT | 响应可接受的内容类型。 |
HTTP_ACCEPT_ENCODING | 响应可接受的编码方式。 |
HTTP_ACCEPT_LANGUAGE | 响应可接受的语言。 |
HTTP_HOST | 客户端发送的 HTTP 主机头。 |
HTTP_REFERER | 引用页面(如果有)。 |
HTTP_USER_AGENT | 客户端的用户代理字符串。 |
QUERY_STRING | 查询字符串,作为单个(未解析的)字符串。 |
REMOTE_ADDR | 客户端的 IP 地址。 |
REMOTE_HOST | 客户端的主机名。 |
REMOTE_USER | Web 服务器验证的用户(如果有)。 |
REQUEST_METHOD | 字符串,如“GET”或“POST”。 |
SERVER_NAME | 服务器的主机名。 |
SERVER_PORT | 服务器的端口(作为字符串)。 |
def get_request_header(request: HttpRequest) -> HttpResponse:return HttpResponse(str(request.META))
服务端响应处理
响应头处理
我们还可以自定义响应头部发送给客户端:
def get_request_header(request: HttpRequest) -> HttpResponse:response: HttpResponse = HttpResponse("Please check the response headers")response["MySelfDefineHeader"] = "MySelfDefineHeaderData"return response
通过这样类似字典的方式,我们就可以实现自定义响应头部字段:
HTTP/1.1 200 OK
Date: Mon, 06 May 2024 06:52:16 GMT
Server: WSGIServer/0.2 CPython/3.10.12
Content-Type: text/html; charset=utf-8
MySelfDefineHeader: MySelfDefineHeaderData
X-Frame-Options: DENY
Content-Length: 33
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-originPlease check the response headers
修改响应头的内容,则可以通过面向对象的方式来进行修改:
def get_request_header(request: HttpRequest) -> HttpResponse:response: HttpResponse = HttpResponse("Please check the response headers")response.status_code = 233return response
输出响应头,发现修改成功:
HTTP/1.1 233 Unknown Status Code
Date: Mon, 06 May 2024 06:55:32 GMT
Server: WSGIServer/0.2 CPython/3.10.12
Content-Type: text/html; charset=utf-8
X-Frame-Options: DENY
Content-Length: 33
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-originPlease check the response headers
页面重定向
我们还可以返回一个HttpResponseRedirect
对象来对客户端进行页面重定向操作,同时也有redirect
方法可直接返回此对象
from django.shortcuts import redirect
from django.http import HttpResponseRedirectdef set_redirect_page(request: HttpRequest) -> HttpResponseRedirect:return redirect("http://www.baidu.com")
这个
redirect
函数的实现并不复杂:def redirect(to, *args, permanent=False, **kwargs):redirect_class = (HttpResponsePermanentRedirect if permanent else HttpResponseRedirect)return redirect_class(resolve_url(to, *args, **kwargs))
其本质上追踪到最后的
HttpResponseRedirectBase
类,实质上就是指定了我们的响应头Location
class HttpResponseRedirectBase(HttpResponse):allowed_schemes = ["http", "https", "ftp"]def __init__(self, redirect_to, *args, **kwargs):super().__init__(*args, **kwargs)self["Location"] = iri_to_uri(redirect_to)...
值得注意的是,我们还可以指定我们的urls.py
中的路由进行重定向,这样的重定向具有三个使用形态,并且相互独立:
- 直接使用路由的
name
信息进行定位页面
urlpatterns = [path('getpage', views.set_redirect_page),path('hello', views.say_hello_world, name='hello'),
]def set_redirect_page(request: HttpRequest) -> HttpResponseRedirect:return redirect("hello")
- 使用相对路径重定向页面
urlpatterns = [path('getpage', views.set_redirect_page),path('hello/', views.say_hello_world),
]def set_redirect_page(request: HttpRequest) -> HttpResponseRedirect:return redirect("hello/")
- 使用绝对路径定向页面
urlpatterns = [path('getpage', views.set_redirect_page),path('hello/', views.say_hello_world, name='hello'),
]def set_redirect_page(request: HttpRequest) -> HttpResponseRedirect:# 在开发环境中此时重定向的页面是http://127.0.0.1:8000/hello/return redirect("/hello/")
JSON响应
JsonResponse
是Django框架中用于返回JSON数据的类。它允许以JSON格式构建响应,并将其发送回客户端。
def get_request_json(request: HttpRequest) -> JsonResponse:data: dict = {"key1": "value1", "key2": "value2", "key3": "value3"}return JsonResponse(data=data)
得到响应:
(.venv) r123@localhost:~/DjamgoTestProject$ curl 'http://127.0.0.1:8000/myfirstapp/getjson'
{"key1": "value1", "key2": "value2", "key3": "value3"}
我们还可以传递其他参数来自定义响应的行为,例如设置响应的状态代码、内容类型或字符编码:
def my_view(request: HttpRequest) -> JsonResponse:data = {'key': 'value'}return JsonResponse(data, status=200, content_type='application/json', charset='utf-8')
此外,JsonResponse
还具有safe
参数,用于处理非字典类型的数据。当safe=True
时,JsonResponse
将尝试将数据转换为可序列化的内容。默认情况下,safe
为True
,但可以显式设置为False
以禁用此行为:
def my_view(request: HttpRequest) -> JsonResponse:data = ['item1', 'item2', 'item3']return JsonResponse(data, safe=False)
状态交互之COOKIES
Cookie:有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。Cookie最早是网景公司的前雇员Lou Montulli在1993年3月的发明。
Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value
保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie名称和值可以由服务器端开发自己定义,这样服务器可以知道该用户是否是合法用户以及是否需要重新登录等。服务器可以利用Cookies包含信息的任意性来筛选并经常性维护这些信息,以判断在HTTP传输中的状态。Cookies最典型的应用就是记住用户名。
Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息,如密码等,因为电脑上的浏览器可能被其它人使用。
Cookie的特点
-
Cookie以键值对的格式进行信息的存储。
-
Cookie基于域名安全,不同域名的Cookie是不能互相访问
-
当浏览器请求某网站时,会将浏览器存储的跟网站相关的所有Cookie信息提交给网站服务器。
设置Cookies
可以通过HttpResponse
对象中的set_cookie
方法来设置cookies
。
def set_cookie(self, key, value="", max_age=None, expires=None, path="/", domain=None,secure=False, httponly=False, samesite=None,
)...
可以将这些属性整理成表格形式如下:
属性 | 含义 | 默认值 |
---|---|---|
key | cookie 的名称 | |
value | cookie 的值 | “” |
max_age | cookie 的最大存活时间(秒) | None |
expires | cookie 的过期时间 | None |
path | cookie 的有效路径 | “/” |
domain | cookie 的有效域名 | None |
secure | cookie 是否只能通过 HTTPS 连接传输 | False |
httponly | cookie 是否只能通过 HTTP(S) 协议访问 | False |
samesite | cookie 的 SameSite 属性,控制跨站点请求时是否发送 cookie | None |
max_age
单位为秒,如果是临时cookie,可将max_age
设置为None。
def get_site_cookies(request: HttpRequest) -> HttpResponse:response: HttpResponse = HttpResponse("set cookies")response.set_cookie(key="cookie1", value="value1")response.set_cookie(key="cookie2", value="value2", max_age=600)return response
读取Cookies
读取cookies可以使用响应的元数据字典,并且在 Django 中,request.COOKIES.get()
方法不会因为未找到指定的 cookie 而报错。如果指定的 cookie 不存在,get()
方法会返回 None
,这样可以避免抛出异常。
def get_client_cookies(request: HttpRequest) -> HttpResponse:cookie1 = request.COOKIES.get("cookie1")cookie2 = request.COOKIES.get("cookie2")return HttpResponse(f"{request.COOKIES} , {cookie1}, {cookie2}")
利用curl单次访问输出,为空内容:
(.venv) r123@localhost:~/DjamgoTestProject$ curl 'http://127.0.0.1:8000/myfirstapp/getcookies'
{}
删除Cookies
删除客户端的cookies可以通过delete_cookie
方法,不过它的删除是基于键的,需要我们提供对应cookie
字段的键,执行此方法会将对应cookie的值的部分置为空
def delete_cookie(self, key, path="/", domain=None, samesite=None)...
示例:
def reset_cookies_status(request: HttpRequest) -> HttpResponse:response: HttpResponse = HttpResponse()# 删除指定cookieresponse.delete_cookie(key="cookie1")# 循环清除所有cookiefor i in request.COOKIES:response.delete_cookie(i)return response
状态交互之SESSION
Session在取得客户端的凭据后进行鉴权生成。在HTTP报文中,Session可以通过两种方式出现:
-
Cookie中:最常见的方式是通过Cookie将Session ID发送到服务器。服务器在响应中设置一个名为"Set-Cookie"的头部,其中包含Session ID,浏览器会将这个Cookie保存下来,并在后续的请求中将其发送到服务器,以维持会话。
-
URL参数中:在某些情况下,Session ID也可以通过URL参数传递。这种方式比较少见(CTF遇到过,现实中没见过),因为URL参数可能会被记录在浏览器的历史记录、日志文件等中,存在一定的安全风险。但在某些情况下,比如浏览器禁用了Cookie时,这种方式可能会被使用。
SESSION存储
在Django中session默认是以中间件的形式加载进Web应用程序的:
MIDDLEWARE = [...'django.contrib.sessions.middleware.SessionMiddleware',...
]
这也就意味着我们也必须将session模块注册进应用中,Django默认也是自动加载的:
INSTALLED_APPS = [...'django.contrib.sessions',...
]
Django提供了几种不同的方式来存储会话(session)数据,具体取决于应用程序需求和环境。主要有以下配置方式,我们可以在settings.py
文件中进行配置:
数据库存储:Django可以将会话数据存储在关系型数据库中,如SQLite、MySQL、PostgreSQL等。可以通过在settings.py
中设置SESSION_ENGINE
来选择使用的数据库引擎(这也是默认的存储方式,其在数据库中只保存键,值,过期时间):
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
缓存存储:Django支持使用缓存系统存储会话数据,这样可以提高性能并减轻数据库负担。可以在settings.py
中配置使用的缓存后端,例如:
CACHES = {'default': {'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache','LOCATION': '127.0.0.1:11211',}
}
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
文件系统存储:还可以将会话数据存储在文件系统中。这种方法适用于单个服务器环境,但在负载较高或多服务器部署时可能会遇到性能问题。可以在settings.py
中配置文件系统路径:
SESSION_ENGINE = 'django.contrib.sessions.backends.file'
SESSION_FILE_PATH = '/path/to/session/files/'
缓存数据库存储:结合缓存和数据库的优点,可以将会话数据存储在缓存中,并在需要时同步到数据库中。这种方法可以在一定程度上提高性能并保证数据的持久性。配置方式如下:
CACHES = {'default': {'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache','LOCATION': '127.0.0.1:11211',}
}
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
在加载缓存服务器时,我们可以指定
SESSION_CACHE_ALIAS
配置加载多个不同缓存服务器来执行不同的功能
SESSION_CACHE_ALIAS
是 Django 中用于配置会话(session)存储时所使用的缓存别名的配置项。它允许指定一个缓存别名,该别名对应于在CACHES
设置中定义的缓存配置。CACHES = {'default': {'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache','LOCATION': '127.0.0.1:11211',},'session_cache': {'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache','LOCATION': '/var/tmp/django_cache',} }SESSION_CACHE_ALIAS = 'session_cache'
在这个例子中,会话数据将会使用名为
session_cache
的缓存配置来进行存储。
SESSION操作
在Django中我们通过HttpRequest对象的session
属性进行会话的读写操作,建立session后将会在客户端cookies中建立session_id
字段:
def set_session(request: HttpRequest) -> HttpResponse:request.session["key"] = "value"request.session.set_expiry(60)return HttpResponse("set sesssion ok")
常见操作如下:
操作 | 示例 |
---|---|
设置session键值对 | request.session["key"] = "value" |
读取请求中的session信息 | request.session.get("key", "default_value") |
清除所有session | request.session.clear() |
清除session数据 | request.session.flush() |
删除指定键及值 | del request.session["key"] |
设置有效期60s | request.session.set_expiry(60) |
request.session.set_expiry
接收值value
- 如果
value
是一个整数,session
将在value
秒没有活动后过期。 - 如果
value
为0
,那么用户session
的Cookie
将在用户的浏览器关闭时过期。 - 如果
value
为None
,那么session
有效期将采用系统默认值,默认为两周,可以通过在settings.py
中设置SESSION_COOKIE_AGE
来设置全局默认值。
注意:与COOKIES的获取一致,在 Django 中,如果使用
request.session.get('key')
方法获取会话(session)值时,如果键'key'
不存在,不会抛出异常。相反,它将返回None
。
我们可以抓个包来看看:
HTTP/1.1 200 OK
Date: Tue, 14 May 2024 08:51:41 GMT
Server: WSGIServer/0.2 CPython/3.10.12
Content-Type: text/html; charset=utf-8
X-Frame-Options: DENY
Content-Length: 12
Vary: Cookie
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
Set-Cookie: sessionid=8qwysr46bcgpfw8djsgvf0pr71wna8jn; expires=Tue, 14 May 2024 08:52:41 GMT; HttpOnly; Max-Age=60; Path=/; SameSite=Laxset sesssion ok
类视图请求交互
在Django中,类视图(Class-Based Views)是一种处理HTTP请求的方式,使用基于类的视图可以让你的代码更加简洁和可重用。类视图基于Python类的概念,每个类视图处理一个特定类型的请求(如GET、POST等),并根据请求执行相应的操作,下面来编写一个类视图处理请求:
# 在路由时需要使用视图类的as_view方法
from . import views
from django.urls import pathurlpatterns = [path("classview", views.MyFirstClassView.as_view())
]
as_view
方法是 Django 类视图(class-based views)中一个重要的方法,其作用是将类视图转换为函数视图,以便它们可以在URL配置中使用。类视图通过as_view
方法提供了一种便捷的方式来处理不同的HTTP请求方法(如GET、POST等)
class MyFirstClassView(View):def get(self, request: HttpRequest) -> HttpResponse:return HttpResponse("The Method is GET") def post(self, request: HttpRequest) -> HttpResponse:return HttpResponse("The Method is POST")
访问视图得到以下内容:
(.venv) r123@localhost:~/DjamgoTestProject$ curl -X GET "http://127.0.0.1:8000/myfirstapp/classview"
The Method is GET
(.venv) r123@localhost:~/DjamgoTestProject$ curl -X POST "http://127.0.0.1:8000/myfirstapp/classview"
The Method is POST
而在源码层面,Django使用了反射来动态处理类视图请求处理的方法:
class View:"""Intentionally simple parent class for all views. Only implementsdispatch-by-method and simple sanity checking."""http_method_names = ["get","post","put","patch","delete","head","options","trace",]...def dispatch(self, request, *args, **kwargs):# Try to dispatch to the right method; if a method doesn't exist,# defer to the error handler. Also defer to the error handler if the# request method isn't on the approved list.if request.method.lower() in self.http_method_names:handler = getattr(self, request.method.lower(), self.http_method_not_allowed)else:handler = self.http_method_not_allowedreturn handler(request, *args, **kwargs)...
自定义简易中间件
在Django中所谓的中间件实质上是一个又一个子应用,在收到请求时优先对请求进行预处理,由于篇幅,讨论一个完整的中间件包是不太合适的,我们可以定义一个闭包来实现我们的中间件功能:
from typing import Callable
from django.http import HttpRequest, HttpResponsedef custom_middleware(get_response: Callable[[HttpRequest], HttpResponse]) -> (Callable[[HttpRequest], HttpResponse]):print("自动调用中间件请求处理前逻辑")def middleware(request: HttpRequest) -> HttpResponse:print("中间件处理请求前的逻辑")response = get_response(request) # 处理请求的视图调用print("中间件处理响应后的逻辑")return responsereturn middleware
然后注册到中间件中,就实现了一个简单的中间件:
MIDDLEWARE = [...'myfirstapp.middleware.custom_middleware',...
]
同样的,我们也可以使用__call__
方法来定义一个简易的类中间件:
from typing import Callable
from django.http import HttpRequest, HttpResponseclass CustomMiddleware:def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]) -> None:self.get_response = get_responsedef __call__(self, request: HttpRequest) -> HttpResponse:# 中间件处理请求前的逻辑response = self.get_response(request)# 中间件处理响应后的逻辑return response
模板渲染系统使用
Django模板是Django框架中的一种用于生成HTML的系统。它使用模板语言(Django Template Language, DTL)来动态生成网页。Django模板的核心理念是分离业务逻辑和展示逻辑,使开发者可以更方便地管理和维护代码。首先在setting中配置模板路径:
TEMPLATES = [{...'DIRS': [os.path.join(BASE_DIR, "templates")],...},
]
视图上下文传递
模板从视图中传递上下文(context),上下文是一个字典,包含需要在模板中使用的数据
- 借助Template类进行模板渲染
from django.template import Template, loaderdef get_template(request: HttpRequest) -> HttpResponse:template: Template = loader.get_template("index.html")data: dict = {'user1': {'name': 'A','age': 28},'user2': {'name': 'B','age': 23},'list_data': [233, 2333, 23333]}return HttpResponse(template.render(context=data))
- 直接进行模板渲染
from django.shortcuts import renderdef get_template(request: HttpRequest) -> HttpResponse:data: dict = {'user1_name': 'A','user2': {'name': 'B','age': 23},'list_data': [233, 2333, 23333]}return render(request=request, template_name="index.html", context=data)
模板语法-变量渲染
在django中有两种模板语法,第一种是直接输出数据,类似于变量的方式。当字典中元素的值部分为单独常量时,可直接在模板中填入该元素的键,而值为可迭代类型时,我们则需要使用.
来访问对应的索引或键名:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Document</title>
</head>
<body>user1_name: {{ user1_name }}<br/>user2_age: {{ user2.age }}<br/>list_element0: {{ list_data.0 }}<br/>list_element1: {{ list_data.1 }}<br/>
</body>
</html>
我们将会在前端得到渲染后的源代码:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Document</title>
</head>
<body>user1_name: A<br/>user2_age: 23<br/>list_element0: 233<br/>list_element1: 2333<br/>
</body>
</html>
django的模板语法也支持注释:
{# content #}
模板语法-结构渲染
条件控制结构
-
基本条件判断:
{% if condition %}<!-- 条件为真时显示的内容 --> {% endif %}
-
else
语句:{% if condition %}<!-- 条件为真时显示的内容 --> {% else %}<!-- 条件为假时显示的内容 --> {% endif %}
-
elif
语句:{% if condition1 %}<!-- 条件1为真时显示的内容 --> {% elif condition2 %}<!-- 条件2为真时显示的内容 --> {% else %}<!-- 条件都为假时显示的内容 --> {% endif %}
循环控制结构
循环语句用于在模板中遍历列表或字典等可迭代对象。Django模板语言提供了{% for %}
标签来实现循环。
-
基本循环:
{% for item in item_list %}<!-- 对每个item进行操作 --> {% endfor %}
-
循环控制:
forloop.counter
:循环计数,从1开始forloop.counter0
:循环计数,从0开始forloop.revcounter
:从1开始的倒计数forloop.revcounter0
:从0开始的倒计数forloop.first
:是否是第一个元素forloop.last
:是否是最后一个元素
-
遍历列表:
<ul> {% for item in item_list %}<li>{{ item }}</li> {% endfor %} </ul>
-
遍历字典:
<ul> {% for key, value in my_dict.items %}<li>{{ key }}: {{ value }}</li> {% endfor %} </ul>
-
使用循环控制变量:
<ul> {% for item in item_list %}<li>{{ forloop.counter }}: {{ item }}</li> {% endfor %} </ul>
模板语法-内容过滤器
Django模板系统提供了许多内置的过滤器,可以让我们在模板中对变量进行简单的操作和格式化。以下是一些常见的过滤器:
-
date: 格式化日期。
{{ some_date|date:"Y-m-d" }}
-
time: 格式化时间。
{{ some_time|time:"H:i" }}
-
default: 如果变量值为空,使用默认值。
{{ some_variable|default:"默认值" }}
-
length: 返回列表或字符串的长度。
{{ some_list|length }}
-
lower: 将字符串转换为小写。
{{ some_string|lower }}
-
upper: 将字符串转换为大写。
{{ some_string|upper }}
-
join: 将列表连接成字符串,用指定的字符分隔。
{{ some_list|join:", " }}
-
slice: 对列表或字符串进行切片。
{{ some_list|slice:":3" }}
-
first: 返回列表或字符串的第一个元素。
{{ some_list|first }}
-
last: 返回列表或字符串的最后一个元素。
{{ some_list|last }}
-
striptags: 去除字符串中的HTML标签。
{{ some_html_string|striptags }}
-
escape: 对字符串进行HTML转义。
{{ some_string|escape }}
-
linebreaks: 将换行符转换为
<p>
和<br>
标签。{{ some_text|linebreaks }}
-
urlencode: 对字符串进行URL编码。
{{ some_string|urlencode }}
-
capfirst: 将字符串的首字母大写。
{{ some_string|capfirst }}
-
wordcount: 计算字符串中的单词数。
{{ some_string|wordcount }}
-
pluralize: 根据变量的值决定单复数形式。
{{ some_number|pluralize:"项,项" }}
-
add: 将数字相加。
{{ some_number|add:5 }}
-
default_if_none: 如果变量值为
None
,使用默认值。{{ some_variable|default_if_none:"默认值" }}
-
empty:用于判断一个变量是否为空。
{% if some_variable|empty %}<p>变量为空</p> {% else %}<p>变量不为空</p> {% endif %}
empty常用于与for循环联动
<ul>{% for item in item_list %}<li>{{ item }}</li>{% empty %}<li>没有内容</li>{% endfor %} </ul>
-
safe:用于标记字符串为安全的HTML,防止自动转义
<p>{{ html_content|safe }}</p>
模板语法-模板继承
Django的模板继承是一种强大的机制,可以在构建网页时复用HTML代码。模板继承允许创建一个基础模板,然后在此基础上创建更具体的模板,从而减少重复代码,提高代码的可维护性和可读性。模板继承通过{% block %}
标签和{% extends %}
标签来实现:
{% block %}
:定义一个可被子模板重载的可扩展区域。{% extends %}
:指示该模板继承自另一个模板。
创建基础模板
首先,我们需要创建一个基础模板,通常包含网站的通用结构,比如头部、导航栏和页脚。示例代码如下:
<!-- base.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>{% block title %}我的网站{% endblock %}</title>
</head>
<body><header><h1>我的网站</h1></header><nav><!-- 导航栏内容 --></nav><main>{% block content %}{% endblock %}</main><footer><p>版权所有 © 2024</p></footer>
</body>
</html>
在这个基础模板中,定义了两个可扩展的块:title
和content
。
创建子模板
接下来,创建一个子模板,它将继承基础模板,并提供具体的内容:
<!-- home.html -->
{% extends "base.html" %}{% block title %}首页 - 我的网站{% endblock %}{% block content %}<h2>欢迎来到我的网站</h2><p>这是首页的内容。</p>
{% endblock %}
在这个子模板中,通过{% extends "base.html" %}
指示继承自base.html
。然后,使用{% block %}
标签重载基础模板中的title
和content
块。
块的灵活重载
我们可以在基础模板中定义多个块,然后在子模板中选择性地重载这些块。示例如下:
<!-- base.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>{% block title %}我的网站{% endblock %}</title>{% block extra_head %}{% endblock %}
</head>
<body><header><h1>我的网站</h1></header><nav><!-- 导航栏内容 --></nav><main>{% block content %}{% endblock %}</main><footer><p>版权所有 © 2024</p></footer>{% block extra_foot %}{% endblock %}
</body>
</html>
<!-- about.html -->
{% extends "base.html" %}{% block title %}关于我们 - 我的网站{% endblock %}{% block content %}<h2>关于我们</h2><p>这是关于我们的页面内容。</p>
{% endblock %}{% block extra_foot %}<script src="path/to/extra/script.js"></script>
{% endblock %}
在这个例子中,基础模板中定义了extra_head
和extra_foot
块,子模板可以选择性地重载这些块以添加额外的头部或脚部内容。