├── .gitignore ├── statics ├── 1-1.jpg ├── 1-2.jpg ├── 1-3.jpg ├── 1-4.jpg ├── 1-5.png ├── 1-6.png ├── 1-7.png ├── 1-8.png ├── 1-9.png ├── 2-1.jpg ├── 2-2.jpg ├── 2-3.jpg ├── 2-4.jpg ├── 2-5.jpg ├── 2-6.jpg ├── 1-10.png ├── 1-11.png ├── 1-12.png ├── 1-13.png ├── 1-14.png ├── 2-1-1.png ├── class-diagram.png ├── wireframe-boards.png ├── wireframe-posts.png ├── wireframe-reply.png ├── wireframe-topics.png ├── bootstrap-download.png ├── basic-class-diagram.png ├── wireframe-new-topic.png ├── boards-homepage-render.png ├── class-diagram-post-user.png ├── boards-homepage-render-2.png ├── class-diagram-attributes.png ├── class-diagram-board-topic.png ├── class-diagram-topic-post.png ├── class-diagram-topic-user.png ├── boards-homepage-httpresponse.png ├── class-diagram-django-models.png └── test-homepage-view-status-code-200.png ├── DjangoORM.md ├── Deployment.md ├── Authentication.md ├── ClassBasedViews.md ├── README.md ├── Fundamentals-4.md ├── AdvancedConcepts.md ├── Fundamentals.md ├── GettingStarted.md ├── GettingStarted-2.md ├── Fundamentals-2.md └── Fundamentals-3.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS* 2 | -------------------------------------------------------------------------------- /statics/1-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/1-1.jpg -------------------------------------------------------------------------------- /statics/1-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/1-2.jpg -------------------------------------------------------------------------------- /statics/1-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/1-3.jpg -------------------------------------------------------------------------------- /statics/1-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/1-4.jpg -------------------------------------------------------------------------------- /statics/1-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/1-5.png -------------------------------------------------------------------------------- /statics/1-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/1-6.png -------------------------------------------------------------------------------- /statics/1-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/1-7.png -------------------------------------------------------------------------------- /statics/1-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/1-8.png -------------------------------------------------------------------------------- /statics/1-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/1-9.png -------------------------------------------------------------------------------- /statics/2-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/2-1.jpg -------------------------------------------------------------------------------- /statics/2-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/2-2.jpg -------------------------------------------------------------------------------- /statics/2-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/2-3.jpg -------------------------------------------------------------------------------- /statics/2-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/2-4.jpg -------------------------------------------------------------------------------- /statics/2-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/2-5.jpg -------------------------------------------------------------------------------- /statics/2-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/2-6.jpg -------------------------------------------------------------------------------- /statics/1-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/1-10.png -------------------------------------------------------------------------------- /statics/1-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/1-11.png -------------------------------------------------------------------------------- /statics/1-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/1-12.png -------------------------------------------------------------------------------- /statics/1-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/1-13.png -------------------------------------------------------------------------------- /statics/1-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/1-14.png -------------------------------------------------------------------------------- /statics/2-1-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/2-1-1.png -------------------------------------------------------------------------------- /statics/class-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/class-diagram.png -------------------------------------------------------------------------------- /statics/wireframe-boards.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/wireframe-boards.png -------------------------------------------------------------------------------- /statics/wireframe-posts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/wireframe-posts.png -------------------------------------------------------------------------------- /statics/wireframe-reply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/wireframe-reply.png -------------------------------------------------------------------------------- /statics/wireframe-topics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/wireframe-topics.png -------------------------------------------------------------------------------- /statics/bootstrap-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/bootstrap-download.png -------------------------------------------------------------------------------- /statics/basic-class-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/basic-class-diagram.png -------------------------------------------------------------------------------- /statics/wireframe-new-topic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/wireframe-new-topic.png -------------------------------------------------------------------------------- /DjangoORM.md: -------------------------------------------------------------------------------- 1 | DjangoORM.md 2 | 3 | https://simpleisbetterthancomplex.com/series/2017/10/02/a-complete-beginners-guide-to-django-part-5.html -------------------------------------------------------------------------------- /statics/boards-homepage-render.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/boards-homepage-render.png -------------------------------------------------------------------------------- /statics/class-diagram-post-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/class-diagram-post-user.png -------------------------------------------------------------------------------- /Deployment.md: -------------------------------------------------------------------------------- 1 | Deployment.md 2 | 3 | https://simpleisbetterthancomplex.com/series/2017/10/16/a-complete-beginners-guide-to-django-part-7.html -------------------------------------------------------------------------------- /statics/boards-homepage-render-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/boards-homepage-render-2.png -------------------------------------------------------------------------------- /statics/class-diagram-attributes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/class-diagram-attributes.png -------------------------------------------------------------------------------- /statics/class-diagram-board-topic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/class-diagram-board-topic.png -------------------------------------------------------------------------------- /statics/class-diagram-topic-post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/class-diagram-topic-post.png -------------------------------------------------------------------------------- /statics/class-diagram-topic-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/class-diagram-topic-user.png -------------------------------------------------------------------------------- /Authentication.md: -------------------------------------------------------------------------------- 1 | Authentication.md 2 | 3 | https://simpleisbetterthancomplex.com/series/2017/09/25/a-complete-beginners-guide-to-django-part-4.html -------------------------------------------------------------------------------- /statics/boards-homepage-httpresponse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/boards-homepage-httpresponse.png -------------------------------------------------------------------------------- /statics/class-diagram-django-models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/class-diagram-django-models.png -------------------------------------------------------------------------------- /statics/test-homepage-view-status-code-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wzhbingo/django-beginners-guide/HEAD/statics/test-homepage-view-status-code-200.png -------------------------------------------------------------------------------- /ClassBasedViews.md: -------------------------------------------------------------------------------- 1 | ClassBasedViews.md 2 | 3 | https://simpleisbetterthancomplex.com/series/2017/10/09/a-complete-beginners-guide-to-django-part-6.html 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # django-beginners-guide 2 | 3 | [A Complete Beginner's Guide to Django](https://simpleisbetterthancomplex.com/series/beginners-guide/1.11/) 翻译计划,由公众号「Python之禅」 发起,可能是史上最浅显易懂的Django教程。 4 | 5 | 6 | 教程将围绕一个论坛项目展开,从基本的开发环境搭建开始讲解,再到领域模型的设计,模板、视图、URL、模型,Django Admin,单元测试的基本介绍。 7 | 8 | 9 | 10 | * [Django入门指南-第1部分(环境搭建)](./GettingStarted.md) 11 | * [Django入门指南-第1部分(项目创建)](./GettingStarted-2.md) 12 | * [Django入门指南-第2部分(系统设计)](./Fundamentals.md) 13 | * [Django入门指南-第2部分(Django 模型设计)](./Fundamentals-2.md) 14 | * [Django入门指南-第2部分(视图、模板、静态文件)](./Fundamentals-3.md) 15 | * [Django入门指南-第2部分(Django Admin)](./Fundamentals-4.md) 16 | 17 | ### TODO 18 | 19 | * Part 3 - [Advanced Concepts](./AdvancedConcepts.md) 20 | * Part 4 - [Authentication](./Authentication.md) 21 | * Part 5 - [Django ORM](./DjangoORM.md) 22 | * Part 6 - [Class-Based Views](./ClassBasedViews.md) 23 | * Part 7 - [Deployment](./Deployment.md) 24 | 25 | 前五部分已被认领翻译 26 | 27 | 28 | -------------------------------------------------------------------------------- /Fundamentals-4.md: -------------------------------------------------------------------------------- 1 | # 一个完整的 Django 入门指南 - 第2部分 2 | 3 | >译者:liuzhijun 4 | >原文: 5 | https://simpleisbetterthancomplex.com/series/2017/09/11/a-complete-beginners-guide-to-django-part-2.html 6 | 7 | 8 | 9 | ![featured](./statics/2-1.jpg) 10 | 11 | 12 | 到目前为止,我们使用交互式控制台(python manage.py shell)添加新的版块。但我们需要一个更好的方式来实现。在这一节中,我们将为网站管理员实现一个管理界面来管理这些数据。 13 | 14 | ### Django Admin 简介 15 | 当我们开始一个新项目时,Django已经配置了Django Admin,这个应用程序列出的INSTALLED_APPS。 16 | 17 | ![2-5](./statics/2-6.jpg) 18 | 19 | 20 | 使用 Django Admin的一个很好的例子就是用在博客中; 它可以被作者用来编写和发布文章。另一个例子是电子商务网站,工作人员可以创建,编辑,删除产品。 21 | 22 | 现在,我们将配置 Django Admin 来维护我们应用程序的版块。 23 | 24 | 我们首先创建一个管理员帐户: 25 | 26 | ```sh 27 | python manage.py createsuperuser 28 | ``` 29 | 按照说明操作: 30 | 31 | ```sh 32 | Username (leave blank to use 'vitorfs'): admin 33 | Email address: admin@example.com 34 | Password: 35 | Password (again): 36 | Superuser created successfully. 37 | ``` 38 | 在浏览器中打开该URL:http://127.0.0.1:8000/admin/ 39 | 40 | ![https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/django-admin-login.png](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/django-admin-login.png) 41 | 42 | 43 | 输入用户名和密码登录到管理界面: 44 | 45 | ![admin](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/django-admin.png) 46 | 47 | 它已经配置了一些功能。在这里,我们可以添加用户和组来管理权限。这些概念在后面我们将探讨更多。 48 | 49 | 添加Board模型非常简单。打开boards目录中的admin.py文件,并添加以下代码: 50 | 51 | **boards/admin.py** 52 | 53 | ```python 54 | 55 | from django.contrib import admin 56 | from .models import Board 57 | 58 | admin.site.register(Board) 59 | ``` 60 | 61 | 保存admin.py文件,然后刷新网页浏览器中的页面: 62 | 63 | ![baords](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/django-admin-boards.png) 64 | 65 | 对!它已准备好被使用了。点击Boards链接查看现有版块列表: 66 | 67 | ![name](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/django-admin-boards-list.png) 68 | 69 | 我们可以通过点击 Add Board 按钮添加一个新的版块: 70 | 71 | ![new](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/django-admin-boards-add.png) 72 | 73 | 点击保存按钮: 74 | 75 | ![save](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/django-admin-boards-list-2.png) 76 | 77 | 我们可以检查一切是否正常,打开URL http://127.0.0.1:8000 78 | 79 | ![home](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/boards-homepage-bootstrap-3.png) 80 | 81 | ### 总结 82 | 83 | 在本教程中,我们探讨了许多新概念。我们为项目定义了一些需求,创建了第一个模型,迁移了数据库,开始玩 Models API。我们创建了第一个视图并编写了一些单元测试。同时我们还配置了Django模板引擎,静态文件,并将Bootstrap 4库添加到项目中。最后,我们简要介绍了Django Admin界面。 84 | 85 | 我希望你喜欢本系列教程的第二部分!下一部分,我们将探索Django的URL路由,表单API,可重用模板以及更多测试。 86 | 87 | 该项目的源代码在GitHub上可用。本来的代码可以在发布标签v0.2-lw下找到。下面的链接将带你到正确的地方: 88 | 89 | https://github.com/sibtc/django-beginners-guide/tree/v0.2-lw -------------------------------------------------------------------------------- /AdvancedConcepts.md: -------------------------------------------------------------------------------- 1 | AdvancedConcepts.md 2 | 3 | https://simpleisbetterthancomplex.com/series/2017/09/18/a-complete-beginners-guide-to-django-part-3.html 4 | 5 | 在这个课程,我们将深入两个基本概念: URLs 和 Forms。在这个过程中,我们将学习一些其他的概念,如创建可重用模板和安装第三方库。我们还将编写大量单元测试。 6 | 如果你是从这个系列教程的 part 1 跟着这个教程一步步地编写的你的项目,你可能需要在开始之前更新你的 **models.py**: 7 | 8 | **boards/models.py** 9 | ```python 10 | class Topic(models.Model): 11 | # other fields... 12 | # Add `auto_now_add=True` to the `last_updated` field 13 | last_updated = models.DateTimeField(auto_now_add=True) 14 | 15 | class Post(models.Model): 16 | # other fields... 17 | # Add `null=True` to the `updated_by` field 18 | updated_by = models.ForeignKey(User, null=True, related_name='+') 19 | ``` 20 | 21 | 现在在激活的 virtualenv 环境下运行命令: 22 | 23 | > python manage.py makemigrations 24 | python manage.py migrate 25 | 26 | 如果你程序中的 `update_by` 字段中已经有了 `null=True` 且 `last_updated` 字段中有了 `auto_now_add=True`,你可以放心地忽略以上的说明。 27 | 28 | 如果你更喜欢使用我的代码作为出发点,你可以在 GitHub 上找到它。 29 | 30 | 本项目现在的代码,可以在 **v0.2-lw** 标签下找到。下面是链接: 31 | 32 | [https://github.com/sibtc/django-beginners-guide/tree/v0.2-lw][1] 33 | 34 | 35 | 我们的开发正式开始。 36 | 37 | 38 | ---------- 39 | 40 | **URLs** 41 | 随着我们项目的开发,我们现在需要有一个属于 **Board** 的展示所有 **topics** 的页面。总结来说,就如你在上个课程所看到的线框图: 42 | 43 | ![此处输入图片的描述][1] 44 | 45 | 46 | 图1: **Board** 线框图, 在 **Django board** 中列出所有的 **topics** 列表 47 | 48 | 我们将从编写 **myproject** 文件夹中的 **urls.py** 开始: 49 | 50 | **myproject/urls.py** 51 | 52 | ```python 53 | from django.conf.urls import url 54 | from django.contrib import admin 55 | 56 | from boards import views 57 | 58 | urlpatterns = [ 59 | url(r'^$', views.home, name='home'), 60 | url(r'^boards/(?P\d+)/$', views.board_topics, name='board_topics'), 61 | url(r'^admin/', admin.site.urls), 62 | ] 63 | ``` 64 | 65 | 现在让我们分析一下 **urlpatterns** 和 **url**。 66 | 67 | **URL dispatcher** 和 **URLconf (URL configuration)** 是Django 应用中的基础部分。在开始的时候,这个看起来让人很困惑;我记得我第一次开始使用 Django 开发的时候也有一段时间学起来很困难。 68 | 69 | 现在 Django 开发者在致力于简化路由语法。但是现在我们使用的是 1.11 版本的 Django,我们需要使用这些语法,所以让我们尝试着去了解它是怎么工作的。 70 | 71 | 一个项目可以有很多 **urls.py** 分布在应用中。Django 需要一个 **url.py** 去作为起点。这个特别的 **urls.py** 叫做 **root URLconf**。它被定义在 **settings.py** 中。 72 | 73 | **myproject/settings.py** 74 | 75 | ```python 76 | ROOT_URLCONF = 'myproject.urls' 77 | ``` 78 | 79 | 它已经自动配置好了,你不需要去改变它任何东西。 80 | 81 | 当 Django 接受一个请求(request), 它就会在项目的 URLconf 中寻找匹配项。他从 **urlpatterns** 变量的第一条开始,然后在每个 **url** 中去测试出请求的 URL。 82 | 83 | 如果 Django 找到了一个匹配路径,他会把请求(request)发送给 **url** 的第二个参数 **view function**。**urlpatterns**中的顺序很重要,因为Django一旦找到匹配就会停止搜索。如果 Django 在 URLconf 中没有找到匹配项,他会通过 **Page Not Found** 的错误处理代码抛出一个 **404** 异常。 84 | 85 | 这是 **url** 方法的剖析: 86 | ```python 87 | def url(regex, view, kwargs=None, name=None): 88 | # ... 89 | ``` 90 | 91 | - **regex**: 匹配 URL patterns 的正则表达式。注意:正则表达式会忽略掉 **GET** 或者 **POST** 的参数。在一个 **http://127.0.0.1:8000/boards/?page=2** 的请求中,只有 **/boards/** 会被处理。 92 | - **view**: view 的方法被用来处理用户请求所匹配的 URL,它也接受 被用于引用外部的 **urls.py** 文件的 **django.conf.urls.include** 函数的返回。例如你可以使用它来定义一组特定于应用的URLs,使用前缀将其包含在根 URLconf 中。我们会在后面继续探讨这个概念。 93 | - **kwargs**:传递给目标视图的任意关键字参数,它通常用于在可重用视图上进行一些简单的定制,我们不是经常使用它。 94 | - **name:**:给定 URL 的唯一标识符。他是一个非常重要的特征。要始终记得为你的 URLs 命名。所以,很重要的一点是:不要在 views(视图) 或者 templates(模板) 中写很复杂的 URLs 代码, 并始终通过它的名字去引用 URLs。 95 | 96 | 97 | [1]: https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-3/wireframe-topics.png -------------------------------------------------------------------------------- /Fundamentals.md: -------------------------------------------------------------------------------- 1 | # 一个完整的 Django 入门指南 - 第2部分 2 | 3 | >译者:liuzhijun 4 | >原文: 5 | https://simpleisbetterthancomplex.com/series/2017/09/11/a-complete-beginners-guide-to-django-part-2.html 6 | 7 | 8 | 9 | ![featured](./statics/2-1.jpg) 10 | 11 | 12 | ### 前言 13 | 14 | 欢迎来到 Django 教程的第二节,在第一节中,我们安装了项目所需要的一切,希望你安装的是 Python3.6,并且在虚拟环境中运行 Django1.11,这节课我们继续在这个项目上编写代码。 15 | 16 | 咱们先讨论一些项目的背景知识,然后再学习 Django 的基础,包括:模型(models),管理后台(admin),视图(views),模板(templates),和路由(URLs) 17 | 18 | 动手吧! 19 | 20 | ### 论坛项目 21 | 22 | 我不知道你是怎样认为的,个人觉得,通过看实际例子和代码片段可以学到东西,但就我个人而言,当你在例子中读到诸如 `class A` 和 `class B` 这样的代码,或者看到诸如 `foo(bar)` 这样的例子时,是很难解释清楚这些概念的,所以,我不想让你这样做。(译注:作者要表达的意思是光写些demo例子意义并不大,而是要做些实际的项目才有帮助) 23 | 24 | 所以,在进入模型,视图等其它有趣的部分之前,先让我们花点时间,简要地讨论我们将要开发的这个项目。 25 | 26 | 如果你已经有了 Web 开发的经验并且觉得它太繁琐了,那么你可以浏览一下图片以了解我们将要构建的内容,然后直接跳转到本教程的模型部分。 27 | 28 | 但是如果你对 Web 开发不熟悉,我强烈建议你继续阅读下去。我将为你提供关于Web应用程序建模和设计上的一些见解。Web开发和软件开发可不仅仅只是编码。 29 | 30 | ![pic2-1](./statics/2-2.jpg) 31 | 32 | 33 | 34 | ### 用例图 35 | 36 | 我们的项目是一个论坛系统,整个项目的构思是维护几个论坛版块(boards),每个版块像一个分类一样。在指定的版块里面,用户可以通过创建新主题(Topic)开始讨论,其他用户可以参与讨论回复。 37 | 38 | 我们需要找到一种方法来区分普通用户和管理员用户,因为只有管理员可以创建版块。下图概述了主要的用例和每种类型的用户角色: 39 | 40 | ![usercase](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/use-case-diagram.png) 41 | 42 | 图1:Web Board提供的核心功能用例图 43 | 44 | 45 | ### 类图 46 | 47 | 从用例图中,我们可以开始思考项目所需的实体类有哪些。这些实体就是我们要创建的模型,它与我们的Django应用程序处理的数据非常密切。 48 | 49 | 为了能够实现上面描述的用例,我们需要至少实现下面几个模型:Board,Topic,Post和User。 50 | 51 | ![class](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/basic-class-diagram.png) 52 | 53 | 图2:Web Board类图 54 | 55 | * Board:版块 56 | * Topic:主题 57 | * Post:帖子(译注:其实就是主题的回复或评论) 58 | 59 | 花点时间考虑模型之间如何相互关联也很重要。类与类之间的实线告诉我们,在一个主题(Topic)中,我们需要有一个字段(译注:其实就是通过外键来关联)来确定它属于哪个版块(Board)。同样,帖子(Post)也需要一个字段来表示它属于哪个主题,这样我们就可以列出在特定主题内创建的帖子。最后,我们需要一个字段来表示主题是谁发起的,帖子是谁发的。 60 | 61 | 62 | 用户和版块之间也有联系,谁创建的版块。但是这些信息与应用程序无关。还有其他方法可以跟踪这些信息,稍后您会看到。 63 | 64 | 现在我们的类图有基本的表现形式,我们还要考虑这些模型将承载哪些信息。这很容易让事情变得复杂,所以试着先把重要的内容列出来,这些内容是我们启动项目需要的信息。后面我们再使用 Django 的迁移(Migrations)功能来改进模型,您将在下一节中详细了解这些内容。 65 | 66 | 但就目前而言,这是模型最基本的内容: 67 | 68 | ![models](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/class-diagram.png) 69 | 70 | 图3:强调类(模型)之间关系的类图 71 | 72 | 73 | 这个类图强调的是模型之间的关系,这些线条和箭头最终会在稍后转换为字段。 74 | 75 | 对于 **Board** 模型,我们将从两个字段开始:name 和 description。 name字段必须是唯一的,为了避免有重复的名称。description 用于说明这个版块是做什么用的。 76 | 77 | **Topic** 模型包括四个字段:subject 表示主题内容,last_update 用来定义话题的排序,starter 用来识别谁发起的话题,board 用于指定它属于哪个版块。 78 | 79 | **Post** 模型有一个 message 字段,用于存储回复的内容,created_at 在排序时候用(最先发表的帖子排最前面),updated_at 告诉用户是否更新了内容,同时,还需要有对应的 User 模型的引用,Post 由谁创建的和谁更新的。 80 | 81 | 最后是 User 模型。在类图中,我只提到了字段 username,password,email, is_superuser 标志,因为这几乎是我们现在要使用的所有东西。 82 | 83 | 需要注意的是,我们不需要创建 User 模型,因为Django已经在contrib包中内置了User模型,我们将直接拿来用。 84 | 85 | 关于类图之间的对应关系(数字 1,0..* 等等),这里教你如何阅读: 86 | 87 | 一个topic 必须与一个(1)Board(这意味着它不能为空)相关联,但是 Board 下面可能与许多个或者0个 topic 关联 (0..*)。这意味着 Board 下面可能没有主题。(译注:一对多关系) 88 | 89 | ![board](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/class-diagram-board-topic.png) 90 | 91 | 一个 Topic 至少有一个 Post(发起话题时,同时会发布一个帖子),并且它也可能有许多 Post(1..*)。一个Post 必须与一个并且只有一个Topic(1)相关联。 92 | 93 | ![post](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/class-diagram-topic-post.png) 94 | 95 | 一个 Topic 必须有一个且只有一个 User 相关联,topic 的发起者是(1)。而一个用户可能有很多或者没有 topic(0..*)。 96 | 97 | ![topic](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/class-diagram-topic-user.png) 98 | 99 | 100 | Post 必须有一个并且只有一个与之关联的用户,用户可以有许多或没有 Post(0..*)。Post 和 User之间的第二个关联是直接关联(参见该行最后的箭头),就是 Post 可以被用户修改(updated_by),updated_by 有可能是空(Post 没有被修改) 101 | 102 | 103 | 画这个类图的另一种方法是强调字段而不是模型之间的关系: 104 | 105 | ![class](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/class-diagram-attributes.png) 106 | 107 | 图4:强调类(模型)与属性(字段)的类图 108 | 109 | 上面的表示方式与前面的表示方式是对等的,不过这种方式更接近我们将要使用 Django Models API 设计的内容。在这种表示方式中,我们可以更清楚地看到,在 Post 模型中,关联了 Topic,created_by(创建者)和 updated_by(更新者)字段。另一个值得注意的事情是,在 Topic 模型中,有一个名为 `posts()`的操作(一个类方法)。我们将通过反向关系来实现这一目标,Django 将自动在数据库中执行查询以返回特定主题的所有帖子列表。 110 | 111 | 112 | 好了,现在已经够UML了!为了绘制本节介绍的图表,我使用了 [StarUML](http://staruml.io/) 工具。 113 | 114 | 115 | ### 线框图(原型图) 116 | 117 | 花了一些时间来设计应用程序的模型后,我喜欢创建一些线框来定义需要完成的工作,并且清楚地了解我们将要做什么。 118 | 119 | ![2-3.jpg](./statics/2-3.jpg) 120 | 121 | 基于线框图,我们可以更深入地了解应用程序中涉及的实体。 122 | 123 | 首先,我们需要在主页上显示所有版块: 124 | 125 | ![boards](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/wireframe-boards.png) 126 | 127 | 图5:论坛项目线框主页列出所有可用的版块。 128 | 129 | 如果用户点击一个链接,比如点击Django版块,它应该列出所有Django相关的主题: 130 | 131 | ![topics](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/wireframe-topics.png) 132 | 133 | 图6:论坛项目线框图列出了Django版块中的所有主题 134 | 135 | 这里有两个入口:用户点击“new topic“ 按钮创建新主题,或者点击主题链接查看或参与讨论。 136 | 137 | “new topic” 页面: 138 | 139 | ![topic](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/wireframe-new-topic.png) 140 | 141 | 142 | 现在,主题页面显示了帖子和讨论: 143 | 144 | ![](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/wireframe-posts.png) 145 | 146 | 如果用户点击回复按钮,将看到下面这个页面,并以倒序的方式(最新的在第一个)显示帖子列表: 147 | 148 | ![](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/wireframe-reply.png) 149 | 150 | 绘制这些线框,你可以使用[draw.io](https://draw.io/)服务,它是免费的。 151 | -------------------------------------------------------------------------------- /GettingStarted.md: -------------------------------------------------------------------------------- 1 | # 一个完整的Django入门指南 - 第1部分 2 | 3 | > 译者:[vimiix](https://github.com/vimiix) 4 | > 5 | > 原文地址:[https://simpleisbetterthancomplex.com/series/2017/09/04/a-complete-beginners-guide-to-django-part-1.html](https://simpleisbetterthancomplex.com/series/2017/09/04/a-complete-beginners-guide-to-django-part-1.html) 6 | 7 | 8 | ![](./statics/1-1.jpg) 9 | 10 | ---- 11 | 12 | ## 前言 13 | 14 | ![](./statics/1-2.jpg) 15 | 16 | 今天我将开始一个关于 Django 基础知识的全新系列教程。这是一个开始学习 Django 的完整入门指南。教程材料一共会被分为七个部分。我们将从安装,开发环境的准备,模型,视图,模板,URL到更高级的主题(如迁移,测试和部署)中详细探讨所有基本概念。 17 | 18 | 我想做一些不一样的事情。一个容易学习,内容丰富且不失趣味的教程。我的想法是在文章中穿插一些漫画的方式来演示说明相应的概念和场景。我希望大家能够享受这种阅读! 19 | 20 | 但是在我们开始之前... 21 | 22 | 当年我在一所大学担任代课教授时,我曾经在计算机科学专业给新来的学生讲授网络开发学科。那时我总是会用下面这个孔夫子的名言开始新的课程: 23 | 24 | ![](./statics/1-3.jpg) 25 | 26 | *(译者注:不确定是孔子讲的,但这句话早在中国古代就有所提到,出自荀子《儒效篇》“不闻不若闻之,闻之不若见之,见之不若知之,知之不若行之;学至于行之而止矣”)* 27 | 28 | 所以,请动起手来!不要只是阅读教程。我们一起来练习!通过实践和练习你会收获的更多。 29 | 30 | --- 31 | 32 | ## 为什么要学习Django? 33 | 34 | Django是一个用python编写的Web框架。Web框架是一种软件,基于web框架可以开发动态网站,各种应用程序以及服务。它提供了一系列工具和功能,可以解决许多与Web开发相关的常见问题,比如:安全功能,数据库访问,会话,模板处理,URL路由,国际化,本地化,等等。 35 | 36 | 使用诸如 Django 之类的网络框架,使我们能够以标准化的方式快速开发安全可靠的Web应用程序,而无需重新发明轮子。 37 | 38 | 那么,Django有什么特别之处呢?对于初学者来说,它是一个Python Web框架,这意味着你可以受益于各种各样的开源库包。[python软件包资料库(pypi)](https://pypi.python.org/pypi)拥有超过11.6万个软件包(2017年9月6日的数据)。如果当你想要解决一个特定的问题的时候,可能有人已经为它实现了一个库来供你使用。 39 | 40 | Django是用python编写的最流行的web框架之一。它绝对是最完整的,提供了各种各样的开箱即用的功能,比如用于开发和测试的独立Web服务器,缓存,中间件系统,ORM,模板引擎,表单处理,基于Python单元测试的工具接口。Django还自带*内部电池*,提供内置应用程序,比如一个认证系统,一个可用于 `CRUD`(增删改查) 操作并且自动生成页面的后台管理界面,生成订阅文档(RSS/Atom),站点地图等。甚至在django中内建了一个地理信息系统(GIS)框架。 41 | 42 | Django的开发得到了[Django软件基金会](https://www.djangoproject.com/foundation/)的支持,并且由jetbrains和instagram等公司赞助。Django现在已经存在了相当长的一段时间了。到现在为止,活跃的项目开发时间已经超过12年,这也证明了它是一个成熟,可靠和安全的网络框架。 43 | 44 | ### 谁在使用Django? 45 | 46 | 知道谁在使用Django是很好的,同时也想一想你可以用它来做些什么。在使用Django的大型网站有:[Instagram](https://instagram.com/),[Disqus](https://disqus.com/),[Mozilla](https://www.mozilla.org/),[Bitbucket](https://bitbucket.org/),[Last.fm](https://www.last.fm/),[国家地理](http://www.nationalgeographic.com/)。 47 | 48 | 想知道更多的示例,你可以到[Django Sites](https://www.djangosites.org/)数据库中查看,它提供超过五千个Django驱动的网站列表。 49 | 50 | 顺便说一下,去年在Django 2016年发布会上,Django核心开发人员,Instagram员工 51 | `carl meyer`,就[Instagram如何大规模使用Django以及它如何支持他们的用户增长](https://www.youtube.com/watch?v=lx5WQjXLlq8)做过一次分享。这是个一小时的演讲,如果你有兴趣学习了解更多的话,这是一次很有趣的演讲。 52 | 53 | --- 54 | 55 | ## 安装 56 | 57 | 我们需要做的第一件事是在我们的电脑上安装一些程序,以便能够开始使用django。基本的设置包括安装**Python**,**Virtualenv**和**Django**。 58 | 59 | ![](./statics/1-4.jpg) 60 | 61 | 使用虚拟环境不是强制性的,但是我还是强烈建议大家这样做。如果你是一个初学者,那么最好形成一个良好的开端。 62 | 63 | 当你在用 Django 开发一个网站或者一个Web项目的时候,不得不安装外部库以支持开发是非常常见的事情。使用虚拟环境,你开发的每个项目都会有其独立的环境。这样的话,包之间的依赖关系不会发生冲突。同时也使得你能在不同Django版本上运行的本地机器的项目。 64 | 65 | 在后面你会看到使用它是非常简单的! 66 | 67 | ## 安装 Python 3.6.2 68 | 69 | 我们想要做的第一件事是安装最新版的Python,那就是**Python 3.6.2**。至少是在我写这篇教程的时候。如果有更新的版本,请使用新版。接下来的步骤也应该保持大致相同的做法。 70 | 71 | 我们将使用Python 3,因为大部分主要的Python库已经被移植到python 3,并且下一个主要的django版本(2.x)也将不再支持python 2。所以Python 3是正确的选择。 72 | 73 | 最好的方法是通过[**Homebrew**](https://brew.sh/)安装。 如果你的Mac还没有安装Homebrew的话,在终端中执行下面的命令: 74 | 75 | ```bash 76 | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 77 | ``` 78 | 79 | 如果你没有安装命令行工具(**Command Line Tools**),Homebrew的安装可能需要稍长一点的时间。但它会帮助你处理好一切,所以不用担心。只需要坐下来等到安装完成即可。 80 | 81 | 当你看到以下消息时,就代表安装完成了: 82 | 83 | ``` 84 | ==> Installation successful! 85 | 86 | ==> Homebrew has enabled anonymous aggregate user behaviour analytics. 87 | Read the analytics documentation (and how to opt-out) here: 88 | https://docs.brew.sh/Analytics.html 89 | 90 | ==> Next steps: 91 | - Run `brew help` to get started 92 | - Further documentation: 93 | https://docs.brew.sh 94 | ``` 95 | 96 | 执行下面的命令来安装Python 3: 97 | 98 | ```bash 99 | brew install python3 100 | ``` 101 | 102 | 由于macOS原本已经安装了python 2,所以在安装python 3之后,你将可以同时使用这两个版本。 103 | 104 | 需要运行Python 2的话,在终端中通过命令 `python` 启动。如果想运行Python 3,则使用`python3` 来启动。 105 | 106 | 我们可以在终端中测试一下: 107 | 108 | ```bash 109 | python3 --version 110 | Python 3.6.2 111 | ``` 112 | 113 | ![](./statics/1-5.png) 114 | 115 | 很棒,python已经启动并正在运行。下一步:虚拟环境! 116 | 117 | ## 安装 Virtualenv 118 | 119 | 接下来这一步,我们将通过**pip**(*一个管理和安装Python包的工具*)来安装**Virtualenv**。 120 | 121 | 请注意,Homebrew已经为你安装好了**pip**,在python 3.6.2下的名称为 `pip3`。 122 | 123 | 在终端中,执行下面的命令: 124 | 125 | ```bash 126 | sudo pip3 install virtualenv 127 | ``` 128 | 129 | ![](./statics/1-6.png) 130 | 131 | 到目前为止,我们执行的安装都是在操作系统环境下运行的。从现在开始,我们安装的所有东西,包括django本身,都将安装在虚拟环境中。 132 | 133 | 这样想一下:对于你开始的每个Django项目,你首先会为它创建一个虚拟环境。这就像每个Django项目都有一个沙盒。所以你随意运行,安装软件包,卸载软件包而不会破坏任何东西。 134 | 135 | 我习惯在电脑上创建一个名为**Development**的文件夹。然后,我用它来组织我所有的项目和网站。但你也可以按照接下来的步骤创建适合你自己的目录。 136 | 137 | 通常,我首先在**Development**文件夹中创建一个项目名称的新文件夹。既然这将是我们第一个项目,我们没必要挑选一个独特的名字。现在,我们可以称之为**myproject**。 138 | 139 | ```bash 140 | mkdir myproject 141 | cd myproject 142 | ``` 143 | 144 | ![](./statics/1-7.png) 145 | 146 | 这个文件夹是级别较高的目录,将存储与我们的Django项目相关的所有文件和东西,包括它的虚拟环境。 147 | 148 | 所以让我们开始创建我们的第一个虚拟环境并安装django。 149 | 150 | 在**myproject**文件夹中: 151 | 152 | ```bash 153 | virtualenv venv -p python3 154 | ``` 155 | 156 | ![](./statics/1-8.png) 157 | 158 | 这样我们的虚拟环境就创建好了。在开始使用它之前,我们需要先激活一下环境: 159 | 160 | ```bash 161 | source venv/bin/activate 162 | ``` 163 | 164 | 如果你在命令行前面看到 **(venv)**,就代表激活成功了,就像这样: 165 | 166 | ![](./statics/1-9.png) 167 | 168 | 让我们试着了解一下这里发生了什么。我们创建了一个名为**venv**的特殊文件夹。该文件夹内包含了一个python的副本。在我们激活了**venv**环境之后,当我们运行`Python`命令时,它将使用我们存储在venv里面的本地副本,而不是我们之前在操作系统中安装的那个。 169 | 170 | 另一个重要的事情是,**pip**程序也已经安装好了,当我们使用它来安装Python的软件包(比如Django)时,它将被安装在**venv**环境中。 171 | 172 | 请注意,当我们启用venv时,我们将使用命令`python`(而不是`python3`)来调用Python 3.6.2,并且仅使用`pip`(而不是`pip3`)来安装软件包。 173 | 174 | 顺便说一句,要想退出**venv**环境,运行下面的命令: 175 | 176 | ```bash 177 | deactivate 178 | ``` 179 | 180 | 但是,我们现在先保持激活状态来进行下一步。 181 | 182 | ## 安装 Django 1.11.4 183 | 184 | 很简单,现在我们已经启动了**venv**,运行以下命令来安装django: 185 | 186 | ```bash 187 | pip install django==1.11.4 188 | 189 | # 译注:目前django已经升级到2.x版本,这里为了跟后续教程内容保持一直,所以必须指定版本号进行安装 190 | # 除非你有能力 debug,否则不建议你使用django 2.x 191 | ``` 192 | 193 | ![](./statics/1-10.png) 194 | 195 | 现在一切就绪! 196 | 197 | ![](./statics/1-11.png) 198 | 199 | -------------------------------------------------------------------------------- /GettingStarted-2.md: -------------------------------------------------------------------------------- 1 | # 一个完整的Django入门指南 - 第1部分 2 | 3 | > 译者:[vimiix](https://github.com/vimiix) 4 | > 5 | > 原文地址:[https://simpleisbetterthancomplex.com/series/2017/09/04/a-complete-beginners-guide-to-django-part-1.html](https://simpleisbetterthancomplex.com/series/2017/09/04/a-complete-beginners-guide-to-django-part-1.html) 6 | 7 | 8 | ![](./statics/1-1.jpg) 9 | 10 | 11 | 12 | ## 启动一个新项目 13 | 14 | 执行下面的命令来创建一个新的 Django 项目: 15 | 16 | ```bash 17 | django-admin startproject myproject 18 | ``` 19 | 20 | 命令行工具**django-admin**会在安装Django的时候一起自动安装好。 21 | 22 | 执行了上面的命令以后,系统会为Django项目生成基础文件夹结构。 23 | 24 | 现在,我们的**myproject**目录结构如下所示: 25 | 26 | ``` 27 | myproject/ <-- 高级别的文件夹 28 | |-- myproject/ <-- Django项目文件夹 29 | | |-- myproject/ 30 | | | |-- __init__.py 31 | | | |-- settings.py 32 | | | |-- urls.py 33 | | | |-- wsgi.py 34 | | +-- manage.py 35 | +-- venv/ <-- 虚拟环境文件夹 36 | ``` 37 | 38 | 我们最初的项目结构由五个文件组成: 39 | 40 | * **manage.py**:使用**django-admin**命令行工具的快捷方式。它用于运行与我们项目相关的管理命令。我们将使用它来运行开发服务器,运行测试,创建迁移等等。 41 | * **__init.py**:这个空文件告诉python这个文件夹是一个python包。 42 | * **settings.py**:这个文件包含了所有的项目配置。将来我们会一直提到这个文件! 43 | * **urls.py**:这个文件负责映射我们项目中的路由和路径。例如,如果你想在访问URL `/ about/` 时显示某些内容,则必须先在这里做映射关系。 44 | * **wsgi.py**:该文件是用于部署的简单网关接口。你可以暂且先不用关心她的内容,就先让他在那里就好了。 45 | 46 | django自带了一个简单的网络服务器。在开发过程中非常方便,所以我们无需安装任何其他软件即可在本地运行项目。我们可以通过执行命令来测试一下它: 47 | 48 | ```bash 49 | python manage.py runserver 50 | ``` 51 | 52 | 现在,你可以忽略终端中出现的迁移错误;我们将在稍后讨论。 53 | 54 | 现在在Web浏览器中打开URL:**[http://127.0.0.1:8000](http://127.0.0.1:8000)**,你应该看到下面的页面: 55 | 56 | ![](./statics/1-12.png) 57 | 58 | 使用组合键 `Control + C`来终止开发服务器。 59 | 60 | ---- 61 | 62 | 63 | ## Django 应用 64 | 65 | 在Django的哲学中,我们有两个重要的概念: 66 | 67 | * **app**:是一个可以做完成某件事情的Web应用程序。一个应用程序通常由一组**models(数据库表)**,**views(视图)**,**templates(模板)**,**tests(测试)** 组成。 68 | * **project**:是配置和应用程序的集合。一个项目可以由多个应用程序或一个应用程序组成。 69 | 70 | 请注意,如果没有一个**project**,你就无法运行Django应用程序。像博客这样的简单网站可以完全在单个应用程序中编写,例如可以将其命名为**blog**或**weblog**。 71 | 72 | ![](./statics/1-13.png) 73 | 74 | 这是组织源代码的一种方式。现在刚开始,判断什么是或不是应用程序这些还不太重要。包括如何组织代码等。现在不用担心那些问题!首先让我们对Django的API和基础知识进行梳理一遍。 75 | 76 | 好的!那么,为了方便说明,我们来创建一个简单的*网络论坛*或*讨论区*。要创建我们的第一个应用程序,请跳转到**manage.py**文件所在的目录并执行以下命令: 77 | 78 | ```bash 79 | django-admin startapp boards 80 | ``` 81 | 82 | 注意!我们这次使用的命令是**startapp**。 83 | 84 | 通过这条指令,系统会给我们创建以下目录结构: 85 | 86 | ``` 87 | myproject/ 88 | |-- myproject/ 89 | | |-- boards/ <-- 我们新的Django应用(app)! 90 | | | |-- migrations/ 91 | | | | +-- __init__.py 92 | | | |-- __init__.py 93 | | | |-- admin.py 94 | | | |-- apps.py 95 | | | |-- models.py 96 | | | |-- tests.py 97 | | | +-- views.py 98 | | |-- myproject/ 99 | | | |-- __init__.py 100 | | | |-- settings.py 101 | | | |-- urls.py 102 | | | |-- wsgi.py 103 | | +-- manage.py 104 | +-- venv/ 105 | ``` 106 | 107 | 下面,我们来探讨每个文件的作用: 108 | 109 | * **migrations/**:在这个文件夹里,Django会存储一些文件以跟踪你在**models.py**文件中创建的变更,用来保持数据库和**models.py**的同步。 110 | * **admin.py**:这个文件为一个django内置的应用程序**Django Admin**的配置文件。 111 | * **apps.py**:这是应用程序本身的配置文件。 112 | * **models.py**:这里是我们定义Web应用程序数据实例的地方。models会由Django自动转换为数据库表。 113 | * **tests.py**:这个文件用来写当前应用程序的单元测试。 114 | * **views.py**:这是我们处理Web应用程序请求(request)/响应(resopnse)周期的文件。 115 | 116 | 现在我们创建了我们的第一个应用程序,让我们来配置一下项目以便启用这个应用程序。 117 | 118 | 要做到这一点,打开**settings.py**并尝试找到`INSTALLED_APPS`变量: 119 | 120 | **settings.py** 121 | 122 | ```python 123 | INSTALLED_APPS = [ 124 | 'django.contrib.admin', 125 | 'django.contrib.auth', 126 | 'django.contrib.contenttypes', 127 | 'django.contrib.sessions', 128 | 'django.contrib.messages', 129 | 'django.contrib.staticfiles', 130 | ] 131 | ``` 132 | 133 | 如你所见,Django默认已经安装了6个内置应用程序。它们提供大多数Web应用程序所需的常用功能,如身份验证,会话,静态文件管理(图像,JavaScript,CSS等)等。 134 | 135 | 我们将会在本系列教程中探索这些应用程序。但现在,先不管它们,只需将我们的应用程序**boards**添加到`INSTALLED_APPS`列表即可: 136 | 137 | ```python 138 | INSTALLED_APPS = [ 139 | 'django.contrib.admin', 140 | 'django.contrib.auth', 141 | 'django.contrib.contenttypes', 142 | 'django.contrib.sessions', 143 | 'django.contrib.messages', 144 | 'django.contrib.staticfiles', 145 | 146 | 'boards', # 译者注:建议和作者一样空一行来区别内置app和自定义的app 147 | ] 148 | ``` 149 | 150 | 使用前面漫画正方形和圆圈的比喻,黄色的圆圈就是我们的**boards**应用程序,**django.contrib.admin, django.contrib.auth**等就是红色的圆圈。 151 | 152 | ---- 153 | 154 | ## Hello, World! 155 | 156 | 现在来写我们的第一个**视图(view)**。我们将在下一篇教程中详细探讨它。但现在,让我们试试看看如何用Django创建一个新页面。 157 | 158 | 打开**boards**应用程序中的**views.py**文件,并添加以下代码: 159 | 160 | **views.py** 161 | 162 | ```python 163 | from django.http import HttpResponse 164 | 165 | def home(request): 166 | return HttpResponse('Hello, World!') 167 | 168 | ``` 169 | 170 | 视图是接收`httprequest`对象并返回一个`httpresponse`对象的Python函数。接收 *request* 作为参数并返回 *response* 作为结果。这个流程你必须记住! 171 | 172 | 我们在这里定义了一个简单的视图,命名为**home**,它只是简单地返回一个信息,一个字符串**hello,world!**。 173 | 174 | 现在我们必须告诉Django什么时候会调用这个view。这需要在urls.py文件中完成: 175 | 176 | **urls.py** 177 | 178 | ```python 179 | from django.conf.urls import url 180 | from django.contrib import admin 181 | 182 | from boards import views 183 | 184 | urlpatterns = [ 185 | url(r'^$', views.home, name='home'), 186 | url(r'^admin/', admin.site.urls), 187 | ] 188 | ``` 189 | 190 | 如果你将上面的代码片段与你的**urls.py**文件进行比较,你会注意到我添加了以下新代码:`url(r'^ $',views.home,name ='home')`并通过`from boards import views`从我们的应用程序**boards**中导入了**views**模块。 191 | 192 | 和我之前提到的一样,我们将在稍后详细探讨这些概念。 193 | 194 | 现在,Django使用**正则表达式**来匹配请求的URL。对于我们的**home**视图,我使用`^$` 正则,它将匹配一个空路径,也就是主页(这个URL:[http://127.0.0.1:8000](http://127.0.0.1:8000) )。如果我想匹配的URL是 **[http://127.0.0.1:8000/homepage/](http://127.0.0.1:8000/homepage/)** ,那么我的URL正则表达式就会是:`url(r'^homepage/$', views.home, name='home')`。 195 | 196 | 我们来看看会发生什么: 197 | 198 | ```bash 199 | python manage.py runserver 200 | ``` 201 | 202 | 在一个Web浏览器中,打开 **[http://127.0.0.1:8000](http://127.0.0.1:8000)** 这个链接: 203 | 204 | ![](./statics/1-14.png) 205 | 206 | 就是这样!你刚刚成功创建了你的第一个视图。 207 | 208 | ---- 209 | 210 | ## 总结 211 | 212 | 这是本系列教程的第一部分。在本教程中,我们学习了如何安装最新的Python版本以及如何设置开发环境。我们还介绍了虚拟环境,开始了我们的第一个django项目,并已经创建了我们的初始应用程序。 213 | 214 | 我希望你会喜欢第一部分!第二部分将于2017年9月11日下周发布。它将涉及模型,视图,模板和URLs。我们将一起探索Django所有的基础知识!如果您希望在第二部分发布时收到通知,可以订阅我们的[邮件列表](http://eepurl.com/b0gR51)。 215 | 216 | 为了让我们能够保持学习过程中页面同步,我在Github上提供了源代码。这个项目的当前状态可以在**release tag v0.1-lw**下找到。下面是直达链接: 217 | 218 | [https://github.com/sibtc/django-beginners-guide/tree/v0.1-lw](https://github.com/sibtc/django-beginners-guide/tree/v0.1-lw) -------------------------------------------------------------------------------- /Fundamentals-2.md: -------------------------------------------------------------------------------- 1 | # 一个完整的 Django 入门指南 - 第2部分 2 | 3 | >译者:liuzhijun 4 | >原文: 5 | https://simpleisbetterthancomplex.com/series/2017/09/11/a-complete-beginners-guide-to-django-part-2.html 6 | 7 | ![featured](./statics/2-1.jpg) 8 | 9 | ### 模型 10 | 11 | 12 | 这些模型基本上代表了应用程序的数据库设计。我们在本节中要做的是创建 Django 所表示的类,这些类就是在上一节中建模的类:Board,Topic和Post。User 模型被命名为内置应用叫 **auth**,它以命名空间 django.contrib.auth 的形式出现在 `INSTALLED_APPS` 配置中。 13 | 14 | 我们要做的工作都在 boards/models.py 文件中。以下是我们在Django应用程序中如何表示类图的代码: 15 | 16 | 17 | ```python 18 | from django.db import models 19 | from django.contrib.auth.models import User 20 | 21 | 22 | class Board(models.Model): 23 | name = models.CharField(max_length=30, unique=True) 24 | description = models.CharField(max_length=100) 25 | 26 | 27 | class Topic(models.Model): 28 | subject = models.CharField(max_length=255) 29 | last_updated = models.DateTimeField(auto_now_add=True) 30 | board = models.ForeignKey(Board, related_name='topics') 31 | starter = models.ForeignKey(User, related_name='topics') 32 | 33 | 34 | class Post(models.Model): 35 | message = models.TextField(max_length=4000) 36 | topic = models.ForeignKey(Topic, related_name='posts') 37 | created_at = models.DateTimeField(auto_now_add=True) 38 | updated_at = models.DateTimeField(null=True) 39 | created_by = models.ForeignKey(User, related_name='posts') 40 | updated_by = models.ForeignKey(User, null=True, related_name='+') 41 | ``` 42 | 43 | 所有模型都是**django.db.models.Model**类的子类。每个类将被转换为数据库表。每个字段由 **django.db.models.Field**子类(内置在Django core)的实例表示,它们并将被转换为数据库的列。 44 | 45 | 字段 CharField,DateTimeField等等,都是 **django.db.models.Field** 的子类,包含在Django的核心里面-随时可以使用。 46 | 47 | 在这里,我们仅使用 CharField,TextField,DateTimeField,和ForeignKey 字段来定义我们的模型。不过在Django提供了更广泛的选择来代表不同类型的数据,例如 IntegerField,BooleanField, DecimalField和其它一些字段。我们会在需要的时候提及它们。 48 | 49 | 有些字段需要参数,例如CharField。我们应该始终设定一个 `max_length`。这些信息将用于创建数据库列。Django需要知道数据库列需要多大。该 `max_length`参数也将被Django Forms API用来验证用户输入。 50 | 51 | 在`Board`模型定义中,更具体地说,在`name`字段中,我们设置了参数 `unique=True`,顾名思义,它将强制数据库级别字段的唯一性。 52 | 53 | 在`Post`模型中,`created_at`字段有一个可选参数,`auto_now_add`设置为`True`。这将告诉Django创建`Post`对象时为当前日期和时间。 54 | 55 | 模型之间的关系使用`ForeignKey`字段。它将在模型之间创建一个连接,并在数据库级别创建适当的关系(译注:外键关联)。该`ForeignKey`字段需要一个位置参数`related_name`,用于引用它关联的模型。(译注:例如 created_by 是外键字段,关联的User模型,表明这个帖子是谁创建的,related_name=posts 表示在 User 那边可以使用 user.posts 来查看这个用户创建了哪些帖子) 56 | 57 | 例如,在`Topic`模型中,`board`字段是`Board`模型的`ForeignKey`。它告诉Django,一个`Topic`实例只涉及一个Board实例。`related_name`参数将用于创建反向关系,`Board`实例通过属性`topics`访问属于这个版块下的`Topic`列表。 58 | 59 | Django自动创建这种反向关系,`related_name`是可选项。但是,如果我们不为它设置一个名称,Django会自动生成它:`(class_name)_set`。例如,在`Board`模型中,所有`Topic`列表将用`topic_set`属性表示。而这里我们将其重新命名为了`topics`,以使其感觉更自然。 60 | 61 | 在`Post`模型中,该`updated_by`字段设置`related_name='+'`。这指示Django我们不需要这种反向关系,所以它会被忽略(译注:也就是说我们不需要关系用户修改过哪些帖子)。 62 | 63 | 下面您可以看到类图和Django模型的源代码之间的比较,绿线表示我们如何处理反向关系。 64 | 65 | ![relate](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/class-diagram-django-models.png) 66 | 67 | 这时,你可能会问自己:“主键/ ID呢?”?如果我们没有为模型指定主键,Django会自动为我们生成它。所以现在一切正常。在下一节中,您将看到它是如何工作的。 68 | ### 迁移模型 69 | 70 | 下一步是告诉Django创建数据库,以便我们可以开始使用它。 71 | 72 | 打开终端 ,激活虚拟环境,转到 manage.py文件所在的文件夹,然后运行以下命令: 73 | 74 | ```python 75 | python manage.py makemigrations 76 | ``` 77 | 你会看到输出的内容是: 78 | 79 | ``` 80 | Migrations for 'boards': 81 | boards/migrations/0001_initial.py 82 | - Create model Board 83 | - Create model Post 84 | - Create model Topic 85 | - Add field topic to post 86 | - Add field updated_by to post 87 | 88 | ``` 89 | 90 | 此时,Django 在 boards/migrations 目录创建了一个名为 `0001_initial.py`的文件。它代表了应用程序模型的当前状态。在下一步,Django将使用该文件创建表和列。 91 | 92 | 93 | 迁移文件将被翻译成SQL语句。如果您熟悉SQL,则可以运行以下命令来检验将是要被数据库执行的SQL指令 94 | 95 | ```python 96 | python manage.py sqlmigrate boards 0001 97 | 98 | ``` 99 | 100 | 如果你不熟悉SQL,也不要担心。在本系列教程中,我们不会直接使用SQL。所有的工作都将使用Django ORM来完成,它是一个与数据库进行通信的抽象层。 101 | 102 | 下一步是将我们生成的迁移文件应用到数据库: 103 | 104 | ```python 105 | python manage.py migrate 106 | 107 | ``` 108 | 109 | 输出内容应该是这样的: 110 | 111 | ```python 112 | Operations to perform: 113 | Apply all migrations: admin, auth, boards, contenttypes, sessions 114 | Running migrations: 115 | Applying contenttypes.0001_initial... OK 116 | Applying auth.0001_initial... OK 117 | Applying admin.0001_initial... OK 118 | Applying admin.0002_logentry_remove_auto_add... OK 119 | Applying contenttypes.0002_remove_content_type_name... OK 120 | Applying auth.0002_alter_permission_name_max_length... OK 121 | Applying auth.0003_alter_user_email_max_length... OK 122 | Applying auth.0004_alter_user_username_opts... OK 123 | Applying auth.0005_alter_user_last_login_null... OK 124 | Applying auth.0006_require_contenttypes_0002... OK 125 | Applying auth.0007_alter_validators_add_error_messages... OK 126 | Applying auth.0008_alter_user_username_max_length... OK 127 | Applying boards.0001_initial... OK 128 | Applying sessions.0001_initial... OK 129 | ``` 130 | 131 | 因为这是我们第一次迁移数据库,所以`migrate`命令把Django contrib app 中现有的迁移文件也执行了,这些内置app列在了`INSTALLED_APPS`。这是预料之中的。 132 | 133 | `Applying boards.0001_initial... OK`是我们在上一步中生成的迁移脚本。 134 | 135 | 好了!我们的数据库已经可以使用了。 136 | 137 | ![featured](./statics/2-4.jpg) 138 | 139 | >需要注意的是SQLite是一个产品级数据库。SQLite被许多公司用于成千上万的产品,如所有Android和iOS设备,主流的Web浏览器,Windows 10,MacOS等。 140 | 141 | >但这不适合所有情况。SQLite不能与MySQL,PostgreSQL或Oracle等数据库进行比较。大容量的网站,密集型写入的应用程序,大的数据集,高并发性的应用使用SQLite最终都会导致问题。 142 | 143 | >我们将在开发项目期间使用SQLite,因为它很方便,不需要安装其他任何东西。当我们将项目部署到生产环境时,再将切换到PostgreSQL(译注:后续,我们后面可能使用MySQL)。对于简单的网站这种做法没什么问题。但对于复杂的网站,建议在开发和生产中使用相同的数据库。 144 | 145 | 146 | ### 试验 Models API 147 | 148 | 使用Python进行开发的一个重要优点是交互式shell。我一直在使用它。这是一种快速尝试和试验API的方法。 149 | 150 | 您可以使用manage.py 工具加载我们的项目来启动 Python shell : 151 | 152 | ```sh 153 | python manage.py shell 154 | 155 | ``` 156 | 157 | ``` 158 | Python 3.6.2 (default, Jul 17 2017, 16:44:45) 159 | [GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)] on darwin 160 | Type "help", "copyright", "credits" or "license" for more information. 161 | (InteractiveConsole) 162 | >>> 163 | ``` 164 | 165 | 这与直接输入`python`指令来调用交互式控制台是非常相似的,除此之外,项目将被添加到`sys.path`并加载Django。这意味着我们可以在项目中导入我们的模型和其他资源并使用它。 166 | 167 | 让我们从导入Board类开始: 168 | 169 | ```sh 170 | from boards.models import Board 171 | 172 | ``` 173 | 174 | 要创建新的 boarrd 对象,我们可以执行以下操作: 175 | 176 | ```python 177 | board = Board(name='Django', description='This is a board about Django.') 178 | 179 | ``` 180 | 181 | 为了将这个对象保存在数据库中,我们必须调用save方法: 182 | 183 | 184 | ```python 185 | board.save() 186 | 187 | ``` 188 | 189 | `save`方法用于创建和更新对象。这里Django创建了一个新对象,因为这时Board 实例没有id。第一次保存后,Django会自动设置ID: 190 | 191 | ```python 192 | board.id 193 | 1 194 | ``` 195 | 196 | 您可以将其余的字段当做Python属性访问: 197 | 198 | ```python 199 | board.name 200 | 'Django' 201 | ``` 202 | 203 | ```python 204 | board.description 205 | 'This is a board about Django.' 206 | ``` 207 | 208 | 要更新一个值,我们可以这样做: 209 | 210 | ```python 211 | board.description = 'Django discussion board.' 212 | board.save() 213 | ``` 214 | 215 | 每个Django模型都带有一个特殊的属性; 我们称之为**模型管理器(Model Manager)**。n你可以访问它的objects属性。它主要用于数据库操作。例如,我们可以使用它来直接创建一个新的Board对象: 216 | 217 | 218 | ```python 219 | board = Board.objects.create(name='Python', description='General discussion about Python.') 220 | 221 | ``` 222 | 223 | ```python 224 | board.id 225 | 2 226 | ``` 227 | 228 | ```python 229 | board.name 230 | 'Python' 231 | ``` 232 | 233 | 所以,现在我们有两个版块了。我们可以使用`objects`列出数据库中所有现有的版块: 234 | 235 | ```python 236 | Board.objects.all() 237 | , ]> 238 | ``` 239 | 240 | 结果是一个QuerySet。稍后我们会进一步了解。基本上,它是从数据库中查询的对象列表。我们看到有两个对象,但显示的名称是 Board object。这是因为我们尚未实现 Board 的`__str__` 方法。 241 | 242 | 243 | `__str__`方法是对象的字符串表示形式。我们可以使用版块的名称来表示它。 244 | 245 | 首先,退出交互式控制台: 246 | 247 | ```python 248 | exit() 249 | 250 | ``` 251 | 252 | 现在编辑boards app 中的 models.py 文件: 253 | 254 | ```python 255 | class Board(models.Model): 256 | name = models.CharField(max_length=30, unique=True) 257 | description = models.CharField(max_length=100) 258 | 259 | def __str__(self): 260 | return self.name 261 | ``` 262 | 263 | 让我们重新查询,再次打开交互式控制台: 264 | 265 | ```python 266 | python manage.py shell 267 | ``` 268 | 269 | ```python 270 | from boards.models import Board 271 | 272 | Board.objects.all() 273 | , ]> 274 | ``` 275 | 276 | 好多了,对吧? 277 | 278 | 我们可以将这个QuerySet看作一个列表。假设我们想遍历它并打印每个版块的描述: 279 | 280 | ```python 281 | boards_list = Board.objects.all() 282 | for board in boards_list: 283 | print(board.description) 284 | ``` 285 | 结果是: 286 | ```python 287 | Django discussion board. 288 | General discussion about Python. 289 | ``` 290 | 同样,我们可以使用模型的 **Manager** 来查询数据库并返回单个对象。为此,我们要使用 `get` 方法: 291 | ```python 292 | django_board = Board.objects.get(id=1) 293 | 294 | django_board.name 295 | 'Django' 296 | ``` 297 | 298 | 但我们必须小心这种操作。如果我们试图查找一个不存在的对象,例如,查找id=3的版块,它会引发一个异常: 299 | 300 | ```python 301 | board = Board.objects.get(id=3) 302 | 303 | boards.models.DoesNotExist: Board matching query does not exist. 304 | ``` 305 | 306 | 我们可以在`get`上使用模型的任何字段,但最好使用可唯一标识对象的字段来查询。否则,查询可能会返回多个对象,这也会导致异常。 307 | ```python 308 | Board.objects.get(name='Django') 309 | 310 | ``` 311 | 请注意,查询区分大小写,小写“django”不匹配: 312 | 313 | ```python 314 | Board.objects.get(name='django') 315 | boards.models.DoesNotExist: Board matching query does not exist. 316 | ``` 317 | ### 模型操作总结 318 | 下面是我们在本节中关于模型学到的方法和操作总结,并使用Board模型作为参考。大写的 **Board`指类,小写的**board**指**Board**的一个实例(或对象) 319 | 320 | |操作|代码示例| 321 | |:----|:-----| 322 | |创建一个对象而不保存| board = Board()| 323 | |保存一个对象(创建或更新)| board.save()| 324 | |数据库中创建并保存一个对象| Board.objects.create(name='...', description='...')| 325 | |列出所有对象| Board.objects.all()| 326 | |通过字段标识获取单个对象|Board.objects.get(id=1)| 327 | 328 | 在下一小节中,我们将开始编写视图并在HTML页面中显示我们的版块。 329 | -------------------------------------------------------------------------------- /Fundamentals-3.md: -------------------------------------------------------------------------------- 1 | # 一个完整的 Django 入门指南 - 第2部分 2 | 3 | >译者:liuzhijun 4 | >原文: 5 | https://simpleisbetterthancomplex.com/series/2017/09/11/a-complete-beginners-guide-to-django-part-2.html 6 | 7 | 8 | 9 | ### 视图、模板、静态文件 10 | 11 | 目前我们已经有一个视图函数叫`home`,这个视图在我们的应用程序主页上显示为“Hello,World!” 12 | 13 | **myproject/urls.py** 14 | 15 | ```python 16 | from django.conf.urls import url 17 | from django.contrib import admin 18 | 19 | from boards import views 20 | 21 | urlpatterns = [ 22 | url(r'^$', views.home, name='home'), 23 | url(r'^admin/', admin.site.urls), 24 | ] 25 | ``` 26 | 27 | **boards/views.py** 28 | 29 | ```python 30 | from django.http import HttpResponse 31 | 32 | def home(request): 33 | return HttpResponse('Hello, World!') 34 | ``` 35 | 36 | 我们可以从这里开始写。如果你回想起我们的原型图,图5显示了主页应该是什么样子。我们想要做的是在表格中列出一些版块的名单以及其他一些描述信息。 37 | 38 | ![board](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/wireframe-boards.png) 39 | 40 | 首先要做的是导入Board模型并列出所有现有的boards 41 | 42 | **boards/views.py** 43 | 44 | ```python 45 | from django.http import HttpResponse 46 | from .models import Board 47 | 48 | def home(request): 49 | boards = Board.objects.all() 50 | boards_names = list() 51 | 52 | for board in boards: 53 | boards_names.append(board.name) 54 | 55 | response_html = '
'.join(boards_names) 56 | 57 | return HttpResponse(response_html) 58 | ``` 59 | 60 | 结果就是这个简单的HTML页面: 61 | 62 | ![boards](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/boards-homepage-httpresponse.png) 63 | 64 | 等等,我们在这里先停一下。真正的项目里面我们不会这样去渲染HTML。对于这个简单视图函数,我们做的就是列出所有版块,然后渲染部分是Django模板引擎的职责。 65 | 66 | 67 | ### Django 模板引擎设置 68 | 69 | 在manage.py所在的目录创建一个名为 **templates**的新文件夹: 70 | 71 | ```sh 72 | myproject/ 73 | |-- myproject/ 74 | | |-- boards/ 75 | | |-- myproject/ 76 | | |-- templates/ <-- 这里 77 | | +-- manage.py 78 | +-- venv/ 79 | ``` 80 | 在templates文件夹中,创建一个名为home.html的HTML文件: 81 | 82 | 83 | **templates/home.html** 84 | 85 | ```html 86 | 87 | 88 | 89 | 90 | Boards 91 | 92 | 93 |

