全面学习Django框架

TOC

一、认识Django框架

什么是Django?

官方解释:
Django是一个用Python编写的开源Web应用开发框架,它的目标是让开发者能够更快捷、更高效地开发出安全、可扩展的Web应用。
我的理解:
我的理解是Django更加系统化,不用像Flask和FastAPI框架要自己设计代码架构,并且Django搭建一个web项目架构就跟搭积木一样简单,从小房子到摩天大楼Django都能够快速搭建。

为什么用Django?

Django的特点:

  • MTV架构:Django使用MTV(Model-Template-View),
    • Model(模型):负责数据层,定义数据库结构。
    • Template(模板):负责页面层,渲染 HTML。
    • View(视图):负责业务逻辑,处理请求和响应。
  • ORM系统:通过自带的Python类操作数据库,而不需要直接写 SQL。
  • 管理后台:自动生成的后台管理系统,可以让你不用写额外代码就能管理数据。
  • 用户认证:登录注册功能现成的,不用自己写。
  • 安全防护:默认帮你处理很多安全问题,例如 SQL 注入、XSS、CSRF 攻击等。
  • 可扩展和复用性:有丰富的第三方插件,开发者也可以很容易地拆分、复用自己的应用。

二、Django基础开发

环境搭建

安装Django

pip install django

安装了django模块之后,django-admin命令会安装到python安装目录/bin这个目录下。

创建项目

# 自动创建项目目录,目录名必须未存在该目录文件夹,否则报错
django-admin startproject mydemo

创建项目的目录结构:

$ tree mydemo 
mydemo
├── manage.py
└── mydemo
    ├── __init__.py
    ├── asgi.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

2 directories, 6 files

我们也可以使用PyCharm企业版自带的创建Django项目的功能创建。

补充说明:
命令行,创建的项目是标准的结构和内容。
PyCharm,创建的项目在命令行的基础上添加了一些内容:
1.创建了一个templates目录
2.setting.py中DIRS项中多了templates目录配置

Django框架项目初始文件介绍:

  • manage.py:项目的管理,启动项目、创建应用和数据管理(不要动,常用)
  • mydemo/asgi.py:接收网络请求(不要动)
  • mydemo/wsgi.py:接收网络请求(不要动)
  • mydemo/urls.py:URL路由和函数的对应关系(常用)
  • mydemo/setting.py:项目配置相关(常用)

创建app

使用manage.py来创建app

python3 manage.py startapp myapp

创建app应用的目录结构:

$ tree myapp
myapp
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│   └── __init__.py
├── models.py
├── tests.py
└── views.py

2 directories, 7 files

Django框架app应用初始文件介绍:

  • admin.py:用来注册你在 models.py 中定义的数据模型,注册后可以通过 Django 自带的 后台管理系统(admin site)进行管理。
  • apps.py:定义 app 的一些配置信息(比如 app 名称),Django 在加载 app 时会读取这里的配置,INSTALLED_APPS 里引用的就是这里的配置类。
  • migrations/:用来存放数据库迁移文件,Django 使用 makemigrations 和 migrate 命令时,会在这里生成和记录数据库表结构变化的文件,类似数据库版本管理器。
  • models.py:用来定义数据库模型(ORM 映射),一个类对应一张数据库表,类属性对应表的字段,比如:定义User类,就会映射成一张user表。
  • tests.py:存放单元测试代码,可以编写测试用例验证你的业务逻辑是否正确。
  • views.py:定义业务逻辑处理函数或类(视图函数 / 视图类),接收请求,处理数据,返回响应等。

快速开始Django项目示例

1.注册app应用,修改setting.py文件添加app应用的类。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp.apps.MyappConfig' # 在这里添加
]

补充说明: 可以在创建的app目录下的apps.py文件中的类查看类名。

2.添加URL路由,修改urls.py文件添加URL和视图函数对应关系。

from myapp import views  # 导入myapp的views模块

urlpatterns = [
    # path('admin/', admin.site.urls),
    path('index/', views.index) # 绑定路由和视图函数
]

