创建一个博客
本节的文档将包含各种教程。这些教程旨在引导您从零开始使用Masonite构建各种类型的项目。我们可能不会对每个部分都进行十分详细的解释,因为文档的这一部分只是为了让您熟悉Masonite的工作原理。
由于文档的这一部分旨在帮助您熟悉Masonite并使用它进行编码工作,因此拓展性信息都在“提示块”中。一旦您对某个信息有疑问或想深入了解的话,建议您仔细阅读对应的提示块并按照链接深入参考文档,后者会做更多的解释。
提示块
您将在整个文档中看到各种提示块。下面是各种颜色表示的示例。
提示:您将看到绿色的提示块,如果您想了解有关当前讨论的主题的更多信息,则应遵循这些提示块。
提示:您还将看到蓝色的提示块。这些内容不应被忽略,并且通常包含您需要进一步了解的背景信息。
安装
本教程是基于假设您已经安装了Masonite进行讲解的。如果您还没有安装Masonite,请阅读文档《Masonite 介绍和安装》章节进行操作;反之,请继续阅读文档。
创建博客
在本节教程中,我们将介绍如何创建博客以及Masonite的系统结构,学习完之后您应该可以独立的使用它构建应用程序。
路由
通常,使用Masonite进行开发的第一个起点是创建路由。所有路由规则都包含在routes/web.py
这个文件中。它们由请求方法和路由方法组成。路由只是说明传入URI应该指向哪些控制器。
例如,要创建 GET
请求路由,它将如下所示:
routes/web.py
from masonite.routes import Route
ROUTES = [
Route.get('/url', 'Controller@method')
]
稍后我们将详细介绍控制器。
提示:你可以通过阅读 路由 这节文档了解关于路由的更多信息。
创建路由规则
我们将从如何创建视图和控制器开始讲解博客文章页面的开发。
控制器是从Masonite的控制器类继承并包含控制器方法的简单类。这些控制器方法将是我们的路由将调用的方法,因此它们将包含我们应用程序的大部分业务逻辑。
提示:如果您使用过Django框架的话,那么可以将控制器方法视为
views.py
文件中的函数。
我们可以将所有路由规则放在routes/web.py
和ROUTES
列表中。默认会有一条主页的路由规则。现在添加一条博客的路由开始我们的编码。
routes/web.py
ROUTES = [
Route.get('/', 'WelcomeController@show').name('welcome'),
# Blog Routes
Route.get('/blog', 'BlogController@show')
]
您会注意到,这里有一个BlogController@show
字符串。这意味着「这条路由规则的访问路径是blog控制器的show方法」。这里唯一的问题是我们还没有博客控制器。
创建控制器
默认情况下,所有控制器都位于app/controllers
目录中,并且 Masonite 为每个文件结构升级一个控制器。事实证明,这对于大型应用程序开发非常有效,因为大多数开发人员使用具有高级搜索功能的文本编辑器,例如 Sublime、VSCode 或 Atom。在这种情况下,类之间的切换很简单,并且可以促进更快的开发。很容易记住控制器的确切位置,因为文件名就是控制器。
当然,您可以将控制器移动到任何您喜欢的地方,但craft命令行工具将默认将其放在单独的文件中。如果您觉得这很奇怪,那么不妨尝试一下,看看您是否喜欢这种固执己见的布局。
创建我们的第一个控制器
与Masonite的大多数使用者一样,您可以使用craft命令构建控制器:
terminal
$ python craft controller Blog
这将在app/controllers
目录中创建如下所示的控制器:
app/http/controller/BlogController.py
from masonite.controllers import Controller
from masonite.views import View
class BlogController(Controller):
def show(self, view: View):
return view.render("")
很简单,对吧?你会注意到我们在寻找一个 show 方法。这些称为「控制器方法」,类似于 Django 所说的「视图」
但也要注意,我们现在有了前面在路由中指定的 show 方法。
返回视图
我们可以在控制器中返回很多不同的东西,但现在我们可以从控制器中返回一个视图。 Masonite 中的视图是 html 文件或「模板」。 它们本身不像其他 Python 框架那样是 Python 对象。 视图是用户将看到的 \
(或视图\
)。
这是我们第一次介绍 Masonite 的 IOC 容器。 我们在参数列表中指定我们需要一个视图类,Masonite 将为我们注入它。
现在我们不会关注整个控制器,而只会关注我们需要关心的部分。 ...
表示中间有我们不需要关心的代码:
app/controllers/BlogController.py
from masonite.view import View
# ...
def show(self, view: View):
return view.render('blog')
注意这里我们“类型提示”了 View
类。 这就是 Masonite 所说的「自动解析依赖注入」,如果这对你现在没有意义,请不要担心。 你读的越多,你就会明白的越多。
创建视图
你现在会注意到我们正在返回 blog
视图,但它还不存在。
所有视图都在 templates
目录中。 我们可以创建一个名为 templates/blog.html
的新文件。
我们可以在这个文件中放一些文本,比如:
templates/blog.html
This is a blog
让我们第一次运行迁移:
terminal
$ python craft migrate
然后运行服务器
terminal
$ python craft serve
并打开http://localhost:8000/blog
。 你将在浏览器中看到「这是一个博客」。
身份校验
很多时候,大多数程序都需要某些特殊的身份验证方式 Masonite 自带了一个craft命令,来帮您搭建一个身份校验系统框架. 通常情况下,建议您应该在全新的Masonite下使用,因为它会自动帮您创建新的控制器、路由器和视图.
对于我们的博客来说,我们需要创建某种形式的注册方式,以至于允许新的用户在我们的博客上发言。 我们可以通过运行 craft 命令来创建一个身份验证系统:
terminal
$ python craft auth
运行后我们会受到一条成功提示,表明已经创建了新的资源. 你可以查看先您的控制器文件夹,在里面您应该能看到一些新的、应该注册的控制器
然后你就可以将身份校验的路由添加到你的项目中
from masonite.authentication import Auth
ROUTES = [
Route.get('/', 'WelcomeController@show').name('welcome'),
# Blog Routes
Route.get('/blog', 'BlogController@show')
]
ROUTES += Auth.routes()
稍后,我们就能看到为我们创建了什么样的内容
Database Setup
为了让这些用户成功注册, 我们还需要一个数据库。 默认的,Masonite 使用的是SQLite,如果你想使用其他的数据库,您能直接修改 .env
文件中开头为DB_的选项,如果是要运行 MySQL 或 Postgres数据库服务,您需要确保已经运行这些服务
重新运行我们之前已经运行过的迁移命令,即:
terminal
$ python craft migrate
如果您要使用 MySQL作为您的数据库,请打开项目根目录中的.env
文件,并将DB_CONNECTION
更改为mysql
。当然,也能修改成其他你所喜欢的
terminal
.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=masonite
DB_USERNAME=root
DB_PASSWORD=
执行迁移
配置完成数据库连接信息后, 就可以进行数据库迁移. 在 Masonite
中提供一个基础的用户迁移模型。默认配置能够满足大多数情况,你可以修改迁移模型中的配置或者在以后再对迁移模型进行修改。
$ python craft migrate
该操作会创建一个 users
表和 migrations
,在 migrations
表中记录着我们所有的迁移记录。
创建用户
现在我们已经迁移完成,下面开始创建第一个用户,通过 craft auth
命令,可以创建新的模板和控制器。
下面我们开始运行服务:
$ python craft serve
访问 localhost:8000/register 并完成注册,你可以使用你喜欢的用户名和邮箱进行注册,本教程使用下面信息进行注册:
Username: demo
Email: demo@email.com
Password: password!!11AA
迁移
我们已经了解了迁移的基本流程,下面我们来创建一个新的数据迁移。
在我们的应用中已经完成 users
的迁移,下面我们来创建一个 posts
模型迁移,用来存储我们的文章信息。
我们的帖子表应该有一些明显的列,我们将在本教程部分进行简化。
Craft 命令
我们可以使用 craft 命令创建数据迁移。详情查看数据库迁移文档,此处我们将简化此命令并稍作解释:
terminal
$ python craft migration create_posts_table --create posts
该命令创建了一个数据库迁移文件,我们可以用它来新建 posts 表格。按照命名规范,表格名应该是复数(而模型名称应该为单数)。
这将会在 databases/migrations
目录新建一个迁移文件。打开该文件,会看到第六行开始其内容如下:
databases/migrations/20YY_MM_DD_ABCDEF_create_posts_table.py
def up(self):
"""
Run the migrations.
"""
with self.schema.create('posts') as table:
table.increments('id')
table.timestamps()
接下来,我们将添加 title、author 和 body 字段到 posts 表格中。
databases/migrations/2018_01_09_043202_create_posts_table.py
def up(self):
"""
Run the migrations.
"""
with self.schema.create('posts') as table:
table.increments('id')
table.string('title')
table.integer('author_id').unsigned()
table.foreign('author_id').references('id').on('users')
table.string('body')
table.timestamps()
提示:这已经相当直白易懂了。如果你想了解跟多,请查阅数据库迁移文档。
现在,我们就可以运行迁移,创建 post 表了
terminal
$ python craft migrate
模型
既然已经完成了表格迁移,有了 posts 表,那我们就来为其创建一个模型吧。
Masonite 中的模型与其他 Python 框架稍有一点不同。Masonite 使用的是Active Record ORM。在 Masonite 中模型和迁移是分离的。无论数据库表内部字段如何调整,模型都能适用表格。
创建模型
使用 craft 命令创建模型:
terminal
$ python craft model Post
注意,模型名称使用单数。默认情况下,Masonite ORM 会假定表格的名称为模型名称的复数形式,此例中类名的Post复数为posts。稍后,我们将稍微解释一下如何指定表格名称。
当前创建的这一模型在 app/models/Post.py
中,其代码如下:
app/models/Post.py
"""Post Model."""
from masoniteorm.models import Model
class Post(Model):
pass
很简单吧?如前所述,我们可以不必操作模型。即便我们创建或者修改迁移,模型仍然适用表格。
表名
重申一下,模型中使用的表名,是模型名称的复数形式。不过,如果你使用了不同的表名。比如,表名使用的是 “user_posts” 而非 “posts” 时,我们可以明确指定表名:
app/Post.py
"""Post Model."""
from masoniteorm.models import Model
class Post(Model):
__table__ = 'user_posts'
批量赋值
默认情况下, Masonite ORM 出于安全考虑对批量赋值启用了保护,因此,我们需要另外设置哪些字段是可填充的(fillable),这样我们才可以将字段名传入 create
和 update
方法中。
app/Post.py
"""A Post Database Model."""
from masoniteorm.models import Model
class Post(Model):
__fillable__ = ['title', 'author_id', 'body']
表关联
此处的表关联相当直接易懂。我们在数据迁移(migration)文件中,添加了一个外键。我们可以在模型中像这样创建关联:
app/models/Post.py
"""Post Model."""
from masoniteorm.models import Model
from masoniteorm.relationships import belongs_to
class Post(Model):
__fillable__ = ['title', 'author_id', 'body']
@belongs_to('author_id', 'id')
def author(self):
from app.models.User import User
return User
由于Masonite 模型的实现方式,有一些模型会相互依赖。因此最好像我们上面所做的那样,在关联内部进行引入,以避免可能的循环引入。
提示:此处不会涉及不同类型关联的详情,需要了解更多信息请查阅Masonite ORM Relationships文档。
设计博客
我们来准备一些 HTML ,借此了解一下视图(view)是如何运作的。在这一部分中,我们会准备一个很基础的模板,这样才不至于被太多的HTML耽搁,同时又能了解足够多的基础。
既然模型和数据库迁移都已准备好,后台的其他东西也都已经有了,现在我们只需创建布局,然后就可以开始创建和更新博客文章了。
同时,我们也需要在创建模板前检查用户是否已经登录。
用于创建记录的模板
添加记录的URL为 /blog/create
,其中会有一个简单的表单用于添加博客文章
templates/blog.html
<form action="/blog/create" method="POST">
{{ csrf_field }}
<input type="name" name="title">
<textarea name="body"></textarea>
</form>
注意此处 <form>
标签之下有一个 {{ csrf_field }}
。Masonite 自带 CSRF 保护,因此我们需要添加一个隐藏的字段带上 CSRF token。
现在,我们需要确保用户已经登录,因此我们可以对模板稍加修改:
templates/blog.html
@if auth()
<form action="/blog/create" method="POST">
{{ csrf_field }}
<label> Title </label>
<input type="name" name="title"><br>
<label> Body </label>
<textarea name="body"></textarea>
<input type="submit" value="Post!">
</form>
@else
<a href="/login">Please Login</a>
@endif
auth()
是一个辅助函数,返回当前用户或者 None
。
提示:Masonite 适用 Jinja2 模板。如果你不了解这一模板,请查看相关文档。
静态文件
为简化起见,我们不会采用像Bootstrap这样的框架来编写博客样式。不过,了解像CSS这样的静态文件,是如何在 Masonite 中使用,仍然很重要。接下来,我们一起来看看如何添加CSS文件到博客当中吧。
首先,转到 storage/static/
目录并新建一个 blog.css
文件,在其中定义你的样式。此处我们将让页面变灰。
storage/static/blog.css
html {
background-color: #ddd;
}
现在我们可以将其添加到在模板头部:
templates/blog.html
<link href="/static/blog.css" rel="stylesheet">
@if auth()
<form action="/blog/create" method="POST">
{{ csrf_field }}
<label> Title </label>
<input type="name" name="title"><br>
<label> Body </label>
<textarea name="body"></textarea>
<input type="submit" value="Post!">
</form>
@else
<a href="/login">Please Login</a>
@endif
就是这样。静态文件很简单。了解它们的运行原理很重要,不过本教程会暂时略过,主要关注于后端的实现。
Javascript 文件也完全一样:
templates/blog.html
<link href="/static/blog.css" rel="stylesheet">
@if auth()
<form action="/blog/create" method="POST">
{{ csrf_field }}
<label> Title </label>
<input type="name" name="title"><br>
<label> Body </label>
<textarea name="body"></textarea>
<input type="submit" value="Post!">
</form>
@else
<a href="/login">Please Login</a>
@endif
<script src="/static/script.js"></script>
提示:更多关于静态文件的信息,请查看 静态文件相关文档。
用于新建记录的控制器和容器
请注意,我们的操作会转到 /blog/create
,隐藏我们需要将路由连接到控制器方法中。此例中,我们将会让其指向 store
方法。
回到 routes/web.py
文件并创建一个新的路由。将其添加到 ROUTES
列表中:
routes/web.py
from masonite.routes import Route
# ...
ROUTES = [
# ...
Route.post('/blog/create', 'BlogController@store')
]
然后再控制器中添加 store
方法:
app/controllers/BlogController.py
...
def show(self, view: View):
return view.render('blog')
# 新增store方法
def store(self):
pass
现在注意以上表单,我们会收到两个表单输入数据: title 和 body。这里引入 Post
模型,然后使用输入数据创建新的 post 文章。
app/controllers/BlogController.py
from app.models.Post import Post
from masonite.request import Request
# ...
def store(self, request: Request):
Post.create(
title=request.input('title'),
body=request.input('body'),
author_id=request.user().id
)
return 'post created'
注意,这里我们使用了 request: Request
。它是 Request
对象。它从哪里来的?这就是 Masonite 强大和优美之处,也是首次介绍服务容器。服务容器是一种极其强大的工具,让你可以向 Masonite 请求并获取一个对象 (如本例的 Request
) 。这是一个需要掌握的重要的概念,所以请阅读文档做进一步了解。
同时请注意,我们使用了 input()
方法。Masonite 不歧视不同的请求方法,因此从 GET
或 POST
请求获得的输入,在使用 input
方法时,是一样的。
使用 craft serve 再次启动服务,然后转到 http://localhost:8000/blog
并创建一个新文章。这会使用 POST
方法提交到 /blog/create
路由,然后我们就会看到”post created”。
展示文章列表
接下来,我们继续看看怎样展示刚刚创建的博客文章。既然我们更愿意使用框架,这部分我们将创建两个模板,用来展示所有文章和单篇文章。
创建模板
我们将创造两个模板。
templates/posts.html
templates/single.html
我们先来展示文章列表
创建控制器
接下来我们将为文章创建一个控制器并且从 BlogController
中分离出来。
terminal
$ python craft controller Post
很好,现在在我们 show
方法中,我们可以展示所有文章了。接下来我们将创建一个 single
方法,用来显示指定文章。
Show 方法
接下来,我们来实现 show
方法,并返回所有文章的视图:
app/controllers/PostController.py
from app.models.Post import Post
...
def show(self, view: View):
posts = Post.all()
return view.render('posts', {'posts': posts})
文章(Posts)路由
我们需要为此方法添加一个路由:
routes/web.py
Route.get('/posts', 'PostController@show')
文章列表(Posts)视图
视图可以非常简单:
<!-- templates/posts.html -->
@for post in posts
{{ post.title }}
<br>
{{ post.body }}
<hr>
@endfor
启动服务,然后打开 http://localhost:8000/posts
路由。你就会看到文章列表的基本展示。如果你只看到一篇,你可以到 http://localhost:8000/blog
添加更多文章。
显示作者
之前我们添加 author 关联。Masonite ORM 可以获取这一关联,并从中读取属性,将作者名字显示出来:
templates/posts.html
@for post in posts
{{ post.title }} by {{ post.author.name }}
<br>
{{ post.body }}
<hr>
@endfor
我们再来重复一遍这一过程,稍微调整一下流程。
单篇文章路由
接下来,我们将要显示单篇文章。我们需为此添加路由:
routes/web.py
Route.get('/post/@id', 'PostController@single')
注意此处我们有一个 @id
字符串。我们可以在接下来的控制器中,用它来抓取URL中相应位置的参数。
Single 方法
我们来新建一个 single
方法,用来显示单篇文章。
app/controllers/PostController.py
from app.models.Post import Post
from masonite.request import Request
from masonite.views import View
...
def single(self, view: View, request: Request):
post = Post.find(request.param('id'))
return view.render('single', {'post': post})
我们使用了 param()
方法来获取 URL 中的 id。请记住这一参数名,我们已经在前面的路由中通过 @id
对其进行了设置。
提示:在真实的应用中,我们可能会在路由中使用
@slug
,然后通过request().param('slug')
获取。
单篇文章视图
我们只需显示一篇文章,因此我们且用一个简单的视图:
templates/single.html
{{ post.title }}
<br>
{{ post.body }}
<hr>
运行服务器,然后跳到 http://localhost:8000/post/1
和 http://localhost:8000/post/2
两个路由,观察它们有何不同。
更新及删除文章
至此,前面用过所有这些业务逻辑已经足以让我们更进一步,因此接下来我们将快速完成文章的更新和删除操作。
Update 控制器方法
接下来,我们在 PostController
中创建一个 update 方法:
app/controllers/PostController.py
def update(self, view: View, request: Request):
post = Post.find(request.param('id'))
return view.render('update', {'post': post})
def store(self, request: Request):
post = Post.find(request.param('id'))
post.title = request.input('title')
post.body = request.input('body')
post.save()
return 'post updated'
由于我们对控制器更满意,我们可以继续制作两个。我们制作了一个视图,该视图显示了一个更新帖子的表单,然后是一个实际使用数据库更新帖子的视图。
创建视图
templates/update.html
templates/update.html
<form action="/post/{{ post.id }}/update" method="POST">
{{ csrf_field }}
<label for="">Title</label>
<input type="text" name="title" value="{{ post.title }}"><br>
<label>Body</label>
<textarea name="body">{{ post.body }}</textarea><br>
<input type="submit" value="Update">
</form>
创建路由:
请记住,我们创建了 2 个控制器方法,所以让我们在这里将它们附加到一个路由:
routes/web.py
Route.get('/post/@id/update', 'PostController@update'),
Route.post('/post/@id/update', 'PostController@store'),
应该是这样!我们现在可以更新我们的帖子。
删除方法
让我们扩展一下并创建一个删除方法。
app/controllers/PostController.py
from masonite.request import Request
...
def delete(self, request: Request):
post = Post.find(request.param('id'))
post.delete()
return 'post deleted'
制作路由:
routes/web.py
Route.get('/post/@id/delete', 'PostController@delete'),
请注意,我们在这里使用了GET
路由,使用POST
方法会更好,但为简单起见,假设您现在可以创建一个。我们将只添加一个链接到我们的更新方法,这将删除帖子。
更新模板
我们可以在更新模板中直接添加一个删除链接:
templates/update.html
<form action="/post/{{ post.id }}/update" method="POST">
{{ csrf_field }}
<label for="">Title</label>
<input type="text" name="title" value="{{ post.title }}"><br>
<label>Body</label>
<textarea name="body">{{ post.body }}</textarea><br>
<input type="submit" value="Update">
<a href="/post/{{ post.id }}/delete"> Delete </a>
</form>
太棒了!您现在拥有一个可用于创建、查看、更新和删除帖子的博客!继续创造惊人的东西!
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。