Django认识
Django 是一个由 Python 编写的一个开放源代码的 Web 应用框架,只要很少的代码,Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务。
Django 提供了全栈开发所需的工具,包括数据库 ORM、模板引擎、路由系统、用户认证等,大幅减少重复代码。

核心特点
- **快速开发:**Django 提供了大量内置功能,如认证、管理后台、表单处理等,让开发者专注于业务逻辑,而非底层实现。
- **自动化管理后台:**只需简单的模型定义,即可生成强大的后台管理界面,支持增删改查。
- **ORM 数据库映射:**Django 内置 ORM (Object-Relational Mapping),可以让开发者使用 Python 类与数据库交互,无需编写 SQL。
- **强大的 URL 路由:**使用正则表达式灵活定义 URL,轻松实现页面路由。
- **模板引擎:**内置强大的模板系统,支持逻辑判断、循环处理,方便渲染 HTML 页面。
- **国际化支持:**Django 支持多语言国际化,非常适合全球化应用。
- **高安全性:**内置多种安全保护措施,如防止 SQL 注入、XSS 攻击、CSRF 攻击等。
- **丰富的社区与扩展:**大量开源的第三方库,如 Django REST framework、Django CMS 等,快速扩展功能。
架构
Django 遵循 MVC(Model-View-Controller)架构,但在 Django 中更常被称为 MTV(Model-Template-View)。