3.编写视图函数,修改myapp/views.py定义路由功能。

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


def index(request): # 编写index函数方法
    return HttpResponse('欢迎访问Django项目!!!')

4.启动Django服务。

python3 manage.py runserver
......
Django version 5.2.4, using settings 'mydemo.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

路由管理

在Django框架中,我们所有的路由都是通过urls.py文件进行管理,其中我们都是通过path()include()来管理设置我们的路由。

path()函数

path()函数至少需要两个参数:route 和 view,也就是路由路径和视图函数。

# urls.py
from django.urls import path  # 导入path方法


urlpatterns = [
    path("访问路径", 视图函数, name="路由名字"),
]

include()函数

引用其他URLconfs,这个概念跟Flask中的蓝图(Blueprint)FastAPI中的APIRouter概念差不多,就是对路由的管理进行分别。当你具有多个app应用架构的时候如果把所有的app的views中的视图都放在urls.py中进行映射,这样会显得结构非常的复杂不清晰,这个时候就可以使用include()来进行路由区分。

# urls.py
from django.urls import include, path

urlpatterns = [
    path("app/", include("myapp.urls")),
]

引用了之后我们只需要在我们myapp应用目录下新增一个urls.py文件就可以在自己的应用下定义该应用的子路由了。

# myapp/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('index/', views.index),
]

现在我们需要访问首页就不能访问/index路径了会报错找不到,因为我们加了一个主路由,所以我们需要访问/app/index才能正常访问。

reverse()路由反转

当我们通过url请求视图函数的时候,我们在视图函数中想要获取访问的路径只是就可以用到reverse()函数来实现。就是通过视图函数的名字(或 URL 的 name 别名)来反向生成对应的URL。
语法用法:

from django.urls import reverse # 导入reverse模块

# 静态路径
url = reverse('视图函数名|name别名')
# 带参数使用args或者使用kwargs
url = reverse('视图函数名|name别名', args=[6])
url = reverse('视图函数名|name别名', kwargs={'参数名': 6})

templates模板

Django的模板系统用来生成 HTML 页面,模板语法支持DTL,也就是Django自己的模板语法,也支持三方模板语法,比如:Jinja2。

templates模板路径配置
命令创建:如果是命令创建的Django项目则默认templates目录在创建的app应用目录下,目录名字为templates,把所需要的模板文件放在该目录下即可。
PyCharm创建:使用PyCharm企业版创建的Django项目,setting.py文件里配置了模板路径,如下所示:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates']  # 定义了项目全局模板目录
        ,
        'APP_DIRS': True,  # 
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

项目里面所有的app应用加载模板都会指向改设置的模板路径。

总结:setting.py文件中配置DIRS,空则是默认,配置路径则是定义全局模板路径。

使用templates模板

1.在app应用目录下创建templates目录并增加模板文件,如下所示:

# 创建templates目录
mkdir -p myapp/templates
# 查看目录结构
$ tree myapp
myapp
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│   └── __init__.py
├── models.py
├── templates
│   └── test.html
├── tests.py
└── views.py

模板test.html文件内容:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>登高 - 杜甫</title>
</head>
<body>
    <h1>登高</h1>
    <h3>杜甫</h3>
    <p>
        风急天高猿啸哀,<br>
        渚清沙白鸟飞回。<br>
        无边落木萧萧下,<br>
        不尽长江滚滚来。<br>
        万里悲秋常作客,<br>
        百年多病独登台。<br>
        艰难苦恨繁霜鬓,<br>
        潦倒新停浊酒杯。
    </p>
</body>
</html>

2.编辑urls.py文件修改URLS路由配置,如下所示:

# urls.py
from myapp import views as myapp_views  

urlpatterns = [  
    # path('admin/', admin.site.urls),  
    path('test/', myapp_views.test),
]

3.路由绑定视图函数,如下所示:

# myapp/views.py
def test(request):  
    return render(request, "test.html")

4.测试访问http://127.0.0.1:8000/test查看页面,如下图所示:
django

