欢迎来到我们的Django教程的第二部分! 在上一课中,我们安装了所需的一切。 希望您安装了Python 3.6并在虚拟环境中运行Django 1.11。 我们已经创建了我们将要玩的项目。 在本课中,我们将继续在同一个项目中编写代码。
在下一节中,我们将讨论我们将要开发的项目,以便为您提供一些背景信息。 然后,您将学习所有Django基础知识:模型,管理员,视图,模板和URL。
动手!
Web Board项目
我不了解你,但就个人而言,我通过查看实际例子和代码片段了解更多。 对我来说,很难处理一个概念,在例子中你读到了Class A和Class B ,或者当我看到经典的foo(bar)例子时。 我不想和你这样做。
所以,在我们进入有趣的部分之前,玩模型,视图和一切。 让我们花一点时间,简要讨论一下我们将要开发的这个项目。
如果您已经具有Web开发经验并且感觉它太详细,那么您可以浏览图片以了解我们将要构建的内容,然后跳转到本教程的“ 模型”部分。
但如果您是Web开发的新手,我强烈建议您继续阅读。 它将为您提供有关Web应用程序建模和设计的一些很好的见解。 Web开发和软件开发通常不仅仅是编码。
用例图
我们的项目是一个讨论板(一个论坛)。 整个想法是维护几个板 ,其行为类似于类别。 然后,在特定的板内,用户可以通过创建新主题来开始新的讨论。 在本主题中,其他用户可以参与讨论发布回复。
我们需要找到一种方法来区分常规用户和管理员用户,因为只有管理员应该创建新的主板。 下面是我们主要用例的概述以及每种用户的角色:
图1:Web Board提供的核心功能的用例图
类图
从用例图中,我们可以开始考虑项目的实体 。 实体是我们将创建的模型,它与我们的Django应用程序将处理的数据密切相关。
为了能够实现上一节中描述的用例,我们需要至少实现以下模型: Board , Topic , Post和User 。
图2:Web Board类图的草稿
花时间思考模型如何相互关联也很重要。 实线告诉我们的是,在主题中 ,我们需要有一个字段来识别它所属的董事会 。 类似地, 帖子将需要一个字段来表示它所属的主题 ,以便我们可以在讨论中仅列出在特定主题中创建的帖子 。 最后,我们将需要在主题中的字段来了解谁开始讨论以及在帖子中,以便我们可以确定谁发布了回复。
我们还可以与董事会和用户模型建立关联,以便我们确定谁创建了一个给定的董事会 。 但是这些信息与应用程序无关。 还有其他方法可以跟踪此信息,稍后您将看到。
现在我们已经有了基本的类表示,我们必须考虑每个模型将携带哪种信息。 这种事情很容易变得复杂。 所以尽量把重点放在重点上。 开始开发所需的信息。 稍后,我们可以使用迁移来改进模型,您将在下一个教程中详细了解。
但就目前而言,这将是我们模型领域的基本表现:
图3:强调类(模型)之间关系的类图
该类图强调模型之间的关系。 这些线条和箭头最终会在以后翻译成字段。
对于Board模型,我们将从两个字段开始: name和description 。 名称字段必须是唯一的,以避免重复的电路板名称。 描述只是为了暗示董事会的全部内容。
主题模型将由四个字段组成: 主题 ,将用于定义主题排序的最后更新日期,用于标识启动主题的用户的 主题启动器 ,以及用于定义特定主题属于哪个主板的字段。
Post模型将有一个消息字段,用于存储帖子回复的文本, 创建日期和时间字段主要用于在主题中订购帖子 , 更新日期和时间字段以通知用户何时以及是否已编辑某个帖子 。 与日期和时间字段一样,我们还必须引用用户模型: 由其创建和更新 。
最后, 用户模型。 在类图中,我只提到了用户名 , 密码 , 电子邮件等字段,并且是超级用户标志,因为这几乎是我们现在要使用的全部内容。 重要的是要注意,我们不需要创建User模型,因为Django已经在contrib包中提供了内置的User模型。 我们打算用它。
关于类图中的多重性(数字0..*等),这里是你如何阅读它:
主题必须与一( 1 )个板相关联(这意味着它不能为空),并且Board可能与许多主题相关联或者没有( 0..* )。 这意味着董事会可能没有一个主题 。
主题应该至少有一个帖子 (起始帖子 ),它也可能有很多帖子 ( 1..* )。 帖子必须与一个帖子相关联,并且只能与一个主题 ( 1 )相关联。
主题必须有一个,并且只有一个用户与主题起始用户 ( 1 )相关联。 用户可能有很多或没有主题 ( 0..* )。
帖子必须有一个,只有一个用户关联: 由 ( 1 ) 创建 。 用户可能有很多帖子 ( 0..* )。 Post和User之间的第二个关联是直接关联 (参见行尾的箭头),这意味着我们只对用户编辑给定帖子的关系的一方感兴趣。 它将被翻译成更新的字段。 多重性表示0..1 ,意味着更新的字段可能为空( Post未编辑),并且最多只能与一个用户相关联。
绘制此类图的另一种方法是强调字段而不是模型之间的关系:
图4:强调类(模型)属性(字段)的类图上面的表示等同于前一个表示,它也更接近我们将使用Django Models API设计的内容。 在此表示中,我们可以更清楚地看到,在Post模型中,关联主题 由创建的模型字段创建并更新 。 另一个有趣的事情是在Topic模型中我们现在有一个名为posts()的操作 (类方法) 。 我们将通过实现反向关系来实现这一点,其中Django将自动在数据库中执行查询以返回属于特定主题的所有帖子的列表。
好吧,现在足够的UML了! 为了绘制本节中提供的图表,我使用了StarUML工具。
线框
在花了一些时间设计应用程序模型之后,我想创建一些线框来定义需要完成的工作,并清楚地了解我们的目标。
然后,基于线框,我们可以更深入地了解应用程序中涉及的实体。
首先,我们需要在主页上显示所有主板:
图5:Boards项目线框主页列出了所有可用的板。如果用户点击链接,例如在Django板中,它应该列出所有主题:
图6:Boards项目线框列出了Django板中的所有主题。这里我们有两条主要路径:用户点击“新主题”按钮创建新主题,或者用户点击主题以查看或参与讨论。
“新主题”屏幕:
图7:新主题屏幕现在是主题屏幕,显示帖子和讨论:
图8:主题帖子列表屏幕如果用户点击回复按钮,他们将看到下面的屏幕,其中的帖子摘要按相反的顺序排列(最新的):
图9:回复主题屏幕要绘制线框,您可以使用draw.io服务,它是免费的。
楷模
这些模型基本上代表了应用程序的数据库布局。 我们在本节中要做的是创建我们在上一节中建模的类的Django表示: Board , Topic和Post 。 用户模型已在名为auth的内置应用程序中定义,该应用程序在命名空间django.contrib.auth下的INSTALLED_APPS配置中列出。
我们将在boards / models.py文件中完成所有工作。 以下是我们表示类图的方法(参见图4 )。 在Django应用程序中:
from django.db import models from django.contrib.auth.models import User class Board ( models . Model ): name = models . CharField ( max_length = 30 , unique = True ) description = models . CharField ( max_length = 100 ) class Topic ( models . Model ): subject = models . CharField ( max_length = 255 ) last_updated = models . DateTimeField ( auto_now_add = True ) board = models . ForeignKey ( Board , related_name = 'topics' ) starter = models . ForeignKey ( User , related_name = 'topics' ) class Post ( models . Model ): message = models . TextField ( max_length = 4000 ) topic = models . ForeignKey ( Topic , related_name = 'posts' ) created_at = models . DateTimeField ( auto_now_add = True ) updated_at = models . DateTimeField ( null = True ) created_by = models . ForeignKey ( User , related_name = 'posts' ) updated_by = models . ForeignKey ( User , null = True , related_name = '+' )
所有模型都是django.db.models.Model类的子类。 每个类都将转换为数据库表 。 每个字段由django.db.models.Field子类(内置Django核心)的实例表示,并将被转换为数据库列 。
CharField , DateTimeField等字段都是django.db.models.Field的子类,它们包含在Django核心中 – 随时可以使用。
这里我们只使用CharField , TextField , DateTimeField和ForeignKey字段来定义我们的模型。 但Django提供了多种选项来表示不同类型的数据,例如IntegerField , BooleanField , DecimalField等等。 我们将根据需要参考它们。
某些字段需要参数,例如CharField 。 我们应该总是设置一个max_length 。 此信息将用于创建数据库列。 Django需要知道数据库列需要多大。 Django Forms API也将使用max_length参数来验证用户输入。 稍后会详细介绍。
在Board模型定义中,更具体地说,在name字段中,我们还设置参数unique=True ,顾名思义,它将在数据库级别强制执行字段的唯一性。
在Post模型中, created_at字段有一个可选参数, auto_now_add设置为True 。 这将指示Django设置创建Post对象的当前日期和时间。
在模型之间创建关系的一种方法是使用ForeignKey字段。 它将在模型之间创建链接,并在数据库级别创建适当的关系。 ForeignKey字段需要一个位置参数,其中包含与其相关的模型的引用。
例如,在Topic模型中, board字段是Board模型的ForeignKey 。 它告诉Django一个Topic实例只涉及一个Board实例。 related_name参数将用于创建反向关系 ,其中Board实例将访问属于它的Topic实例的列表。
Django自动创建这种反向关系 – related_name是可选的。 但是如果我们没有为它设置名称,Django将使用以下名称生成它: (class_name)_set 。 例如,在Board模型中, Topic实例将在topic_set属性下可用。 相反,我们只是将其重命名为topics ,以使其感觉更自然。
在Post模型中, updated_by字段设置related_name=’+’ 。 这指示Django我们不需要这种反向关系,所以它会忽略它。
下面你可以看到类图和源代码之间的比较,用Django生成模型。 绿线代表我们如何处理反向关系。
此时,您可能会问自己:“主键/ ID怎么样”? 如果我们没有为模型指定主键,Django将自动为我们生成它。 所以我们现在很好。 在下一节中,您将看到更好的工作方式。
迁移模型
下一步是告诉Django创建数据库,以便我们可以开始使用它。
打开 命令行工具 ,激活虚拟环境,转到manage.py文件所在的文件夹,然后运行以下命令:
python manage.py makemigrations
作为输出,你将得到这样的东西:
Migrations for 'boards' : boards/migrations/0001_initial.py - Create model Board - Create model Post - Create model Topic - Add field topic to post - Add field updated_by to post
此时,Django在boards / migrations目录中创建了一个名为0001_initial.py的文件。 它代表了我们应用程序模型的当前状态。 在下一步中,Django将使用此文件来创建表和列。
迁移文件被转换为SQL语句。 如果您熟悉SQL,则可以运行以下命令来检查将在数据库中执行的SQL指令:
python manage.py sqlmigrate boards 0001
如果您不熟悉SQL,请不要担心。 在本系列教程中,我们不会直接使用SQL。 所有的工作都只使用Django ORM完成,它是一个与数据库通信的抽象层。
现在的下一步是将我们生成的迁移应用于数据库:
python manage.py migrate
输出应该是这样的:
Operations to perform: Apply all migrations: admin, auth, boards, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying boards.0001_initial... OK Applying sessions.0001_initial... OK
因为这是我们第一次迁移数据库,所以migrate命令还应用了来自INSTALLED_APPS列出的Django contrib应用程序的现有迁移文件。 这是预料之中的。
Applying boards.0001_initial… OK是我们在上一步中生成的迁移。
而已! 我们的数据库已准备好使用。
注意:重要的是要注意SQLite是一个生产质量的数据库。 SQLite被许多公司用于数千种产品,如所有Android和iOS设备,所有主要的Web浏览器,Windows 10,macOS等。
它并不适合所有情况。 SQLite不与MySQL,PostgreSQL或Oracle等数据库进行比较。 高容量网站,写入密集型应用程序,非常大的数据集,高并发性,在某些情况下最终会通过使用SQLite导致问题。
我们将在项目开发期间使用SQLite,因为它很方便,我们不需要安装任何其他东西。 当我们将项目部署到生产环境时,我们将切换到PostgreSQL。 对于简单的网站,这工作正常。 但对于复杂的网站,建议使用相同的数据库进行开发和生产。
尝试使用Models API
使用Python开发的一大优势是交互式shell。 我用它所有的时间。 这是一种快速尝试和实验库和API的方法。
您可以使用manage.py实用程序加载我们的项目来启动Python shell:
python manage.py shell
Python 3.6.2 ( v3.6.2:5fd33b5, Jul 8 2017, 04:57:36 ) [ MSC v.1900 64 bit ( AMD64 )] on win32 Type "help" , "copyright" , "credits" or "license" for more information. ( InteractiveConsole ) >>>
这与通过键入python调用交互式控制台非常相似,除非我们使用python manage.py shell ,我们将我们的项目添加到sys.path并加载Django。 这意味着我们可以导入我们的模型和项目中的任何其他资源并使用它。
让我们从导入Board类开始:
from boards.models import Board
要创建新的板对象,我们可以执行以下操作:
board = Board ( name = 'Django' , description = 'This is a board about Django.' )
要在数据库中保留此对象,我们必须调用save方法:
board . save ()
save方法用于创建和更新对象。 这里Django创建了一个新对象,因为Board实例没有id 。 在第一次保存之后,Django将自动设置id:
board . id 1
您可以以Python属性访问其余字段:
board . name 'Django'
board . description 'This is a board about Django.'
要更新我们可以执行的值:
board . description = 'Django discussion board.' board . save ()
每个Django模型都带有一个特殊属性; 我们称之为模型经理 。 您可以通过Python属性objects访问它。 它主要用于在数据库中执行查询。 例如,我们可以使用它直接创建一个新的Board对象:
board = Board . objects . create ( name = 'Python' , description = 'General discussion about Python.' )
board . id 2
board . name 'Python'
所以,现在我们有两块板。 我们可以使用这些objects列出数据库中所有现有的板:
Board . objects . all () < QuerySet [ < Board : Board object > , < Board : Board object > ] >
结果是一个QuerySet 。 我们稍后会详细了解。 基本上,它是数据库中的对象列表。 我们可以看到我们有两个对象,但我们只能读取Board对象 。 那是因为我们没有在Board模型中定义__str__方法。
__str__方法是对象的String表示形式。 我们可以使用电路板名称来表示它。
首先,退出交互式控制台:
exit ()
现在编辑板应用程序内的models.py文件:
class Board ( models . Model ): name = models . CharField ( max_length = 30 , unique = True ) description = models . CharField ( max_length = 100 ) def __str__ ( self ): return self . name
我们再试一次。 再次打开交互式控制台:
python manage.py shell
from boards.models import Board Board . objects . all () < QuerySet [ < Board : Django > , < Board : Python > ] >
好多了,对吧?
我们可以将此QuerySet视为列表。 假设我们想迭代它并打印每个板的描述:
boards_list = Board . objects . all () for board in boards_list : print ( board . description )
结果将是:
Django discussion board. General discussion about Python.
同样,我们可以使用模型管理器查询数据库并返回单个对象。 为此,我们使用get方法:
django_board = Board . objects . get ( id = 1 ) django_board . name 'Django'
但是我们必须小心这种操作。 如果我们尝试获取一个不存在的对象,例如id=3的板,它将引发异常:
board = Board . objects . get ( id = 3 ) boards . models . DoesNotExist : Board matching query does not exist .
我们可以将get方法用于任何模型字段,但最好使用可以唯一标识对象的字段。 否则,查询可能会返回多个对象,这将导致异常。
Board . objects . get ( name = 'Django' ) < Board : Django >
请注意,查询区分大小写 ,小写“django”不匹配:
Board . objects . get ( name = 'django' ) boards . models . DoesNotExist : Board matching query does not exist .
模型运作总结
下面是使用Board模型作为参考,在本节中学习的方法和操作的摘要。 大写板指的是类,小写板指的是Board模型类的实例(或对象):
手术 | 代码示例 |
---|---|
创建一个不保存的对象 | board = Board() |
保存对象(创建或更新) | board.save() |
在数据库中创建并保存对象 | Board.objects.create(name=’…’, description=’…’) |
列出所有对象 | Board.objects.all() |
获取由字段标识的单个对象 | Board.objects.get(id=1) |
在下一节中,我们将开始编写视图并在HTML页面中显示我们的板。
视图,模板和静态文件
目前我们已经有了一个名为home的视图,在我们的应用程序主页上显示“Hello,World!”。
MyProject的/ urls.py
from django.conf.urls import url from django.contrib import admin from boards import views urlpatterns = [ url ( r'^$' , views . home , name = 'home' ), url ( r'^admin/' , admin . site . urls ), ]
板/ views.py
from django.http import HttpResponse def home ( request ): return HttpResponse ( 'Hello, World!' )
我们可以用它作为起点。 如果你想起我们的线框, 图5显示了主页应该是什么样子。 我们想要做的是在表格中显示一个董事会列表以及一些其他信息。
首先要做的是导入Board模型并列出所有现有的板:
板/ views.py
from django.http import HttpResponse from .models import Board def home ( request ): boards = Board . objects . all () boards_names = list () for board in boards : boards_names . append ( board . name ) response_html = '<br>' . join ( boards_names ) return HttpResponse ( response_html )
结果将是这个简单的HTML页面:
但是我们就在这里停下来。 我们不会像这样渲染HTML。 对于这个简单的视图,我们所需要的只是一个电路板列表; 然后渲染部分是Django模板引擎的工作 。
Django模板引擎设置
创建一个名为templates的新文件夹以及board和mysite文件夹:
myproject/ |-- myproject/ | |-- boards/ | |-- myproject/ | |-- templates/ <-- here! | +-- manage.py +-- venv/
现在在templates文件夹中,创建一个名为home.html的HTML文件:
模板/ home.html做为
<!DOCTYPE html> <html> <head> <meta charset= "utf-8" > <title> Boards </title> </head> <body> <h1> Boards </h1> {% for board in boards %} {{ board.name }} <br> {% endfor %} </body> </html>
在上面的例子中,我们将原始HTML与一些特殊标签混合{ % for … in … % } { % for … in … % } { % for … in … % }和{ { variable } } { { variable } } { { variable } } 。 它们是Django模板语言的一部分。 上面的示例显示了如何使用for迭代对象列表。 { { board.name } } { { board.name } } 在HTML模板中呈现电路板的名称,生成动态HTML文档。
在我们使用这个HTML页面之前,我们必须告诉Django在哪里可以找到我们的应用程序模板。
打开myproject目录中的settings.py并搜索TEMPLATES变量并将DIRS键设置为os.path.join(BASE_DIR, ‘templates’) :
TEMPLATES = [ { 'BACKEND' : 'django.template.backends.django.DjangoTemplates' , 'DIRS' : [ os . path . join ( BASE_DIR , 'templates' ) ], 'APP_DIRS' : True , 'OPTIONS' : { 'context_processors' : [ 'django.template.context_processors.debug' , 'django.template.context_processors.request' , 'django.contrib.auth.context_processors.auth' , 'django.contrib.messages.context_processors.messages' , ], }, }, ]
基本上这条线正在做的是找到项目目录的完整路径并向其添加“/ templates”。
我们可以使用Python shell调试它:
python manage.py shell
from django.conf import settings settings . BASE_DIR '/Users/vitorfs/Development/myproject' import os os . path . join ( settings . BASE_DIR , 'templates' ) '/Users/vitorfs/Development/myproject/templates'
看到? 它只是指向我们在前面的步骤中创建的模板文件夹。
现在我们可以更新我们的主页视图:
板/ views.py
from django.shortcuts import render from .models import Board def home ( request ): boards = Board . objects . all () return render ( request , 'home.html' , { 'boards' : boards })
生成的HTML:
我们可以改进HTML模板以使用表格:
模板/ home.html做为
<!DOCTYPE html> <html> <head> <meta charset= "utf-8" > <title> Boards </title> </head> <body> <h1> Boards </h1> <table border= "1" > <thead> <tr> <th> Board </th> <th> Posts </th> <th> Topics </th> <th> Last Post </th> </tr> </thead> <tbody> {% for board in boards %} <tr> <td> {{ board.name }} <br> <small style= "color: #888" > {{ board.description }} </small> </td> <td> 0 </td> <td> 0 </td> <td></td> </tr> {% endfor %} </tbody> </table> </body> </html>
测试主页
这将是一个经常性的主题,我们将在整个教程系列中共同探讨不同的概念和策略。
让我们来写第一个测试。 现在,我们将在board app中的tests.py文件中工作:
板/ tests.py
from django.core.urlresolvers import reverse from django.test import TestCase class HomeTests ( TestCase ): def test_home_view_status_code ( self ): url = reverse ( 'home' ) response = self . client . get ( url ) self . assertEquals ( response . status_code , 200 )
这是一个非常简单的测试用例,但非常有用。 我们正在测试响应的状态代码 。 状态代码200表示成功 。
我们可以在控制台中检查响应的状态代码:
如果有未捕获的异常,语法错误或其他任何内容,Django将返回状态代码500 ,这意味着内部服务器错误 。 现在,假设我们的应用程序有100个视图。 如果我们为所有视图编写这个简单的测试,只需一个命令,我们就可以测试所有视图是否返回成功代码,因此用户在任何地方都看不到任何错误消息。 没有自动化测试,我们需要逐个检查每个页面。
要执行Django的测试套件:
python manage.py test
Creating test database for alias 'default'... System check identified no issues (0 silenced). . ---------------------------------------------------------------------- Ran 1 test in 0.041s OK Destroying test database for alias 'default'...
现在我们可以测试Django是否为请求的URL返回了正确的视图函数。 这也是一个有用的测试,因为随着开发的进展,你会看到urls.py模块变得非常庞大和复杂。 URL conf就是解析正则表达式。 在某些情况下,我们有一个非常宽松的URL,因此Django最终可能会返回错误的视图函数。
我们是这样做的:
板/ tests.py
from django.core.urlresolvers import reverse from django.urls import resolve from django.test import TestCase from .views import home class HomeTests ( TestCase ): def test_home_view_status_code ( self ): url = reverse ( 'home' ) response = self . client . get ( url ) self . assertEquals ( response . status_code , 200 ) def test_home_url_resolves_home_view ( self ): view = resolve ( '/' ) self . assertEquals ( view . func , home )
在第二个测试中,我们正在使用resolve函数。 Django使用它来将请求的URL与urls.py模块中列出的URL列表进行匹配。 此测试将确保URL / (根URL)返回主视图。
再试一次:
python manage.py test
Creating test database for alias 'default'... System check identified no issues (0 silenced). .. ---------------------------------------------------------------------- Ran 2 tests in 0.027s OK Destroying test database for alias 'default'...
要查看有关测试执行的更多详细信息,请将详细程度设置为更高级别:
python manage.py test --verbosity = 2
Creating test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')... Operations to perform: Synchronize unmigrated apps: messages, staticfiles Apply all migrations: admin, auth, boards, contenttypes, sessions Synchronizing apps without migrations: Creating tables... Running deferred SQL... Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying boards.0001_initial... OK Applying sessions.0001_initial... OK System check identified no issues (0 silenced). test_home_url_resolves_home_view (boards.tests.HomeTests) ... ok test_home_view_status_code (boards.tests.HomeTests) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.017s OK Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...
详细程度决定了将打印到控制台的通知和调试信息的数量; 0表示无输出,1表示正常输出,2表示详细输出。
静态文件设置
静态文件是CSS,JavaScripts,Fonts,Images或我们可用于组成用户界面的任何其他资源。
实际上,Django不提供这些文件。 除了在开发过程中,所以让我们的生活更轻松。 但是Django提供了一些功能来帮助我们管理静态文件。 这些功能在INSTALLED_APPS配置中已列出的django.contrib.staticfiles应用程序中提供。
有这么多的前端组件库可供使用,我们没有理由继续渲染基本的HTML文档。 我们可以轻松地将Bootstrap 4添加到我们的项目中。 Bootstrap是一个开源工具包,用于使用HTML,CSS和JavaScript进行开发。
在项目根目录中,与board , templates和myproject文件夹一起,创建一个名为static的新文件夹,并在static文件夹中创建另一个名为css的文件夹:
myproject/ |-- myproject/ | |-- boards/ | |-- myproject/ | |-- templates/ | |-- static/ <-- here | | +-- css/ <-- and here | +-- manage.py +-- venv/
转到getbootstrap.com并下载最新版本:
下载Compiled CSS和JS版本。
在您的计算机中,提取从Bootstrap网站下载的bootstrap-4.0.0-beta-dist.zip文件,将文件css / bootstrap.min.css复制到我们项目的css文件夹中:
myproject/ |-- myproject/ | |-- boards/ | |-- myproject/ | |-- templates/ | |-- static/ | | +-- css/ | | +-- bootstrap.min.css <-- here | +-- manage.py +-- venv/
下一步是指示Django在哪里找到静态文件。 打开settings.py ,滚动到文件的底部,然后在STATIC_URL之后添加以下内容:
STATIC_URL = '/static/' STATICFILES_DIRS = [ os . path . join ( BASE_DIR , 'static' ), ]
和TEMPLATES目录一样,还记得吗?
现在我们必须在模板中加载静态文件(Bootstrap CSS文件):
模板/ home.html做为
{% load static %}<!DOCTYPE html> <html> <head> <meta charset= "utf-8" > <title> Boards </title> <link rel= "stylesheet" href= " {% static 'css/bootstrap.min.css' %} " > </head> <body> <!-- body suppressed for brevity ... --> </body> </html>
首先,我们使用{ % load static % }加载静态文件应用程序模板标记 { % load static % } { % load static % }在模板的开头。
模板标签{ % static % } { % static % } { % static % }用于组成资源所在的URL。 在这种情况下, { % static ‘css/bootstrap.min.css’ % } { % static ‘css/bootstrap.min.css’ % } { % static ‘css/bootstrap.min.css’ % }将返回/static/css/bootstrap.min.css ,相当于http://127.0.0.1:8000/static/css/bootstrap.min.css 。
{ % static % } { % static % } { % static % }模板标记使用STATIC_URL中的STATIC_URL配置来组成最终的URL。 例如,如果您将静态文件托管在https://static.example.com/这样的子域中,我们会将STATIC_URL=https://static.example.com/设置为{ % static ‘css/bootstrap.min.css’ % } { % static ‘css/bootstrap.min.css’ % } { % static ‘css/bootstrap.min.css’ % }将返回https://static.example.com/css/bootstrap.min.css 。
如果这对你来说没有任何意义,请不要担心。只需记住在需要时使用CSS,JavaScript或图像文件。稍后,当我们开始使用Deployment时,我们将讨论更多内容。现在,我们都已成立。{ % static % }
刷新页面127.0.0.1:8000我们可以看到它工作:
现在我们可以编辑模板,以便利用Bootstrap CSS:
{% load static %}<!DOCTYPE html> <html> <head> <meta charset= "utf-8" > <title> Boards </title> <link rel= "stylesheet" href= " {% static 'css/bootstrap.min.css' %} " > </head> <body> <div class= "container" > <ol class= "breadcrumb my-4" > <li class= "breadcrumb-item active" > Boards </li> </ol> <table class= "table" > <thead class= "thead-inverse" > <tr> <th> Board </th> <th> Posts </th> <th> Topics </th> <th> Last Post </th> </tr> </thead> <tbody> {% for board in boards %} <tr> <td> {{ board.name }} <small class= "text-muted d-block" > {{ board.description }} </small> </td> <td class= "align-middle" > 0 </td> <td class= "align-middle" > 0 </td> <td></td> </tr> {% endfor %} </tbody> </table> </div> </body> </html>
结果现在:
到目前为止,我们正在使用交互式控制台(python manage.py shell)添加新的主板。但我们需要一种更好的方法来做到这一点。在下一节中,我们将为网站管理员实施管理界面来管理它。
Django Admin简介
当我们开始一个新项目时,Django已经配置了在中列出的Django AdminINSTALLED_APPS。
Django Admin的一个很好的用例就是在博客中; 作者可以使用它来撰写和发表文章。另一个例子是电子商务网站,工作人员可以在其中创建,编辑和删除产品。
目前,我们将配置Django Admin来维护我们的应用程序板。
让我们从创建管理员帐户开始:
python manage.py createsuperuser
按照说明操作:
Username (leave blank to use 'vitorfs'): admin Email address: admin@example.com Password: Password (again): Superuser created successfully.
现在在Web浏览器中打开URL:http://127.0.0.1:8000 / admin /
输入用户名和密码以登录管理界面:
它已经配置了一些功能。在这里,我们可以添加用户和组来管理权限。我们稍后将探讨更多这些概念。
添加Board模型非常简单。打开boards目录中的admin.py文件,并添加以下代码:
板/ admin.py
from django.contrib import admin from .models import Board admin.site.register(Board)
保存admin.py文件,然后在Web浏览器上刷新页面:
就是这样!它已准备好使用。单击Boards链接以查看现有板的列表:
我们可以通过单击Add Board按钮添加新板:
单击“ 保存”按钮:
我们可以检查一切是否正常,打开http://127.0.0.1:8000网址:
结论
在本教程中,我们探索了许多新概念。我们为项目定义了一些需求,创建了第一个模型,迁移了数据库,开始使用Models API。我们创建了第一个视图并编写了一些单元测试。我们还配置了Django模板引擎,静态文件,并将Bootstrap 4库添加到项目中。最后,我们简要介绍了Django Admin界面。
我希望你喜欢本教程系列的第二部分!第三部分将于2017年9月18日下周发布。在下一部分中,我们将探讨Django的URL路由,表单API,可重用模板以及更多测试。如果您希望在第三部分出来时收到通知,您可以订阅我们的邮件列表。
该项目的源代码可在GitHub上获得。可以在发布标记v0.2-lw下找到项目的当前状态。以下链接将带您到正确的位置:
https://github.com/sibtc/django-beginners-guide/tree/v0.2-lw