创建Django项目

我使用Pycharm来创建Django项目,也可以自己手动创建虚拟环境等,配置如下,所有代码可点击这里查看

image-20250710162353796

运行,在浏览器输入http://localhost:8000/,显示如下表明项目创建正确

image-20250710170151023

创建应用程序

终端输入python manage.py migrate创建数据库,用于将项目相关的信息存储,然后输入python manage.py startapp learning_log来创建应用程序,项目结构如下

image-20250710184027773

.venv/

  • Python 虚拟环境目录。
  • 包含你项目安装的所有依赖(如 Django)。
  • 通常会在 .gitignore 中忽略它。

manage.py

  • 管理工具脚本,用于执行各种 Django 命令,如:

    1
    2
    3
    4
    python manage.py runserver      # 启动开发服务器
    python manage.py makemigrations # 创建迁移
    python manage.py migrate # 应用迁移到数据库
    python manage.py createsuperuser # 创建管理员账户

__init__.py

  • 让该目录成为 Python 包可为空

settings.py

  • Django 项目的配置文件
  • 包含数据库设置、应用注册、模板路径、静态文件配置、安全设置等

urls.py

  • 项目级URL 路由入口

  • 将不同的 URL 请求分发给对应的 app 处理

  • 通常写法:

    1
    2
    3
    4
    5
    from django.urls import path, include
    urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('learning_log.urls')), # 指向 app 的 url
    ]

wsgi.py

  • 用于部署到 WSGI 服务器
  • 生产环境使用

asgi.py

  • 用于部署到 ASGI 服务器
  • 用于支持 WebSocket、异步视图

__init__.py

  • 表示这是一个 Python 包

admin.py

  • 用于注册模型到 Django 管理后台(admin site)

  • 示例:

    1
    2
    3
    from django.contrib import admin
    from .models import Entry
    admin.site.register(Entry)

apps.py

  • 定义 app 的元信息,通常不需修改
  • 当你在 INSTALLED_APPS 中注册 app 时,这里也会用到

models.py

  • 定义数据模型(Model)类
  • 每个模型对应数据库中的一张表

views.py

  • 视图函数,用来处理用户请求并返回响应
  • 例如返回 HTML 页面、JSON 数据等

tests.py

  • 单元测试文件,可编写自动化测试
  • Django 内建测试框架基于 Python 的 unittest

migrations/

  • 自动生成的数据库迁移文件

  • 每当你修改模型(models.py)并运行:

    1
    python manage.py makemigrations

    就会在此生成一个迁移脚本,用于记录数据库变更

templates/

  • 存放 HTML 模板文件的目录
  • 用于 Django 的模板引擎渲染页面

例如:
你可以在 templates/ 中创建 index.html,然后在视图中渲染:

1
2
3
from django.shortcuts import render
def index(request):
return render(request, 'index.html')

db.sqlite3

  • 默认使用的 SQLite 数据库文件
  • 存储了你项目所有模型的数据内容
文件/文件夹 作用说明
.venv/ 虚拟环境,包含依赖
manage.py 管理命令脚本
DjangoProject/settings.py 项目配置
DjangoProject/urls.py URL 路由入口
DjangoProject/wsgi.py 部署用 WSGI 接口
learning_log/ 自定义 app
models.py 定义数据库模型
views.py 编写视图函数
admin.py 注册后台模型
migrations/ 数据库变更记录
templates/ HTML 模板目录
db.sqlite3 SQLite 数据库文件

应用设置

修改models.py文件如下

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

# Create your models here.

class Topic(models.Model):
"""A topic the user is learning about."""
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)

def __str__(self):
"""Return a string representation of the model."""
return self.text

settings.pyINSTALLED_APPS中添加learning_log,然后执行数据库迁移,如下

1
2
python manage.py makemigrations
python manage.py migrate

image-20250710220145445

创建超级用户

输入python manage.py createsuperuser来创建管理者,账户名、密码、邮箱自行设置,其中密码会被隐藏

image-20250710220315252

随后在admin.py文件中修改代码如下

1
2
3
4
5
from django.contrib import admin
from learning_log.models import Topic

# Register your models here.
admin.site.register(Topic)

这段代码用于向管理网站注册Topic

由于我在创建项目时没有勾选“启用Django admin”,因此这里需要对项目进行简单的更改

  1. 将urls.py中注释的部分取消

    1
    2
    3
    4
    5
    6
    # from django.contrib import admin
    from django.urls import path

    urlpatterns = [
    # path('admin/', admin.site.urls),
    ]
  2. 在settings.py的INSTALLED_APPS中添加django.contrib.admin