使用templates模板时遇到的坑

特别注意:在Django里,如果多个app下的templates/目录里存在同名模板文件,确实会出现只加载其中一个(通常是“第一个被找到的”),而不是你预期的按app区分。

为什么会出现这种情况?
1.Django会依次遍历TEMPLATES['DIRS']指定的全局模板目录。
2.如果APP_DIRS=True,它会在每个app下的templates/目录里找。
3.一旦找到匹配的模板文件名,就不会继续往下找。

解决方案
给每个app的模板加一个子文件夹(以app名字命名),这样就不会冲突了,如下:

myadmin/
├── templates/
│   └── myadmin/
│       └── index.html
myapp/
├── templates/
│   └── myapp/
│       └── index.html

static静态文件

跟templates模板一样,我们可以在应用目录下创建一个static目录来存放比如:css样式文件、js执行文件、png或者jpg之类的图片文件等静态文件。
我们一般目录结构如下规划:

$ tree static 
static
├── css
├── images
└── js

这样方便我们对静态资源分类存放。

使用static静态文件

1.在app应用目录下创建static目录,存放静态资源文件(我放的图片文件:myapp_logo.png),如下所示:

$ tree static 
static
└── images
    └── myapp_logo.png

2.编写HTML模板文件加载图片。
在DTC模板语法中可以通过以下语法来加载static静态文件,如下所示:

<!-- 加载static目录变量 -->
{% load static %}
<!-- 使用static变量来设置静态文件路径 -->
{% static 'images/myapp_logo.png' %}

模板look_logo.html文件内容:

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Myapp</title>
</head>
<body>
    <h1>Myapp logo</h1>
    <img src="{% static 'images/myapp_logo.png' %}" alt="Logo">
</body>
</html>

3.编辑urls.py文件修改URLS路由配置,如下所示:

# urls.py
from myapp import views as myapp_views  

urlpatterns = [  
    # path('admin/', admin.site.urls),  
    path('myapp/logo/', myapp_views.look_logo),
]

4.路由绑定视图函数,如下所示:

# myapp/views.py
def look_logo(request):  
    return render(request, "myapp/look_logo.html")

5.测试访问http://127.0.0.1:8000/myapp/logo查看页面图片加载情况,如下图所示:
django

特别注意: 我在使用static静态文件的时候也遇到了像使用templates模板时遇到的在多个app的static/目录里存在同名的文件,则会使用第一个找到的文件。

三、请求和响应

路径参数和查询参数

路径参数

通过动态路径来传递参数来请求。
比如:http://127.0.0.1:8000/user/3
【示例】代码如下:

# urls.py,定义URL路径绑定视图函数
urlpatterns = [
    path('user/<user_id>', views.get_user_path),
]
# views.py,视图函数编写
def get_user_path(request, user_id):
    return HttpResponse(f"查询用户的id号为: {user_id}")

请求效果如下图所示:
django

查询参数

就是通过传递query字符串来请求。
比如:http://127.0.0.1:8000/user?user_id=3
【示例】代码如下:

# urls.py,定义URL路径绑定视图函数
urlpatterns = [
    path('user', views.get_user_id),
]
# views.py,视图函数编写
def get_user_id(request):
    user_id = request.GET.get("user_id")
    return HttpResponse(f"查询用户的id号为: {user_id}")

请求效果如下图所示:
django

请求对象

在Django我们需要获取请求对象信息的话我们可以通过在视图函数中定义的request变量来进行获取。
get请求获取请求信息
【示例】代码如下:

# urls.py,定义URL路径绑定视图函数
urlpatterns = [
    path('request', myapp_views.get_request_info),
]

