概述

Django 是一个开源的 Python Web 应用程序框架,它遵循了模型-视图-控制器(MVC)架构模式。它使得开发人员可以更快速、更高效地构建复杂的 Web 应用程序,并提供了许多内置的功能和工具来简化开发过程。

搭建框架

首先是 pip install djangoDjango 安装下来。安装之后使用指令 python -m django --version 查看版本。

使用指令 django-admin startproject mysite 来初始化 Django 配置。mysite 可为任意名。

1
2
3
4
5
6
7
8
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py

这些文件的作用:

  • 最外层的 mysite/ 根目录只是你项目的容器, 根目录名称对 Django 没有影响,你可以将它重命名为任何你喜欢的名称。
  • manage.py: 一个让你用各种方式管理 Django 项目的命令行工具。你可以阅读 django-adminmanage.py 获取所有 manage.py 的细节。
  • 里面一层的 mysite/ 目录包含你的项目,它是一个纯 Python 包。它的名字就是当你引用它内部任何东西时需要用到的 Python 包名。 (比如 mysite.urls).
  • mysite/__init__.py:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。
  • mysite/settings.pyDjango 项目的配置文件。
  • mysite/urls.pyDjango 项目的 URL 声明,就像你网站的“目录”。
  • mysite/asgi.py:作为你的项目的运行在 ASGI 兼容的 Web 服务器上的入口。
  • mysite/wsgi.py:作为你的项目的运行在 WSGI 兼容的 Web 服务器上的入口。

切换到 mysite/ 目录下,运行指令 python manage.py runserver,应当可以看到如下输出:

1
2
3
4
5
6
7
8
9
10
11
Performing system checks...

System check identified no issues (0 silenced).

You have unapplied migrations; your app may not work properly until they are applied.
Run 'python manage.py migrate' to apply them.

七月 13, 2023 - 15:50:53
Django version 4.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

通过浏览器访问 http://127.0.0.1:8000/ 。应当看到一个“祝贺”页面,有一只火箭正在发射。那么我们已经成功搭建起框架了!

应用

Django 中,每一个应用都是一个 python 包。Django 可以帮助我们生成应用的基础目录。在项目的根目录下运行指令 python manage.py startapp polls,应当会创建一个目录 polls,文件结构应当如下所示:

1
2
3
4
5
6
7
8
9
polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py

打开 polls/views.py,输入以下内容:

1
2
3
4
5
from django.http import HttpResponse


def index(request):
return HttpResponse("Hello, world.")

这是 Django 中最简单的视图。我们需要让一个 URL 映射到它。

polls/urls.py 中输入以下内容:

1
2
3
4
5
6
7
from django.urls import path

from . import views

urlpatterns = [
path("", views.index, name="index"),
]

然后在根 URLconf 文件中指定我们创建的模块。在 mysite/urls.py 文件中的 urlpatterns 列表里插入一个 include()

1
2
3
4
5
6
7
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
path("polls/", include("polls.urls")),
path("admin/", admin.site.urls),
]

通过以下命令验证是否工作:python manage.py runserver。通过浏览器访问 http://localhost:8000/polls/,应当能看到 Hello, world.

数据库配置

mysite/settings.py 中包含了 Django 的设置的 python 模块。这个配置文件使用 SQLite 作为默认数据库。

在此文件的 INSTALLED_APPS 中包含了以下默认应用:

  • django.contrib.admin:管理员站点。
  • django.contrib.auth:认证授权系统。
  • django.contrib.contenttypes:内容类型框架。
  • django.contrib.sessions:会话框架。
  • django.contrib.messages:消息框架。
  • django.contrib.staticfiles:管理静态文件的框架。

这些应用被默认启用是为了给常规项目提供方便。

默认开启的某些应用需要至少一个数据表,所以,在使用他们之前需要在数据库中创建一些表。请执行以下命令:python manage.py migrate

这个命令查看 INSTALLED_APPS 配置,并根据 mysite/settings.py 文件中的数据库配置和随应用提供的数据库迁移文件,创建任何必要的数据库表。

创建模型

Django 里写一个数据库驱动的 Web 应用的第一步是定义模型 —— 也就是数据库结构设计和附加的其它元数据。

一个模型就是单个定义你的数据的信息源。模型中包含了不可缺少的数据区域和你存储数据的行为。Django 的迁移代码是由你的模型文件自动生成的,它本质上是个历史记录,Django 可以用它来进行数据库的滚动更新,通过这种方式使其能够和当前的模型匹配。

我们尝试做一个投票 app。那么我们需要在应用中创建两个模型:问题 Question 和选项 ChoiceQuestion 模型包括问题描述和发布时间。Choice 模型有两个字段,选项描述和当前得票数。每个选项属于一个问题。

按下面的例子编辑 polls/models.py

