一个完整的Django入门指南 - 第2部分:基本原理

第1部分:http://mp.weixin.qq.com/s/EsZSAo7tiY3gktWBPrB_zA
原文地址:https://simpleisbetterthancomplex.com/series/2017/09/11/a-complete-beginners-guide-to-django-part-2.html


这一部分的教程介绍的是模型、视图、模板、测试和管理

介绍

欢迎来到我们的Django教程的第二部分!在上一部分中,我们安装了所有我们需要的东西。希望您已经安装了Python 3.6,并在虚拟环境中运行Django 1.11。我们已经创建了一个我们要玩的项目。在这部分中,我们将继续在同一个项目中编写代码。


网页版块

我不知道你的情况,但我个人认为,通过看到实际的例子和代码片段,我能学到更多东西。对于我来说,在例子中你读类A和类B,或者当我看到经典类foo(bar)例子时来处理一个概念是很难的。我不想和你一起这样做。
所以, 在我们进入有趣的部分,玩模型,视图和一切之前。让我们花店时间,简要的讨论一下我们将要开发的这个项目。
如果您已经有了Web开发的经验,并且觉得它太过详细,您可以浏览一下图片,了解我们将要构建的内容,然后跳到本教程的模型部分。
但是如果你是网络开发的新手,我强烈建议你继续阅读。它将使您对Web应用程序的建模和设计有一些很好的了解。总的来说,Web开发和软件开发不仅仅是关于编码的。

一个完整的Django入门指南 - 第2部分:基本原理_第1张图片

  • 我知道我们要建造的不是火箭科学。
  • 但是即使是简单的过程也需要一个适当的计划。
  • 事实上,缺乏规划是许多软件项目失败的主要原因之一。
用例图

我们的项目是一个讨论版块(一个论坛)。整个想法要保持几个版块,就像类别一样。然后,在一个特定的版块中,用户可以通过创建一个新的主题来开始新的讨论。在这个主题中, 其他用户可以参与讨论发布回复。

我们需要找到一种方法来区分普通用户和管理用户,因为只有管理员才能创建新的版块。下面,概述我们的主要用例和每种类型的角色用户。

一个完整的Django入门指南 - 第2部分:基本原理_第2张图片

类图

从用例图中,我们可以开始考虑项目的实体。实体是我们要创建的模型,它与Django应用程序处理的数据密切相关。

