Django
初始化
介绍
Django 是一个 python 网络框架,内置了数据库模型等,开发简单。
安装
需要预先安装 Python:
https://www.python.org/downloads/
然后通过 pip 安装 Django
python -m pip install Django通过命令验证:
python -m django --version如果成功输出了一个版本号,则安装成功。
初试
创建项目
使用命令:
django-admin startproject <project-name> <project-directory>eg:
django-admin startproject mysite djangotutorial这将在 djangotutorial 文件夹下新建一个 mysite 项目
目录结构
djangotutorial/
manage.py -> 一个让你用各种方式管理 Django 项目的命令行工具。详细:django-admin 和 manage.py | Django 文档
mysite/ -> 一个项目的实际 Python 包,名称是导入内容的 Python 包名称(例如 mysite.urls)。
__init__.py -> 一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。
settings.py -> Django 项目的配置文件。Django 配置 | Django 文档
urls.py -> Django 项目的 URL 声明,就像你网站的“目录”。URL调度器
asgi.py -> 作为你的项目的运行在 ASGI 兼容的 Web 服务器上的入口。如何使用 ASGI 来部署 | Django 文档
wsgi.py -> 作为你的项目的运行在 WSGI 兼容的Web服务器上的入口。如何使用 WSGI 进行部署 | Django 文档
🤯 ASGI 和 WSGI 是什么?
🤯 ASGI 和 WSGI 是两种用于在 Python Web 开发中处理请求和响应的标准接口。它们为 Web 服务器与 Web 应用程序之间的通信提供了一种规范化的方法。
WSGI (Web Server Gateway Interface)
简介:
WSGI 是 Python Web 应用程序与服务器之间的标准接口,由 PEP 3333 定义。
它被广泛应用于传统的同步 Web 框架,如 Django 和 Flask。
特点:
同步接口:WSGI 设计之初是为处理简单的 HTTP 请求响应循环,非常适合 I/O 阻塞的同步代码。
简单易用:大多数流行的 Python Web 框架最初都是基于 WSGI 的,开发者社区成熟。
局限性:
不支持异步编程模型,因此对于需要高并发和实时更新的应用(例如 WebSockets)可能不太理想。
ASGI (Asynchronous Server Gateway Interface)
简介:
ASGI 是为了解决 WSGI 在异步处理上的局限性而提出的,它是面向异步编程的接口。
由 PEP 484 提议,旨在支持现代的异步框架和协议。
特点:
异步接口:ASGI 支持异步任务,这使得它能够很好地处理长连接、WebSocket 和 HTTP/2 等需求。
灵活性:允许在同一个应用中混合使用同步和异步代码。
应用场景:
非常适合需要高性能和实时通信的应用程序,例如在线聊天、游戏或数据流应用。
支持的框架包括 FastAPI、Django Channels、Starlette 等。
总结:
如果你的 Web 应用主要是同步请求(如 CRUD 操作),并且不需要支持 WebSockets 或其他异步功能,那么 WSGI 是一个非常成熟可靠的选择。
如果你需要处理异步任务或 WebSockets,则 ASGI 提供了更先进和灵活的环境。
WSGI 应用:是一个同步程序,每次调用 application 函数时都会阻塞直到请求完成。
ASGI 应用:使用 async/await,允许进行非阻塞 I/O 操作。在上面的示例中,app 是一个异步函数,在接收和发送 HTTP 消息时不会阻塞。
运行
使用命令:
python3 manage.py runserver如果出现如下

在 settings.py中找到 ALLOWED_HOSTS,将 localhost 加到数组中。
成功运行:

创建应用
在 Django 中,每一个应用就是一个 Python 包,并且遵循着相同的约定,Django 自带一个工具,可以帮你生成应用的基础目录结构,这样就能专心写代码,而不是创建目录。
🤓 在 Django 中,项目(Project)和应用(App)是两个不同的概念,以 AI 聊天软件为例
项目是一整个项目,比如这个 AI 聊天应用就是一个项目。
应用是实现特定功能的模块,比如 AI 聊天应用包含:Ai 聊天、支付系统、生图系统,每一个实现特定功能的模块,都叫做一个应用
一个项目可以有多个应用,同时一个应用也可以用于多个项目,只要引入即可,实现了 Python 的扩展性。
使用命令:
python manage.py startapp polls这将在项目下新建一个文件夹

