美文网首页
Django初学者入门指南2-基础知识(译&改)

Django初学者入门指南2-基础知识(译&改)

作者: 心甘情愿_Root | 来源:发表于2020-09-22 16:28 被阅读0次

    Django初学者入门指南1-初识(译&改)

    Django初学者入门指南2-基础知识(译&改)

    Django初学者入门指南3-高级概念(译&改)

    Django初学者入门指南4-登录认证(译&改)

    Django初学者入门指南5-存储数据(译&改)

    Django初学者入门指南6-基于类的页面(译&改)

    Django初学者入门指南7-部署发布(译&改)

    >>原文地址 By Vitor Freitas

    简介

    欢迎来到Django教程的第二部分!在上一部分中,我们安装了所需的python3.6,并在虚拟环境中运行django1.11,同时也已经创建了我们的第一个项目。接下来,我们将继续在同一个项目中编写代码。

    在下一节中,我们讨论将要开发的项目来了解一些背景知识。然后学习Django的基础知识:模型(models)、管理员(admin)、视图(views)、模板(templates)和路由(URLs)。

    让我们动起来!


    网页面板

    不知道大家是不是这样,就我个人而言,通过实际的例子和代码片段可以让我更快地掌握想要学习的内容。我较难通过抽象简单的例子,如Class AClass B和经典示例foo(bar),来学习和掌握知识。所以我不打算这么干,在开始学习模型、视图以及其他东西前,让我们花点时间来简单讨论一下我们要开发的这个项目。

    如果你有web开发的经验,觉得这部分比较啰嗦的话,可以简单浏览一下插图了解我们要构建的东西,就可以跳到本教程的模型(models)部分。

    但如果你是一位web开发新手,那就强烈建议你继续阅读,这将给你讲解关于web应用程序建模和设计的方法,web开发,乃至程序开发,可不仅仅知识编码而已。

    用例图

    我们的项目是一个论坛,整体思路是通过维护几个版块(board),它们类似于类别,区分各个版块的不同内容方向。在某一版块里,用户可以通过创建新的主题(topic)来开始新的讨论。在某一主题中,其他用户可以发布、回复帖子(post)来参与讨论。

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

    图 1: 用户权限用例图
    类图

    我们可以依据用例图来考虑项目的实体(entities)。实体就是是我们将创建的模型,它与我们的Django应用程序将处理的数据密切相关。

    为了能够实现上一节中描述的用例,我们至少需要实现以下模型:版块(board)主题(topic)帖子(post)用户(user)

    图 2 : 基本类图

    花时间思考模型之间的相互关系也至关重要。图中的实线告诉我们,在主题(topic)中,需要一个字段来标识它属于哪个版块(board)。同样,帖子(post)需要一个字段来表示它属于哪个主题(topic),这样就可以在讨论中只列出帖子(post)在一个特定的主题(topic)中创建。最后,需要在主题(topic)帖子(post)中增加字段来记录是谁发起了讨论,这样就可以确定谁在发布回复。

    我们还可以给版块(board)用户(user)模型建立联系,这样就可以确定谁创建了一个特定的版块(board)。但这些信息与应用程序无关。还有其他方法可以跟踪这些信息,稍后我们再进行探讨。

    现在我们已经有了基本的类图,必须考虑每个模型将携带什么样的信息。这个过程很容易考虑得过于宽泛,我们试着把注意力集中在重要的点上,仅关注开发所需的信息。后面可以使用迁移(migrations)来改进模型,我们将在下一篇教程中详细介绍这一点。

    下图的设计包含了我们现在需要的信息:

    图 3: 包含基本关系和基础信息的类图

    这个类图强调了模型之间的关系,线条和箭头最终将被转换成字段。

    对于Board模型,我们将从两个字段开始:namedescriptionname字段必须是唯一的,以避免重复的线路板名称。这个description只是为了给大家一个提示,说明这个版块是关于什么的。

    Topic模型由四个字段组成:subjectlast_update上次更新的时间,用于定义主题排序,starter用于标识发起TopicUser,以及一个名为board的字段,用于定义特定Topic属于哪个Board

    Post模型将有一个message字段,该字段将用于存储帖子回复的文本;一个created_at的时间字段,主要用于在Topic内对Post进行排序;一个updated_at的时间字段,用于记录User何时编辑了某个帖子。与时间字段一样,我们还需要引用User模型:created_byupdate_by

    最后是User模型,在类图中,只提到了字段usernamepasswordemailis_superuser标志,这就是现在要使用的全部内容。需要注意的是,我们不需要创建User模型,因为Django已经在contrib包中提供了一个内置的User模型。我们可以直接使用它。

    关于类图中的多重性(数字10..*,等等),这里简单说明一下:

    一个Topic必须与一个(1Board关联(即不能为空),一个Board可以关联多个Topic或者没有(0..*)。也就是说Board可能没有一个Topic而存在。

    版块和主题的一对多关系

    一个Topic应该至少有一个Post(主题帖Post),也可以有很多Post1..*)。一个Post必须与一个Topic1)关联。

    主题和帖子的一对多关系

    一个Topic必须有一个且只有一个User关联:主题发起者User1)。一个User可能有许多或没有Topic0..*)。

    用户和主题的一对多关系

    一个Post必须有一个,并且只有一个UserPostcreated_by1)关联。一个User可能创建了许多或没有Post0..*)。Post还有一个updated_by字段与User关联,多重性0..1表示updated_by字段可能为空(Post未编辑),最多只能关联一个User(最多被1个用户编辑)。

    帖子和用户的对应关系

    <span id='Figure_4'>
    绘制类图的另一种方法是强调字段而不是模型之间的关系:
    </span>

    图 4: 强调类字段的类图

    上面的表示与前面的表示相同,它也更接近于我们将使用Django模型API设计的内容。在这个类图中,我们可以更清楚地看到在Post模型中,关联topiccreated_byupdated_by成为模型字段。另一个有趣的地方是,在Topic模型中,我们现在有一个名为posts()operation(一个类方法)。我们将通过实现一个反向关系来实现这一点,Django将在数据库中自动执行一个查询,返回属于特定Topic的所有Post列表。

    类图完成了,这就够了!为了绘制本节中的图表,我使用了StarUML工具。

    线框

    在花了一些时间设计应用程序模型之后,我喜欢创建一些线框草图来定义需要做的事情,并且对我们的发展方向有一个清晰的了解。

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

    <span id='Figure_5'>
    首先,我们需要在主页上显示所有的版块Board
    </span>

    图 5: 项目首页,显示所有的版块

    如果用户点击一个版块Board的链接,比如Django,应该显示出所有主题Topic

    图6 : 版块内容,显示该版块下的所有主题

    这里有两个主要功能:用户单击“New topic(新建主题)”按钮创建新主题,或者用户单击某个主题查看或参与讨论。

    “New topic(新建主题)”页面:

    图7 : “New topic(新建主题)”页面

    进入主题后的页面,显示帖子和讨论:

    图8 :指定主题下的帖子

    如果用户单击“Reply(回复)”按钮,将看到下面的页面,并以时间倒序显示帖子的摘要(最新消息在最上面):

    图9 :回复帖子的页面

    要绘制线框,可以使用免费工具draw.io


    模型Models

    这些模型基本上是应用程序数据库表的表示。在本节中,我们要做的是创建上一节中建模的类的Django表示:BoardTopicPostUser模型已经在Django的一个名为auth的内置应用程序中定义,它在INSTALLED APPS配置下的命名空间django.contrib.auth中.

    我们将在boards/models.py文件中完成所有的模型工作,(参考 图 4)。完成后的内容如下:

    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的子类。每个类将被转换为database table。每个字段是django.db.models.Field的子类(Django core的定义),并将被转换为database columns

    字段CharFieldDateTimeField等都是django.db.models.Field,它们包含在Django core中,随时可以使用。

    这里我们只使用CharFieldTextFieldDateTimeFieldForeignKey字段来定义模型。但是Django提供了各种各样的选项来表示不同类型的数据,比如IntegerFieldBooleanFieldDecimalField等等,根据实际情况进行定义。

    有些字段需要参数,例如CharField。我们应该设置一个max_length。此信息将用于创建database column。Django需要知道database column的大小。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将会忽略这个反向关系。

    下面您可以看到类图和用Django生成模型的源代码之间的关系。绿线表示我们如何处理反向关系。

    此时,你可能会问:“主键呢?我应该怎么处理?”如果我们不为模型指定主键,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就会使用这个文件来创建数据库表和列database tables and columns

    迁移文件被转换成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内置应用程序中的迁移文件,这些文件列在INSTALLED_apps里。

    Applying boards.0001_initial... OK指的就是我们前面生成的迁移文件。

    好了,数据库准备好了,我们可以继续开发了!

    ** 注意:** SQLite是一款可以在生产环境直接使用的数据库产品,许多公司的成千上万个产品都是使用的SQLite数据库,如Android和iOS设备、还有主流的网络浏览器、Windows 10、macOS等等。但它并不适用于所有场景,SQLite在大容量网站、写密集应用、巨型数据集、高并发的使用场景中的表现,不如MySQLPostgreSQLOracle等数据库。

    我们将在项目开发期间使用SQLite,因为它很方便,而且不需要安装任何其他东西。需要将项目部署到生产环境中时,我们将切换到PostgreSQL,对于简单的网站来说这样就可以了。但对于复杂的网站,最好使用相同的数据库进行开发和生产。

    尝试使用Models API

    使用Python进行开发的最大优点之一是交互式shell。我一直在用它,这是一种快速的方法,可以用它调试LibrariesAPIs功能。

    使用manage.py工具可以直接启动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方法可以创建或者更新对象的数据。我们这里创建Board对象时并未赋值id,Django会自动为它分配一个值,通过下面的方式我们可以查看值:

    board.id
    1
    

    你同样可以通过这种方式访问该对象的其他属性:

    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,在后面的教程中我们会深入探讨。这个数据集就是在数据库中读取出来的对象列表。这里我们可以看出,数据库中存储了2条版块数据对象,打印的时候输出为对象类型Board object,这是因为我们没有定义和实现Board类的__str__方法。

    __str__方法是对象的字符串描述,这里我们可以返回版块的名称。

    先让我们离开shell:

    exit()
    

    编辑boards应用程序文件目录下的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.
    

    同样的,当我们想用模型管理器(Model Manager)查询某一个对象时,可以使用get方法:

    django_board = Board.objects.get(id=1)
    
    django_board.name
    'Django'
    

    在使用get方法时一定要注意,如果我们试图获取一个不存在的对象,比如说id=3的版块,它将抛出一个异常boards.models.DoesNotExist

    board = Board.objects.get(id=3)
    
    boards.models.DoesNotExist: Board matching query does not exist.
    

    我们也可以在get方法里使用其他的对象属性进行查询,但最好是可以定位到某一个对象的属性值,否则这个查询方法将返回多个结果可能导致错误。

    Board.objects.get(name='Django')
    <Board: Django>
    

    也需要注意查询语句是对大小写敏感的case sensitive,通过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页面中显示版块Board


    页面视图(Views),模版(Templates),和静态文件(Static Files)

    现在我们的应用已经有一个显示Hello, World!的页面home

    <details>
    <summary>原始版本</summary>
    原始版本的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),
    ]
    

    </details>

    修订版本的myproject/urls.py

    from django.urls import re_path
    from django.contrib import admin
    
    from boards import views
    
    urlpatterns = [
        re_path(r'^$', views.home, name='home'),
        re_path(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。对于这个简单的视图,只需要一个版块对象的列表,然后页面渲染部分就交给Django Template Engine来完成吧。

    Django Template Engine(模板引擎)

    在项目目录下,与boards同级的位置创建一个名为templates的文件夹:

    myproject/
     |-- myproject/
     |    |-- boards/
     |    |-- myproject/
     |    |-- templates/   <-- 是的,放这里!
     |    +-- manage.py
     +-- venv/
    

    templates目录下,创建一个html文件,取名为home.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拼起来,这样就可以得到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模板,添加一个列表:

    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>
    
    测试首页(homepage)

    测试将始终贯穿这个教程,我们将讨论不同的测试概念和策略。

    让我们来创建第一个测试用例,这里将会用到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)
    

    这是一个简单的测试用例,但是它非常有用。我们测试请求的返回的状态码(status_code),断言(assertEquals)返回的状态码为200200表明请求成功(success).

    通常我们可以直接在控制台中看到请求返回的状态码(status_code):

    如果出现未捕获的异常、语法错误或其他任何情况,Django将返回状态代码500,这意味着Internal Server Error。现在,假设我们的应用程序有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返回了正确的view函数。这也是一个有用的测试,因为随着开发的进展,您将看到urls.py模块可以变得非常大和复杂,而URL路由是通过正则匹配定位的,在某些特定情况下可能匹配到目标以外的URL,因此Django最终可能返回错误的view函数。

    按下面的方式编写测试用例:

    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/返回的是首页(homepage)。

    再测试一次看看:

    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表示详细输出。

    静态文件(Static Files)配置

    静态文件指的是CSS、JavaScripts、字体、图片或我们可以用来生成界面的任何其他资源。

    事实上,Django不提供这些文件,除非能在开发过程中为我们提供更多的便利。不过Django提供了一些功能来帮助我们管理静态文件。这些功能由已在INSTALLED_APPS配置中列出的django.contrib.staticfiles应用程序提供。

    有这么多的前端组件库可用,没有理由继续使用简陋的HTML文档,我们可以很容易地将bootstrap4添加到我们的项目中。Bootstrap是一个开源工具包,用于使用HTML、CSS和JavaScript进行开发。

    在myproject根目录下,与boardstemplatesmyproject文件夹一起,新建一个名为static的文件夹,在static文件夹中再创建一个名为css的文件夹:

    myproject/
     |-- myproject/
     |    |-- boards/
     |    |-- myproject/
     |    |-- templates/
     |    |-- static/       <-- 这里
     |    |    +-- css/     <-- 这里这里!
     |    +-- manage.py
     +-- venv/
    

    打开getbootstrap.com,下载他们的最新Release版本:

    下载Compiled CSS and JS版本.

    在电脑上打开下载好的bootstrap-4.0.0-beta-dist.zip文件(这里可能是其他更新的版本),并将css/bootstrap.min.css拷贝到刚刚创建的css目录下:

    myproject/
     |-- myproject/
     |    |-- boards/
     |    |-- myproject/
     |    |-- templates/
     |    |-- static/
     |    |    +-- css/
     |    |         +-- bootstrap.min.css    <-- 这里
     |    +-- manage.py
     +-- venv/
    

    下一步就是配置Django项目,让它能够定位到静态文件。打开settings.py文件,在文档最后面,紧跟STATIC_URL添加下面的代码:

    STATIC_URL = '/static/'
    
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static'),
    ]
    

    还记得吗?跟之前配置TEMPLATES一样。

    现在我们需要在我们的html文件中使用这些静态文件(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>
        <!-- body suppressed for brevity ... -->
      </body>
    </html>
    

    首先我们在模板最开始加上{% load static %}

    {% static %}这个标签会通过设置文件settings.py去找到资源文件的目录STATIC_URL,在这里就将{% static 'css/bootstrap.min.css' %}表示为/static/css/bootstrap.min.css,最终就是访问到http://127.0.0.1:8000/static/css/bootstrap.min.css

    假如需要将STATIC_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,我们就可以看到它的作用:

    现在我们来编辑一下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>
        <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管理模块简介

    当创建一个项目时,Django就自动为我们创建了Django Admin应用程序并记录在INSTALLED_APPS下。

    举两个用户管理权限的例子:在博客类应用中,作者角色有编写和发布文章的权限;而在电商类网站中,工作人员有创建、编辑、删除产品的权限。

    现在,我们先为Django管理员添加版块的管理权限。

    首先创建一个超级管理员账户:

    python manage.py createsuperuser
    

    按照说明补充信息(可以自行决定信息内容):

    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/

    使用刚刚创建的超级管理员进行登录,输入usernamepassword

    现在已经可以看到默认为我们添加了一些功能,可以配置UsersGroups的权限,稍后我们再对此进行深入讨论。

    添加对版块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按钮:

    然后点击save按钮:

    我们可以打开首页http://127.0.0.1:8000来看看是否添加成功:


    小结

    在本教程中,我们探讨了许多新概念。我们为我们的项目定义了一些需求,创建了第一个模型,迁移了数据库,开始使用模型API。我们创建了第一个视图并编写了一些单元测试。我们还配置了Django模板引擎、静态文件,并将bootstrap4库添加到项目中。最后,我们简要介绍了Django管理接口。

    项目的源代码可以在GitHub上找到。项目的当前状态可以在发布标签v0.2-lw下找到。下面的链接将带您找到正确的位置:

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

    上一节:Django初学者入门指南1-初识(译&改)

    下一节:Django初学者入门指南3-高级概念(译&改)

    相关文章

      网友评论

          本文标题:Django初学者入门指南2-基础知识(译&改)

          本文链接:https://www.haomeiwen.com/subject/mazvyktx.html