Boards

94 | 95 | {% for board in boards %} 96 | {{ board.name }}
97 | {% endfor %} 98 | 99 | 100 | 101 | ``` 102 | 103 | 在上面的例子中,我们混入了原始HTML和一些特殊标签 `{% for ... in ... %}` 和 `{{ variable }}` 。它们是Django模板语言的一部分。上面的例子展示了如何使用 `for`遍历列表对象。`{{ board.name }}`会在 HTML 模板中会被渲染成版块的名称,最后生成动态HTML文档。 104 | 105 | 在我们可以使用这个HTML页面之前,我们必须告诉Django在哪里可以找到我们应用程序的模板。 106 | 107 | 打开**myproject**目录下面的**settings.py**文件,搜索`TEMPLATES`变量,并设置`DIRS` 的值为 `os.path.join(BASE_DIR, 'templates')`: 108 | 109 | 110 | ```python 111 | TEMPLATES = [ 112 | { 113 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 114 | 'DIRS': [ 115 | os.path.join(BASE_DIR, 'templates') 116 | ], 117 | 'APP_DIRS': True, 118 | 'OPTIONS': { 119 | 'context_processors': [ 120 | 'django.template.context_processors.debug', 121 | 'django.template.context_processors.request', 122 | 'django.contrib.auth.context_processors.auth', 123 | 'django.contrib.messages.context_processors.messages', 124 | ], 125 | }, 126 | }, 127 | ] 128 | ``` 129 | 130 | 本质上,刚添加的这一行所做的事情就是找到项目的完整路径并在后面附加“/templates” 131 | 132 | 我们可以使用Python shell进行调试: 133 | 134 | ```sh 135 | python manage.py shell 136 | 137 | ``` 138 | 139 | ```python 140 | from django.conf import settings 141 | 142 | settings.BASE_DIR 143 | '/Users/vitorfs/Development/myproject' 144 | 145 | import os 146 | 147 | os.path.join(settings.BASE_DIR, 'templates') 148 | '/Users/vitorfs/Development/myproject/templates' 149 | ``` 150 | 看到了吗?它只是指向我们在前面步骤中创建的**templates**文件夹。 151 | 152 | 现在我们可以更新**home**视图: 153 | 154 | **boards/views.py** 155 | 156 | ```python 157 | from django.shortcuts import render 158 | from .models import Board 159 | 160 | def home(request): 161 | boards = Board.objects.all() 162 | return render(request, 'home.html', {'boards': boards}) 163 | ``` 164 | 165 | 生成的HTML: 166 | 167 | ![html](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/boards-homepage-render.png) 168 | 169 | 我们可以table表示替换,改进HTML模板: 170 | 171 | **templates/home.html** 172 | 173 | ```html 174 | 175 | 176 | 177 | 178 | Boards 179 | 180 | 181 |