为了使我们能够实现上一节中描述的用例,我们需要至少实现以下模型:版块(Board)、主题(Topic)、帖子(Post)、用户(User

一个完整的Django入门指南 - 第2部分:基本原理_第3张图片

同样重要的是,花点时间去思考模型是如何相互关联的。实践告诉我们的是,在一个主题(Topic)中,我们需要有一个字段来确定它是属于哪个版块(Board)的。类似的,帖子(Post)需要一个字段来表示它所属的主题(Topic),这样我们就可以在讨论中只列出特定主题(Topic)中创建的帖子(Post)。最后,我们在主题(Topic)和帖子(Post)中都需要字段来了解谁开始了讨论,这样我们就可以确定谁在发布回复。

我们还可以让版块(Board)和用户(User)模型建立联系,这样我们就可以确定谁创建了一个版块(Board)。但是这个信息与应用程序无关。还有其他方法可以追踪这些信息,稍后你将看到。

既然我们已经有了基本的类表示,我们就必须考虑这些模型中的每一种信息。这种事情很容易变的复杂。所以,试着把重点放在重要的部分上。你需要启动开发信息。稍后,我们可以使用迁移来改进模型,你将在下一篇教程中详细的看到这一点。

但就目前而言,这将是我们模型字段的基本表现:

一个完整的Django入门指南 - 第2部分:基本原理_第4张图片

这个类图强调了模型之间的关系。这些行和箭头最终将被转换为以后的字段。

对于版块(Board)模型,我们将从两个字段开始:namedescriptionname 字段必须是唯一的,这样才能避免重复的名称。description 只是为了给大家一个提示,说明版块(Board)的一切。

主题(Topic)模型将由四个字段组成:subjectlast update用于定义主题排序,topic starter用于定义启动主题(Topic)的用户(User),以及一个名为board的字段,用来定义一个特定的主题属于哪个版块(Board)。

帖子(Post)模型有一个message字段,用于存储回复的文本;一个created at日期时间字段,用于主题(Topic)中帖子(Post)的排序;一个updated at日期时间字段,来通知用户何时以及是否编辑了新的帖子。我们还必须用同样的日期时间字段关联用户(User)模型:created byupdated by

最后,用户(User)模型。在类图中,我只提到了usernamepasswordemailis superuser标志字段,因为这是我们现在要使用的全部内容。需要注意的是,我们不需要创建一个用户(User)模型,因为Django已经在设计包中内置了一个内置的用户模型。我们要使用它。

关于类图中的多样性(数字1,0 .. *。等等),以下是说明:

一个完整的Django入门指南 - 第2部分:基本原理_第5张图片
一个主题(Topic)必须与一个(1)版块(Board)相关联(这意味着它不能为空),并且一个版块(Board)可能与许多主题(Topic)相关联(0 .. *。)。这意味着一个版块(Board)可能没有任何一个主题(Topic)。

一个完整的Django入门指南 - 第2部分:基本原理_第6张图片
一个主题(Topic)需要至少一个帖子(Post)(发起帖子),并且可能有许多帖子(Post)(1 .. *)。一个帖子(Post)必须有且只有一个主题(Topic)关联。

一个完整的Django入门指南 - 第2部分:基本原理_第7张图片
一个主题(Topic)必须有且只有一个用户(User)关联:主题发起者(1)。一个用户(User)可能有许多或者没有主题(Topic)(0 .. *)。

一个完整的Django入门指南 - 第2部分:基本原理_第8张图片
一个帖子(Post)有且只有一个用户(User)关联:create by(1)。一个用户(User)可能有许多或者没有帖子(Post)(0 .. *)。帖子(Post)和用户(User)之间的第二个关联是一个直接关联(参见行末尾的箭头),这意味着我们只对关系的一端感兴趣,这是用户编辑帖子的内容。它将被翻译成updated by字段。多重数表示0..1,意思是更新的字段可能是空的(这个帖子(Post)没有编辑),最多只能与一个用户(User)关联。

绘制这个类图的另一种方法是强调字段而不是模型之间的关系:
一个完整的Django入门指南 - 第2部分:基本原理_第9张图片

上面的表示与前面的相同,而且它也更接近我们将要使用Django模型API设计的内容。在这个表示中,我们可以清楚的看到,在帖子(Post)模型中,关联字段topiccreated byupdated by变成了模型字段。另一件值得关注的事情是,在主题(Topic)模型中,我们有一个名为posts()的操作方法(类方法)。我将通过实现反向关系来实现这一点,Django将在数据库中自动执行查询,返回属于某个特定主题的所有帖子的列表。

线框图

在花了一些时间来设计应用程序模型之后,我喜欢创建一些线框图来定义需要完成的工作,以及对我们将要进行的工作有一个清晰的了解。

一个完整的Django入门指南 - 第2部分:基本原理_第10张图片

  • 有时,如果需求不清楚…
  • …或者如果实体之间的关系不明显
  • 我们可以先画线框

然后基于线框图,我们可以对应用程序中设计的实体有更深的理解。

首先,我们需要在主页上显示所有的版块:
一个完整的Django入门指南 - 第2部分:基本原理_第11张图片

如果用户点击一个链接,比如Django版,它应该列出所有的主题:
一个完整的Django入门指南 - 第2部分:基本原理_第12张图片

这里我们有两条主要路径:用户点击“New topic”按钮来创建一个新主题,或者用户点击某个主题来查看或参与讨论。

“New topic”界面:
一个完整的Django入门指南 - 第2部分:基本原理_第13张图片

主题界面,显示文章和讨论:
一个完整的Django入门指南 - 第2部分:基本原理_第14张图片

如果用户点击了Reply按钮,他们将看到以下界面,帖子摘要的反向顺序陈列(最新的第一个):
一个完整的Django入门指南 - 第2部分:基本原理_第15张图片


模型

这些模型基本上是应用程序的数据库布局的表示。在这一节中,我们要做的是创建我们在上一节中建模的类的Django表示:Board、Topic和Post。User模型已经在一个名为auth的内置应用程序中定义,该应用程序在名为django.contrib.auth的名称空间中列出。

我们将在 boards/models.py 文件中完成所有的工作。下面是我们如何在Django应用程序中表示类图:

from django.db import models
from django.contrib.auth.models import User

# 在翻译这篇文章的时候,使用的是python 3.6.4, Django 2.0.2
# ForeignKey中有一个on_delete参数

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, models.CASCADE, related_name='topics')
    starter = models.ForeignKey(User, models.CASCADE, related_name='topics')

