1 | 学习笔记来自[https://kgithub.com/pythonzhichan/django-beginners-guide](https://kgithub.com/pythonzhichan/django-beginners-guide) |
先检查一下Python环境是否正确安装。
cmd输入python --version
1 | C:\Users\Administrator>python --version |
就可以开始了。
安装虚拟环境
1 | pip3 install virtualenv |
创建文件夹backend
输入以下命令安装虚拟环境
1 | virtualenv venv -p python3 |
激活虚拟环境
激活之后命令行前面出现(venv)即为成功
1 | venv\Scripts\activate.bat |
在上一步中,我们创建了一个名为venv的特殊文件夹。该文件夹内包含了一个python的副本。在我们激活了venv环境之后,当我们运行Python命令时,它将使用我们存储在venv里面的本地副本,而不是我们之前在操作系统中安装的那个。
另一个重要的事情是,pip程序也已经安装好了,当我们使用它来安装Python的软件包(比如Django)时,它将被安装在venv环境中。
顺便说一句,要想退出venv环境,运行下面的命令:
1 | deactivate |
现在先保持激活状态来进行下一步。
1 | pip install django |
1 | django-admin startproject certificate_analysis |
命令行工具django-admin会在安装Django的时候一起自动安装好。
执行了上面的命令以后,系统会为Django项目生成基础文件夹结构。
1 | certificate_analysis/ <-- 高级别的文件夹 |
/ about/ 时显示某些内容,则必须先在这里做映射关系。django自带了一个简单的网络服务器。在开发过程中非常方便,所以我们无需安装任何其他软件即可在本地运行项目。我们可以通过执行命令来测试一下它:
1 | python manage.py runserver |
可以看到服务已经启动了
Ctrl + C 终止服务器
新建的应用程序就是上图中黄色的小圆圈,一个Django项目中可以包含多个应用程序
创建名为dashboard的应用程序
1 | django-admin startapp dashboard |
dashboard文件夹结构是:
1 | Directory of D:\code\github\certificate_analysis\certificate_analysis\backend\certificate_analysis\dashboard |
下面,我们来探讨每个文件的作用:
现在我们创建了我们的第一个应用程序,让我们来配置一下项目以便启用这个应用程序。
配置一下项目以便启用这个应用程序:要做到这一点,打开settings.py并尝试找到INSTALLED_APPS变量:
以上显示的是已经安装在当前Django项目中的应用程序,如身份验证,会话,静态文件管理(图像,JavaScript,CSS等)等。
要想让我们的应用程序运行,应该先把自己的项目名称添加到配置文件中
1 | INSTALLED_APPS = [ |
使用前面漫画正方形和圆圈的比喻,黄色的圆圈就是我们的boards应用程序,django.contrib.admin, django.contrib.auth等就是红色的圆圈。
==创建视图==
编辑应用程序dashboard中的views.py
1 | from django.http import HttpResponse |
视图是接收httprequest对象并返回一个httpresponse对象的Python函数。接收 request 作为参数并返回 response 作为结果。
这个视图返回一个字符串信息
==告诉服务器什么时候返回这个视图==
应该在项目层面管理url,而不是应用层面。
因此应该在项目管理所在的文件夹编写相关代码。
urls.py
1 | title: django中url、path和re_path |
1 | from django.contrib import admin |
现在,Django使用正则表达式来匹配请求的URL。对于我们的home视图,我使用^$ 正则,它将匹配一个空路径,也就是主页(这个URL:http://127.0.0.1:8000 )。如果我想匹配的URL是 http://127.0.0.1:8000/homepage/ ,那么我的URL正则表达式就会是:url(r'^homepage/$', views.home, name='home')。
选择路径的时候直接选择manage.py所在的文件夹。
需要在设置中选择Django项目进行设置
项目运行配置
项目启动
1 | 主要内容来自于 https://github.com/pythonzhichan/django-beginners-guide/blob/master/Fundamentals-1.md |
图2:Web Board类图
花点时间考虑模型之间如何相互关联也很重要。类与类之间的实线告诉我们,在一个主题(Topic)中,我们需要有一个字段(译注:其实就是通过外键来关联)来确定它属于哪个版块(Board)。同样,帖子(Post)也需要一个字段来表示它属于哪个主题,这样我们就可以列出在特定主题内创建的帖子。最后,我们需要一个字段来表示主题是谁发起的,帖子是谁发的。
用户和版块之间也有联系,谁创建的版块。但是这些信息与应用程序无关。还有其他方法可以跟踪这些信息,稍后您会看到。
现在我们的类图有基本的表现形式,我们还要考虑这些模型将承载哪些信息。这很容易让事情变得复杂,所以试着先把重要的内容列出来,这些内容是我们启动项目需要的信息。后面我们再使用 Django 的迁移(Migrations)功能来改进模型,您将在下一节中详细了解这些内容。
但就目前而言,这是模型最基本的内容:
图3:强调类(模型)之间关系的类图
这个类图强调的是模型之间的关系,这些线条和箭头最终会在稍后转换为字段。
对于 Board 模型,我们将从两个字段开始:name 和 description。 name字段必须是唯一的,为了避免有重复的名称。description 用于说明这个版块是做什么用的。
Topic 模型包括四个字段:subject 表示主题内容,last_update 用来定义话题的排序,starter 用来识别谁发起的话题,board 用于指定它属于哪个版块。
Post 模型有一个 message 字段,用于存储回复的内容,created_at 在排序时候用(最先发表的帖子排最前面),updated_at 告诉用户是否更新了内容,同时,还需要有对应的 User 模型的引用,Post 由谁创建的和谁更新的。
最后是 User 模型。在类图中,我只提到了字段 username,password,email, is_superuser 标志,因为这几乎是我们现在要使用的所有东西。
需要注意的是,我们不需要创建 User 模型,因为Django已经在contrib包中内置了User模型,我们将直接拿来用。
==主页==
==主题==
==新建话题==
==讨论==
==回复==
这些模型基本上代表了应用程序的数据库设计。我们在本节中要做的是创建 Django 所表示的类,这些类就是在上一节中建模的类:Board,Topic和Post。User 模型被命名为内置应用叫 auth,它以命名空间 django.contrib.auth 的形式出现在 INSTALLED_APPS 配置中。
我们要做的工作都在 boards/models.py 文件中。以下是我们在Django应用程序中如何表示类图的代码:
1 | django 升级到2.0之后,表与表之间关联的时候,必须要写on_delete参数,否则会报异常: |
1 | from django.db import models |
所有模型都是django.db.models.Model类的子类。每个类将被转换为数据库表。每个字段由 django.db.models.Field子类(内置在Django core)的实例表示,它们并将被转换为数据库的列。
字段 CharField,DateTimeField等等,都是 django.db.models.Field 的子类,包含在Django的核心里面-随时可以使用。
在这里,我们仅使用 CharField,TextField,DateTimeField,和ForeignKey 字段来定义我们的模型。不过在Django提供了更广泛的选择来代表不同类型的数据,例如 IntegerField,BooleanField, DecimalField和其它一些字段。我们会在需要的时候提及它们。
有些字段需要参数,例如CharField。我们应该始终设定一个 max_length。这些信息将用于创建数据库列。Django需要知道数据库列需要多大。该 max_length参数也将被Django Forms API用来验证用户输入。
在Board模型定义中,更具体地说,在name字段中,我们设置了参数 unique=True,顾名思义,它将强制数据库级别字段的唯一性。
在Post模型中,created_at字段有一个可选参数,auto_now_add设置为True。这将告诉Django创建Post对象时为当前日期和时间。
模型之间的关系使用ForeignKey字段。它将在模型之间创建一个连接,并在数据库级别创建适当的关系(译注:外键关联)。该ForeignKey字段需要一个位置参数related_name,用于引用它关联的模型。(译注:例如 created_by 是外键字段,关联的User模型,表明这个帖子是谁创建的,related_name=posts 表示在 User 那边可以使用 user.posts 来查看这个用户创建了哪些帖子)
例如,在Topic模型中,board字段是Board模型的ForeignKey。它告诉Django,一个Topic实例只涉及一个Board实例。related_name参数将用于创建反向关系,Board实例通过属性topics访问属于这个版块下的Topic列表。
Django自动创建这种反向关系,related_name是可选项。但是,如果我们不为它设置一个名称,Django会自动生成它:(class_name)_set。例如,在Board模型中,所有Topic列表将用topic_set属性表示。而这里我们将其重新命名为了topics,以使其感觉更自然。
在Post模型中,该updated_by字段设置related_name='+'。这指示Django我们不需要这种反向关系,所以它会被忽略(译注:也就是说我们不需要关系用户修改过哪些帖子)。
下面您可以看到类图和Django模型的源代码之间的比较,绿线表示我们如何处理反向关系。
如果我们没有为模型指定主键,Django会自动为我们生成它。
创建数据库
1 | python manage.py makemigrations |
此时,Django 在 boards/migrations 目录创建了一个名为 0001_initial.py的文件。它代表了应用程序模型的当前状态。在下一步,Django将使用该文件创建表和列。
迁移文件将被翻译成SQL语句。如果您熟悉SQL,则可以运行以下命令来检验将是要被数据库执行的SQL指令
迁移到数据库
1 | python manage.py migrate |
现在数据库已经可以使用了。
可以使用manage.py 工具加载我们的项目来启动 Python shell :
1 | python manage.py shell |
让我们从导入Board类开始:
1 | from boards.models import Board |
要创建新的 boarrd 对象,我们可以执行以下操作:
1 | board = Board(name='Django', description='This is a board about Django.') |
为了将这个对象保存在数据库中,我们必须调用save方法:
1 | board.save() |
save方法用于创建和更新对象。这里Django创建了一个新对象,因为这时Board 实例没有id。第一次保存后,Django会自动设置ID:
1 | board.id |
您可以将其余的字段当做Python属性访问:
1 | board.name |
要更新一个值,我们可以这样做:
1 | board.description = 'Django discussion board.' |
每个Django模型都带有一个特殊的属性; 我们称之为**模型管理器(Model Manager)**。你可以通过属性objects 来访问这个管理器,它主要用于数据库操作。例如,我们可以使用它来直接创建一个新的Board对象:
1 | board = Board.objects.create(name='Python', description='General discussion about Python.') |
所以,现在我们有两个版块了。我们可以使用objects列出数据库中所有现有的版块:
1 | Board.objects.all() |
结果是一个QuerySet。稍后我们会进一步了解。基本上,它是从数据库中查询的对象列表。我们看到有两个对象,但显示的名称是 Board object。这是因为我们尚未实现 Board 的__str__ 方法。
__str__方法是对象的字符串表示形式。我们可以使用版块的名称来表示它。
首先,退出交互式控制台:
1 | exit() |
现在编辑boards app 中的 models.py 文件:
1 | class Board(models.Model): |
让我们重新查询,再次打开交互式控制台:
1 | python manage.py shell |
好多了,对吧?
我们可以将这个QuerySet看作一个列表。假设我们想遍历它并打印每个版块的描述:
1 | boards_list = Board.objects.all() |
结果是:
1 | Django discussion board. |
同样,我们可以使用模型的 管理器(Manager) 来查询数据库并返回单个对象。为此,我们要使用 get 方法:
1 | django_board = Board.objects.get(id=1) |
但我们必须小心这种操作。如果我们试图查找一个不存在的对象,例如,查找id=3的版块,它会引发一个异常:
1 | board = Board.objects.get(id=3) |
get方法的参数可以是模型的任何字段,但最好使用可唯一标识对象的字段来查询。否则,查询可能会返回多个对象,这也会导致异常。
1 | Board.objects.get(name='Django') |
请注意,查询区分大小写,小写“django”不匹配:
1 | Board.objects.get(name='django') |
| 操作 | 代码示例 |
|---|---|
| 创建一个对象而不保存 | board = Board() |
| 保存一个对象(创建或更新) | board.save() |
| 数据库中创建并保存一个对象 | Board.objects.create(name=’…’, description=’…’) |
| 列出所有对象 | Board.objects.all() |
| 通过字段标识获取单个对象 | Board.objects.get(id=1) |
views中返回的就是我们看到的东西。
修改boards/views.py
1 | from django.http import HttpResponse |
在manage.py所在的目录创建一个名为 templates的新文件夹
1 |
|
在我们可以使用这个HTML页面之前,我们必须告诉Django在哪里可以找到我们应用程序的模板。
打开myproject目录下面的settings.py文件,搜索TEMPLATES变量,并设置DIRS 的值为 os.path.join(BASE_DIR, 'templates'):
1 | TEMPLATES = [ |
本质上,刚添加的这一行所做的事情就是找到项目的完整路径并在后面附加“/templates”
现在我们可以更新home视图:
1 | from django.shortcuts import render |
也可以进行一个装饰。
1 |
|
我们来开始写第一个测试。现在,我们将在boards应用程序内的tests.py文件中操作
boards/tests.py
1 | from django.test import TestCase |
静态文件是指 CSS,JavaScript,字体,图片或者是用来组成用户界面的任何其他资源。
实际上,Django 本身是不负责处理这些文件的,但是为了让我们的开发过程更轻松,Django 提供了一些功能来帮助我们管理静态文件。这些功能可在 INSTALLED_APPS 的 django.contrib.staticfiles 应用程序中找到(译者:Django为了使得开发方便,也可以处理静态文件,而在生产环境下,静态文件一般直接由 Nginx 等反向代理服务器处理,而应用工服务器专心负责处理它擅长的业务逻辑)。
市面上很多优秀前端组件框架,我们没有理由继续用简陋的HTML文档来渲染】。我们可以轻松地将Bootstrap 4添加到我们的项目中。Bootstrap是一个用HTML,CSS和JavaScript开发的前端开源工具包。
在项目根目录中,除了 boards, templates 和myproject文件夹外,再创建一个名为static的新文件夹,并在static文件夹内创建另一个名为css的文件夹:
1 | certificate_analysis/ |
转到getbootstrap.com并下载最新版本:
解压 bootstrap-4.0.0-beta-dist.zip 文件,将文件 css/bootstrap.min.css 复制到我们项目的css文件夹中:
1 | certificate_analysis/ |
下一步是告诉Django在哪里可以找到静态文件。打开settings.py,拉到文件的底部,在STATIC_URL后面添加以下内容
1 | STATIC_URL = '/static/' |
现在我们必须在模板中加载静态文件(Bootstrap CSS文件)
index.html
1 | {% load static %} |
首先,我们在模板的开头使用了 Static Files App 模板标签 {% load static %}。
模板标签{% static %}用于构成资源文件完整URL。在这种情况下,{% static 'css/bootstrap.min.css' %}将返回 /static/css/bootstrap.min.css,它相当于 http://127.0.0.1:8000/static/css/bootstrap.min.css
{% static %}模板标签使用 settings.py文件中的 STATIC_URL 配置来组成最终的URL,例如,如果您将静态文件托管在像 https://static.example.com/这样的子域中 ,那么我们将设置 STATIC_URL=https://static.example.com/ ,然后 {% static 'css/bootstrap.min.css' %}返回的是 https://static.example.com/css/bootstrap.min.css
如果目前这些对你来说搞不懂也不要担心。只要记得但凡是需要引用CSS,JavaScript或图片文件的地方就使用{% static %}。稍后,当我们开始部署项目到正式环境时,我们将讨论更多。现在都设置好了。
刷新页面 http://127.0.0.1:8000 ,我们可以看到它可以正常运行,现在我们可以编辑模板,以利用Bootstrap CSS:
到目前为止,我们使用交互式控制台(python manage.py shell)添加了几个新的版块。但我们需要一个更好的方式来实现。在下一节中,我们将为网站管理员实现一个管理界面来管理这些数据。
Django已经配置了Django Admin,这个应用程序列出的INSTALLED_APPS
现在,我们将配置 Django Admin 来维护我们应用程序的版块。
我们首先创建一个管理员帐户:
1 | python manage.py createsuperuser |
按照说明操作
1 | Username (leave blank to use 'administrator'): |
在浏览器访问 http://localhost:8000/admin/
输入用户名和密码
它已经配置了一些功能。在这里,我们可以添加用户和组的权限管理,这些概念在后面我们将探讨更多。
添加Board模型非常简单。打开boards目录中的admin.py文件,并添加以下代码:
boards/admin.py
1 | from django.contrib import admin |
保存admin.py文件,然后刷新网页浏览器中的页面:
添加一个项目
检查一下是否正常:
1 | 更多内容查看https://github.com/pythonzhichan/django-beginners-guide/blob/master/AdvancedConcepts.md |
我们将从 myproject 目录中编写 urls.py 开始:
myproject/urls.py
1 | urlpatterns = [ |
一个项目可以有很多 urls.py 分布在多个应用(app)中。Django 需要一个 url.py 作为入口。这个特殊的 urls.py 叫做 根路由配置(root URLconf)。它被定义在 settings.py 中。
myproject/settings.py
1 | ROOT_URLCONF = 'myproject.urls' |
它已经自动配置好了,你不需要去改变它任何东西。
当 Django 接受一个请求(request), 它就会在项目的 URLconf 中寻找匹配项。他从 urlpatterns 变量的第一条开始,然后在每个 url 中去匹配请求的 URL。
如果 Django 找到了一个匹配路径,他会把请求(request)发送给 url 的第二个参数 视图函数(view function)。urlpatterns 中的顺序很重要,因为 Django 一旦找到匹配就会停止往后搜索。如果 Django 在 URLconf 中没有找到匹配项,他会通过 Page Not Found 的错误处理代码抛出一个 404 异常。
这是 url 函数的剖析:
1 | def url(regex, view, kwargs=None, name=None): |
1 | 如果你想给用户个人主页设置一个很酷的主页的URL,那么避免与静态资源冲突最简单的方法是添加一个前缀,例如:/u/vitorfs,或者像 Medium 一样使用 @ 作为前缀 /@vitorfs/。 |
url解析:
1 | url(r'^boards/(?P<pk>\d+)/$', views.board_topics, name='board_topics') |
正则表达式中的 \d+ 会匹配一个任意大小的整数值。这个整数值用来从数据库中取到 指定的 Board。现在注意我们这样写这个正则表达式 (?P<pk>\d+),这是告诉 Django 将捕获到的值放入名为 pk 的关键字参数中。
这个pk必须和视图函数中的参数pk相对应。
1 | def board_topics(request, pk): |
1 | PK or ID? PK 表示主键(Primary key),这是访问模型的主键ID的简写方法,所有Django模型都有这个属性,更多的时候,使用pk属性和使用id是一样的,这是因为如果我们没有给model定义主键时,Django将自动创建一个 AutoField 类型的字段,名字叫做 id,它就是主键。 |
urls.py
1 | from django.contrib import admin |
在boards/views.py中创建视图函数
1 | def board_topics(req, pk): |
在 templates 目录中,创建一个名为 topics.html 的模板
1 | {% load static %} |
浏览器访问http://127.0.0.1:8000/boards/1/应该出现以下页面:
编辑测试单元
test.py
1 | class BoardTopicTests(TestCase): |
这里需要注意几件事情。这次我们使用了 setUp 方法。在这个方法中,我们创建了一个 Board 实例来用于测试。我们必须这样做,因为 Django 的测试机制不会针对当前数据库跑你的测试。运行 Django 测试时会即时创建一个新的数据库,应用所有的model(模型)迁移 ,运行测试完成后会销毁这个用于测试的数据库。
因此在 setUp 方法中,我们准备了运行测试的环境,用来模拟场景。
test_board_topics_view_success_status_code 方法:测试 Django 是否对于现有的 Board 返回 status code(状态码) 200(成功)。test_board_topics_view_not_found_status_code 方法:测试 Django 是否对于不存在于数据库的 Board 返回 status code 404(页面未找到)。test_board_topics_url_resolves_board_topics_view 方法:测试 Django 是否使用了正确的视图函数去渲染 topics。现在来运行一下测试:
1 | python manage.py test |
输出了错误
看到了一个错误:“boards.models.DoesNotExist: Board matching query does not exist.”
在 DEBUG=False 的生产环境中,访问者会看到一个 500 Internal Server Error 的页面。但是这不是我们希望得到的。
我们想要一个 404 Page Not Found 的页面。让我们来重写我们的视图函数。
views.py
1 | from django.shortcuts import render |
重新测试一下
现在是404了
Django 有一个快捷方式去得到一个对象,或者返回一个不存在的对象 404。
因此让我们再来重写一下 board_topics 函数:
1 | from django.shortcuts import render, get_object_or_404 |
依然成功
boards/test.py
1 | class HomeTests(TestCase): |
现在我们同样在 HomeTests 中添加了 setUp 方法。这是因为我们现在需要一个 Board 实例,并且我们将 url 和 response 移到了 setUp,所以我们能在新测试中重用相同的 response。
这里的新测试是 test_home_view_contains_link_to_topics_page。我们使用 assertContains 方法来测试 response 主体部分是否包含给定的文本。我们在测试中使用的文本是 a 标签的 href 部分。所以基本上我们是在测试 response 主体是否包含文本 href="/boards/1/"。
现在进行测试是报错的,因为我们的html模版中还没有写相关的逻辑
接下来编写index.html,更改tbody标签的内容
1 | <!-- code suppressed for brevity --> |
始终使用 {% url %} 模板标签去写应用的 URL。第一个参数是 URL 的名字(定义在 URLconf, 即 urls.py),然后你可以根据需求传递任意数量的参数。
现在已经可以点击链接了。
可以先写测试
1 | def test_board_topics_view_contains_link_back_to_homepage(self): |
当然现在也是不行的,因为我们还没有创建相关的模版引擎
修改topics.html
1 | {% load static %} |
重新打开得到:
| 主键 自增字段 | |
|---|---|
| 正则表达式 | (?P<pk>\d+) |
| 举例 | url(r'^questions/(?P<pk>\d+)/$', views.question, name='question') |
| 有效 URL | /questions/934/ |
| 捕获数据 | {'pk': '934'} |
| Slug 字段 |
| ————- |:————-:|
| 正则表达式 | (?P<slug>[-\w]+) |
| 举例 | url(r'^posts/(?P<slug>[-\w]+)/$', views.post, name='post') |
| 有效 URL| /posts/hello-world/ |
|捕获数据|{'slug': 'hello-world'}|
| 有主键的 Slug 字段 |
| ————- |:————-:|
| 正则表达式 | (?P<slug>[-\w]+)-(?P<pk>\d+) |
| 举例 |url(r'^blog/(?P<slug>[-\w]+)-(?P<pk>\d+)/$', views.blog_post, name='blog_post') |
| 有效 URL|/blog/hello-world-159/ |
|捕获数据|{'slug': 'hello-world', 'pk': '159'}|
| Django 用户名 |
| ————- |:————-:|
| 正则表达式 | (?P<username>[\w.@+-]+) |
| 举例 |url(r'^profile/(?P<username>[\w.@+-]+)/$', views.user_profile, name='user_profile') |
| 有效 URL|/profile/vitorfs/ |
|捕获数据| {'username': 'vitorfs'}|
| Year |
| ————- |:————-:|
| 正则表达式 | (?P<year>[0-9]{4}) |
| 举例 | url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive, name='year') |
| 有效 URL| /articles/2016/ |
|捕获数据| {'year': '2016'}|
|Year / Month|
| ————- |:————-:|
| 正则表达式 | (?P<year>[0-9]{4})/(?P<month>[0-9]{2}) |
| 举例 | url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive, name='month')|
| 有效 URL| /articles/2016/01/ |
|捕获数据| {'year': '2016', 'month': '01'}|
在这一节我们将重写 HTML 模板,创建一个 **master page(母版页)**,其他模板添加它所独特的部分。
在 templates 文件夹中创建一个名为 layout.html 的文件:
1 | {% load static %} |
这是我们的母版页。每个我们创建的模板都 extend(继承) 这个特殊的模板。现在我们介绍 {% block %} 标签。它用于在模板中保留一个空间,一个”子”模板(继承这个母版页的模板)可以在这个空间中插入代码和 HTML。
在 {% block title %} 中我们还设置了一个默认值 “Django Boards.”。如果我们在子模板中未设置 {% block title %} 的值它就会被使用。
现在让我们重写我们的两个模板: index.html 和 topics.html。
1 | {% extends 'layout.html' %} |
home.html 的第一行是 {% extends 'base.html' %}。这个标签用来告诉 Django 使用 base.html 作为母版页。之后,我们使用 blocks 来放置这个页面独有的部分。
templates/topics.html
1 | {% extends 'layout.html' %} |
在 topics.html 中,我们改变了 {% block title %} 的默认值。注意我们可以通过调用 {{ block.super }} 来重用 block 的默认值。这里我们使用的网页标题是 base.html 中定义的 “Django Boards”。所以对于 “Python” 的 board 页面,这个标题是 “Python - Django Boards”,对于 “Random” board 标题会是 “Random - Django Boards”。
我们可以在顶部导航栏中添加一个nav-bar
layout.html
1 | {% load static %}<!DOCTYPE html> |
目的:实现以下表单
现在让我们抽象一些细节,重点了解一下怎么在数据库中保存用户的输入。
首先,先创建一个新的 URL 路由,命名为 new_topic:
myproject/urls.py
1 | from django.contrib import admin |
现在来创建 new_topic 的 视图函数:
boards/views.py
1 | def new_topic(req, pk): |