Boards

182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | {% for board in boards %} 194 | 195 | 199 | 200 | 201 | 202 | 203 | {% endfor %} 204 | 205 |
BoardPostsTopicsLast Post
196 | {{ board.name }}
197 | {{ board.description }} 198 |
00
206 | 207 | 208 | 209 | ``` 210 | 211 | ![table](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/boards-homepage-render-2.png) 212 | 213 | 214 | 215 | ### 测试主页 216 | 217 | ![2-5](./statics/2-5.jpg) 218 | 219 | 测试将是一个反复出现的主题,我们将在整个教程系列中一起探讨不同的概念和策略。 220 | 221 | 我们来开始写第一个测试。现在,我们将在**boards**应用程序内的**tests.py**文件中操作 222 | 223 | **boards/tests.py** 224 | 225 | ```ptyhon 226 | from django.core.urlresolvers import reverse 227 | from django.test import TestCase 228 | 229 | class HomeTests(TestCase): 230 | def test_home_view_status_code(self): 231 | url = reverse('home') 232 | response = self.client.get(url) 233 | self.assertEquals(response.status_code, 200) 234 | ``` 235 | 236 | 这是一个非常简单但非常有用的测试用例,我们测试的是请求该URL后返回的响应状态码。状态码200意味着成功。 237 | 238 | 请求一下主页后,我们可以在控制台中看到响应的状态代码: 239 | 240 | ![code](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/test-homepage-view-status-code-200.png) 241 | 242 | 如果出现未捕获的异常,语法错误或其他任何情况,Django会返回状态代码500,这意味着**内部服务器错误**。现在,想象我们的应用程序有100个视图。如果我们为所有视图编写这个简单的测试,只需一个命令,我们就能够测试所有视图是否返回成功代码,因此用户在任何地方都看不到任何错误消息。如果没有自动化测试,我们需要逐一检查每个页面。 243 | 244 | 执行Django的测试套件: 245 | 246 | ```sh 247 | python manage.py test 248 | ``` 249 | 250 | ```sh 251 | Creating test database for alias 'default'... 252 | System check identified no issues (0 silenced). 253 | . 254 | ---------------------------------------------------------------------- 255 | Ran 1 test in 0.041s 256 | 257 | OK 258 | Destroying test database for alias 'default'... 259 | ``` 260 | 261 | 现在我们可以测试Django是否在请求的URL的时候返回了正确的视图函数。这也是一个有用的测试,因为随着开发的进展,您会发现urls.py模块可能变得非常庞大而复杂。URL conf 全部是关于解析正则表达式的。有些情况下有一个非常宽容的URL(译注:本来不应该匹配的,却因为正则表达式写的过于宽泛而错误的匹配了),所以Django最终可能返回错误的视图函数。 262 | 263 | 我们可以这样做: 264 | 265 | **boards/tests.py** 266 | 267 | ```python 268 | from django.core.urlresolvers import reverse 269 | from django.urls import resolve 270 | from django.test import TestCase 271 | from .views import home 272 | 273 | class HomeTests(TestCase): 274 | def test_home_view_status_code(self): 275 | url = reverse('home') 276 | response = self.client.get(url) 277 | self.assertEquals(response.status_code, 200) 278 | 279 | def test_home_url_resolves_home_view(self): 280 | view = resolve('/') 281 | self.assertEquals(view.func, home) 282 | ``` 283 | 284 | 在第二个测试中,我们使用了`resolve`函数。Django使用它来将浏览器发起请求的URL与urls.py模块中列出的URL进行匹配。该测试用于确定URL `/` 返回 home 视图。 285 | 286 | 再次测试: 287 | 288 | ```sh 289 | python manage.py test 290 | ``` 291 | 292 | ```sh 293 | ```sh 294 | Creating test database for alias 'default'... 295 | System check identified no issues (0 silenced). 296 | .. 297 | ---------------------------------------------------------------------- 298 | Ran 2 tests in 0.027s 299 | 300 | OK 301 | Destroying test database for alias 'default'... 302 | ``` 303 | 304 | 要查看有关测试执行时更详细的信息,可将**verbosity**的级别设置得更高一点: 305 | 306 | 307 | ```sh 308 | python manage.py test --verbosity=2 309 | ``` 310 | 311 | ```sh 312 | Creating test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')... 313 | Operations to perform: 314 | Synchronize unmigrated apps: messages, staticfiles 315 | Apply all migrations: admin, auth, boards, contenttypes, sessions 316 | Synchronizing apps without migrations: 317 | Creating tables... 318 | Running deferred SQL... 319 | Running migrations: 320 | Applying contenttypes.0001_initial... OK 321 | Applying auth.0001_initial... OK 322 | Applying admin.0001_initial... OK 323 | Applying admin.0002_logentry_remove_auto_add... OK 324 | Applying contenttypes.0002_remove_content_type_name... OK 325 | Applying auth.0002_alter_permission_name_max_length... OK 326 | Applying auth.0003_alter_user_email_max_length... OK 327 | Applying auth.0004_alter_user_username_opts... OK 328 | Applying auth.0005_alter_user_last_login_null... OK 329 | Applying auth.0006_require_contenttypes_0002... OK 330 | Applying auth.0007_alter_validators_add_error_messages... OK 331 | Applying auth.0008_alter_user_username_max_length... OK 332 | Applying boards.0001_initial... OK 333 | Applying sessions.0001_initial... OK 334 | System check identified no issues (0 silenced). 335 | test_home_url_resolves_home_view (boards.tests.HomeTests) ... ok 336 | test_home_view_status_code (boards.tests.HomeTests) ... ok 337 | 338 | ---------------------------------------------------------------------- 339 | Ran 2 tests in 0.017s 340 | 341 | OK 342 | Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')... 343 | ``` 344 | Verbosity决定了将要打印到控制台的通知和调试信息量; 0是无输出,1是正常输出,2是详细输出。 345 | 346 | 347 | ### 静态文件设置 348 | 349 | 静态文件是指 CSS,JavaScript,字体,图片或者是用来组成用户界面的任何其他资源。 350 | 351 | 实际上,Django 本身是不负责处理这些文件的,为了让我们的开发过程更轻松,Django 提供了一些功能来帮助我们管理静态文件。这些功能可在 `INSTALLED_APPS` 的 **django.contrib.staticfiles** 应用程序中找到(译者:Django为了使得开发方便,也负责处理静态文件,而在生产环境下,静态文件直接由Nginx等反向代理服务器处理,而应用工服务器专心负责处理它擅长的业务逻辑)。 352 | 353 | 市面上这么多的前端组件库,我们没有理由继续渲染基本的HTML文档。我们可以轻松地将Bootstrap 4添加到我们的项目中。Bootstrap是一个用HTML,CSS和JavaScript开发的开源工具包。 354 | 355 | 在项目根目录中,除了 boards, templates 和myproject文件夹外,再创建一个名为static的新文件夹,并在static文件夹内创建另一个名为css的文件夹: 356 | 357 | ```sh 358 | myproject/ 359 | |-- myproject/ 360 | | |-- boards/ 361 | | |-- myproject/ 362 | | |-- templates/ 363 | | |-- static/ <-- here 364 | | | +-- css/ <-- and here 365 | | +-- manage.py 366 | +-- venv/ 367 | ``` 368 | 369 | 转到[getbootstrap.com](https://getbootstrap.com/docs/4.0/getting-started/download/#compiled-css-and-js)并下载最新版本: 370 | 371 | 372 | ![bootstrap](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/bootstrap-download.png) 373 | 374 | 375 | 下载编译版本的CSS和JS 376 | 377 | 在你的计算机中,解压 bootstrap-4.0.0-beta-dist.zip 文件,将文件 css/bootstrap.min.css 复制到我们项目的css文件夹中: 378 | 379 | ```python 380 | myproject/ 381 | |-- myproject/ 382 | | |-- boards/ 383 | | |-- myproject/ 384 | | |-- templates/ 385 | | |-- static/ 386 | | | +-- css/ 387 | | | +-- bootstrap.min.css <-- here 388 | | +-- manage.py 389 | +-- venv/ 390 | ``` 391 | 392 | 下一步是告诉Django在哪里可以找到静态文件。打开settings.py,拉到文件的底部,在**STATIC_URL**后面添加以下内容: 393 | 394 | ```python 395 | STATIC_URL = '/static/' 396 | 397 | STATICFILES_DIRS = [ 398 | os.path.join(BASE_DIR, 'static'), 399 | ] 400 | ``` 401 | 402 | 还记得 **TEMPLATES**目录吗,和这个配置是一样的 403 | 404 | 现在我们必须在模板中加载静态文件(Bootstrap CSS文件): 405 | 406 | 407 | **templates/home.html** 408 | 409 | ```html 410 | {% load static %} 411 | 412 | 413 | 414 | Boards 415 | 416 | 417 | 418 | 419 | 420 | 421 | ``` 422 | 423 | 首先,我们在模板的开头使用了 Static Files App 模板标签 `{% load static %}`。 424 | 425 | 426 | 模板标签`{% static %}`用于组成资源所在的URL。在这种情况下,`{% static 'css/bootstrap.min.css' %}`将返回 **/static/css/bootstrap.min.css**,它相当于 **http://127.0.0.1:8000/static/css/bootstrap.min.css**。 427 | 428 | `{% 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** 429 | 430 | 如果目前这些对你来说搞不懂也不要担心。只要记得但凡是需要引用CSS,JavaScript或图片文件的地方就使用`{% static %}`。稍后,当我们开始部署项目到正式环境时,我们将讨论更多。现在,我们都设置好了。 431 | 432 | 刷新页面 127.0.0.1:8000 ,我们可以看到它可以正常运行: 433 | 434 | ![b](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/boards-homepage-bootstrap.png) 435 | 436 | 现在我们可以编辑模板,以利用Bootstrap CSS: 437 | 438 | ```html 439 | {% load static %} 440 | 441 | 442 | 443 | Boards 444 | 445 | 446 | 447 |
448 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | {% for board in boards %} 462 | 463 | 467 | 468 | 469 | 470 | 471 | {% endfor %} 472 | 473 |
BoardPostsTopicsLast Post
464 | {{ board.name }} 465 | {{ board.description }} 466 | 00
474 |
475 | 476 | 477 | ``` 478 | 479 | 显示效果: 480 | 481 | ![](https://simpleisbetterthancomplex.com/media/series/beginners-guide/1.11/part-2/boards-homepage-bootstrap-2.png) 482 | 483 | 484 | 到目前为止,我们使用交互式控制台(python manage.py shell)添加新的版块。但我们需要一个更好的方式来实现。在下一节中,我们将为网站管理员实现一个管理界面来管理这些数据。 485 | --------------------------------------------------------------------------------