class Post(models.Model):
    message = models.TextField(max_length=4000)
    topic = models.ForeignKey(Topic, models.CASCADE, related_name='posts')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(null=True)
    created_by = models.ForeignKey(User, models.CASCADE, related_name='posts')
    updated_by = models.ForeignKey(User, models.CASCADE, null=True, related_name='+')

所有的模型都是django.db.models.Model的子类。每个类将被转换为数据库表。每格字段都是由django.db.models.Field(内置的Django核心)实例表示,并将被转换为数据库列。

CharFieldDateTimeField等字段都是django.db.models.Field的子类,他们包含在Django核心中准备使用。

我们只使用了CharFieldTextFieldDateTimeFieldForeignKey字段来定义我们的模型。但是Django还提供了各种各样的选项来表示不同类型的数据,比如IntegerFieldBooleanFieldDecimalField。我们将根据需要使用他们。

有些字段需要参数,比如CharField。我们应该始终设置一个max_length。此信息将用于创建数据库列。Django需要知道数据库列需要有多大。max_length参数也将被Django Forms API用于验证用户输入。稍后将进行更详细的讨论。

Board模型定义中,具体来说是在name字段中,我们还设置了参数unique=True,顾名思义,它将强制在数据库级执行字段的唯一性。

Post模型中,created_at字段有一个可选参数,auto_now_add设置为True。这将指示Django在创建Post对象时设置当前日期和时间。

创建模型之间的关系的一种方法是使用ForeignKey字段。它将在模型之间创建一个链接,并在数据库级别创建适当的关系。ForeignKey字段的位置参数需要一个它将关联的模型。

例如,在Topic模型中,board字段对Board模型来说是一个外键。它将告诉Django一个Topic实例只涉及一个Board实例。related_name参数将用于创建反向关系,在该关系中,Board实例将访问属于它的Topic实例的列表。

Django自动创建了这种反向关系——相关的名称是可选的。但是,如果我们不为它设置一个名称,Django将使用:(class_name)_set。例如,在Borad模型中,Topic实例将使用topic_set。相反,我们只是将其重新命名为topics,使其更自然。

Post模型中,updated_by字段设置了related_name='+'。这告诉Django我们不需要这种反向关系,所以它会忽略它。

下面你可以看到类图和源代码之间的比较,用Django生成模型。绿线代表了我们处理逆向关系的方式。
一个完整的Django入门指南 - 第2部分:基本原理_第16张图片

此时,你可能会想:“主键/IDs怎么样?”如果我们不指定一个模型的主键,Django会自动为我们生成它。所以现在我们很好。在下一节中,你将看到它是如何工作的。

迁移模型

下一步是告诉Django创建数据库,这样我们就可以开始使用它了。

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 upodated_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开发应用程序现有的迁移文件。这是预期。