编写视图
打开 polls/views.py,这个文件用于当请求路径匹配时,反馈信息,输入代码:
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world. You're at the polls index.")这是一个 Django 最基本的视图。但是我们要在浏览器中访问他,我们需要将其映射到一个 URL,所以我们需要定义一个 URL 配置,这类配置简称为“URLconf”。这些 URL 配置是在每个 Django 应用程序内部中定义的,定义为 urls.py文件。
需要给 polls应用定义一个 URLconf,创建一个名为 polls/urls.py文件,并包含:
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index")
]下一步是在 mysite 项目中配置全局 URLconf,以包含在 polls.urls 中定义的 URLconf。为此,在 mysite/urls.py 中添加对 django.urls.include 的导入,并在 urlpatterns 列表中插入一个 include(),
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("polls/", include("polls.urls")),
path("admin/", admin.site.urls),
]path() 函数至少需要两个参数:route 和 view。include() 函数允许引用其他 URLconf。每当 Django 遇到 include() 时,它都会截断匹配到该点的 URL 部分,并将剩余字符串发送到包含的 URLconf 进行进一步处理。
这时访问 http://127.0.0.1:8000/polls将会看到

访问流程解析
请求路径polls/nice
当用户请求地址为 polls 时,首先会到
website/urls中寻找,这是一个项目的总 URLconf,path('polls/', include("polls.urls"))将会寻找 polls.urls,也就是 polls 文件夹下 urls 文件,
path("nice", views.nice, name="nice")
找到 nice 路径,将会使用 views 文件中 nice 方法进行处理
def hello(request):
return HttpResponse("Nice to meet you!")返回 Nice to meet you!
数据库
配置数据库
Django 默认支持 SQLite,如果想要其他的数据库,需要进行配置:如何安装 Django | Django 文档
以 MySQL 为例
安装 mysqlclient:
pip install mysqlclientwebsite 文件夹下新增一个
local.py文件,用于存储 MySQL 连接配置,相当于环境变量:
import os
os.environ['MYSQL_HOST'] = 'localhost'
os.environ['MYSQL_PORT'] = '3306'
os.environ['MYSQL_NAME'] = 'py_test'
os.environ['MYSQL_USER'] = 'root'
os.environ['MYSQL_PASSWORD'] = 'chenshibo'在
__init__.py文件中导入local.py文件,这样我们就可以在settings.py中使用环境变量
from .local import *在
settings.py中设置数据库
import os
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
# MySQL database host ip.
'HOST': os.environ['MYSQL_HOST'],
# # port number.
'PORT': os.environ['MYSQL_PORT'],
# # database name.
'NAME': os.environ['MYSQL_NAME'],
# # user name.
'USER': os.environ['MYSQL_USER'],
# # password
'PASSWORD': os.environ['MYSQL_PASSWORD'],
}
}创建数据表
Django 会自动的把应用中的 models.py 文件中定义的内容创建为数据库表。
例如,在 polls/models.py 中新增:
from django.db import models
# 在 Python 中,这样写是继承关系,即类 Question 继承于 models.Model
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField("date published")
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)每个模型被表示为 django.db.models.Model的字类。
每个字段都是Field类的实例。比如:字符字段被表示为CharField,日期字段被表示为 DateTimeField。
每个 Field类实例变量的名字,比如(quesiton_text, pub_date)是字段名。
激活模型
如果一个 Django 项目,有很多的项目表,我们通过命令,在使用数据库之前创建这些表:
python manage.py migrate这个命令会查看 INSTALLED_APPS配置,并根据 mysite/settings.py文件中的数据库配置创建这些数据表。
在上边的示例中,我们已经创建了一个 models 文件,为了让我们的项目中包含这个应用,我们需要配置 mysite/settings.py中的 INSTALLED_APPS配置,将设置好的应用添加进去。
因为 PollsConfig类写在文件 polls/apps.py中,所以它的路径为 polls.apps.PollsConfig,在文件 mysite/settings.py中 INSTALLED_APPS子项添加路径后,如下:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'polls.apps.PollsConfig', # 或者直接写 polls 也可以
]现在项目就已经包含 polls应用了,接着执行:
python manage.py makemigrations polls这个命令会将修改的部分创建一次迁移,可以理解为 Django 并不会直接修改数据库,而是会创建一个文件保存迁移,如果我们需要迁移,则继续执行命令
这就有如下输出:
(base) chenshibo@chenshibodeMac-mini HelloWorld % python manage.py makemigrations
Migrations for 'polls':
polls/migrations/0001_initial.py
+ Create model Question
+ Create model Choice同时,这个操作会在 polls/migrations中创建一个文件 0001_initial.py,文件内容如下:
# Generated by Django 5.1.3 on 2024-11-20 02:45
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Question',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('question_test', models.CharField(max_length=200)),
('pub_date', models.DateTimeField(verbose_name='date published')),
],
),
migrations.CreateModel(
name='Choice',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('choice_text', models.CharField(max_length=200)),
('votes', models.IntegerField(default=0)),
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.question')),
],
),
]可以通过命令查看这个文件具体会执行哪些 SQL 语句,使用 sqlmigrate命令查看:
python manage.py sqlmigrate polls 0001可以看到如下输出:
--
-- Create model Question
--
CREATE TABLE `polls_question` (
`id` bigint AUTO_INCREMENT NOT NULL PRIMARY KEY,
`question_test` varchar(200) NOT NULL,
`pub_date` datetime(6) NOT NULL
);
--
-- Create model Choice
--
CREATE TABLE `polls_choice` (
`id` bigint AUTO_INCREMENT NOT NULL PRIMARY KEY,
`choice_text` varchar(200) NOT NULL,
`votes` integer NOT NULL,
`question_id` bigint NOT NULL
);
ALTER TABLE `polls_choice`
ADD CONSTRAINT `polls_choice_question_id_c5b4b260_fk_polls_question_id`
FOREIGN KEY (`question_id`)
REFERENCES `polls_question` (`id`);
可以看到:
输出的内容和数据库有关
数据库的表名是由应用名(polls)和模型名的小谢形式(question 和 choice)连接而来
主键 id 会被自动创建,也可以自定义
默认情况,Django 会在外键字段名后追加字符串"_id"
这个
sqlmigrate命令并不是在数据库中执行迁移,仅仅是把命令输出到屏幕上,可以看到后续需要执行哪些 SQL。
再次运行 migrate命令,就可以执行迁移,在数据库中创建新定义的模型的数据表:
python manage.py migrate这个命令选中所有还没有执行过的迁移(Django 通过在数据库中创建爱你一个特殊的表 django_migrationgs 来跟踪执行过哪些迁移),应用在数据库上