MVC (Model-View-Controller)
- Model (模型):处理与数据库的交互,定义数据的结构和业务逻辑。
- View (视图):负责数据展示,生成用户看到的 HTML 页面。
- Controller (控制器):接收用户请求,调用 Model 处理数据,并将结果传递给 View 渲染页面。
流程:
- 用户发送请求到 Controller。
- Controller 处理逻辑,调用 Model 获取数据。
- Controller 将数据传递给 View。
- View 渲染并返回 HTML 页面给用户。
MVT (Model-Template-View) ------ Django 的实现方式
Django 中采用了 MVT 设计模式,类似于 MVC,但有一些区别:
- Model (模型):与数据库交互,处理数据的创建、读取、更新、删除。
- Template (模板):负责页面渲染,生成最终的 HTML 内容。
- View (视图):Django 的 View 更偏向于控制器的角色,接收请求并决定使用哪个模板和数据。
流程:
- 用户访问 URL,请求被 Django 的
urls.py映射到相应的 View。 - View 处理业务逻辑,调用 Model 获取数据。
- View 将数据传递给 Template。
- Template 渲染 HTML,最终返回给用户。
Django 的适用场景
适合用 Django 的项目:
- ✅ 内容管理系统(CMS)(如新闻网站、博客)
- ✅ 社交平台(用户系统、动态发布)
- ✅ 电子商务网站(订单管理、支付集成)
- ✅ API 后端(结合 Django REST framework)
不适合的场景:
- ❌ 超高性能要求的实时系统(如高频交易平台,推荐 Go 或 Rust)
- ❌ 极度轻量级的微服务(可以考虑 Flask 或 FastAPI)
命令行创建django程序
先创建虚拟环境(略)
python
pip freeze > requirements.txt ##生成环境配置包
pip install -r requirements.txt ##安装环境配置包
创建django程序
python
#1.创建目录
cd 指定目录
#2.创建项目
django-admin startproject mysite ##(项目名)
python manage.py startapp mysiteweb ##(文件名)
#3.编写urls.py文件
#4.运行
cd 项目
python manage.py runserver 网址:端口(默认127.0.1.1:8000)
python
#框架结构
mysite
|------ manage.py [项目的管理工具]
|------ mysite
| |------ __init__.py
| |------ settings.py [配置文件,不全]
| |------ urls.py [主路由,在里面编写 url与函数的关系]
| |------ asgi.py [异步]
| |------ wsgi.py [同步]
|------ web
|------ __init__.py
|------ admin.py
|------ apps.py
|------ migrations
| |------ __init__.py
|------ models.py [ORM,基于models可以对数据库进行简要操作]
|------ tests.py
|------ views.py [视图函数]
路由
路由简单的来说就是根据用户请求的 URL 链接来判断对应的处理程序,并返回处理结果,也就是 URL 与 Django 的视图建立映射关系。
Django 路由在 urls.py 配置,urls.py 中的每一条配置对应相应的处理方法。
正则和传统写法
Django1.1.x 版本
url() 方法:普通路径和正则路径均可使用,需要自己手动添加正则首位限制符号。
python
from django.conf.urls import url # 用 url 需要引入
urlpatterns = [
url(r'^admin/$', admin.site.urls),
url(r'^index/$', views.index), # 普通路径
url(r'^articles/([0-9]{4})/$', views.articles), # 正则路径 无名分组按位置传参 一一对应
]
Django 2.2.x 之后的版本
- path:用于普通路径,不需要自己手动添加正则首位限制符号,底层已经添加。
- re_path:用于正则路径,需要自己手动添加正则首位限制符号。
python
from django.urls import re_path # 用re_path 需要引入
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index), # 普通路径
re_path(r'^articles/([0-9]{4})/$', views.articles), # 正则路径 无名分组按位置传参 一一对应
re_path('runoob/(?P<id>\w+-\d+)/(?P<num>\d+)/', views.runoob), #有名分组 按关键字传参,与位置顺序无关 语法:(?P<组名>正则表达式)
]
分发
存在问题:Django 项目里多个app目录共用一个 urls 容易造成混淆,后期维护也不方便。
解决:使用路由分发(include),让每个app目录都单独拥有自己的 urls。
步骤:
- 1、在每个 app 目录里都创建一个 urls.py 文件。
- 2、在项目名称目录下的 urls 文件里,统一将路径分发给各个 app 目录。
自动分发
include + app(名称) 将功能拆分到不同的app 中
python
from django.contrib import admin
from django.urls import path,include # 从 django.urls 引入 include
urlpatterns = [
path('admin/', admin.site.urls),
path("app01/", include("app01.urls")),
path("app02/", include("app02.urls")),
]
#在各自 app 目录下,写自己的 urls.py 文件,进行路径跳转。
#app01 目录:
from django.urls import path,re_path
from app01 import views # 从自己的 app 目录引入 views
urlpatterns = [
path('xxx/', views.xxx),
]
#app02 目录:
from django.urls import path,re_path
from app02 import views # 从自己的 app 目录引入views
urlpatterns = [
path('xxx/', views.xxx),
]
手动分发
帮助提取功能的url,防止重复编写。
python
path('xxx/add/', views.xxx),
path('xxx/delete/', views.xxx),
path('xxx/edit/', views.xxx),
path('xxx/list/', views.xxx),
path('xxx/', ([
path('add/', views.xxx),
path('delete/', views.xxx),
path('edit/', views.xxx),
path('list/', views.xxx),
],None,None)),
路由分发的本质
-
URL对应函数
pythonpath('xxx/list/', views.xxx), -
URL对应元组
pythonpath('xxx/list/', (元素,appname元素,namespance元素)),
反向解析(Name)
- 反向解析一般用在模板中的超链接及视图中的重定向。
- 利用反向解析,当路由层 url 发生改变,在视图层和模板层动态反向解析出更改后的 url,免去修改的操作。
普通路径
在 urls.py 中给路由起别名,name="路由别名"。
python
path("login1/", views.login, name="login")
在 views.py 中,从 django.urls 中引入 reverse,利用 reverse("路由别名") 反向解析:
python
return redirect(reverse("login"))
在模板 templates 中的 HTML 文件中,利用 {% url "路由别名" %} 反向解析。
html
<form action="{% url 'login' %}" method="post">
正则路径(无名分组)
在 urls.py 中给路由起别名,name="路由别名"。
python
re_path(r"^login/([0-9]{2})/$", views.login, name="login")
在 views.py 中,从 django.urls 中引入 reverse,利用 reverse("路由别名",args=(符合正则匹配的参数,)) 反向解析。
python
return redirect(reverse("login",args=(10,)))
在模板 templates 中的 HTML 文件中利用 {% url "路由别名" 符合正则匹配的参数 %} 反向解析。
html
<form action="{% url 'login' 10 %}" method="post">
正则路径(有名分组)
在 urls.py 中给路由起别名,name="路由别名"。
python
from django.urls import path, re_path
from djangoProject import views
urlpatterns = [
re_path(r'runoob/(?P<id>\w+-\d+)/(?P<num>\d+)/', views.runoob, name="runoob"),
]
在 views.py 中,从 django.urls 中引入 reverse,利用 reverse("路由别名",kwargs={"分组名":符合正则匹配的参数}) 反向解析。
python
def runoob(request,id,num):
print(id,num)
from django.urls import reverse
url = reverse("runoob", kwargs={'id':'sdai-2929','num':20})
print(url)
return redirect(url)
在模板 templates 中的 HTML 文件中,利用 {% url "路由别名" 分组名=符合正则匹配的参数 %} 反向解析。
html
<form action="{% url 'login' id='sdai-2929' num=20 %}" method="post">
命名空间(Namespace)
命名空间是表示标识符的可见范围。
一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。
一个新的命名空间中可定义任何标识符,它们不会与任何重复的标识符发生冲突,因为重复的定义都处于其它命名空间中。
**存在问题:**路由别名 name 没有作用域,Django 在反向解析 URL 时,会在项目全局顺序搜索,当查找到第一个路由别名 name 指定 URL 时,立即返回。当在不同的 app 目录下的urls 中定义相同的路由别名 name 时,可能会导致 URL 反向解析错误。
**解决:**使用命名空间。
普通路径
定义命名空间(include 里面是一个元组)格式如下:
python
include(("app名称:urls","app名称"))
#实例
from django.urls import path, include
urlpatterns = [
path('mysite/', include("mysite.urls", namespace="app")),
]
#手动分发
urlpatterns = [
path('mysite/', ([
re_path(r'(\w+-\d+)/', views.test, name="test"),
],"app","app")),
] ## 支持多层嵌套 app:test:xx:...
在 app01/urls.py 中起相同的路由别名。
python
from django.urls import re_path
from mysite import views
urlpatterns = [
re_path(r'(\w+-\d+)/', views.test, name="test"),
]
# namespace需要设置app_name
app_name = 'app'
在 views.py 中使用名称空间,语法格式如下:
python
reverse("app名称:路由别名")
#实例
def test(request, uuid):
print(uuid)
from django.urls import reverse
url = reverse("app:test", args=('s-1',))
print(url)
return HttpResponse("Hello World")
在 templates 模板的 HTML 文件中使用名称空间,语法格式如下:
html
{% url "app名称:路由别名" %}
<-- 实例 -->
<form action="{% url 'app:test' %}" method="post">
视图
一个视图函数,简称视图,是一个简单的 Python 函数,它接受 Web 请求并且返回 Web 响应。
每个视图函数都负责返回一个 HttpResponse 对象,对象中包含生成的响应。
视图层中有两个重要的对象:请求对象(request)与响应对象(HttpResponse)。
HttpRequest 对象
python
# 1.当前URL
request.path_info
# 2.URL传递参数
request.GET
request.GET.get("key")
# 3.请求方式 GET/POST
request.method
# 4.如果post请求,传递请求体(原始数据)
request.body
# 4.1请求体 + 请求头
request.POST
request.POST.get("key")
# 4.2 请求体 + 请求头 文件
request.FILES
request.FILES.get("key")
# 5. 请求头
request.headers
# 5.1 请求头中的特殊cookie
request.COOKIES
HttpResponse 对象
响应对象主要有三种形式:HttpResponse()、render()、redirect()。
HttpResponse(): 返回文本,参数为字符串,字符串中写文本内容。如果参数为字符串里含有 html 标签,也可以渲染。
redirect():重定向,跳转新页面。参数为字符串,字符串中填写页面路径。一般用于 form 表单提交后,跳转到新页面。
render(): 返回文本,第一个参数为 request,第二个参数为字符串(页面名称),第三个参数为字典(可选参数,向页面传递的参数:键为页面参数名,值为views参数名)。
render 和 redirect 是在 HttpResponse 的基础上进行了封装:
- render:底层返回的也是 HttpResponse 对象
- redirect:底层继承的是 HttpResponse 对象
python
# 1.返回字符串/字节/文本数据(图片验证码)
return HttpResponse("login")
return HttpResponse("<a href='https://www.runoob.com/'>login</a>")
# 2.返回JSON格式(前后端分离、app小程序后端、ajax请求)
return JsonResponse("data_dict")
# 3.重定向
return redirect("logout/")
return redirect("logout") #name
# 4.渲染
return render(request,'login.html')
FBV CBV
**FBV:**视图用函数的形式编写
**CBV:**视图用类的形式编写(前后端分离)
静态资源
静态文件:css、js、图片
根目录 : /static/
app目录下的/static/文件夹下
settings 配置路径
python
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/
STATIC_URL = 'static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static')
)
html 引用
多app开发,各自app功能的图片放在各自的/static/app_name/...这个路径下
html
{% load static %}
<html>
...
<img src = "{% static 'app/a.png' %}">
</html>
媒体文件:用户上传的数据(execl/pdf/vid)
根目录 : /media/
url.py(根目录下)
python
from django.urls import path, include
from django.conf.urls.static import static
from djangoProject import settings
urlpatterns = [
path('mysite/', include("mysite.urls", namespace="app")),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
html
html
<html>
...
<img src = "/media/mysite/a.png">
</html>
模板
Django 的模板系统(Template System)是用于将业务逻辑(Python)与展示层(HTML)分离的核心组件,它允许开发者通过简单的标签和变量动态生成 HTML 页面。
模板标签
python
语法 : {% tag %}
控制模板逻辑,常用标签:
{{ variable }} 动态显示数据
{% static 'path' %} 加载 CSS/JS/图片
{% for %} 循环遍历列表/字典
{% if %} 条件判断
{% extends %} 继承基模板
{% block %} 定义可被子模板覆盖的内容块
{% include %} 嵌入其他模板片段
{% url %} 反向解析 URL(避免硬编码路径)
{% csrf_token %} 生成 CSRF 令牌(用于 POST 表单)
{% for %} 循环遍历列表/字典
每一次循环中,模板系统会渲染在 {% for %} 和 {% endfor %} 之间的所有内容。
html
<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>
嵌套使用:
{% for athlete in athlete_list %}
<h1>{{ athlete.name }}</h1>
<ul>
{% for sport in athlete.sports_played %}
<li>{{ sport }}</li>
{% endfor %}
</ul>
{% endfor %}
给标签增加一个 reversed 使得该列表被反向迭代:
html
{% for athlete in athlete_list reversed %}
...
{% endfor %}
遍历字典: 可以直接用字典 .items 方法,用变量的解包分别获取键和值。
html
{% for i,j in views_dict.items %}
{{ i }}---{{ j }}
{% endfor %}
在 {% for %} 标签里可以通过 {{forloop}} 变量获取循环序号。
html
{% for i in listvar %}
{{ forloop.counter }} {# 顺序获取循环序号,从 1 开始计算 #}
{{ forloop.counter0 }} {# 顺序获取循环序号,从 0 开始计算 #}
{{ forloop.revcounter }} {# 倒序获取循环序号,结尾序号为 1 #}
{{ forloop.revcounter0 }} {# 倒序获取循环序号,结尾序号为 0 #}
{{ forloop.first }} {# 第一条数据返回 True,其他数据返回 False (一般配合if标签使用)#}
{{ forloop.last }} {# 最后一条数据返回 True,其他数据返回 False (一般配合if标签使用)#}
{% endfor %}
可选的 {% empty %} 从句:在循环为空的时候执行(即 in 后面的参数布尔值为 False )。
html
{% for i in listvar %}
{{ forloop.counter0 }}
{% empty %}
空空如也~
{% endfor %}
{% if %} 条件判断
html
{% if condition %}
... display
{% endif %}
或者
{% if condition1 %}
... display 1
{% elif condition2 %}
... display 2
{% else %}
... display 3
{% endif %}
接受 and , or 或者 not 关键字来对多个变量做判断 ,或者对变量取反( not )
{% if athlete_list and coach_list %}
athletes 和 coaches 变量都是可用的。
{% endif %}
{% csrf_token %} 生成 CSRF 令牌(用于 POST 表单)
如果不用 {% csrf_token %} 标签,在用 form 表单时,要再次跳转页面会报 403 权限错误。
用了{% csrf_token %} 标签,在 form 表单提交数据时,才会成功。
过滤器
模板过滤器可以在变量被显示前修改它,过滤器使用管道字符
常用过滤器
python
模板语法:
{{ 变量名 | 过滤器:可选参数 }}
时间格式化:
{{ time|date:"Y-m-d" }}
显示文件的大小:
{{ num|filesizeformat}}
返回对象的长度:
{{ name|length}}
文档大写转换文本为小写:
{{ name|lower }}
default 为变量提供一个默认值,如果 views 传的变量的布尔值是 false,则使用指定的默认值。:
{{ name|default:"hello world" }}
如果字符串包含的字符总个数多于指定的字符数量,那么会被截断掉后面的部分。截断的字符串将以 ... 结尾。
{{ views_str|truncatechars:2}}
自定义模板
filter:数据处理,参数1-2,if条件
simpe_tag:参数无限制&文本格式
inclusion_tag:参数无限制&html片段
步骤:
- 根目录下创建文件夹/templatetags/
- 创建test.py文件
python
from django import template
register = template.Library()
@register.filters
def test(value):
return value.upper()
@register.simple_tag
def test2():
return "this is a test"
@register.simple_tag
def test3(a, b):
return a + "run" + b
@register.inclusion_tag("app/test.html")
def test4(arg):
return {"key": "include", "funct": "tag"}
- html
html
{% load test %} 导入模块
<html>
...
<h2>{{ value|test }}</h2>
<p> {% test2 %} </p>
<p> {% test3 "Django" "the first time"%} </p>
<p> {% test4 %} </p>
</html>
继承
模板可以用继承的方式来实现复用,减少冗余内容。
网页的头部和尾部内容一般都是一致的,我们就可以通过模板继承来实现复用。
父模板用于放置可重复利用的内容,子模板继承父模板的内容,并放置自己的内容。
父模板:
标签 block...endblock: 父模板中的预留区域,该区域留给子模板填充差异性的内容,不同预留区域名字不能相同。
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
<link rel="stylesheet" href="公共css文件">
</head>
<body>
<h1>Hello World!</h1>
<p>菜鸟教程 Django 测试。</p>
{% block mainbody %}
<p>original</p>
{% endblock %}
<script src="公共js文件"></script>
</body>
</html>
子模板:
子模板使用标签 extends 继承父模板:
html
{% extends "base.html" %}
{% block css %}
...
{% endblock %}
{% block mainbody %}
<p>继承了 base.html 文件</p>
{% endblock %}
{% block js %}
...
{% endblock %}
{%extends "base.html" %}
中间件
Django 中间件是修改 Django request 或者 response 对象的钩子,可以理解为是介于 HttpRequest 与 HttpResponse 处理之间的一道处理过程。
Django 中间件作用:
- 修改请求,即传送到 view 中的 HttpRequest 对象。
- 修改响应,即 view 返回的 HttpResponse 对象。
中间件组件配置在 settings.py 文件的 MIDDLEWARE 选项列表中。
配置中的每个字符串选项都是一个类,也就是一个中间件。
Django 默认的中间件配置:
py
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',
]
自定义中间件
创建中间件文件middlewares及内容
python
# 1.原始方式
class MD1(MiddlewareMixin):
def __init__(self,get_response):
self.get_response = get_response
def __call__(self,request):
print("this is a test!")
response = self.get_response(request)
print("test is over!")
return response
# 2.建议方式
from django.utils.deprecation import MiddlewareMixin
class MD1(MiddlewareMixin):
def process_request (self,request):
print("this is a test!")
def process_response (self,request,response):
print("test is over!")
return response
在 settings.py 中的 MIDDLEWARE 里注册自定义的中间件类:
python
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',
'app01.middlewares.MD1',
]
自定义中间件类的方法有:process_request 和 process_response。
process_request 方法
process_request 方法有一个参数 request,这个 request 和视图函数中的 request 是一样的。
process_request 方法的返回值可以是 None 也可以是 HttpResponse 对象。
- 返回值是 None 的话,按正常流程继续走,交给下一个中间件处理。
- 返回值是 HttpResponse 对象,Django 将不执行后续视图函数之前执行的方法以及视图函数,直接以该中间件为起点,倒序执行中间件,且执行的是视图函数之后执行的方法。
process_request 方法是在视图函数之前执行的。
当配置多个中间件时,会按照 MIDDLEWARE中 的注册顺序,也就是列表的索引值,顺序执行。
不同中间件之间传递的 request 参数都是同一个请求对象。
process_response 方法
process_response 方法有两个参数,一个是 request,一个是 response,request 是请求对象,response 是视图函数返回的 HttpResponse 对象,该方法必须要有返回值,且必须是response。
process_response 方法是在视图函数之后执行的。
当配置多个中间件时,会按照 MIDDLEWARE 中的注册顺序,也就是列表的索引值,倒序执行。
process_view 方法
process_view 方法格式如下:
process_view(request, view_func, view_args, view_kwargs)
process_view 方法有四个参数:
- request 是 HttpRequest 对象。
- view_func 是 Django 即将使用的视图函数。
- view_args 是将传递给视图的位置参数的列表。
- view_kwargs 是将传递给视图的关键字参数的字典。
view_args 和 view_kwargs 都不包含第一个视图参数(request)。
process_view 方法是在视图函数之前,process_request 方法之后执行的。
返回值可以是 None、view_func(request) 或 HttpResponse 对象。
- 返回值是 None 的话,按正常流程继续走,交给下一个中间件处理。
- 返回值是 HttpResponse 对象,Django 将不执行后续视图函数之前执行的方法以及视图函数,直接以该中间件为起点,倒序执行中间件,且执行的是视图函数之后执行的方法。
- c.返回值是 view_func(request),Django 将不执行后续视图函数之前执行的方法,提前执行视图函数,然后再倒序执行视图函数之后执行的方法。
- 当最后一个中间件的 process_request 到达路由关系映射之后,返回到第一个中间件 process_view,然后依次往下,到达视图函数。
process_exception 方法
process_exception 方法如下:
process_exception(request, exception)
参数说明:
- request 是 HttpRequest 对象。
- exception 是视图函数异常产生的 Exception 对象。
process_exception 方法只有在视图函数中出现异常了才执行,按照 settings 的注册倒序执行。
在视图函数之后,在 process_response 方法之前执行。
process_exception 方法的返回值可以是一个 None 也可以是一个 HttpResponse 对象。
返回值是 None,页面会报 500 状态码错误,视图函数不会执行。
process_exception 方法倒序执行,然后再倒序执行 process_response 方法。
返回值是 HttpResponse 对象,页面不会报错,返回状态码为 200。
视图函数不执行,该中间件后续的 process_exception 方法也不执行,直接从最后一个中间件的 process_response 方法倒序开始执行。
若是 process_view 方法返回视图函数,提前执行了视图函数,且视图函数报错,则无论 process_exception 方法的返回值是什么,页面都会报错, 且视图函数和 process_exception 方法都不执行。
直接从最后一个中间件的 process_response 方法开始倒序执行
ORM
Django 模型使用自带的 ORM。
对象关系映射(Object Relational Mapping,简称 ORM )用于实现面向对象编程语言里不同类型系统的数据之间的转换。
ORM 在业务逻辑层和数据库层之间充当了桥梁的作用。
ORM 是通过使用描述对象和数据库之间的映射的元数据,将程序中的对象自动持久化到数据库中。
使用 ORM 的好处:
- 提高开发效率。
- 不同数据库可以平滑切换。
使用 ORM 的缺点:
- ORM 代码转换为 SQL 语句时,需要花费一定的时间,执行效率会有所降低。
- 长期写 ORM 代码,会降低编写 SQL 语句的能力。
ORM 解析过程:
- 1、ORM 会将 Python 代码转成为 SQL 语句。
- 2、SQL 语句通过 pymysql 传送到数据库服务端。
- 3、在数据库中执行 SQL 语句并将结果返回。
ORM 对应关系表:

表结构
!WARNING
注意不能手动修改表结构
在app中的models.py里按照规则编写类 --> 表结构
-
编写类
pythonfrom django.db import models class Department(models.Model): Depart_name = models.CharField(verbose_name="部门名称", max_length=20) # 一对多 class User(models.Model): # unique 唯一索引 db_index 普通索引 id = models.IntegerField(primary_key=True, unique=True) name = models.CharField(verbose_name="姓名", max_length=16, default="Lisa", null=True, blank=True, db_index=True) # choices 选项,数据表里只会有这两种数据 sex = models.IntegerField(verbose_name="性别", default=0, choices=((1, "男"), (2, "女"))) age = models.IntegerField() email = models.EmailField(verbose_name="邮箱") # auto_now自动填写时间 register_date = models.DateTimeField(verbose_name="注册时间", auto_now=True) # 级联删除 # depart = models.ForeignKey(verbose_name="部门id", to="Department", to_field="id", on_delete=models.CASCADE) # 删除部门,关联表字段修正为默认值 depart = models.ForeignKey(verbose_name="部门id", to="Department", to_field="id", on_delete=models.SET_NULL, null=True, default=2) class Boy(models.Model): name = models.CharField(verbose_name="姓名", max_length=16) class Girl(models.Model): name = models.CharField(verbose_name="姓名", max_length=16) # Django自动生成对应关系表,但是表只能有三个字段,id\bid\gid # relation = models.ManyToManyField(verbose_name="男女关系", to="Boy") # 多对多 class BTG(models.Model): bid = models.ForeignKey(verbose_name="男生id", to="Boy", to_field="id", on_delete=models.CASCADE) gid = models.ForeignKey(verbose_name="女生id", to="Girl", to_field="id", on_delete=models.CASCADE) -
注册app(settings文件里)
pythonINSTALLED_APPS = [ # 'django.contrib.admin', # 'django.contrib.auth', # 'django.contrib.contenttypes', # 'django.contrib.sessions', # 'django.contrib.messages', 'django.contrib.staticfiles', 'mysite.apps.MysiteConfig', ] #数据库相关配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } -
命令,django根据models中的类生成一个对
数据库操作的配置文件-->migrationspythonpython manage.py makemigrations -
命令,将配置文件转换生成表,修改表SQL-->连接数据库运行
pythonpython manage.py migrate # 创建表结构
数据库配置(settings.py)
!NOTE
ENGINE:用于特定的数据库引擎的配置,不同的数据库字段不同,常见的有:
pythondjango.db.backends.postgresql # PostgreSQL django.db.backends.mysql # mysql django.db.backends.sqlite3 # sqlite django.db.backends.oracle # oracle
sqlite配置
python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
mysql配置
python
DATABASES = {
'default':
{
'ENGINE': 'dj_db_conn_pool.backends.mysql', # 数据库引擎
'NAME': 'runoob', # 数据库名称
'USER': 'root', # 数据库用户名
'PASSWORD': '123456', # 数据库密码
'HOST': '127.0.0.1', # 数据库地址,本机 ip 地址 127.0.0.1
'PORT': 3306, # 端口
'POOL_OPTIONS': {
'POOL_SIZE': 10, # 最小
'MAX_OVERFLOW': 10, # 在最小的基础上,还可以增加10个,即最大20个
'RECYCLE': 24 * 60 * 60, # 连接可以被重复用多久,超过会重新创建,-1表示永久
'TIMEOUT': 30, # 池中没有连接最多等待的时间
}
}
}
!NOTE
MySQL修改、忘记root密码(8.0版本)
cmd## 1、首先终止MySQL服务 net stop mysql ## 2、绕过密码登录: mysqld --console --skip-grant-tables --shared-memory ## 3、保持上述的窗口(切记),再以管理员身份新开cmd,执行: mysql -uroot -p ## 4、接下来修改root的密码为空 UPDATE mysql.user SET authentication_string='' WHERE user='root'; ## 5、此时关闭所有命令行界面,正常开启MySQL服务后进行登录,无需密码即可进入成功。此时进行修改密码操作: use mysql ALTER user root@'localhost' identified by '123456';
连接池
django默认内置没有数据库连接池,pymysql操作数据库,DBUtils连接池
第三组件
python
pip install django-db-connection-pool
DATABASES = {
'default': {
'ENGINE': 'dj_db_conn_pool.backends.mysql'
'POOL_OPTIONS': {
'POOL_SIZE': 10,
'MAX_OVERFLOW': 10,
'RECYCLE': 24 * 60 * 60
}
}
}
多数据库
django支持项目连接多个数据库
读写分离
python
host test master 写
组件
host bak slave 读
配置
python
DATABASES = {
'test': {
'ENGINE': 'dj_db_conn_pool.backends.mysql', # 数据库引擎
'NAME': 'test', # 数据库名称
'USER': 'root', # 数据库用户名
'PASSWORD': 'kxy123', # 数据库密码
'HOST': 'localhost', # 数据库地址,本机 ip 地址 127.0.0.1
'PORT': 3306, # 端口
'POOL_OPTIONS': {
'POOL_SIZE': 10, # 最小
'MAX_OVERFLOW': 10, # 在最小的基础上,还可以增加10个,即最大20个
'RECYCLE': 24 * 60 * 60, # 连接可以被重复用多久,超过会重新创建,-1表示永久
'TIMEOUT': 30, # 池中没有连接最多等待的时间
}
},
'bak': {
'ENGINE': 'dj_db_conn_pool.backends.mysql', # 数据库引擎
'NAME': 'bak', # 数据库名称
'USER': 'root', # 数据库用户名
'PASSWORD': 'kxy123', # 数据库密码
'HOST': 'localhost', # 数据库地址,本机 ip 地址 127.0.0.1
'PORT': 3306, # 端口
'POOL_OPTIONS': {
'POOL_SIZE': 10, # 最小
'MAX_OVERFLOW': 10, # 在最小的基础上,还可以增加10个,即最大20个
'RECYCLE': 24 * 60 * 60, # 连接可以被重复用多久,超过会重新创建,-1表示永久
'TIMEOUT': 30, # 池中没有连接最多等待的时间
}
}
}
生成数据库
python
python manage.py makemigrations
python manage.py migrate --database=test
python manage.py migrate --database=bak
数据读写 [view.py]
python
# 写入
models.User.objects.using("bak").create(id=1, name='kk', age=18, sex=2, email='<EMAIL>')
# 读取
models.User.objects.using("bak").all
编写router类,简化【后续再进行开发时】
先创建utils文件夹,setting里进行相关配置,最后创建类
python
DATABASE_ROUTERS = ['utils.router.DemoRouter']
python
class DemoRouter(object):
def db_for_read(self, model, **hints):
print('read')
print(model._meta.app_label)
print(model._meta.module_name)
print(hints)
if model._meta.module_name == 'user':
return 'bak'
return 'default'
python
router = ["DemoRouter"]
分库
根据不同功能里的models进行分库
python
python manage.py makemigrations
python manage.py migrate app01 --database=test
python manage.py migrate app02 --database=bak
python
class DemoRouter(object):
def db_for_read(self, model, **hints):
print('read')
print(model._meta.app_label)
print(model._meta.module_name)
print(hints)
if model._meta.app_label == 'app01':
return 'default'
if model._meta.module_name == 'app02':
return 'bak'
单表分库
python
class DemoRouter(object):
def allow_migrate(self, db, app_label, model_name=None, **options):
if db == 'bak':
if model_name in ["role", "depart"]:
return True
else:
return False
if db == 'default':
if model_name in ["user"]:
return True
else:
return False
def db_for_read(self, model, **options):
if model._meta.model_name in ["role", "depart"]:
return "bak"
if model._meta.model_name in ["user"]:
return "default"
def db_for_write(self, model, **options):
if model._meta.model_name in ["role", "depart"]:
return "bak"
if model._meta.model_name in ["user"]:
return "default"
单表数据操作
python
def index(request):
# 创建数据
objects1 = models.User.objects.using("bak").create(id=1, name='ll')
objects2 = models.User.objects.using("bak").create(**{'id': 1, 'name': 'll'})
objects3 = models.User(**{'id': 2, 'name': 'll'})
objects4 = models.User(id=1, name='ll')
objects3.save()
print(objects1)
print(objects2)
print(object.id)
print(object.name)
# 删除
models.User.objects.filter(id=1).delete()
# 修改
models.User.objects.all().update(id=2)
models.User.objects.filter(id=2).update(id=3, name='kk')
models.User.objects.filter(id=2).update(*{'id': 2, 'name': 'ld'})
# 查询
v = models.User.objects.all()
for i in v:
print(i, i.id, i.name)
# where id = 2
v2 = models.User.objects.filter(id=2)
# where id <> 2
v2 = models.User.objects.exclude(id=2)
# where id >= 2
v3 = models.User.objects.filter(id__gte=2)
# where id > 2
v4 = models.User.objects.filter(id__gt=2)
# where id < 2
v5 = models.User.objects.filter(id__lt=2)
# where id in (2,3)
v6 = models.User.objects.filter(id__in=[2, 3])
# where name like binary '%kk%'
v6 = models.User.objects.filter(name__contains='kk')
# where name like binary 'kk%'
v6 = models.User.objects.filter(name__startswith='kk')
# where name is null
v7 = models.User.objects.filter(name__isnull='ll')
# 查询sql语句
print(v6.query)
for i in v2:
print(i, i.id, i.name)
return HttpResponse("Hello World")
exclude() 方法用于查询不符合条件的数据。
返回的是 QuerySet 类型数据,类似于 list,里面放的是不满足条件的模型类的对象,可用索引下标取出模型类的对象。
get() 方法用于查询符合条件的返回模型类的对象符合条件的对象只能为一个,如果符合筛选条件的对象超过了一个或者没有一个都会抛出错误。
order_by() 方法用于对查询结果进行排序。
返回的是 QuerySet类型数据,类似于list,里面放的是排序后的模型类的对象,可用索引下标取出模型类的对象。
!NOTE
- 参数的字段名要加引号。
- 降序为在字段前面加个负号 -。
reverse() 方法用于对查询结果进行反转。
返回的是 QuerySe t类型数据,类似于 list,里面放的是反转后的模型类的对象,可用索引下标取出模型类的对象。
count() 方法用于查询数据的数量返回的数据是整数。
first() 方法返回第一条数据返回的数据是模型类的对象也可以用索引下标 [0]。
last() 方法返回最后一条数据返回的数据是模型类的对象不能用索引下标 [-1],ORM 没有逆序索引。
exists() 方法用于判断查询的结果 QuerySet 列表里是否有数据。返回的数据类型是布尔,有为 true,没有为 false。
**注意:**判断的数据类型只能为 QuerySet 类型数据,不能为整型和模型类的对象。
values() 方法用于查询部分字段的数据。
返回的是 QuerySet 类型数据,类似于 list,里面不是模型类的对象,而是一个可迭代的字典序列,字典里的键是字段,值是数据。
!NOTE
- 参数的字段名要加引号
- 想要字段名和数据用 values
values_list() 方法用于查询部分字段的数据。
返回的是 QuerySet 类型数据,类似于 list,里面不是模型类的对象,而是一个个元组,元组里放的是查询字段对应的数据。
!NOTE
- 参数的字段名要加引号
- 只想要数据用 values_list
distinct() 方法用于对数据进行去重。
返回的是 QuerySet 类型数据。
!NOTE
- 对模型类的对象去重没有意义,因为每个对象都是一个不一样的存在。
- distinct() 一般是联合 values 或者 values_list 使用。
filter() 方法用于查询符合条件的数据。
返回的是 QuerySet 类型数据,类似于 list,里面放的是满足条件的模型类的对象,可用索引下标取出模型类的对象。
基于双下划线的模糊查询(exclude 同理):
!NOTE
filter 中运算符号只能使用等于号 = ,不能使用大于号 > ,小于号 < ,等等其他符号。
- __in 用于读取区间,= 号后面为列表 。
- __gt 大于号 ,= 号后面为数字。
- __gte 大于等于,= 号后面为数字。
- __lt 小于,=号后面为数字。
- __lte 小于等于,= 号后面为数字。
- __range 在 ... 之间,左闭右闭区间,= 号后面为两个元素的列表。
- __contains 包含,= 号后面为字符串。
- __icontains 不区分大小写的包含,= 号后面为字符串。
- __startswith 以指定字符开头,= 号后面为字符串。
- __endswith 以指定字符结尾,= 号后面为字符串。
- __year 是 DateField 数据类型的年份,= 号后面为数字。
- __month 是DateField 数据类型的月份,= 号后面为数字。
- __day 是DateField 数据类型的天数,= 号后面为数字。
cookie
Cookie 是存储在客户端计算机上的文本文件,并保留了各种跟踪信息。
识别返回用户包括三个步骤:
- 服务器脚本向浏览器发送一组 Cookie。例如:姓名、年龄或识别号码等。
- 浏览器将这些信息存储在本地计算机上,以备将来使用。
- 当下一次浏览器向 Web 服务器发送任何请求时,浏览器会把这些 Cookie 信息发送到服务器,服务器将使用这些信息来识别用户。
设置 cookie:
rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐',...)
获取 cookie:
request.COOKIES.get(key)
删除 cookie:
rep =HttpResponse || render || redirect
rep.delete_cookie(key)
Session
服务器在运行时可以为每一个用户的浏览器创建一个其独享的 session 对象,由于 session 为用户浏览器独享,所以用户在访问服务器的 web 资源时,可以把各自的数据放在各自的 session 中,当用户再去访问该服务器中的其它 web 资源时,其它 web 资源再从用户各自的 session 中取出数据为用户服务。
工作原理:
-
a. 浏览器第一次请求获取登录页面 login。
-
b. 浏览器输入账号密码第二次请求,若输入正确,服务器响应浏览器一个 index 页面和一个键为 sessionid,值为随机字符串的 cookie,即 set_cookie ("sessionid",随机字符串)。
-
c. 服务器内部在 django.session 表中记录一条数据。
django.session 表中有三个字段。
- session_key:存的是随机字符串,即响应给浏览器的 cookie 的 sessionid 键对应的值。
- session_data:存的是用户的信息,即多个 request.session["key"]=value,且是密文。
- expire_date:存的是该条记录的过期时间(默认14天)
-
d. 浏览器第三次请求其他资源时,携带 cookie :{sessionid:随机字符串},服务器从 django.session 表中根据该随机字符串取出该用户的数据,供其使用(即保存状态)。
!NOTE
django.session 表中保存的是浏览器的信息,而不是每一个用户的信息。 因此, 同一浏览器多个用户请求只保存一条记录(后面覆盖前面),多个浏览器请求才保存多条记录。
cookie 弥补了 http 无状态的不足,让服务器知道来的人是"谁",但是 cookie 以文本的形式保存在浏览器端,安全性较差,且最大只支持 4096 字节,所以只通过 cookie 识别不同的用户,然后,在对应的 session 里保存私密的信息以及超过 4096 字节的文本。
session 设置:
request.session["key"] = value
执行步骤:
- a. 生成随机字符串
- b. 把随机字符串和设置的键值对保存到 django_session 表的 session_key 和 session_data 里
- c. 设置 cookie:set_cookie("sessionid",随机字符串) 响应给浏览器
session 获取:
request.session.get('key')
执行步骤:
- a. 从 cookie 中获取 sessionid 键的值,即随机字符串。
- b. 根据随机字符串从 django_session 表过滤出记录。
- c. 取出 session_data 字段的数据。
session 删除:
删除整条记录(包括 session_key、session_data、expire_date 三个字段)
request.session.flush()
删除 session_data 里的其中一组键值对:
del request.session["key"]
执行步骤:
- a. 从 cookie 中获取 sessionid 键的值,即随机字符串
- b. 根据随机字符串从 django_session 表过滤出记录
- c. 删除过滤出来的记录