boards.0001_initial...OK是我们在前一步中生成的迁移。

一个完整的Django入门指南 - 第2部分:基本原理_第17张图片

  • “但是……什么数据库?”你可能会问自己
  • python附带SQLite — 一个嵌入式的数据库引擎
  • 它不适合生产。但是我们可以在开发过程中使用它

注意:需要注意的是,SQLite是一个生产质量的数据库。SQLite被许多公司使用,比如所有的Android和iOS设备,所有主要的网络浏览器,Windows 10,macOS等。
它不适合所有的情况。SQLite不与MySQL、PostgreSQL或Oracle等数据库进行比较。大容量的网站、写密集型应用程序、非常大的数据集、高并发性,这些都是使用SQLite最终会导致问题的一些情况。
在项目开发过程中,我们将使用SQLite,因为它很方便,我们不需要安装其他任何东西。当我们将项目部署到生产环境时,我们将切换到PostgreSQL。对于简单的网站来说,这样就很好。但是对于复杂的网站,建议使用相同的数据库进行开发和生产。

对模型API进行试验

使用Python开发的最大优点之一是交互式shell。我一直在用它。这是一种快速尝试的方法,可以尝试库和API。

你可以使用manage.py工具来加载我们的项目用以启动Python shell。

python manage.py shell

这非常类似于通过输入python来调用交互式控制台,除非我们使用python manage.py shell,我们将项目添加到sys.path并且加载Django。这意味着我们可以导入我们的模型和项目中的任何其他资源并使用它。

让我们开始导入Board类吧:

from boards.models import Board

创建一个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.descritption = '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'

现在,我没有两个board。我们可以使用objects来列出数据库中的所有现有的board:

Board.objects.all()
object (1)>, object (2)>]>

结果是一个QuerySet。我们以后会学到更多。基本上,它是来自数据库的对象列表。我们可以看到,我们有两个对象,但我们只能读取Board object。这是因为我们还没有在Board模型中定义__str__方法。

__str__方法是一个对象的字符串表示。我们可以用版块名字来表示它。

首先,退出交互式控制台:

exit()

models.py文件的boardsapp中编辑:

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.object.all()
, ]>

更好了,是吗?

我们可以把这个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')

注意,查询是大小写敏感的,小写的“django”不匹配:

Board.objects.get(name='django')
boards.models.DoesNotExist: Board matching query does not exist.
总结模型操作

以下是我们在本节中学习的方法和操作的总结,使用Board模型作为参考。大写的Board指的是类,小写的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),
]

boards/views.py

from django.http import HttpResponse

def home(request):
    return HtpResponse('Hello, World!')

我们可以用这个作为起始点。如果你还记得我们的线框图,它显示了主页应该是怎样的。我们要做的是在一个表中显示一个列表,并提供一些其他信息。

首先要做的是导入Board模型,列出所有现有的版块:

boards/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 = '
'
.join(boards_names) return HttpResponse(response_html)

结果是一个简单的HTML页面:
一个完整的Django入门指南 - 第2部分:基本原理_第18张图片

让我们在这里停一下。我们不会像这样渲染HTML。对于这个简单的视图,我们需要的只是一个版块列表,然后呈现部分是Django模板引擎的工作。

Django模板引擎设置

在boards和myproject文件夹旁边,创建一个名为templates的新文件夹

myproject/
 |-- myproject/
 |    |-- boards/
 |    |-- myproject/
 |    |-- templates/   <-- here!
 |    +-- manage.py
 +-- venv/

templates文件夹中,创建一个名为home.html的HTML文件:

templates/home.html


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Boardstitle>
head>
<body>
    <h1>Boardsh1>

    {% for board in boards %}
        {{ board.name }} <br>
    {% endfor %}

body>
html>