# views.py,视图函数编写
def get_request_info(request):
    # 请求方法(GET、POST、PUT、DELETE等)
    rq_method = request.method
    # 请求路径(什么也不带)
    rq_path_a = request.path
    # 请求的完整路径(带查询参数,不带域名)
    rq_path_b = request.get_full_path()
    # 请求的完整路径(带域名和查询参数)
    rq_path_c = request.build_absolute_uri()
    # 所有的请求头(字典格式返回)
    headers = {
        'SERVER_PORT': request.META.get('SERVER_PORT'),
        'HTTP_HOST': request.META.get('HTTP_HOST'),
        'HTTP_USER_AGENT': request.META.get('HTTP_USER_AGENT'),
        'HTTP_ACCEPT': request.META.get('HTTP_ACCEPT'),
        'HTTP_ACCEPT_ENCODING': request.META.get('HTTP_ACCEPT_ENCODING'),
        'HTTP_ACCEPT_LANGUAGE': request.META.get('HTTP_ACCEPT_LANGUAGE'),
        'HTTP_COOKIE': request.META.get('HTTP_COOKIE'),
    }
    # 请求体的编码方式
    rq_encoding = request.encoding
    # 获取GET请求参数
    user_id = request.GET.get("user_id")
    # 定义返回内容结构
    response_data = {
        '请求方法': rq_method,
        '请求头列表': headers,
        '请求路径': {'请求路径(什么也不带)': rq_path_a, '完整请求路径(带查询参数,不带域名)': rq_path_b, '完整请求路径(带域名和查询参数)': rq_path_c},
        'user_id变量': user_id,
        # '请求编码': rq_encoding
    }
    import json
    print(json.dumps(response_data, indent=2, ensure_ascii=False))
    return HttpResponse('获取请求对象成功!!!')
''' 运行结果如下
{
  "请求方法": "GET",
  "请求头列表": {
    "SERVER_PORT": "8000",
    "HTTP_HOST": "127.0.0.1:8000",
    "HTTP_USER_AGENT": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36",
    "HTTP_ACCEPT": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
    "HTTP_ACCEPT_ENCODING": "gzip, deflate, br, zstd",
    "HTTP_ACCEPT_LANGUAGE": "zh-CN,zh;q=0.9",
    "HTTP_COOKIE": "csrftoken=5OrmSKzVrAekNXU8EWb2E2ZFgXk6qsap"
  },
  "请求路径": {
    "请求路径(什么也不带)": "/request",
    "完整请求路径(带查询参数,不带域名)": "/request?user_id=5",
    "完整请求路径(带域名和查询参数)": "http://127.0.0.1:8000/request?user_id=5"
  },
  "user_id变量": "5"
}
'''

post请求获取请求体
如果是post请求的话一般获取请求体。
【示例】代码如下:

# urls.py,定义URL路径绑定视图函数
urlpatterns = [
    path('post_request', myapp_views.post_request_body),
]

# views.py,视图函数编写
@csrf_exempt
def post_request_body(request):
    # 获取POST请求参数
    username = request.POST.get("username")
    password = request.POST.get("password")
    # 获取与请求体内容
    body_data = request.body.decode("utf-8")
    return JsonResponse({'username': username, 'password': password, 'body': body_data})

使用curl命令访问测试,结果如下所示:

# 请求类型为 application/json
$ curl -X POST http://127.0.0.1:8000/post_request \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Jack",
    "age": 28,
    "email": "jack@163.com"
}' \
  -d 'username=Django' \
  -d 'password=2jduh2i9jdishwsjs'
# 运行结果
{"username": null, "password": null, "body": "{\n    \"name\": \"Jack\",\n    \"age\": 28,\n    \"email\": \"jack@163.com\"\n}&username=Django&password=2jduh2i9jdishwsjs"}
# 请求类型为 application/x-www-form-urlencoded
$ curl -X POST http://127.0.0.1:8000/post_request \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d '{
    "name": "Jack",
    "age": 28,
    "email": "jack@163.com"
}' \
  -d 'username=Django' \
  -d 'password=2jduh2i9jdishwsjs'
# 运行结果
{"username": "Django", "password": "2jduh2i9jdishwsjs", "body": "{\n    \"name\": \"Jack\",\n    \"age\": 28,\n    \"email\": \"jack@163.com\"\n}&username=Django&password=2jduh2i9jdishwsjs"}