数据库迁移不会影响数据表原结构和数据,平滑的进行升级。
改变数据表需要这三步:
编辑 models.py 文件,改变模型。
运行
python manage.py makemigrations为模型的改变生成迁移文件
可以使用
python manage.py sqlmigrate polls 0001命令查看 SQL 文件
运行
python manage.py migrate应用数据库迁移
数据库 API
通过使用命令:
python manage.py shell可以进入交互式 Python 命令行。
插入数据
q = Question(question_text="What's new?", pub_date=timezone.now())这将会创建一个 Question 类,如果想要将其保存到数据库中,只需要:
q.save()这将会把修改保存到数据库中,可以查看我们的数据:
print(q.id) # 1
print(q.question_text) # What's new?
print(q.pub_date) # datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=datetime.timezone.utc)也可以直接使用
p = Question.objects.create(question_text="Nice to meet you", pub_date=timezone.now())更新数据
我们也可以进行修改:
q.question_text="What's up?"
q.save()这将会保存我们的修改,这时查看:
print(q.question_text) # What's up?如果是简单的更新数据,我们可以使用:
from django.db.models import F
selected_choice.votes = F("votes") + 1
selected_choice.save()在 Django 中,F() 是一个用于引用数据库字段的表达式对象。它允许你在查询中避免先取出值再进行计算,而是直接在数据库层面上进行运算。这在更新操作中尤其有用,因为它可以防止竞争条件。例如:
selected_choice.votes = F("votes") + 1
这行代码的意思是在数据库层直接将 votes 字段的值加一,而不是先将其取出到 Python 中,加一后再保存回去。这种方式更加高效,并且可以避免多线程环境下的竞争条件问题。
F() 的使用主要涉及以下几个方面:
数据库运算:直接在数据库中执行数学、文本或日期运算。
性能优化:减少从数据库读取和写入数据的次数。
竞争条件:确保在多用户环境中数据的准确性。
因此,F() 对于需要在数据库层进行字段运算的场景是非常实用的工具。
获取数据
获取全部数据:
print(Questio.objects.all()) # <QuerySet [<Question: Question object (1)>]>但是这样看的话,获取不到我们想要的的信息,我们可以通过编辑 Question模型的代码修复这个问题,只需要新增上 __str__()方法
from django.db import models
class Question(models.Model):
# ...
def __str__(self):
return self.question_text
class Choice(models.Model):
# ...
def __str__(self):
return self.choice_text此时,再次执行:
print(Questio.objects.all())
# <QuerySet [<Question: What's up>, <Question: Hello>]>这样就可以看到具体的信息了。
也可以为模型增加自定义方法:
import datetime
from django.db import models
from django.utils import timezone
class Question(models.Model):
# ...
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)获取一系列符合条件的内容:
Question.objects.filter(id=1)
# <QuerySet [<Question: What's up>]>
# 在字符串后可以直接添加上字符串方法,当做筛选条件
Question.objects.filter(question_text_startswith="What")
# 如果是时间类型,可以直接传入年份进行判断
Question.objects.filter(pub_date__year=current_year)
# <QuerySet [<Question: What's up>, <Question: Hello>]>获取唯一内容:
# 获取主键为 1 的数据
Question.objects.get(pk=1)
# 获取到的数据可以调用模型中的自定义方法
q = Question.objects.get(pk=1)
print(q.was_published_recently)
# True外键关联
Django 会自动将拥有外键的模型关联起来,在 Django 中,定义了一个 ForeignKey 之后,Django 自动为关联的对象创建一个反向关系。choice_set 是由 Django 自动创建的,它允许你通过 Question 实例访问相关的 Choice 对象集合。
假如你想为这个问题添加一个新的选项(Choice),可以这样做:
# 获取到 Question 对象
q = Question.objects.get(pk=1)
# 这将查询的所有以 q 为外键的 choice 对象
print(q.choice_set.all())
# <QuerySet []>
# django 会自动拥有一个以 关联键_set 的内容,可以通过这个来进行创建关联当前 q 的内容
q.choice_set.create(choice_text="Not much", votes=0)
q.shoice_set.create(choice_text="The sky", votes=0)
c = q.choice_set.create(choice_text="Just hacking again", votes=0)
# 可以通过被关联对象找到上级对象
print(c.question)
# <Question: What's up>
print(q.choice_set.all())
# <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
print(q.choice_set.count())
# 3
# 可以对外键数据进行筛选
c = q.choice_set.filter(choice_text__startswith="Just hacking")
# 删除该内容
c.delete()
视图(路由)
在每个 Django 应用中,通过 URLconfs 来派发每个请求对应的处理函数,对应路由配置下载 urls.py 文件中,路由处理函数写在 views.py中。
可以在 website/urls.py中写顶层,路由,再依次派发到各个应用中。
from django.urls import path, include
urlpatterns = [
path('polls/', include("polls.urls"))
]然后在 polls/urls.py文件中:
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("nice", views.hello, name="nices")
]path(请求路径,处理函数,路由名)
所有的处理函数写在 views文件中,也叫视图
路由参数
URL params
from django.urls import path
from . import views
urlpatterns = [
# ex: /polls/
path("", views.index, name="index"),
# ex: /polls/5/
path("<int:question_id>/", views.detail, name="detail"),
# ex: /polls/5/results/
path("<int:question_id>/results/", views.results, name="results"),
# ex: /polls/5/vote/
path("<int:question_id>/vote/", views.vote, name="vote"),
]def detail(request, question_id):
return HttpResponse("You're looking at question %s." % question_id)
def results(request, question_id):
response = "You're looking at the results of question %s."
return HttpResponse(response % question_id)
def vote(request, question_id):
return HttpResponse("You're voting on question %s." % question_id)POST / GET
使用 request.POST取出 POST 数据:
def vote(request, question_id):
choice = request.POST["choice"]使用 request.GET取出 GET 数据:
def vote(request, question_id):
choice = request.GET["choice"]抛出错误
抛出 404 错误
使用
Http404("Question does not exist")
from django.http import Http404
from django.shortcuts import render
from .models import Question
# ...
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, "polls/detail.html", {"question": question})也可以使用:
get_object_or_404()
get_object_or_404() 函数将 Django 模型作为其第一个参数,并将任意数量的关键字参数传递给模型管理器的 get() 函数。如果对象不存在,则会引发 Http404。
from django.shortcuts import get_object_or_404, render
from .models import Question
# ...
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, "polls/detail.html", {"question": question})自定义装饰器
使用 @functools.wraps来创建一个装饰器
@functools.wraps是 Python 中的一个装饰器,用于帮助创建装饰器时保留被装饰函数的元数据。使用装饰器时,原始函数的名称、文档字符串和其他属性可能会丢失,而 functools.wraps可以解决这个问题。
创建示例:
装饰器:
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Before calling the function")
result = func(*args, **kwargs)
print("After calling the function")
return result
return wrapper
使用:
@my_decorator
def greet():
"""Returns a friendly greeting"""
return 'Hello!'
print(greet.__name__) # 输出: greet
print(greet.__doc__) # 输出: Returns a friendly greeting如果不使用@functools.wraps,装饰后的函数将丢失原始函数的信息
def simple_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@simple_decorator
def greet():
"""Returns a friendly greeting"""
return "Hello!"
print(greet.__name__) # 输出: wrapper
print(greet.__doc__) # 输出: None