在上面的例子中,我们将原始HTML和一些特殊的标签{% for ... in ... %}{{ variable }}混合在一起。它们是Django模板语言的一部分。上面的例子展示了如何使用for循环遍历对象列表。在HTML模板中,{{ board.name }}显示了版块的名称,生成了动态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'

看到了吗?它只是指向我们在前面步骤中创建的templates文件夹。

现在,我们可以更新我们的home视图:

boards/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结果:
一个完整的Django入门指南 - 第2部分:基本原理_第19张图片

我们可以使用表格来改进HTML模板:

templates/home.html


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Boardstitle>
head>
<body>
    <h1>Boardsh1>

    <table border="1">
        <thead>
        <tr>
            <th>Boardth>
            <th>Poststh>
            <th>Topicsth>
            <th>Last Postth>
        tr>
        thead>
        <tbody>
        {% for board in boards %}
        <tr>
            <td>
                {{ board.name }}<br>
                <small style="color: #888">{{ board.description }}small>
            td>
            <td>0td>
            <td>0td>
            <td>td>
        tr>
        {% endfor %}
        tbody>
    table>

body>
html>

一个完整的Django入门指南 - 第2部分:基本原理_第20张图片

测试首页

一个完整的Django入门指南 - 第2部分:基本原理_第21张图片

  • 测试是web开发的一个基本部分。
  • 不管项目有多简单或多复杂,或者开发人员多老练。
  • 每个项目都应该进行自动化测试。它将帮助我们提高自信并提供更高质量的产品。

这将是一个反复出现的课题,我们将在整个教程系列中探索不同的概念和策略。

让我们写第一个测试。目前,我们将在board应用程序的tests.py文件中工作:

boards/tests.py

from django.core.urlresolvers import reverse
# django2.x版本使用以下导入
# from django.urls 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入门指南 - 第2部分:基本原理_第22张图片

如果有一个未捕获的异常、语法错误或其他任何东西,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返回了正确的视图函数。这也是一个有用的测试,因为当我们在开发过程中取得进展时,您将看到url.py模块可以变得非常大和复杂。URL conf全都是解析正则表达式。在某些情况下,我们有一个非常宽松的URL,因此Django可以返回错误的视图函数。

我们是这样做的:

boards/tests.py

# Django2.x 版本这样导入
from django.urls import reverse, 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 = reverse('/')
        self.assertEquals(view.func, home)

在第二个测试中,我们使用了resolve函数。Django用它来将urls.py模块中的URL列表和请求的URL相匹配。这个测试将确保URL\,即根URL,返回home视图。

再一次测试:

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')...

Verbosity决定将打印到控制台的通知和调试信息的数量;0不是输出,1是正常输出,2是冗长输出。

静态文件设置

静态文件是CSS、javascript、字体、图像,或者我们可以用来组成用户界面的任何其他资源。

实际上,Django并不提供这些文件。除了在开发过程中,让我们的生活更轻松。但是Django提供了一些特性来帮助我们管理静态文件。这些特性在已经在INSTALLED_APPS配置中列出的django.contrib.staticfiles 应用程序中可用。

有了这么多的前端组件库,我们没有理由继续呈现基本的HTML文档。我们可以很容易地在我们的项目中添加Bootstrap 4。Bootstrap是一个用于开发HTML、CSS和JavaScript的开源工具包。

在项目根目录下,连boards、templates和myproject文件夹一起,创建一个名为static的新文件夹,在static文件夹中创建另一个命名为css的文件夹:

myproject/
 |-- myproject/
 |    |-- boards/
 |    |-- myproject/
 |    |-- templates/
 |    |-- static/       <-- here
 |    |    +-- css/     <-- and here
 |    +-- manage.py
 +-- venv/

到 getbootstrap.com,下载最新版本:
一个完整的Django入门指南 - 第2部分:基本原理_第23张图片

下载Compiled CSS and JS。

在你的计算机中,解压你从网站中下载的压缩包,复制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目录相同,记得吗?

现在,我们必须在模板中加载静态文件(引导CSS文件):