上面的测试结果会发现Content-Type不对会导致获取的查询参数会为null。

注意:Django的request.POST是不会解析JSON的,它只会解析表单格式,所以想要获取查询参数的值要用application/x-www-form-urlencodedmultipart/form-data

响应返回

在Django里,视图函数可以返回多种响应对象,它们都继承自django.http.HttpResponse或其子类。

1.基本响应

HttpResponse
说明:最基础的HTTP响应,可返回 HTML、文本等。
【示例】代码如下:

return HttpResponse("欢迎访问.")

HttpResponseNotFound
说明:404 页面响应。
【示例】代码如下:

return HttpResponseNotFound("页面不存在!!!")

HttpResponseForbidden
说明:403 禁止访问。
【示例】代码如下:

return HttpResponseForbidden("没有权限!!!")

HttpResponseServerError
说明:500 服务器错误。
【示例】代码如下:

return HttpResponseServerError("服务器错误!!!")

HttpResponseBadRequest
说明:400 错误。
【示例】代码如下:

return HttpResponseBadRequest("请求错误!!!")

HttpResponseRedirect
说明:临时重定向(302)。
【示例】代码如下:

return HttpResponseRedirect("/login/")

HttpResponsePermanentRedirect
说明:永久重定向(301)。
【示例】代码如下:

return HttpResponsePermanentRedirect("/home/")

2.JSON响应

JsonResponse
说明:返回JSON数据,自动设置Content-Type: application/json
【示例】代码如下:

return JsonResponse({"code": 0, "msg": "ok"})

3.文件响应

FileResponse
说明:返回文件(大文件支持流式)。
【示例】代码如下:

file = open("test.txt", "rb")
return FileResponse(file, as_attachment=True, filename="test.txt")

4.模板/HTML响应

render
说明:结合模板渲染HTML,返回HttpResponse
【示例】代码如下:

return render(request, "index.html", {"name": "Jack"})

5.重定向响应

redirect
说明:非常常用的快捷函数,用来返回重定向响应(让浏览器访问另一个 URL)。它本质上是返回HttpResponseRedirectHttpResponsePermanentRedirect对象。
【示例】代码如下:

# 重定向到网站
return redirect("https://www.example.com/")
# 重定向到路径
return redirect("/login/")

四、DTL模板语法

DTL模板指的是Django Template Language(Django 模板语言),是Django自带的模板系统,用来在HTML页面里嵌入动态数据、逻辑和渲染结果。

DTL基础语法

使用语法:

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

注释

