• 完整的Django入门指南学习笔记2


    part2:

    前沿

    在第一节中,我们安装了项目所需要的一切:Python3.6以及在虚拟环境中运行的Django2.0,这部分教程继续在项目上编写代码。

    开始写代码前,先讨论下项目的相关背景知识,然后再学习 Django 的基础,包括:模型、管理后台、视图、模板和路由。


    论坛项目

    在进入模型,视图等其它有趣的部分之前,花点时间简要地讨论我们将要开发的这个项目。

    例图

    我们的项目是一个论坛系统,整个项目的构思是维护几个论坛版块(boards),每个版块就像一个分类一样。在指定的版块里面,用户可以通过创建新主题(Topic)开始讨论,其他用户参与讨论回复。

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

    类图

    从用例图中,我们可以开始思考项目所需的实体类有哪些。这些实体就是我们要创建的模型,它与我们的Django应用程序处理的数据非常密切。

    为了能够实现上面描述的用例,我们需要至少实现下面几个模型:Board,Topic,Post和User。

    • Board:版块
    • Topic:主题
    • Post:帖子(译注:其实就是主题的回复或评论)

    类与类之间的实线告诉我们,在一个主题(Topic)中,我们需要有一个字段(译注:其实就是通过外键来关联)来确定它属于哪个版块(Board)。

    同样,帖子(Post)也需要一个字段来表示它属于哪个主题,这样我们就可以列出在特定主题内创建的帖子。

    最后,我们需要一个字段来表示主题是谁发起的,帖子是谁发的。

    用户和版块之间也有联系,就是谁创建的版块。但是这些信息与应用程序无关。还有其他方法可以跟踪这些信息,稍后你会看到。

    现在我们的类图有基本的表现形式,我们还要考虑这些模型将承载哪些信息。这很容易让事情变得复杂,所以试着先把重要的内容列出来,这些内容是我们启动项目需要的信息。后面我们再使用 Django 的迁移(Migrations)功能来改进模型,你将在下一节中详细了解这些内容。

    但就目前而言,这是模型最基本的内容:

    这个类图强调的是模型之间的关系,这些线条和箭头最终会在稍后转换为字段。

    对于 Board 模型,我们将从两个字段开始:name 和 description。 name字段必须是唯一的,为了避免有重复的名称。description 用于说明这个版块是做什么用的。

    Topic 模型包括四个字段:subject 表示主题内容,last_update 用来定义话题的排序,starter 用来识别谁发起的话题,board 用于指定它属于哪个版块。

    Post 模型有一个 message 字段,用于存储回复的内容,created_at 在排序时候用(最先发表的帖子排最前面),updated_at 告诉用户是否更新了内容,同时,还需要有对应的 User 模型的引用,Post 由谁创建的和谁更新的。

    最后是 User 模型。在类图中,我只提到了字段 usernamepasswordemail, is_superuser 标志,因为这几乎是我们现在要使用的所有东西。

    需要注意的是,我们不需要创建 User 模型,因为Django已经在contrib包中内置了User模型,我们将直接拿来用。

    关于类图之间的对应关系(数字 1,0..* 等等),这里教你如何阅读:

    一个topic 必须与一个(1)Board(这意味着它不能为空)相关联,但是 Board 下面可能与许多个或者0个 topic 关联 (0..*)。这意味着 Board 下面可能没有主题。(译注:一对多关系)

    一个 Topic 至少有一个 Post(发起话题时,同时会发布一个帖子),并且它也可能有许多 Post(1..*)。一个Post 必须与一个并且只有一个Topic(1)相关联。

    一个 Topic 必须有一个且只有一个 User 相关联,topic 的发起者是(1)。而一个用户可能有很多或者没有 topic(0..*)。

    Post 必须有一个并且只有一个与之关联的用户,用户可以有许多或没有 Post(0..*)。Post 和 User之间的第二个关联是直接关联(参见该行最后的箭头),就是 Post 可以被用户修改(updated_by),updated_by 有可能是空(Post 没有被修改)

    画这个类图的另一种方法是强调字段而不是模型之间的关系:

    上面的表示方式与前面的表示方式是对等的,不过这种方式更接近我们将要使用 Django Models API 设计的内容。在这种表示方式中,我们可以更清楚地看到,在 Post 模型中,关联了 Topic,created_by(创建者)和 updated_by(更新者)字段。

    另一个值得注意的事情是,在 Topic 模型中,有一个名为 posts()的操作(一个类方法)。我们将通过反向关系来实现这一目标,Django 将自动在数据库中执行查询以返回特定主题的所有帖子列表。

    好了,现在已经够UML了!为了绘制本节介绍的图表,我使用了 StarUML 工具。

    线框图(原型图)

    花了一些时间来设计应用程序的模型后,我们来创建一些线框来定义需要完成的工作,并且清楚地了解我们将要做什么。

    基于线框图,我们可以更深入地了解应用程序中涉及的实体。

    首先,我们需要在主页上显示所有版块:

    如果用户点击一个链接,比如点击Django版块,它应该列出所有Django相关的主题:

    这里有两个入口:用户点击“new topic“ 按钮创建新主题,或者点击主题链接查看或参与讨论。

    “new topic” 页面:

    现在,主题页面显示了帖子和讨论:

    如果用户点击回复按钮,将看到下面这个页面,并以倒序的方式(最新的在第一个)显示帖子列表:

    绘制这些线框,你可以使用draw.io服务,它是免费的

    这一部分我们设计的系统的模型,以及模型之间的关系,分清楚了不同用户类型的角色,最后,我们把原型图也画出来了。再小的系统,我们也要先思考。下面我们将设计 Model 类。


    模型

    我们在本节中要做的是创建 Django 所表示的类,这些类就是在上一部分中设计的类:Board,Topic 和 Post。User 模型被命名为内置应用叫 auth,它以命名空间 django.contrib.auth的形式出现在 INSTALLED_APPS 配置中。

    修改 boards/models.py 。以下是我们在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 core)的实例表示,它们并将被转换为数据库的列。

    字段 CharFieldDateTimeField等等,都是 django.db.models.Field 的子类,包含在Django的核心里面,随时可以使用。

    在这里,我们仅使用 CharFieldTextFieldDateTimeField,和ForeignKey 字段来定义我们的模型,当然在Django提供了更广泛的选择来代表不同类型的数据。

    有些字段需要参数,例如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模型的源代码之间的比较,绿线表示我们如何处理反向关系。

    这时,你可能会问自己:“主键/ 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 命令把 Django contrib app 中现有的迁移文件也执行了,这些内置app列在了 INSTALLED_APPS 。这是预料之中的。

    Applying boards.0001_initial... OK 是我们在上一步中生成的迁移脚本。

    好了!我们的数据库已经可以使用了。

    • 需要注意的是SQLite是一个产品级数据库。SQLite被许多公司用于成千上万的产品,如所有Android和iOS设备,主流的Web浏览器,Windows 10,MacOS等。
    • 但这不适合所有情况。SQLite不能与MySQL,PostgreSQL或Oracle等数据库进行比较。大容量的网站,密集型写入的应用程序,大的数据集,高并发性的应用使用SQLite最终都会导致问题。
    • 我们将在开发项目期间使用SQLite,因为它很方便,不需要安装其他任何东西。当我们将项目部署到生产环境时,再将切换到PostgreSQL(译注:后续,我们后面可能使用MySQL)。对于简单的网站这种做法没什么问题。但对于复杂的网站,建议在开发和生产中使用相同的数据库。

    试验 Models API

    使用Python进行开发的一个重要优点是交互式shell。我一直在使用它。这是一种快速尝试和试验API的方法。

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

    python manage.py shell
    
    Python 3.6.2 (default, Jul 17 2017, 16:44:45)
    [GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    (InteractiveConsole)
    >>>
    

    这与直接输入python指令来调用交互式控制台是非常相似的,除此之外,项目将被添加到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.description = 'Django discussion board.'
    board.save()
    

    每个Django模型都带有一个特殊的属性; 我们称之为模型管理器(Model Manager)。你可以通过属性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 object。这是因为我们尚未实现 Board 的__str__ 方法。

    __str__方法是对象的字符串表示形式。我们可以使用版块的名称来表示它。

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

    exit()
    

    现在编辑 boards app 中的 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.
    

    同样,我们可以使用模型的 管理器(Manager) 来查询数据库并返回单个对象。为此,我们要使用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 = 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 HttpResponse('Hello, World!')
    

    我们可以从这里开始写。如果你回想起我们的原型图,图5显示了主页应该是什么样子。我们想要做的是在表格中列出一些版块的名单以及它们的描述信息。

    首先要做的是导入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 = '<br>'.join(boards_names)
    
        return HttpResponse(response_html)
    

    结果就是这个简单的HTML页面:

    等等,我们在这里先停一下。真正的项目里面我们不会这样去渲染HTML。对于这个简单视图函数,我们做的就是列出所有版块,然后渲染部分是Django模板引擎的职责。


    模板引擎设置

    在 manage.py 所在的目录创建一个名为 templates 的新文件夹:

    myproject/
     |-- myproject/
     |    |-- boards/
     |    |-- myproject/
     |    |-- templates/  
     |    +-- manage.py
     +-- venv/
    

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

    templates/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 ... %} 和 {{ variable }} 。它们是Django模板语言的一部分。上面的例子展示了如何使用 for遍历列表对象。{{ 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'
    

    看到了吗?它只是指向我们在前面步骤中创建的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:

    我们可以用一个更漂亮的表格来替换,改进HTML模板:

    templates/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>
    


    第一个测试用例

    测试主页

    测试将是一个反复出现的主题,我们将在整个教程系列中一起探讨不同的概念和策略。

    我们来开始写第一个测试。现在,我们将在boards应用程序内的tests.py文件中操作

    boards/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)
    

    这是一个非常简单但非常有用的测试用例,我们测试的是请求该URL后返回的响应状态码。状态码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最终可能返回错误的视图函数。

    我们可以这样做:

    boards/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 / 返回 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'...
    

    要查看有关测试执行时更详细的信息,可将verbosity的级别设置得更高一点:

    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 应用程序中找到(译者:Django为了使得开发方便,也可以处理静态文件,而在生产环境下,静态文件一般直接由 Nginx 等反向代理服务器处理,而应用服务器专心负责处理它擅长的业务逻辑)。

    市面上很多优秀前端组件框架,我们没有理由继续用简陋的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并下载最新版本:

    下载编译版本的CSS和JS

    在你的计算机中,解压 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文件):

    templates/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>
       <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>
    

    首先,我们在模板的开头使用了 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:

    {% 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 Admin 在这个应用程序列出的INSTALLED_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.
    

    在浏览器中打开该URL:http://127.0.0.1:8000/admin/

    输入用户名和密码登录到管理界面:

    它已经配置了一些功能。在这里,我们可以添加用户和组的权限管理,这些概念在后面我们将探讨更多。

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

    boards/admin.py

    from django.contrib import admin
    from .models import Board
    
    admin.site.register(Board)
    

    保存admin.py文件,然后刷新网页浏览器中的页面:

    对!它已准备好被使用了。点击Boards链接查看现有版块列表:

    我们可以通过点击 Add Board 按钮添加一个新的版块:

    点击保存按钮:

    我们可以检查一切是否正常,打开URL http://127.0.0.1:8000


    总结

    在本教程中,我们探讨了许多新概念。我们为项目定义了一些需求,创建了第一个模型,迁移了数据库,开始玩 Models API。我们创建了第一个视图并编写了一些单元测试。同时我们还配置了Django模板引擎,静态文件,并将Bootstrap 4库添加到项目中。最后,我们简要介绍了Django Admin界面。

    下一部分,我们将探索Django的URL路由,表单API,可重用模板以及更多测试。

    该项目的源代码在GitHub上可用。本来的代码可以在发布标签v0.2-lw下找到。下面的链接将带你到正确的地方:

    https://github.com/sibtc/django-beginners-guide/tree/v0.2-lw

  • 相关阅读:
    函数—函数进阶(二)
    函数—函数进阶(一)
    函数(三)
    函数(二)
    函数(一)
    人丑就要多读书、三元运算、文件处理
    第二章练习题
    Python bytes类型介绍、Python3与2字符串的区别、Python3与2编码总结
    进制运算、字符编码、Python3的执行流程
    去除inline-block元素间间距,比较靠谱的两种办法
  • 原文地址:https://www.cnblogs.com/yanhuidj/p/10012687.html
Copyright © 2020-2023  润新知