templates/home.html

{% load static %}
<html>
  <head>
    <meta charset="utf-8">
    <title>Boardstitle>
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
  head>
  <body>
    
  body>
html>

首先,我们使用模板开头的{% load static %}来加载静态文件。

模板标记{% static %}用于构成资源所在的URL。在这种情况下,{% static 'css/bootstrap.min.css' %}将返回/static/css/bootstrap.min.css,相当于http://127.0.0.1:8000static/css/bootstrap.min.css

{% static %}模板标记使用settings.pySTATIC_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 %}。稍后,当我们开始使用部署时,我们将讨论更多。就目前而言,我们都建立起来了。

刷新页面127.0.0.1:8000,我们可以看到它起作用了:
一个完整的Django入门指南 - 第2部分:基本原理_第24张图片

现在,我们可以编辑模板,以便利用Bootstrap CSS:

{% load static %}
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Boardstitle>
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
head>
<body>
    <div class="container">
        <ol class="breadcrumb my-4">
            <li class="breadcrumb-item active">Boardsli>
        ol>

        <table class="table">
            <thead class="thead-inverse">
            
            
            <tr>
                <th>Boardth>
                <th>Poststh>
                <th>Topicsth>
                <th>Last Postth>
            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">0td>
                <td class="align=middle">0td>
                <td>td>
            tr>
            {% endfor %}
            tbody>
        table>
    div>

body>
html>

现在的结果:
一个完整的Django入门指南 - 第2部分:基本原理_第25张图片

到目前为止,我们使用交互式控制台添加了新板块(python manage.py shell)。但是我们需要一个更好的方法去做它。在下一节中,我们将为网站管理员管理它的管理界面。


Django管理概论

当我们启动一个新项目时,Django已经在INSTALLED_APPS中配置了Django Admin列表。

一个完整的Django入门指南 - 第2部分:基本原理_第26张图片

  • Django管理是一个很好的工具。但是我们需要知道什么时候使用它。
  • 它的目的是用作博客和电子商务等内容驱动网站的后台应用程序
  • 网站的访问者不需要使用它

Django管理的一个很好的用例就是在博客中;作者可以使用它来编写和发布文章。另一个例子是电子商务网站,在那里,员工可以创建、编辑、删除产品。

现在,我们将配置Django管理来维护应用程序的版块。

让我们从创建一个管理员帐户开始:

python manage.py createsuperuser

按照说明依次填写(密码需要8位以上,且不能全为数字):

Username (leave blank to use 'vitorfs'): admin
Email address: admin@example.com
Password:
Password (again):
Superuser created successfully.

现在,在浏览器中打开:http://127.0.0.1:8000/admin/
一个完整的Django入门指南 - 第2部分:基本原理_第27张图片

输入用户名和密码登录到管理界面:
一个完整的Django入门指南 - 第2部分:基本原理_第28张图片

它已经配置了一些特性。在这里,我们可以添加用户(Users)和组(Groups)来管理权限。稍后我们将探讨更多的概念。

添加版块模型非常简单。打开boards目录下的admin.py文件,并添加以下代码:

boards/admin.py

from django.contrib import amdin
from .models import Board

admin.site.register(Board)

保存admin.py文件,刷新你的浏览器页面:
一个完整的Django入门指南 - 第2部分:基本原理_第29张图片

这样就好了!它已经准备好可以使用了。点击Boards查看现有的版块:
一个完整的Django入门指南 - 第2部分:基本原理_第30张图片

我们可以点击Add Board按钮来添加新的版块:
一个完整的Django入门指南 - 第2部分:基本原理_第31张图片

点击save按钮:
一个完整的Django入门指南 - 第2部分:基本原理_第32张图片

我们可以打开http://127.0.0.1:8000来检查一切是否工作正常:
一个完整的Django入门指南 - 第2部分:基本原理_第33张图片

你可能感兴趣的:(Django)