1
2
3
4
5
6
7
8
9
10
11
12
from django.db import models


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 。这将告诉 Django 每个字段要处理的数据类型。

定义某些 Field 类实例需要参数。例如 CharField 需要一个 max_length 参数。

在最后,使用 ForeignKey 定义了一个关系。这将告诉 Django,每个 Choice 对象都关联到一个 Question 对象。Django 支持所有常用的数据库关系:多对一、多对多和一对一。

激活模型

为了在工程中包含这个应用,我们应当修改 INSTALLED_APPS 如下:

1
2
3
4
5
6
7
8
9
INSTALLED_APPS = [
"polls.apps.PollsConfig",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]

接着运行以下命令:python manage.py makemigrations polls。通过 makemigrationsDjango 会检测你对模型文件的修改,并且把修改的部分储存为一次迁移。

sqlmigrate 命令接受一个迁移的名称,并且返回对应的 SQLpython manage.py sqlmigrate polls 0001。该命令没有真正在你的数据库中的执行迁移。相反,它只是把命令输出到屏幕上,让你看看 Django 认为需要执行哪些 SQL 语句。

现在,再次运行 migrate 命令,在数据库里创建新定义的模型的数据表:python manage.py migrate

这个 migrate 命令选中所有还没有执行过的迁移并应用在数据库上。也就是将你对模型的更改同步到数据库结构上。

由此,改变模型只需要三步:

  • 编辑 models.py 文件,改变模型。
  • 运行 python manage.py makemigrations 为模型的改变生成迁移文件。
  • 运行 python manage.py migrate 来应用数据库迁移。

初识 API

让我们进入交互式 python 命令行。通过以下命令打开 python 命令行:python manage.py shell。不直接使用 python 打开命令行是因为 manage.py 会设置环境变量 DJANGO_SETTINGS_MODULE,从而让 Django 根据 mysite/settings.py 文件来设置 python 包的导入路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
>>> from polls.models import Choice, Question  # Import the model classes we just wrote.

# No questions are in the system yet.
>>> Question.objects.all()
<QuerySet []>

# Create a new Question.
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())

# Save the object into the database. You have to call save() explicitly.
>>> q.save()

# Now it has an ID.
>>> q.id
1

# Access model field values via Python attributes.
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=datetime.timezone.utc)

# Change values by changing the attributes, then calling save().
>>> q.question_text = "What's up?"
>>> q.save()

# objects.all() displays all the questions in the database.
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>

让我们为 QuestionChoice 添加 __str__() 方法,为 Question 添加一个自定义方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import datetime

from django.db import models
from django.utils import timezone


class Question(models.Model):
# ...
def __str__(self):
return self.question_text

def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)


class Choice(models.Model):
# ...
def __str__(self):
return self.choice_text

添加 __str__() 方法是很重要的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
>>> from polls.models import Choice, Question

# Make sure our __str__() addition worked.
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>

# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith="What")
<QuerySet [<Question: What's up?>]>

# Get the question that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>

# Request an ID that doesn't exist, this will raise an exception.
>>> Question.objects.get(id=2)
Traceback (most recent call last):
...
DoesNotExist: Question matching query does not exist.

# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to Question.objects.get(id=1).
>>> Question.objects.get(pk=1)
<Question: What's up?>

# Make sure our custom method worked.
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True

# Give the Question a couple of Choices. The create call constructs a new
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set to hold the "other side" of a ForeignKey relation
# (e.g. a question's choice) which can be accessed via the API.
>>> q = Question.objects.get(pk=1)

# Display any choices from the related object set -- none so far.
>>> q.choice_set.all()
<QuerySet []>

# Create three choices.
>>> q.choice_set.create(choice_text="Not much", votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text="The sky", votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text="Just hacking again", votes=0)

# Choice objects have API access to their related Question objects.
>>> c.question
<Question: What's up?>

# And vice versa: Question objects get access to Choice objects.
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3

# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

# Let's delete one of the choices. Use delete() for that.
>>> c = q.choice_set.filter(choice_text__startswith="Just hacking")
>>> c.delete()

Django 管理页面

创建管理员账号

首先,我们得创建一个能登录管理页面的用户。请运行下面的命令:python manage.py createsuperuser

键入用户名,然后回车。

Username: admin

按照提示键入邮箱地址和密码。

启动开发服务器

Django 的管理界面默认就是启用的。让我们启动开发服务器,看看它到底是什么样的。

如果开发服务器未启动,用以下命令启动它:python manage.py runserver

打开地址 http://127.0.0.1:8000/admin/。输入账户和密码,登入管理员站点页面。

向管理员站点中添加投票应用

polls/admin.py 中编辑如下:

1
2
3
4
5
from django.contrib import admin

from .models import Question

admin.site.register(Question)