访问http://localhost:8000/admin/,显示如下

image-20250710222125078

输入账户和密码,登录进去界面显示如下

image-20250710222408941

User和Group是Django自动在管理网站添加的模型,而Topic是我们刚刚自己添加的。此时可以点击Topic,添加任意的主题

定义Entry

在models.py中添加如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Entry(models.Model):
"""Something specific learned about a topic."""
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)

class Meta:
verbose_name_plural = 'entries'

def __str__(self):
"""Return a string representation of the model."""
if len(self.text) > 50:
return f"{self.text[:50]}..."
else:
return self.text

这里主要说一下两个地方

  1. topic = models.ForeignKey(Topic, on_delete=models.CASCADE)是将每一条 Entry 都关联一个 Topic,形成多对一的关系

  2. 元信息配置(Meta):默认 Django 会把模型名称小写加 “s” 做复数,这句是为了告诉 Django这个模型的复数名称设置为 entries,而不是默认的 Entrys

由于修改了模型文件,因此我们需要再次迁移数据库,执行

1
2
python manage.py makemigrations learning_log
python manage.py migrate

然后在管理网站注册Entry:admin.site.register(Entry)(有关模型导入的问题这里不做叙述)

进入管理网站后会发现多出了一个Entries条目,进入该条目,点击下拉框就可以选择对应的主题,如下图

image-20250710230104831

这时候我们就可以创建条目并将其与对应的主题相关联。不过,目前除了后台管理界面,我们还没任何可供用户访问的界面,下一步我们将尝试创建一个网页来让其他人访问

创建网页

使用Django创建网页主要分为三个步骤,定义URL、编写视图、编写模板,完成这三个部分的顺序通常无关紧要,每个人都可以按照个人喜好来实现

映射URL

当前http://localhost:8000返回默认的Django网站,下面进行修改,打开urls.py文件,在`urlpatterns`中添加`path(‘’, include(‘learning_log.urls’)),`表示我们导入应用learning_log中的url。然后在应用learning_log中添加urls.py文件,内容如下

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

app_name = 'learning_log'
urlpatterns = [
# Home page
path('', views.index, name='index'),

]

这段代码主要为当前 app 即(learning_log)配置 URL 路由,其中app_name = 'learning_log',给这个 app 的 URL 配置一个命名空间,防止与其他 app 中的 URL 名称冲突,在模板中使用 {% url 'learning_log:index' %} 就能明确地调用这个 URL。urlpatterns 是 Django 查找 URL 的核心列表,每一个 path() 定义了一个 URL 与视图的映射。path('', views.index, name='index'),第一个参数是一个字符串用于正确的路由请求,这里表示匹配根路径,第二个参数表示当匹配到 / 时,调用 views.py 中的 index() 函数,第三个参数给这个 URL 取了一个名字叫 'index',在模板中可以用 {% url 'learning_log:index' %} 来反向生成地址

编写视图

视图函数接受请求中的信息,准备好生成网页所需的数据,然后将其发送给浏览器。在前面我们调用了 views.py 中的 index() 函数,但该函数还未编写,打开views.py文件,添加代码如下

1
2
3
4
5
from django.shortcuts import render

# Create your views here.
def index(request):
return render(request, 'learning_log/index.html')

它接受一个 request 对象作为参数,这是 Django 在用户访问网页时自动传入的请求信息对象。函数的返回值是 render(request, 'learning_log/index.html'),表示让 Django 去找一个路径为 learning_log/index.html 的模板文件,并渲染它,默认的模板查找路径为app/templates/app_name/文件名.html

编写模板

模板定义网页的外观,每当访问网页时,Django都将填入相关的数据用于显示,我们的template文件夹位于根目录,所以无需在app目录中重新创建,但需要在settings.py的TEMPLATES中检查是否存在'DIRS': [BASE_DIR / 'templates'],以便正确检索html文件

在template文件夹中创建learning_log文件夹并在其中创建index.html,添加代码如下

1
2
3
4
5
6
7
<p>Learning Log</p>

<p>Learning Log is a tool for tracking your learning progress.
It allows you to create entries for each topic you are studying,
including notes, resources, and reflections.
You can also categorize your entries by subject or skill level.
</p>

这里添加了两个段落,运行访问界面如下

image-20250711101454258

创建其他网页

创建父模板

在index.html所在的目录中,创建一个base.html文件,这个模板将包含所有页面的通用元素,并将所有子模板都继承它,添加内容如下