{# 这是一个注释 #}

变量使用

<h1>Hello {{ name }}!</h1>

if条件语句

<!-- if语句示例 -->
{% if user.is_authenticated %}
    <p>欢迎回来,{{ user.username }}!</p>
{% endif %}
<!-- if-else语句示例 -->
{% if score >= 60 %}
    <p>及格</p>
{% else %}
    <p>不及格</p>
{% endif %}
<!-- if-else-elif语句示例 -->
{% if score >= 90 %}
    <p>优秀</p>
{% elif score >= 60 %}
    <p>及格</p>
{% else %}
    <p>不及格</p>
{% endif %}

for循环语句

{% for content in contents %}
    <p>{{ forloop.counter }}:{{ content }}</p>
{% endfor %}

forloop提供循环状态变量:

  • forloop.counter:索引,从1开始。
  • forloop.first:是否为第一个元素。

Filters过滤器

DTL模板的过滤器跟Jinja2模板引擎大部分常用过滤器是一样的,但是也有不一样的。

<!-- 字符串处理 -->
{{ name|upper }}  {# 大写输出 }
{{ name|lower }}  {# 小写输出 #}
{{ value|capfirst }}  {# 字符串首字母大写 }
{{ value|title }}  {# 每个单词首字母大写 }
{{ value|center:"6" }}  {# 居中,两边填充空格凑够6位 }
<!-- 默认值处理 -->
{{ value|default('N/A') }}  {# 设置默认值 }
{{ value|default_if_none:"空值" }}  {# 设置空值}
<!-- 列表与长度 -->
{{ items|length }}  {# 输出字符长度 #}
{{ items|first }}  {# 第一个元素 #}
{{ items|last }}   {# 最后一个元素 #}
{{ list|join(',') }}  {# 列表连接成字符串,比如:a,b,c }
<!-- 数字运算 -->
{{ 200|add:"100" }}       {# 数字相加 }
{{ 3.1415926|floatformat:2 }} {# 保留小数点后两位 }
{{ 42|divisibleby:3 }}     {# 判断返回bool }
<!-- 日期时间 -->
{{ birthday|date:"Y-m-d H:i" }}  {# 格式化日期格式,比如:2025-08-19 10:30 }

模板继承

模板的继承可以快速重复构建页面结构统一的页面,比如:导航栏、公共头部和公共底部页面,还有比如博客页面类似。
通过{% block %}{% endblock %}定义可被子模板覆盖的区域,再通过{% extends %}进行继承使用模板加入可被覆盖的区域进行使用。
1.创建基础模板base.html

<!DOCTYPE html>
<html>
<head>
    {% block head %}
    <title>{% block title %}{% endblock %} - My Website</title>
    {% endblock %}
</head>
<body>
    <h1>主要内容:</h1>
    {% block content %}{% endblock %}
</body>
</html>

2.创建子模板继承基础模板index.html

{% extends "base.html" %}

{% block title %}黄鹤楼 - 崔颢{% endblock %}

{% block content %}
	<h1>黄鹤楼</h1>
	<h3>崔颢</h3>
	<p>
	    昔人已乘黄鹤去,<br>
	    此地空余黄鹤楼。<br>
	    黄鹤一去不复返,<br>
	    白云千载空悠悠。<br>
	    晴川历历汉阳树,<br>
	    芳草萋萋鹦鹉洲。<br>
	    日暮乡关何处是,<br>
	    烟波江上使人愁。
	</p>
{% endblock %}

模板嵌入

把一个模板片段插入到当前模板中,可插入任意小片段,不依赖继承结构。可以重复使用同一个片段,比如:页头、页脚、导航菜单。
主要通过{% include %}进行嵌入部分模板文件。

<body>
    {% include "header.html" %}
    <h1>主页内容</h1>
    {% include "footer.html" %}
</body>

加载静态文件

只需要通过{% load static %}引入静态文件模块,则可以使用static变量进行定义静态文件位置。

{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<img src="{% static 'images/logo.png' %}" alt="Logo">

注意:需要确保settings.py文件配置了static的位置配置,如下:
STATIC_URL = '/static/'
STATICFILES_DIRS = [BASE_DIR / 'static']

五、数据库操作

操作数据库的两种方式:

  • 1.自己写sql语句配置pymysql库对数据库进行操作。
  • 2.使用Django自带的ORM模型内置方法来操作数据库。

对于复杂的数据查询操作可能自己写sql会简洁好用一点,但是常见的数据库操作ORM模型提供的方法会更简洁。
ORM模型对于数据库种类的支持也有很多,比如:MySQL、PostgreSQL、SQLite、Oracle等基本都兼容。

初始化配置

我们这里以MySQL为例,其他数据库配置可查看官方文档:https://docs.djangoproject.com/zh-hans/5.2/ref/settings/#std-setting-DATABASES

1.安装配置MySQL驱动

Django官方推荐用mysqlclient:

pip install mysqlclient

Windows类系统不方便使用mysqlclient的话就用PyMySQL:

pip install PyMySQL

然后在__init__.py里声明使用的MySQL驱动:

# myapp/__init__.py
import pymysql
pymysql.install_as_MySQLdb()

2.配置连接数据库

先创建数据库,因为Django的ORM只会创建表不会自动创建库

CREATE DATABASE dbname DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;
GRANT ALL PRIVILEGES ON dbname.* TO 'username'@'%' IDENTIFIED BY 'password';
FLUSH PRIVILEGES;

在settings.py里修改配置DATABASES:

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",  # 数据库引擎
        "NAME": "dbname",                      # 数据库名
        "USER": "username",                    # 用户名
        "PASSWORD": "password",                # 密码
        "HOST": "127.0.0.1",                   # 数据库地址
        "PORT": "3306",                        # 端口(默认3306)
        "OPTIONS": {
            "charset": "utf8mb4",          # 推荐utf8mb4,支持Emoji
        },
    }
}

定义数据库model

我们定义数据库模型其实就是定义数据库初始化数据要创建的表以及表里的字段,我们需要修改对应app应用目录下的models.py文件,如下所示:

class Users(models.Model):
    user_id = models.CharField(max_length=32)
    name = models.CharField(max_length=50)
    phone_number = models.CharField(max_length=32)
    status = models.CharField(max_length=16)
    create_time = models.DateTimeField(auto_now_add=True, null=True, blank=True)
    remark = models.CharField(max_length=100, null=True, blank=True)

示例解读: 上面的类名可以理解为表的名字,并且名字会自动转化成全小写,但是根据Django默认的appname_modelname形式,比如我应用的表的名字是myapp_users。下面的变量名就是表里面的字段名字,每有一个变量,该表里就会多一个字段,变量的对象方法里面的参数就是该字段的属性定义,比如:字段类型、字段长度等。如果需要创建多个表只需要新增类就行。

如果你想自定义Django生成的数据库表名,而不是默认的appname_modelname形式。Django提供了Meta选项db_table来控制表名。如下所示:

class Users(models.Model):
    user_id = models.CharField(max_length=32)
    name = models.CharField(max_length=50)
    phone_number = models.CharField(max_length=32)
    status = models.CharField(max_length=16)
    create_time = models.DateTimeField(auto_now_add=True, null=True, blank=True)
    remark = models.CharField(max_length=100, null=True, blank=True)

    class Meta:
        db_table = "users"  # 自定义表名,Django 不会加 app 前缀

定义完数据库的模型之后,就需要生成迁移文件(表结构操作备份)和执行迁移文件(建表)

# 生成迁移文件
python3 manage.py makemigrations
# 执行迁移文件
python3 manage.py migrate

提示:生成的迁移文件会存放在app应用目录下的migrations目录下,文件名类似:0001_initial.py,你可以理解这里存放的文件就是你对数据库表结构操作记录,方便管理项目数据结构。

数据基本操作

在对模型的操作前需要导入模型对象:

from myapp.models import Users

新增数据

使用create()方法:

Users.objects.create(
	user_id='2c5a4277afabcca9',
	name='jack',
	phone_number='18292734937',
	status='yes',
	remark=''
)

或者还有一种方式:

user = Users(
	user_id='2c5a4277afabcca9',
	name='jack',
	phone_number='18292734937',
	status='yes',
	remark=''
)
user.save() # 提交保存修改

查询数据

# 查询表所有数据
result = Users.objects.all()
# 过滤状态为'yes'的用户
result = Users.objects.filter(status='yes')
# 查询某个id,获取单个数据对象
result = Users.objects.get(id=1)

修改数据

Users.objects.filter(name='jack').update(status='no')

删除数据

Users.objects.filter(user_id='2c5a4277afabcca9').delete()

QuerySet

了解QuerySet

什么是QuerySet?
QuerySet就是一组数据库查询的结果集,可以看作是SQL查询的Django封装方法,让我们能够更加便捷来操作数据库查询数据。

QuerySet的特点:

  • 惰性执行:创建QuerySet不会立即执行SQL,只有在真正取值时才会执行。
  • 链式调用:每次调用返回新的QuerySet,可以链式拼接,比如:可以多次调用 .filter(), .exclude()等方法组合条件。
  • 可缓存:第一次遍历时会查数据库并缓存结果,再次遍历时直接用缓存。
  • 可切片和迭代:类似 Python 列表,可用切片取部分结果。
  • 延迟加载字段:只在需要时才访问数据库的字段,避免无用查询。

QuerySet常用方法分类

常见操作速查表

操作
ORM示例
说明
查询所有 Model.objects.all() 查询所有数据
条件查询 Model.objects.filter(name='jack') 按照条件过滤查询
单条获取 Model.objects.get(id=1) 查询某个条件的单个数据
排除查询 Model.objects.exclude(status='no') 查询排除条件之内的其他数据
排序 Model.objects.order_by('id') 按照某字段进行排序
查询限制 Model.objects.all()[:10] 查询前10条数据
聚合统计 Model.objects.aggregate(Count('status')) 统计条件数量
新增 Model.objects.create(...) 添加新数据
更新 Model.objects.filter(...).update(status='yes') 更新数据
删除 Model.objects.filter(...).delete() 按照条件删除数据

过滤数据

方法
示例
说明
.filter(**kwargs) Model.objects.filter(name='jack') 返回满足条件的对象集合
.exclude(**kwargs) Model.objects.exclude(status='no') 返回不满足条件的对象集合
.get(**kwargs) Model.objects.get(id='1') 返回单个对象,无结果或多个结果会抛出异常
.first() / .last() Model.objects.filter(status='yes').first()
Model.objects.filter(status='yes').last()
获取查询集中的第一个和最后一个对象
.exists() Model.objects.filter(name='jack').exists() 判断是否存在符合条件的记录,返回布尔值
.count() Model.objects.filter(status='yes').count() 返回符合条件的记录数量

字段查询

查询方式
示例
说明
__exact .filter(name__exact='Jack') 精确匹配(默认就是 exact)
__iexact .filter(name__iexact='jack') 忽略大小写的精确匹配
__contains .filter(name__contains='ma') 包含指定字符串
__icontains .filter(name__icontains='Ma') 忽略大小写的包含
__startswith .filter(name__startswith='a') 以某字符串开头
__istartswith .filter(name__istartswith='A') 忽略大小写的开头匹配
__endswith .filter(name__endswith=s) 以某字符串结尾
__in .filter(id__in=[1,2,3]) 在给定列表中
__gt / __gte .filter(age__gt=100) 大于 / 大于等于
__lt / __lte .filter(age__lt=200) 小于 / 小于等于
__isnull .filter(remark_isnull=True) 判断是否为 NULL
__range .filter(age__range=(15, 18)) 范围查询

排序和切片

方法
示例
说明
.order_by(*fields) Model.objects.all().order_by('id')
Model.objects.all().order_by('-id')
进行排序,正序和倒序
[:10] Model.objects.all()[:10] 切片,取限制数量的数据
.reverse() Model.objects.get(id=1) 反转排序,需要先排序,再反转结果
.distinct() Model.objects.all().distinct() 去重查询

聚合与注解

方法
示例
说明
.annotate() Model.objects.annotate(user_count=Count('user')) 添加额外字段到每个对象上,常与聚合函数一起使用
.aggregate() Model.objects.aggregate(total=聚合函数使用),配合聚合函数使用。
导入聚合函数:from django.db.models import Count, Sum, Avg, Max, Min
对整个查询集进行聚合计算,如求和、平均值等
Count, Sum, Avg, Max, Min Model.objects.aggregate(total=Count('id'))
Model.objects.aggregate(total=Sum('price'))
Model.objects.aggregate(total=Avg('pages'))
Model.objects.aggregate(max_price=Max('price'), min_price=Min('price'))
常见聚合函数

其他实用方法

方法
说明
.values(*fields) 返回字典列表,而不是模型实例
.values_list(*fields, flat=False) 返回元组列表,若只有一个字段且 flat=True 则返回扁平列表
.only(*fields) 只加载指定字段(延迟加载)
.defer(*fields) 排除某些字段(延迟加载)
.select_related() 用于外键优化查询,减少 SQL 查询次数
.prefetch_related() 用于多对多或反向外键的预加载优化
.all() 获取所有对象(返回一个新的 QuerySet)
.none() 返回一个空的 QuerySet