1
2
3
4
5
<p>
<a href="{% url 'learning_log:index' %}">Learning Log</a>
</p>

{% block content %}{% endblock content %}
1
2
3
<p>
<a href="{% url 'learning_log:index' %}">Learning Log</a>
</p>

这段表示页面顶部的一个链接,点击后会跳转到名为 'learning_log:index' 的 URL 对应的页面。

  • {% url 'learning_log:index' %} 是 Django 模板语言提供的 URL 反向解析标签
  • 'learning_log' 是在 learning_log/urls.py 中定义的 app_name
  • 'index' 是在 urlpatterns 中为首页设置的 name

所以最终这个标签会被渲染为:

1
<a href="/">Learning Log</a>

如果将 index 页面映射到 /,那么这个链接就是返回首页的作用。

1
{% block content %}{% endblock content %}

这是 Django 模板语言中非常关键的部分,称为 模板块(template block),它用于定义“可扩展区域”。

解释如下:

  • {% block content %}` 是定义一个名为 `content` 的内容区块。 - `{% endblock content %} 表示这个区块的结束。

换句话说,子模板可以继承这个模板,并用自己的内容替换掉这个 block 区域,例如:

1
2
3
4
5
{% extends "learning_log/base.html" %}

{% block content %}
<h2>This is the home page!</h2>
{% endblock content %}

创建子模板

重写index.html文件,使其继承base.html,如下

1
2
3
4
5
6
7
8
9
{% extends 'learning_log/base.html' %}

{% block content %}
<p>Learning Log is a tool for tracking your learning progress.
It allows you to create entries for each topic you are studying,
including notes, resources, and reflections.
You can also categorize your entries by subject or skill level.
</p>
{% endblock content %}

这里将index从base继承下来,使用{% block content %}`定义一个`content`并插入内容,使用`{% endblock content %}结束content块,里面内容将显示在界面上,此时通过修改父模板将修改所有继承它的界面,方便统一管理

显示所有主题的页面

首先定义URL,添加path('topics/', views.topics, name='topics'),在视图中定义topics函数如下

1
2
3
4
5
def topics(request):
"""Show all topics."""
topics = Topic.objects.order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_log/topics.html', context)

将所有主题按照时间顺序排序并返回,然后创建topics.html,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
{% extends 'learning_log/base.html' %}

{% block content %}
<p>Topics</p>

<ul>
{% for topic in topics %}
<li>{{ topic.text }}</li>
{% empty %}
<li>No topics have been created yet.</li>
{% endfor %}
</ul>
{% endblock content %}

这里主要将topics中的内容通过无序列表的方式呈现出来。随后在base.html中添加连字符以及代码<a href="{% url 'learning_log:topics' %}">Topics</a>,这将产生一个与Topics匹配的URL链接,访问http://localhost:8000/topics/,如下![image-20250712114413813](https://cdn.jsdelivr.net/gh/psmarter/blog-imgs/Python%E5%AE%9E%E6%88%98%E4%B9%8BWeb%E5%BC%80%E5%8F%91%2F2025-07-12-23-47-46-91b35a.png)

显示特定主题

我们已经创建了两次页面,对其基本流程应该有所了解了,后续关于URL、视图、模板相关的内容将进行简略,我们再次创建一个页面用于显示特定的主题及其所有的条目

1
2
3
4
5
6
7
8
9
10
# URL
path('topics/<int:topic_id>/', views.topic, name='topic')

# topic函数
def topic(request, topic_id):
"""Show a single topic and all its entries."""
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_log/topic.html', context)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{# 模板 #}
{% extends 'learning_log/base.html' %}

{% block content %}

<p>Topic:{{ topic.text }}</p>

<p>Entries:</p>
<ul>

{% for entry in entries %}
<li>
<p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
<p>{{ entry.text|linebreaks }}</p>
</li>
{% empty %}
<li>No entries have been created for this topic yet.</li>
{% endfor %}
</ul>

{% endblock content %}

topic函数中先通过id获取到特定的主题,然后将主题中的条目降序排列,最后将TopicEntries返回。模板主要把entries按照特定的格式显示,“|”表示过滤符,也就是将时间按照:January 1, 2025 23:00格式。linebreaks是将文本中的换行符转变为浏览器理解的内容。

然后修改topics.html,将每个主题都映射为对应的链接

1
2
3
4
{% for topic in topics %}
<li>
<a href="{% url 'learning_log:topic' topic.id %}">{{ topic.text }}</a>
</li>

运行之后在Topic页面点击主题,显示如下

image-20250712120315152