eli bendersky
Welcome to ELI5 Full Stack: Breakthrough with Django & EmberJS. This is an introduction to full stack development for everyone, especially beginners. We’ll go step-by-step through the development of a basic web application. A library of sorts. Together we’ll build a back-end to store data and a RESTful API to manage it. Then we’ll construct a front-end user interface for users to view, add, edit, and delete the data.
欢迎使用ELI5 Full Stack:Django和EmberJS的突破 。 这是面向所有人(尤其是初学者)的全栈开发的简介。 我们将逐步开发基本的Web应用程序。 各种各样的图书馆。 我们将共同构建一个后端来存储数据和一个RESTful API来管理数据。 然后,我们将构建一个前端用户界面,供用户查看,添加,编辑和删除数据。
This isn’t meant to be a deep dive into either Django or EmberJS. I don’t want us to get bogged down with too much complexity. Rather its purpose is to show the critical elements of basic full stack development. How to stitch together the back end and front end into a working application. I’ll go into detail about the software, frameworks, and tools used in the process. Every terminal command run and line of code in the final application is present in this tutorial.
这并不是要深入研究Django或EmberJS 。 我不想让我们陷入太多的麻烦之中。 相反,其目的是显示基本的全栈开发的关键要素 。 如何将后端和前端缝合在一起,形成一个可以正常工作的应用程序。 我将详细介绍该过程中使用的软件,框架和工具。 本教程介绍了最终应用程序中每个终端命令运行和代码行。
I’ve kept each section short and to the point so that no one’s head explodes. There are also indicators to mark points for reflection so you can go back and look at what we’ve done and save state. If you don’t know what something means click through to the linked articles which will explain in detail. Remember, this is as an introduction to everyone including beginners. If you don’t need the hand holding push on through to the sections relevant to you.
我把每一节都讲得简短点,以至于没有人爆头。 也有指示器标记要反射的点,因此您可以回头查看我们所做的事情并保存状态。 如果您不知道什么意思,请单击链接的文章,其中将详细说明。 请记住,这是对包括初学者在内的所有人的介绍。 如果不需要,请继续按与您相关的部分。
If you’re a beginner, I that suggest you write every line of code and run each terminal command yourself. Don’t copy and paste. It won’t sink in. Take your time and think about what you’re doing. This is a critical trait of an effective and self-sufficient programmer. You will develop this over time if you write your own code and think about what you’re writing. If you mess up (look at my commit history, I definitely did) don’t sweat it. Go back. This isn’t a race. You’ll be fine if you take your time.
如果您是初学者,我建议您编写每一行代码并自己运行每个终端命令。 不要复制和粘贴。 它不会陷入。花点时间思考一下自己在做什么。 这是一个有效而自给自足的程序员的一个重要特征。 如果您编写自己的代码并考虑所编写的内容,那么随着时间的流逝,您将逐渐发展它。 如果您搞砸了(看看我的提交历史,我确实做到了),请不要大汗。 回去。 这不是比赛。 如果您愿意的话,您会没事的。
Note: I developed this tutorial on a MacBook Pro running macOS High Sierra (10.3.6). I’m using iTerm2 for the terminal and Sublime Text 3 as my text editor. All testing uses the Chrome browser and its built-in tools. The actual code shouldn’t have any differences. You can download the final project files from the Github repository.
注意 :我在运行macOS High Sierra(10.3.6)的MacBook Pro上开发了本教程。 我将iTerm2用于终端,将Sublime Text 3作为文本编辑器。 所有测试均使用Chrome浏览器及其内置工具。 实际的代码不应有任何差异。 您可以从Github存储库下载最终项目文件 。
1.1 Why I Wrote This Tutorial1.2 Back End, Front End. What’s the Difference?1.3 The Concept: A Basic Library Application1.4 Project Directory Structure1.5 Project Directory Setup1.6 Conclusion
1.1为什么编写本教程1.2后端,前端。 有什么区别?1.3概念:基本库应用程序1.4项目目录结构1.5项目目录设置1.6结论
2.1 Install Required Software2.2 Start a Django Project: server2.3 Start a Django App: books2.4 Describe the Book model2.5 Register the Book model with the admin2.6 Conclusion
2.1安装必需的软件2.2启动Django项目:server2.3启动Django应用程序:books2.4描述Book模型2.5向admin注册Book模型2.6结论
3.1 Django REST Framework3.2 Create the books API folder3.3 Create a book serializer3.4 Create a view to GET and POST books data3.5 Create URLs to access books data3.6 Conclusion
3.1 Django REST Framework 3.2创建书籍API文件夹3.3创建书籍序列化程序3.4创建用于GET和POST书籍数据的视图3.5创建用于访问书籍数据的URL 3.6总结
4.1 Install Required Software4.2 Start an Ember Project: client4.3 Displaying books data 4.4 The books route4.5 Displaying real data in the books route4.6 Conclusion
4.1安装所需的软件4.2启动Ember项目:client4.3显示书籍数据4.4书籍路径4.5在书籍路径中显示实际数据4.6结论
5.1 Install the Django REST Framework JSON API5.2 Working with individual book records5.3 The book route5.4 Conclusion
5.1安装Django REST Framework JSON API5.2处理单个书籍记录5.3书籍路径5.4结论
6.1 Adding a new book to the database 6.2 Deleting a book from the database6.3 Editing a book in the database6.4 Conclusion
6.1向数据库中添加新书6.2从数据库中删除书6.3在数据库中编辑书6.4结论
7.1 What’s Next?7.2 Further Reading
7.1下一步是什么?7.2进一步阅读
Imagine that you’ve recently joined a new company. They’ve been in business for some time, and their major products are already out in production. Think of the application you see today as cake. The process of picking the ingredients, recipe, and putting it all together… well that’s long over. You’ll be working on pieces of that finished cake.
想象一下您最近加入了一家新公司。 他们已经有一段时间了,他们的主要产品已经停产。 将您今天看到的应用程序视为蛋糕。 挑选食材,食谱并将它们放在一起的过程……好了,这已经过去了。 您将要完成那块蛋糕。
The developers at the start of a project have laid down certain configurations. These change and conventions are also developed over time as developers come and go. By the time you arrive it may be difficult to comprehend how we’ve gotten to where we are. This was my situation. I felt that dipping into the whole stack would be the only way for me to feel comfortable. It would help me understand where we came from and how to move forward with the software we’re building.
在项目开始时,开发人员已经确定了某些配置。 随着开发人员的到来,这些变更和约定也会随着时间的推移而发展。 到您到达的时候,可能很难理解我们如何到达自己的位置。 这就是我的情况。 我觉得浸入整个堆栈是让我感到舒适的唯一方法。 这将有助于我了解我们来自何处以及如何继续开发我们所构建的软件。
This tutorial is the culmination of my experiences as a junior software developer. I’ve been learning a lot at my time with Closing Folders. It represents a shift in my thinking as I take steps towards more complex full stack development. It also serves as an entry point for developers at the stage where they’re wondering how the cake gets baked. I hope this tutorial is as useful for you as it was instructive for me to create.
本教程是我作为初级软件开发人员的经验的结晶。 我在关闭文件夹方面学到了很多东西。 当我朝着更复杂的全栈开发迈进时,它代表了我思维的转变。 在开发人员想知道如何烘焙蛋糕的阶段,它也可以作为切入点。 我希望本教程对您有帮助,对我的创建很有帮助。
Note: In a typical workflow a developer would start on the back end to set up the database, and create a REST API. Then, they would work on the front end and build the user interface. Things aren’t so simple though. We make mistakes and often have to go back and forth to resolve them. The jumping back and forth will help build more connections in your mind. and help you better understand how all the pieces fit together. Embrace your mistakes. You’ll be making a lot of them!
注意 :在典型的工作流程中,开发人员会从后端开始设置数据库并创建REST API。 然后,他们将在前端工作并构建用户界面。 事情并不是那么简单。 我们会犯错误,并且经常不得不反复来解决错误。 来回跳跃将帮助您建立更多的联系。 并帮助您更好地了解所有部件如何组合在一起。 拥抱你的错误。 您将赚很多!
Note2: Attention Senior Devs, Junior Devs, and Designers! Closing Folders is hiring now so feel free to get in touch.
注意2 :注意高级开发人员,初级开发人员和设计师! 正在关闭文件夹,因此请随时与我们联系。
Back-end development. Front-end development. Full-stack development. So much development... What’s the difference anyway?
后端开发。 前端开发。 全栈开发。 如此多的开发...到底有什么区别?
Think of front-end development as the part of the application that you see and interact with. For example, the user interface is part of the front end. That’s where the user views data and interacts with it.
将前端开发视为您看到并与之交互的应用程序的一部分。 例如,用户界面是前端的一部分。 那是用户查看数据并与之交互的地方。
Back-end development is everything that stores and serves data. Think about what happens when you login to Medium. None of your user profile data or stories exists on the front end. It’s stored and served from the back end.
后端开发是存储和服务数据的一切。 想一想当您登录Medium时会发生什么。 您的用户个人资料数据或故事均不存在于前端。 它是从后端存储和提供的。
The front end and back end work together to form the application. The back end has the instructions for how to store and serve the data. The front end has the instructions to capture the data, and how to display it.
前端和后端一起工作以形成应用程序。 后端具有有关如何存储和提供数据的说明。 前端具有捕获数据以及如何显示数据的说明。
Find out more about the differences in this article.
在本文中找到有关差异的更多信息。
Before we start building anything, let’s outline our plans and what we’re trying to achieve. We want to build a web application called my_library that runs in the browser. The application is exactly what it sounds like, a digital library of books. We won’t be dealing with actual book content though. The books will only have title, author, and description information. Keeping it simple.
在开始构建任何东西之前,让我们概述一下我们的计划和我们要实现的目标。 我们要构建一个在浏览器中运行的名为my_library的Web应用程序 。 该应用程序听起来像是一个数字图书图书馆。 不过,我们不会处理实际的书籍内容。 这些书将仅包含标题,作者和描述信息。 保持简单。
The application will have the following functionality:
该应用程序将具有以下功能:
Take a look at the screenshots below. They depict the application’s final look and functionality:
看看下面的截图。 它们描述了应用程序的最终外观和功能:
There are innumerable ways to structure a given project. I’ll keep everything under one my_library
folder for simplicity’s sake like so:
构造给定项目的方法有无数种。 为了简单起见,我将所有内容都保留在一个my_library
文件夹下,如下所示:
my_library
- server
- server
- books
- api
- db.sqlite3
- manage.py
- client
- app
- adapters
- controllers
- models
- routes
- templates
- styles
router.js
These aren’t all the folders and files that the project will contain, though they’re the main ones. You’ll notice quite a few autogenerated files that you can ignore. Though it would be useful for you to read documentation that explains their purpose.
这些不是主要的文件夹和文件,而是项目将包含的所有文件夹和文件。 您会注意到很多自动生成的文件可以忽略。 尽管阅读说明其用途的文档对您很有用。
The my_library
directory contains folders for the back end and front end sub-projects. server
refers to the Django back end, and client
refers to the EmberJS front end.
my_library
目录包含后端和前端子项目的文件夹。 server
是指Django后端, client
是EmberJS前端。
server
contains another folder called server
. Inside are the top level configurations and settings for the back end.
server
包含另一个名为server
文件夹。 内部是后端的顶级配置和设置。
The books
folder will contain all the models, views, and other configuration for the book data.
books
文件夹将包含书籍数据的所有模型,视图和其他配置。
Inside the books/api
folder we’ll create the serializers, URLs, and views that make up our REST API.
在books/api
文件夹内,我们将创建构成REST API的序列化程序,URL和视图。
client
is our EmberJS front end. It contains routes, templates, models, controllers, adapters, and styles. router.js
describes all the application routes.
client
是我们的EmberJS前端。 它包含路线,模板,模型,控制器,适配器和样式。 router.js
描述了所有应用程序路由。
Let’s go ahead and set up the main project directory my_library
.
让我们继续设置主项目目录my_library
。
Now that we know what we’re going to build, let’s take a few minutes to set up the main project directory my_library
:
既然我们知道要构建什么,那么让我们花几分钟来设置主项目目录my_library
:
# cd into desktop and create the main project folder
cd ~/desktop && mkdir my_library
Create a basic README.md
file inside the folder with the following content:
在具有以下内容的文件夹内创建一个基本的README.md
文件:
# my_library
This is a basic full stack library application built. Check out the tutorial: 'ELI5 Full Stack: Breakthrough with Django & EmberJS'.
Now let’s commit this project to a new Git repository as the project start point.
现在让我们将该项目提交到新的Git存储库作为项目起点。
Git is version control software. We’ll use it to keep track of our project and save our state step-by-step so we can always go back if we make breaking errors. I’m sure most of you’re already familiar with it.
Git是版本控制软件。 我们将使用它来跟踪我们的项目并逐步保存状态,以便在遇到重大错误时始终可以返回。 我敢肯定你们大多数人已经熟悉它。
For the uninitiated, you can find out more here. If you don’t have Git installed, you can download it here.
对于初学者,您可以在这里找到更多信息 。 如果您尚未安装Git,则可以在此处下载。
Check that it installed with:
检查它是否安装了:
$ git --version
I have an account with Github. It’s popular and works well so that’s what I’ll be using. Feel free to use other solutions if they suit you better.
我在Github上有一个帐户。 它很流行并且运行良好,所以这就是我要使用的。 如果其他解决方案更适合您,请随时使用。
Create a new repository and get the remote URL which should look like this:
创建一个新的存储库并获取如下所示的远程URL:
[email protected]:username/repo_name.git
Inside the my_library
folder initialize the empty repository:
在my_library
文件夹中,初始化空的存储库:
git init
Now add the remote URL so Git knows where we’re pushing our files to:
现在添加远程URL,以便Git知道我们要将文件推送到的位置:
git remote add origin [email protected]:username/repo_name.git
# check that it's been set, should display the origin
git remote -v
Time to push our code to Github:
是时候将我们的代码推送到Github了:
# check the status of our repo
# should show the new file README.md, no previous commits
git status
# add all changes
git add .
# create a commit with a message
git commit -m "[BASE] Project Start"
# push changes to the repo's master branch
git push origin master
The remote Git repository updates with the changes we’ve pushed:
远程Git存储库使用我们推送的更改进行更新:
Now that we have a main project directory and a repository we can finally start working on our back end!
现在我们有了一个主项目目录和一个存储库,我们终于可以在后端开始工作了!
NOTE: From this point onward I won’t be going into any more detail about commits. The review and commit indicator below will let you know when it’s a good time to do so:
注意 :从现在开始,我将不再进一步讨论提交。 下面的审查和提交指示符将在适当时通知您:
We’ve come to the end of Section 1 with the following steps completed:
我们已经完成了以下步骤,到了第1节的结尾:
Created the my_library
main project directory
创建了my_library
主项目目录
Installed git
and created a remote project repository on Github
安装了git
并在Github上创建了一个远程项目存储库
Created aREADME.md
file, then committed and pushed all changes
创建一个README.md
文件,然后提交并推送所有更改
This section is all about back-end development with Django. We’ll begin with the installation of the required software.
本节全部关于使用Django进行后端开发。 我们将从安装必需的软件开始。
Next, we’ll move onto the creation of a new Django project called server
and create a new app called books
. In the books
app we describe the Book
model and register the model with the admin.
接下来,我们将继续创建一个名为server
的新Django项目,并创建一个名为books
的新应用程序。 在books
应用中,我们描述Book
模型并向管理员注册该模型。
Once we create a Superuser
account we can login to the Django Admin site. We’ll use the Django Admin site to administrate the database and start seeding it with book data.
创建Superuser
帐户后,我们可以登录Django Admin网站。 我们将使用Django Admin网站来管理数据库,并开始将其与书籍数据一起播种。
Before we begin our back end project we’ll need to install some software:
在开始我们的后端项目之前,我们需要安装一些软件:
Python
Python
pip
点子
virtualenv
虚拟环境
Django
Django的
If your MacOS is up-to-date it likely already has Python 2.7
installed. Feel free to use either 2.7
or 3.x
. They’re the same for the purposes of this tutorial.
如果您的MacOS是最新的,则可能已经安装了Python 2.7
。 随时使用2.7
或3.x
就本教程而言,它们是相同的。
Installation is simple. Download the installer and install as you would a typical MacOS application. Open up the terminal and check that it’s installed:
安装很简单。 下载安装程序并按照典型的MacOS应用程序进行安装。 打开终端并检查是否已安装:
python --version
In simple terms, pip (Pip Installs Packages) is a package management system. It’s used to install and manage software packages written in Python. In the terminal:
简而言之,pip(Pip安装软件包)是一个软件包管理系统。 它用于安装和管理以Python编写的软件包。 在终端中:
# cd into the desktop
cd ~/desktop
# download the pip Python script
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
# run the script
python get-pip.py
# once installation completes, verify that it's installed
pip —-version
Full installation documentation is available here.
完整的安装文档可在此处获得 。
virtualenv is a ‘tool to create isolated Python environments’. These environments have their own installation directories. They don’t share libraries with others. Such silos protect the globally installed libraries from unwanted changes.
virtualenv是一个“ 创建隔离的Python环境的工具”。 这些环境具有自己的安装目录。 他们不与他人共享库。 这样的孤岛可以保护全局安装的库免受不必要的更改。
With it we can play with Python libraries without messing up the global environment. For example, you install exampleSoftware 1.0
on your computer. With a virtual environment activated you can upgrade to exampleSoftware 1.2
and use it. This won’t affect the global install of exampleSoftware 1.0
at all.
有了它,我们可以使用Python库,而不会弄乱全局环境。 例如,您在计算机上安装exampleSoftware 1.0
。 激活虚拟环境后,您可以升级到exampleSoftware 1.2
并使用它。 这完全不会影响exampleSoftware 1.0
的全局安装。
For the development of a particular app you may want to use 1.2
and for other contexts 1.0
will be appropriate. Virtual environments give us the ability to separate these contexts. Full installation documentation is available here.
对于特定应用程序的开发,您可能需要使用1.2
而在其他情况下, 1.0
是合适的。 虚拟环境使我们能够分离这些上下文。 完整的安装文档可在此处获得 。
Now, open up the terminal to install virtualenv:
现在,打开终端以安装virtualenv:
# use pip to install virtualenv
pip install virtualenv
# verify that it's installed
virtualenv —-version
Let’s create a directory to house our virtual environments:
让我们创建一个目录来容纳我们的虚拟环境:
# cd into the root directory
cd ~/
# create a hidden folder called .envs for virtual environments
mkdir .envs
# cd into the virtual environments directory
cd .envs
We can now create a virtual environment for our project:
现在,我们可以为项目创建一个虚拟环境:
# create a virtual environment folder: my_library
virtualenv my_library
# activate the virtual environment from anywhere using
source ~/.envs/my_library/bin/activate
Now that we’ve created a virtual environment called my_library
there are a few rules to keep in mind. Make sure the environment is always activated before installing, or updating any packages.
现在,我们已经创建了一个名为my_library
的虚拟环境,需要牢记一些规则。 在安装或更新任何软件包之前,请确保始终激活环境。
Finally, take a moment to upgrade pip inside this virtual environment:
最后,花一点时间在此虚拟环境中升级pip:
pip install -U pip
Django is a web framework that ‘encourages rapid development and clean, pragmatic design…’
Django是一个Web框架,“ 鼓励快速开发和简洁实用的设计……”
It provides us with a set of common components so we don’t have to reinvent everything from scratch.
它为我们提供了一组通用组件,因此我们不必从头开始重新发明一切。
Examples include:
示例包括:
Checkout out this DjangoGirls article to learn more about Django and why it’s used.
查阅这篇DjangoGirls文章,以了解有关Django及其使用原因的更多信息。
In this project we’ll be using Django to handle the back end. Along with its add-ons, Django provides the basic tools to develop a REST API.
在这个项目中,我们将使用Django处理后端。 Django及其附加组件提供了开发REST API的基本工具。
# inside my_library with virtualenv activated
pip install Django==1.11
# verify that it's installed, open up the Python shell
python
# access the django library and get the version (should be 1.11)
import django
print(django.get_version())
# exit using keyboard shortcut ctrl+D or:
exit()
Full installation documentation is available here.
完整的安装文档可在此处获得 。
Let’s use the django-admin to generate a new Django project. This is Django’s ‘command-line utility for administrative tasks’:
让我们使用django-admin生成一个新的Django项目。 这是Django的“ 用于管理任务的命令行实用程序 ”:
# cd into the project folder
cd ~/desktop/my_library
# initialize the virtual environment
source ~/.envs/my_library/bin/activate
# use Django to create a project: server
django-admin startproject server
# cd into the new Django project
cd server
# synchronize the database
python manage.py migrate
# run the Django server
python manage.py runserver
Now visit http://localhost:8000
in your browser and confirm that the Django project is working:
现在,在浏览器中访问http://localhost:8000
并确认Django项目正在运行:
You can shut down the server with cmd+ctrl
.
您可以使用cmd+ctrl
关闭服务器。
We’ll have to create a superuser to login to the admin site and handle database data. Inside my_library/server
we run:
我们必须创建一个超级用户才能登录到管理站点并处理数据库数据。 在my_library/server
内部,我们运行:
# create superuser
python manage.py createsuperuser
Fill in the fields Username
, Email Address
(optional), and Password
. You should receive a success message.
填写Username
, Email Address
(可选)和Password
字段。 您应该会收到一条成功消息。
Now run the server with python manage.py runserver
and go to localhost:8000/admin
to see the admin login page. Enter your superuser account details to login.
现在,使用python manage.py runserver
运行服务器,然后转到localhost:8000/admin
以查看admin登录页面。 输入您的超级用户帐户详细信息以登录。
Nice! We have access to the Django admin site. Once we create the books
model and do the appropriate setup we’ll be able to add, edit, delete, and view book data.
真好! 我们可以访问Django管理站点。 创建books
模型并进行适当的设置后,我们将能够添加,编辑,删除和查看图书数据。
Logout and shut down the server with cmd+ctrl
.
注销并使用cmd+ctrl
关闭服务器。
Before moving on, we’ll want to update the settings.py file. It contains authentication credentials that we don’t want to expose to the public. We’ll want to keep these credentials out of our remote repository. There are many ways of protecting ourselves. This is my approach to it:
在继续之前,我们将要更新settings.py文件。 它包含我们不想公开的身份验证凭据。 我们希望将这些凭据保留在我们的远程存储库之外。 有很多保护自己的方法。 这是我的处理方法:
# create a config.json file to hold our configuration values
my_library/server/server/config.json
Inside we’ll store our SECRET_KEY
value from settings.py
under API_KEY
:
在内部,我们将来自settings.py
的SECRET_KEY
值存储在API_KEY
下:
{
"API_KEY" : "abcdefghijklmopqrstuvwxyz123456789"
}
In settings.py
import the json
library and load the config variables:
在settings.py
导入json
库并加载配置变量:
import os
import json
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
with open(BASE_DIR + '/server/config.json', 'r') as config:
obj = json.load(config)
SECRET_KEY = obj["API_KEY"]
...
So that config.json
(with the secret key) isn’t pushed to the repository, create a .gitignore
file in my_library
. This ignores it (along with some other autogenerated files and the database):
为了使config.json
(带有密钥)不会被推送到存储库,请在my_library
创建一个.gitignore
文件。 这将忽略它(以及其他一些自动生成的文件和数据库):
### Django ###
config.json
*.log
*.pot
*.pyc
__pycache__/
local_settings.py
db.sqlite3
media
Now when you commit the changes the files and folders listed above aren’t added. Our secrets are safe and our repo won’t contain unnecessary extra files!
现在,当您提交更改时,不会添加上面列出的文件和文件夹。 我们的秘密是安全的,我们的存储库不会包含不必要的额外文件!
Think of Django apps as modules that plugin into your project. We’ll create an app called books
containing the models, views, and other settings. This is how we interact with the books data in the database.
将Django应用视为可插入您项目的模块。 我们将创建一个名为books
的应用程序,其中包含模型,视图和其他设置。 这就是我们与数据库中图书数据交互的方式。
What are the differences between projects and apps in Django? Check out this thread.
Django中的项目和应用之间有什么区别? 签出此线程 。
# create new app: books
python manage.py startapp books
# creates directory: my_library/server/books
Now we’ll install the books
app into the server
project. Open the settings file: my_library/server/server/settings.py
.
现在,我们将books
应用程序安装到server
项目中。 打开设置文件: my_library/server/server/settings.py
。
Scroll to the INSTALLED_APPS
array. Django has installed it's own core apps by default. Install the books
app at the end of the array:
滚动到INSTALLED_APPS
数组。 Django默认已安装了自己的核心应用程序。 在数组末尾安装books
应用程序:
INSTALLED_APPS = [
...
'books'
]
Next we describe the Book
model in the books app. Open the models file my_library/server/books/models.py
.
接下来,我们在books应用程序中描述Book
模型。 打开模型文件my_library/server/books/models.py
。
Describe a Book
model which tells Django that every book in the database will have:
描述一个Book
模型,该模型告诉Django数据库中的每一本书都会有:
a title
field up to 500 characters in length
title
字段,最长500个字符
an author
field up to 100 characters
author
字段,最多100个字符
a description
field with an open-ended number of characters
带有不限字符数的description
字段
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=500)
author = models.CharField(max_length=100)
description = models.TextField()
Now we register the Book
model with the admin for our books
app. This lets us view it in the admin site and manipulate the books data from there. Open the admin file my_library/server/books/admin.py
and add:
现在,我们向管理员为我们的books
应用注册Book
模型。 这使我们可以在管理站点中查看它并从那里操作书籍数据。 打开管理文件my_library/server/books/admin.py
并添加:
from django.contrib import admin
from .models import Book
@admin.register(Book)
class bookAdmin(admin.ModelAdmin):
list_display = ['title', 'author', 'description']
With a new model created we’ll have to make and run migrations so that the database synchronizes:
创建新模型后,我们必须进行并运行迁移,以便数据库同步:
python manage.py makemigrations
python manage.py migrate
Run the server and go to localhost:8000/admin
to login. Notice that the Book model registered with the admin displays:
运行服务器,然后转到localhost:8000/admin
进行登录。 请注意,向管理员注册的Book模型显示:
Clicking on ‘Books’ displays an empty list because there are no books in the database. Click ‘Add’ to begin creating a new book to add to the database. Go ahead and create a few books.
单击“书籍”会显示一个空列表,因为数据库中没有书籍。 单击“添加”开始创建一本新书以添加到数据库。 继续创建一些书。
Save and go back to the list to view the new data. Now it displays the title, author, and description (list_display array
) fields.
保存并返回列表以查看新数据。 现在,它显示标题,作者和描述( list_display array
)字段。
This is great. We can now view our database books in the admin site. Create, edit, and delete functions are also available.
这很棒。 现在,我们可以在管理站点中查看我们的数据库书籍。 还提供创建,编辑和删除功能。
Note: For simplicity’s sake we’ll use the SQLite database. It comes preinstalled with the creation of every Django project. No need to do any extra work with databases for the purposes of this tutorial.
注意 :为简单起见,我们将使用SQLite数据库。 它随每个Django项目的创建而预先安装。 就本教程而言,无需对数据库做任何额外的工作。
Congrats, we made it to the end of Section 2! This is what we’ve done so far:
恭喜,我们到了第二节的结尾! 到目前为止,这是我们所做的:
Installed python
安装的python
Used python
to install the pip
package manager
使用python
安装pip
软件包管理器
Used pip
to install virtualenv
to create virtual environments
使用pip
安装virtualenv
创建虚拟环境
Created a virtual environment in ~/.envs
called my_library
在~/.envs
创建一个名为my_library
的虚拟环境
Activated the my_library
environment and upgraded pip
激活了my_library
环境并升级了pip
Installed Django 1.11 LTS
within the my_library
environment
在my_library
环境中安装了Django 1.11 LTS
Created our project directory my_library
创建了我们的项目目录my_library
Created the Django project server
创建了Django项目server
Created a Superuser
account to access the Django admin site
创建了一个Superuser
帐户来访问Django管理站点
Protected our secrets by moving our SECRET_KEY
into config.json
通过将SECRET_KEY
移到config.json
保护我们的秘密
Ignored autogenerated and/or sensitive files with .gitignore
忽略带有.gitignore
自动生成和/或敏感文件
Created a new app called books
创建了一个名为books
的新应用
Described the Book
model
描述了Book
模型
Registered the Book
model with the admin
向管理员注册Book
模型
In this section we use the Django REST Framework to build our books
API. It has serializers, views, and URLs that query, structure, and deliver the book data. The data and methods are accessible through API endpoints.
在 本节 我们使用Django REST框架来构建books
API。 它具有序列化程序,视图以及可查询,构造和传递图书数据的URL。 可通过API端点访问数据和方法。
These endpoints are one end of a communication channel. Touchpoints of the communication between the API and another system. The other system in this context is our Ember front end client. The Ember client will interact with the database through the API endpoints. We create these endpoints with Django and the Django REST Framework.
这些端点是通信通道的一端。 API与另一个系统之间的通信接触点。 在这种情况下,另一个系统是我们的Ember前端客户端。 Ember客户端将通过API端点与数据库进行交互。 我们使用Django和Django REST Framework创建这些端点。
We used Django to set up the book
model and the admin site that lets us interact with the database. Django REST Framework will help us build the REST API that the front end will use to interact with the back end.
我们使用Django建立了book
模型和管理站点,使我们可以与数据库进行交互。 Django REST Framework将帮助我们构建REST API,前端将使用该REST API与后端进行交互。
Django REST Framework (DRF) builds on top of Django. It simplifies the creation of RESTful Web APIs. It comes with tools to make the process straightforward.
Django REST Framework (DRF)建立在Django之上。 它简化了RESTful Web API的创建。 它带有使流程简单明了的工具。
The developers of DRF have identified common patterns for serializers and views. Since our data and what users can do with it are simple, we’ll use the built-in serializers and views. Remember, our book data only has three fields title
, author
, and description
. Users are able create new records of books, edit, and delete existing records. This functionality is well within the range of basic common patterns. They’re well supported by the built-in serializers and views. We won’t have to build these from scratch.
DRF的开发人员已经确定了序列化器和视图的通用模式。 由于我们的数据和用户可以执行的操作很简单,因此我们将使用内置的序列化器和视图。 请记住,我们的图书数据只有三个字段title
, author
和description
。 用户能够创建书籍的新记录,编辑和删除现有记录。 此功能完全在基本通用模式范围内。 内置的序列化器和视图很好地支持它们。 我们不必从头开始构建它们。
For more complex projects you’ll want to overwrite defaults or make your own. Again, for the purposes of simplicity we’ll use what comes out of the box without undue modification.
对于更复杂的项目,您将需要覆盖默认值或自行创建。 再次,为了简单起见,我们将使用开箱即用的内容,而无需进行不适当的修改。
Enter the my_library
directory and activate the virtual environment. To start working with DRF, install it with pip
:
输入my_library
目录并激活虚拟环境。 要开始使用DRF,请使用pip
安装它:
# enter my_library
cd ~/desktop/my_library
# activate the virtual environment
source ~/.envs/my_library/bin/activate
# install Django REST Framework
pip install djangorestframework
# install Markdown support for the browsable API
pip install markdown
Now open up my_library/server/server/settings.py
. Install DRF right above the books
app in the INSTALLED_APPS
array:
现在打开my_library/server/server/settings.py
。 在INSTALLED_APPS
数组中的books
应用程序上方安装DRF:
INSTALLED_APPS = [
...
'rest_framework',
'books'
]
Add the default settings at the bottom of the file as an object called REST_FRAMEWORK
:
在文件底部将默认设置添加为名为REST_FRAMEWORK
的对象:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
]
}
The settings object contains a DEFAULT_PERMISSION_CLASSES
key with an array. The only item in the array is a permission class. This ‘allows unauthenticated users to have read-only access to the API’. Find out more about permissions here.
设置对象包含带有数组的DEFAULT_PERMISSION_CLASSES
键。 数组中的唯一项是权限类。 这“ 允许未经身份验证的用户具有对API的只读访问权限” 。 在此处查找有关权限的更多信息。
With DRF installed let’s start building the books
API. Create a new folder called api
inside the books
app. Then create an empty __init__.py
file within: my_library/server/books/api/__init__.py
.
安装DRF后,我们开始构建books
API。 在books
应用中创建一个名为api
的新文件夹。 然后在以下位置创建一个空的__init__.py
文件: my_library/server/books/api/__init__.py
。
The empty file tells Python that this folder is a Python module. The api
folder will contain the serializers, views, and URLs for our books data. I’ll get into the meanings of these terms in their respective sections below.
空文件告诉Python该文件夹是Python模块。 api
文件夹将包含我们的图书数据的序列化器,视图和URL。 我将在下面的各个部分中介绍这些术语的含义。
In simple terms, serializers take database data and restructure it. This structure is a blueprint for the data to alternate between application layers. It gets the front end and backend to speak to each other in a common language.
简单来说, 序列化程序将获取数据库数据并对其进行重组。 此结构是数据在应用程序层之间交替的蓝图。 它使前端和后端可以使用一种通用语言相互交谈。
For example, the front end we’ll create expects the response returned to it from a request to be in the JSON format. Serializing the data to be in JSON ensures the front end will be able to read and write it.
例如,我们将创建的前端期望从请求返回给它的响应为JSON格式。 将数据序列化为JSON可确保前端能够读取和写入数据。
from rest_framework import serializers
from books.models import Book
class bookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = (
'id',
'title',
'author',
'description',
)
This serializer takes the data and transforms it into the JSON format. This ensures that it’s understandable to the front end.
该序列化器获取数据并将其转换为JSON格式。 这样可以确保前端可以理解。
We import built-in serializers
from DRF, and the Book
model from our books
app.
我们从DRF导入内置的serializers
,并从books
应用程序导入Book
模型。
from rest_framework import serializers
from books.models import Book
For this project we want a Serializer
class that ‘corresponds to the Model fields’. The serializer should map to the model fields title
, author
, and description
. We can do this with the ModelSerializer
. According to the documentation:
对于此项目,我们需要一个“ 对应于Model字段 ”的Serializer
类。 序列化程序应映射到模型字段的title
, author
和description
。 我们可以使用ModelSerializer
做到这ModelSerializer
。 根据文档:
The ModelSerializer
class is the same as a regular Serializer
class, except that:
ModelSerializer
类与常规Serializer
类相同,除了:
It includes simple default implementations of .create()
and .update()
.
它包括.create()
和.update()
简单默认实现。
The built-in tools are more than capable of handling our basic needs.
内置工具不仅仅能够满足我们的基本需求。
class bookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = (
'id',
'title',
'author',
'description',
)
View functions take in a web request and return web responses. A web request to localhost:8000/api/books
for example elicits a response from the server.
查看功能接收Web请求并返回Web响应。 例如,对localhost:8000/api/books
的Web请求会引起服务器的响应。
This response can be ‘HTML contents of a Web page, or a redirect, or a 404 error, or an XML document, or an image . . . or anything…’ In our case we expect to get back books data structured in the JSON format.
该响应可以是Web页面的HTML内容,重定向,404错误,XML文档或图像。 。 。 或其他任何内容... '在我们的情况下,我们希望取回以JSON格式结构化的图书数据。
Create the views file in my_library/server/books/api/views.py
:
在my_library/server/books/api/views.py
创建视图文件:
from rest_framework import generics, mixins
from books.models import Book
from .serializers import bookSerializer
class bookAPIView(mixins.CreateModelMixin, generics.ListAPIView):
resource_name = 'books'
serializer_class = bookSerializer
def get_queryset(self):
return Book.objects.all()
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
First we import generics
and mixins
from DRF. Then the Book
model from our books
app and the bookSerializer
that we created.
首先,我们从DRF导入generics
和mixins
。 然后是来自我们的books
应用程序和我们创建的bookSerializer
的Book
模型。
generics
refers to API views that ‘map to your database models’. These are ‘pre-built views that provide for common patterns’. mixins
are classes that ‘provide the actions that used to provide the basic view behavior’. Our book model is simplistic. It only has title
, author
, and description
attributes so these provide us with the basics we need.
generics
是指“ 映射到您的数据库模型 ”的API视图。 这些是“ 提供通用模式的预构建视图 ”。 mixins
是“ 提供用于提供基本视图行为的操作 ”的类。 我们的书模型很简单。 它仅具有title
, author
和description
属性,因此它们为我们提供了所需的基础知识。
from rest_framework import generics, mixins
from books.models import Book
from .serializers import bookSerializer
We then create a bookAPIView
which takes in the CreateModelMixin
and ListAPIView
.
然后,我们创建一个bookAPIView
,它接受CreateModelMixin
和ListAPIView
。
CreateModelMixin
provides a .create(request, *args, **kwargs)
method. This implements the creation and persistence of a new model instance. When successful it returns a 201 Create
response. This comes with a serialized representation of the object that it created.
CreateModelMixin
提供了.create(request, *args, **kwargs)
方法。 这个 实现新模型实例的创建和持久化。 成功后,它将返回201 Create
响应。 这带有它创建的对象的序列化表示。
For example, we would make a POST request to create a new book record for the Steve Jobs book by Walter Isaacson. If successful we get back a response with the code 201
. The serialized representation of the book record like so:
例如,我们将发出POST请求,以为Walter Isaacson的Steve Jobs图书创建新的图书记录。 如果成功,我们将返回代码201
的响应。 图书记录的序列化表示形式如下:
{
"data": {
"type": "books",
"id":"10",
"attributes": {
"title": "Steve Jobs",
"author": "Walter Isaacson",
"description": "Based on more than forty interviews with Jobs conducted over two years—as..."
}
}
}
When unsuccessful, we’ll get back a 400 Bad Request
response with errors details. For example, if we try to create a new book record but don’t provide any title
information:
如果不成功,我们将返回400 Bad Request
响应,其中包含错误详细信息。 例如,如果我们尝试创建新的书记录,但不提供任何title
信息:
{
"errors":[
{
"status": "400",
"source": {
"pointer": "/data/attributes/title"
},
"detail": "This field may not be blank."
}
]
}
ListAPIView
serves our read-only endpoints (GET). It represents ‘a collection of model instances’. We use it when we want to get all or many books.
ListAPIView
提供我们的只读终结点(GET)。 它表示“ 模型实例的集合 ”。 当我们想要获得全部或许多书籍时,我们会使用它。
bookAPIView
also takes in the recently created bookSerializer
for its serializer_class
.
bookAPIView
还为其serializer_class
bookSerializer
了最近创建的bookSerializer
。
We set the resource_name
to ‘books’ to ‘specify the type key in the json output’. The front end client data store layer will have a book
model that is case sensitive. We don’t want to book
model in Ember and the Book
model in Django to clash. Setting the resource_name
here nips that issue in the bud.
我们将resource_name
设置为'books'以' 在json输出中 指定 类型 键 '。 前端客户端数据存储层将具有区分大小写的book
模型。 我们不希望book
在灰烬模型和Book
在Django模型发生冲突。 在此处设置resource_name
消除出现在萌芽状态的问题。
class bookAPIView(mixins.CreateModelMixin, generics.ListAPIView):
resource_name = 'books'
serializer_class = bookSerializer
The function get_queryset
returns all the book objects in the database. post
takes in the request and arguments and creates a new database record of a book if the request is valid.
函数get_queryset
返回数据库中的所有book对象。 如果请求有效,则post
接收请求和参数,并创建书籍的新数据库记录。
def get_queryset(self):
return Book.objects.all()
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
URL patterns map a URL to views. For example, visiting localhost:8000/api/books
should map to a URL pattern. That then returns the results of a query to that view.
URL模式将URL映射到视图。 例如,访问localhost:8000/api/books
应该映射到URL模式。 然后,将查询结果返回到该视图。
Create the URLs file in my_library/server/books/api/urls.py
:
在my_library/server/books/api/urls.py
创建URL文件:
from .views import bookAPIView
from django.conf.urls import url
urlpatterns = [
url(r'^$', bookAPIView.as_view(), name='book-create'),
]
We import our view bookAPIView
and url
. We’ll use url
to create a list of url instances.
我们导入视图bookAPIView
和url
。 我们将使用url
创建url实例列表。
from .views import bookAPIView
from django.conf.urls import url
In the urlpatterns
array we create a URL pattern with the following structure:
在urlpatterns
数组中,我们创建具有以下结构的URL模式:
the pattern r'^$'
模式r'^$'
the Python path to the view bookAPIView.as_view()
视图bookAPIView.as_view()
的Python路径
the name name='book-create'
名称name='book-create'
The pattern r’^$’
is a regular expression that ‘matches an empty line/string’. This means it matches to localhost:8000
. It matches to anything that comes after the base URL.
模式r'^$'
是一个正则表达式, 与空行/字符串 ' 相匹配 。 这意味着它与localhost:8000
匹配。 它与基本URL之后的所有内容匹配。
We call .as_view()
on bookAPIView
because to connect the view to the url. It ‘is the function(class method) which will connect [the] class with its url’. Visit a particular URL and the server attempts to match it to the URL pattern. That pattern will then return the bookAPI
view results that we’ve told it to respond with.
我们在bookAPIView
上调用.as_view()
,因为它可以将视图连接到URL。 它是将类与其url连接的函数(类方法) 。 访问特定的URL,服务器尝试将其与URL模式匹配。 然后,该模式将返回bookAPI
视图结果,我们已告诉它进行响应。
The name=’book-create’
attribute provides us with a name
attribute. We use it to refer to our URL throughout the project. Let’s say you want to change the URL or the view it refers to. Change it here. Without name
we would have to go through the entire project to update every reference. Check out this thread to find out more.
name='book-create'
属性为我们提供了name
属性。 我们使用它来引用整个项目中的URL。 假设您要更改URL或它所引用的视图。 在这里更改。 没有name
我们将不得不遍历整个项目来更新每个参考。 查看此线程以了解更多信息。
urlpatterns = [
url(r'^$', bookAPIView.as_view(), name='book-create'),
]
Now let’s open up server
’s URLs file my_library/server/server/urls.py
:
现在,让我们打开server
的URL文件my_library/server/server/urls.py
:
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/books', include('books.api.urls',
namespace='api-books'))
]
Here we import include
and create the r’^api/books’
pattern which takes in any URLs we created in the api
folder. Now the base URL for our books
API URLs becomes localhost:8000/api/books
. Visiting this URL will match to our r’^/api/books’
pattern. This matches to the r’^$’
pattern we constructed in the books
API.
在这里,我们导入include
并创建r'^api/books'
模式,该模式接受我们在api
文件夹中创建的所有URL。 现在,我们的books
API URL的基本URL变为localhost:8000/api/books
。 访问此URL将匹配我们的r'^/api/books'
模式。 这与我们在books
API中构建的r'^$'
模式匹配。
We use namespace=’api-books’
so that the URLs don’t collide with each other. This would happen if they’re named the same in another app we create. Learn more about why we use namespaces
in this thread.
我们使用namespace='api-books'
来避免URL彼此冲突。 如果在我们创建的另一个应用中将它们命名为相同名称,则会发生这种情况。 了解有关为何在此线程中使用namespaces
更多信息。
Now that we have the base REST framework setup let’s check out the data the back end is returning. With the server running, visit localhost:8000/api/books
. The browsable API should return something like this:
现在我们已经有了基本的REST框架设置,让我们检查后端返回的数据。 在服务器运行的情况下,访问localhost:8000/api/books
。 可浏览的API应该返回以下内容:
Awesome, we’re getting going now. By the end of Section 3 we’ve completed the following steps:
太好了,我们现在要开始。 在第3节结束之前,我们已完成以下步骤:
Started building the books
API
开始构建books
API
Created a serializer
for books
创建书籍的serializer
器
Created a view
for books
创建书籍view
Created URLs
for books
为书籍创建URLs
In this section we shift our attention to the front end and begin working with the Ember framework. We’ll install the required software, set up a basic DOM, styles, create the book
model, and the books
route. We’ll also load up fake book data for demonstration purposes before we go on to access real data from the back end.
在本节中,我们将注意力转移到前端,并开始使用Ember框架。 我们将安装所需的软件,设置基本的DOM,样式,创建book
模型和books
路线。 在继续从后端访问真实数据之前,我们还将加载假书数据以进行演示。
To begin front-end development we need to install some software:
要开始前端开发,我们需要安装一些软件:
Node.js, NPM
Node.js,NPM
Ember CLI
灰烬CLI
NodeJS is an open source server environment. We don’t need to get into the details right now. NPM is a package manager for Node.js packages. We use it to install packages like the Ember CLI.
NodeJS是一个开源服务器环境。 我们现在不需要深入细节。 NPM是Node.js软件包的软件包管理器。 我们使用它来安装Ember CLI之类的软件包。
Install NodeJS and NPM using the installation file from the official site.
使用官方网站上的安装文件安装NodeJS和NPM。
Once installation is complete check that everything installed:
安装完成后,检查是否已安装所有内容:
node --version
npm --version
Let’s use NPM to install the Ember CLI. That’s the ‘official command line utility used to create, build, serve, and test Ember.js apps and addons’. Ember CLI comes with all the tools we need to build the front end of our application.
让我们使用NPM安装Ember CLI。 这是“ 用于创建,构建,提供和测试Ember.js应用程序和附加组件的官方命令行实用程序 ”。 Ember CLI附带了构建应用程序前端所需的所有工具。
# install Ember CLI
npm install -g ember-cli
# check that it's installed
ember --version
Let’s create a front end client called client
using Ember CLI:
让我们使用Ember CLI创建一个称为client
的前端客户client
:
# cd into the main project folder
cd ~/desktop/my_library
# create a new app: client
ember new client
# cd into the directory
cd client
# run the server
ember s
Head over to http://localhost:4200
and you should see this screen:
转到http://localhost:4200
,您应该看到以下屏幕:
The base Ember client project is running as expected. You can shut down the server with ctrl+C
.
基本的Ember客户端项目正在按预期运行。 您可以使用ctrl+C
关闭服务器。
Before we make any new commits, let’s update the .gitignore
file. We want to exclude unwanted files from from the repo. Add on to the file below the Django section:
在进行任何新提交之前,让我们更新.gitignore
文件。 我们想从仓库中排除不需要的文件。 添加到Django部分下面的文件:
...
### Ember ###
/client/dist
/client/tmp
# dependencies
/client/node_modules
/client/bower_components
# misc
/client/.sass-cache
/client/connect.lock
/client/coverage/*
/client/libpeerconnection.log
/client/npm-debug.log
/client/testem.log
# ember-try
/client/.node_modules.ember-try/
/client/bower.json.ember-try
/client/package.json.ember-try
Now that we’ve generated a base project, let’s set up a basic DOM and styles. I’m not doing anything fancy here. It’s the least necessary to have our data displaying in a readable format.
现在我们已经生成了一个基础项目,让我们建立一个基本的DOM和样式。 我在这里什么都没做。 以一种可读的格式显示我们的数据是最不需要的。
Locate the file client/app/templates/application.hbs
. Get rid of {{welcome-page}}
and the comments .
找到文件client/app/templates/application.hbs
。 摆脱{{welcome-page}}
和评论。
Next, create a div
with the class .nav
. Use Ember’s built-in {{#link-to}}
helper to create a link to the route books
(we’ll create it later):
接下来,使用.nav
类创建一个div
。 使用Ember的内置{{#link-to}}
帮手创建一个链接到路由books
(我们将在稍后创建它):
Wrap everything including the{{outlet}}
in a div
with the .container
class. Each route template will render inside {{outlet}}
:
使用.container
类将所有内容(包括{{outlet}}
包装在div
。 每个路线模板都将在{{outlet}}
内部呈现:
{{outlet}}
This is the template for the parent level application
route. any sub-routes like books
will render inside the {{outlet}}
. This means that the nav
will always be visible on screen.
这是父级application
路由的模板。 任何类似books
子路线都将在{{outlet}}
内部呈现。 这意味着nav
将始终在屏幕上可见。
I’m not going to get into the nitty-gritty of the CSS. It’s pretty simple to figure out. Locate the file client/app/styles/app.css
and add the following styles:
我不会深入探讨CSS的实质。 弄清楚很简单。 找到文件client/app/styles/app.css
并添加以下样式:
Variables and Utilities
变量和实用程序
:root {
--color-white: #fff;
--color-black: #000;
--color-grey: #d2d2d2;
--color-purple: #6e6a85;
--color-red: #ff0000;
--font-size-st: 16px;
--font-size-lg: 24px;
--box-shadow: 0 10px 20px -12px rgba(0, 0, 0, 0.42),
0 3px 20px 0px rgba(0, 0, 0, 0.12),
0 8px 10px -5px rgba(0, 0, 0, 0.2);
}
.u-justify-space-between {
justify-content: space-between !important;
}
.u-text-danger {
color: var(--color-red) !important;
}
General
一般
body {
margin: 0;
padding: 0;
font-family: Arial;
}
.container {
display: grid;
grid-template-rows: 40px calc(100vh - 80px) 40px;
height: 100vh;
}
Navigation
导航
.nav {
display: flex;
padding: 0 10px;
background-color: var(--color-purple);
box-shadow: var(--box-shadow);
z-index: 10;
}
.nav-item {
padding: 10px;
font-size: var(--font-size-st);
color: var(--color-white);
text-decoration: none;
}
.nav-item:hover {
background-color: rgba(255, 255, 255, 0.1);
}
Headings
标题
.header {
padding: 10px 0;
font-size: var(--font-size-lg);
}
Books List
图书清单
.book-list {
padding: 10px;
overflow-y: scroll;
}
.book {
display: flex;
justify-content: space-between;
padding: 15px 10px;
font-size: var(--font-size-st);
color: var(--color-black);
text-decoration: none;
cursor: pointer;
}
.book:hover {
background: var(--color-grey);
}
Buttons
纽扣
button {
cursor: pointer;
}
Book Detail
书籍详细资料
.book.book--detail {
flex-direction: column;
justify-content: flex-start;
max-width: 500px;
background: var(--color-white);
cursor: default;
}
.book-title {
font-size: var(--font-size-lg);
}
.book-title,
.book-author,
.book-description {
padding: 10px;
}
Add/Edit Book Form
添加/编辑图书表格
.form {
display: flex;
flex-direction: column;
padding: 10px 20px;
background: var(--color-white);
}
input[type='text'],
textarea {
margin: 10px 0;
padding: 10px;
max-width: 500px;
font-size: var(--font-size-st);
border: none;
border-bottom: 1px solid var(--color-grey);
outline: 0;
}
Actions
动作
.actions {
display: flex;
flex-direction: row;
justify-content: flex-end;
padding: 10px 20px;
background-color: var(--color-white);;
box-shadow: var(--box-shadow)
}
Now we have our styles and container DOM in place. Let’s generate a new route that will display all the books in our database:
现在我们有了样式和容器DOM。 让我们生成一条新路线,该路线将显示数据库中的所有书籍:
ember g route books
The router file client/app/router.js
updates with:
路由器文件client/app/router.js
更新为:
import EmberRouter from '@ember/routing/router';
import config from './config/environment';
const Router = EmberRouter.extend({
location: config.locationType,
rootURL: config.rootURL
});
Router.map(function() {
this.route('books');
});
export default Router;
Let’s edit the books route client/app/routes/books.js
to load all books from the database.
让我们编辑书籍路由client/app/routes/books.js
以从数据库加载所有书籍。
import Route from '@ember/routing/route';
export default Route.extend({
model() {
return [
{title: 'Monkey Adventure'},
{title: 'Island Strife'},
{title: 'The Ball'},
{title: 'Simple Pleasures of the South'},
{title: 'Big City Monkey'}
]
}
});
The model hook is returning an array of objects. This is fake data for demonstration purposes. We’ll come back here later and load the actual data from the database using Ember Data when we’re ready.
模型挂钩返回一个对象数组。 这是用于演示目的的虚假数据。 我们稍后会回到这里,并在准备好后使用Ember Data从数据库加载实际数据。
Let’s edit the books route template client/app/templates/books.hbs
. We want to display the books returned in the model.
让我们编辑书籍路径模板client/app/templates/books.hbs
。 我们要显示模型中返回的书。
{{#each model as |book|}}
{{book.title}}
{{/each}}
Ember uses the Handlebars Template Library. Here we use the each
helper to iterate through our array of books data in model
. We wrap each of the items in the array in a div
with the class .book
. Access and display it’s title information with {{book.title}}
.
灰烬使用把手模板库 。 在这里,我们使用each
助手来遍历model
的书籍数据数组。 我们使用.book
类将数组中的每个项目包装在div
。 使用{{book.title}}
访问并显示其标题信息。
Now that we have the DOM, book
model, and books
route setup with some fake data we can see this running in the browser. Take a look at localhost:4200/books
:
现在我们有了一些伪数据的DOM, book
模型和books
路线设置,我们可以看到它在浏览器中运行。 看看localhost:4200/books
:
It’s kind of annoying to have to put a /books
to visit the books
route. Let’s generate the application
route. We can use the redirect
hook to redirect to the books
route when we enter the base route /
.
不得不放置/books
来访问books
路线有点烦人。 让我们生成application
路由。 输入基本路径/
时,可以使用redirect
钩子将内容重定向到books
路径。
ember g route application
If prompted to overwrite the application.hbs
template, say no. We don’t want to overwrite the template we already set up.
如果提示您覆盖application.hbs
模板,请说“否”。 我们不想覆盖我们已经设置的模板。
In client/app/routes/application.js
create the redirect
hook:
在client/app/routes/application.js
创建redirect
挂钩:
import Route from '@ember/routing/route';
export default Route.extend({
redirect() {
this.transitionTo('books');
}
});
Now, if you visit localhost:4200
it will redirect to localhost:4200/books
.
现在,如果您访问localhost:4200
,它将重定向到localhost:4200/books
。
We don’t want to use fake data forever. Let’s connect to the back end using an adapter and start pulling the books data into the client. Think of the adapter as an “object that receives requests from a store’. It ‘translates them into the appropriate action to take against your persistence layer…’
我们不想永远使用虚假数据。 Let's connect to the back end using an adapter and start pulling the books data into the client. Think of the adapter as an “ object that receives requests from a store'. It 'translates them into the appropriate action to take against your persistence layer…'
Generate a new application adapter:
Generate a new application adapter:
ember g adapter application
Locate the file client/app/adapters/application.js
and update it:
Locate the file client/app/adapters/application.js
and update it:
import DS from 'ember-data';
import { computed } from '@ember/object';
export default DS.JSONAPIAdapter.extend({
host: computed(function(){
return 'http://localhost:8000';
}),
namespace: 'api'
});
The JSONAPIAdapter is the ‘default adapter used by Ember Data’. It transforms the store’s requests into HTTP requests that follow the JSON API format. It plugs into the data management library called Ember Data. We use Ember Data to interface with the back end in a more efficient way. It can store and manage data in the front end and make requests to the back end when required. This means minor page updates don’t need constant requests to the back end. This helps the user experience feel more fluid with generally faster loading times
The JSONAPIAdapter is the ' default adapter used by Ember Data '. It transforms the store's requests into HTTP requests that follow the JSON API format. It plugs into the data management library called Ember Data . We use Ember Data to interface with the back end in a more efficient way. It can store and manage data in the front end and make requests to the back end when required. This means minor page updates don't need constant requests to the back end. This helps the user experience feel more fluid with generally faster loading times
We’ll use its store
service to access server
data without writing more complex ajax
requests. These are still necessary for more complex use cases though.
We'll use its store
service to access server
data without writing more complex ajax
requests. These are still necessary for more complex use cases though.
Here the adapter is telling Ember Data that its host
is at localhost:8000
, namespaced to api
. This means that any requests to the server start with http://localhost:8000/api/
.
Here the adapter is telling Ember Data that its host
is at localhost:8000
, namespaced to api
. This means that any requests to the server start with http://localhost:8000/api/
.
Ember Data has particular requirements for mapping its data to what comes from the back end. We’ll generate a book
model so it understands what the data coming from the back end should map to:
Ember Data has particular requirements for mapping its data to what comes from the back end. We'll generate a book
model so it understands what the data coming from the back end should map to:
ember g model book
Locate the file in client/models/book.js
and define the book
model:
Locate the file in client/models/book.js
and define the book
model:
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr(),
author: DS.attr(),
description: DS.attr()
});
The attributes are the same as those we’ve defined in the back end. We define them again so that Ember Data knows what to expect from the structured data.
The attributes are the same as those we've defined in the back end. We define them again so that Ember Data knows what to expect from the structured data.
books
route (4.5.3 Update the books
route)Let’s update the books route by importing the store
service and using it to request data.
Let's update the books route by importing the store
service and using it to request data.
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default Route.extend({
store: service(),
model() {
const store = this.get('store');
return store.findAll('book');
}
});
So far we’ve created an application adapter and updated the books
route to query for all books in the database. Let’s see what we’re getting back.
So far we've created an application adapter and updated the books
route to query for all books in the database. Let's see what we're getting back.
Run both the Django and Ember servers. Then visit localhost:4200/books
and you should see this in the console:
Run both the Django and Ember servers. Then visit localhost:4200/books
and you should see this in the console:
There seems to be a problem with CORS.
There seems to be a problem with CORS.
CORS defines a way in which browser and server interact to determine whether it’s safe to allow a request. We’re making a cross-origin request from localhost:4200
to localhost:8000/api/books
. From the client to the server with the purpose of accessing our books data.
CORS defines a way in which browser and server interact to determine whether it's safe to allow a request. We're making a cross-origin request from localhost:4200
to localhost:8000/api/books
. From the client to the server with the purpose of accessing our books data.
Currently, the front end isn’t an allowed origin to request data from our back-end endpoints. This block is causing our error. We can resolve this issue by allowing requests to pass through.
Currently, the front end isn't an allowed origin to request data from our back-end endpoints. This block is causing our error. We can resolve this issue by allowing requests to pass through.
Begin by installing an app that adds CORS headers to responses:
Begin by installing an app that adds CORS headers to responses:
pip install django-cors-headers
Install it into server
's settings.py
file under the INSTALLED_APPS
array:
Install it into server
's settings.py
file under the INSTALLED_APPS
array:
INSTALLED_APPS = [
...
'books',
'corsheaders'
]
Add it to the top of the MIDDLEWARE
array:
Add it to the top of the MIDDLEWARE
array:
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
...
]
Finally, allow all requests to get through during development:
Finally, allow all requests to get through during development:
CORS_ORIGIN_ALLOW_ALL = DEBUG
Visit localhost:4200
and you should see this in the console:
Visit localhost:4200
and you should see this in the console:
Looks like we solved the CORS issue and we’re receiving a response from server
with the data that we expect:
Looks like we solved the CORS issue and we're receiving a response from server
with the data that we expect:
[
{
"id": 1,
"title": "Conquistador",
"author": "Buddy Levy",
"description": "It was a moment unique in ..."
},
{
"id": 2,
"title": "East of Eden",
"author": "John Steinbeck",
"description": "In his journal, Nobel Prize ..."
}
]
Although get an array of objects in JSON format, it’s still not in the format we want it to be. This is what Ember Data expects:
Although get an array of objects in JSON format, it's still not in the format we want it to be. This is what Ember Data expects:
{
data: [
{
id: "1",
type: "book",
attributes: {
title: "Conquistador",
author: "Buddy Levy",
description: "It was a moment unique in ..."
}
},
{
id: "2",
type: "book",
attributes: {
title: "East of Eden",
author: "John Steinbeck",
description: "In his journal, Nobel Prize ..."
}
}
]
}
Close but not quite there yet.
Close but not quite there yet.
We’ve completed the following steps in Section 4:
We've completed the following steps in Section 4 :
Created a books
route and template to load and display books
Created a books
route and template to load and display books
Created a book
model and updated the books
route to capture back-end data
Created a book
model and updated the books
route to capture back-end data
In this section we’ll use the Django REST Framework JSON API to structure the data in a way that Ember Data can work with. We’ll also update the books
API to return book a single instance of a book record. We’ll also add the functionality to add, edit, and create books. Then we’re done with our application!
In this section we'll use the Django REST Framework JSON API to structure the data in a way that Ember Data can work with. We'll also update the books
API to return book a single instance of a book record. We'll also add the functionality to add, edit, and create books. Then we're done with our application!
First we use pip to install the Django REST Framework JSON API (DRF). It will transform regular DRF responses into an identity
model in JSON API format.
First we use pip to install the Django REST Framework JSON API (DRF). It will transform regular DRF responses into an identity
model in JSON API format .
With the virtual environment enabled:
With the virtual environment enabled:
# install the Django REST Framework JSON API
pip install djangorestframework-jsonapi
Next, update DRF settings in server/server/settings.py
:
Next, update DRF settings in server/server/settings.py
:
REST_FRAMEWORK = {
'PAGE_SIZE': 100,
'EXCEPTION_HANDLER':
'rest_framework_json_api.exceptions.exception_handler',
'DEFAULT_PAGINATION_CLASS': 'rest_framework_json_api.pagination.JsonApiPageNumberPagination',
'DEFAULT_PARSER_CLASSES': (
'rest_framework_json_api.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
),
'DEFAULT_RENDERER_CLASSES': (
'rest_framework_json_api.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
'DEFAULT_METADATA_CLASS': 'rest_framework_json_api.metadata.JSONAPIMetadata',
'DEFAULT_FILTER_BACKENDS': (
'rest_framework.filters.OrderingFilter',
),
'ORDERING_PARAM': 'sort',
'TEST_REQUEST_RENDERER_CLASSES': (
'rest_framework_json_api.renderers.JSONRenderer',
),
'TEST_REQUEST_DEFAULT_FORMAT': 'vnd.api+json'
}
These override the default settings for DRF with defaults from the JSON API. I increased the PAGE_SIZE
so we can get up to 100 books back in a response.
These override the default settings for DRF with defaults from the JSON API. I increased the PAGE_SIZE
so we can get up to 100 books back in a response.
Let’s also update our books
API so that we can retrieve single instances of a book record.
Let's also update our books
API so that we can retrieve single instances of a book record.
Create a new view calledbookRudView
in server/books/api/views.py
:
Create a new view called bookRudView
in server/books/api/views.py
:
class bookRudView(generics.RetrieveUpdateDestroyAPIView):
resource_name = 'books'
lookup_field = 'id'
serializer_class = bookSerializer
def get_queryset(self):
return Book.objects.all()
This view uses the id
lookup_field
to retrieve an individual book record. The RetrieveUpdateDestroyAPIView provides basic GET
, PUT
, PATCH
and DELETE
method handlers. As you might imagine these let us create, update, and delete individual book data.
This view uses the id
lookup_field
to retrieve an individual book record. The RetrieveUpdateDestroyAPIView provides basic GET
, PUT
, PATCH
and DELETE
method handlers. As you might imagine these let us create, update, and delete individual book data.
We’ll need to create a new URL pattern that delivers data through the bookRudView
.
We'll need to create a new URL pattern that delivers data through the bookRudView
.
from .views import bookAPIView, bookRudView
from django.conf.urls import url
urlpatterns = [
url(r'^$', bookAPIView.as_view(), name='book-create'),
url(r'^(?P\d+)', bookRudView.as_view(), name='book-rud')
]
Import bookRudView
, match it to the pattern r'^(?P
;\d+)', and give it the name book-rud
.
Import bookRudView
, match it to the pattern r'^(?P
;\d+)', and give it the name book-rud
.
Finally, update the books
API URL pattern in server/server/urls.py
. We want to match to patterns which begin after books/
:
Finally, update the books
API URL pattern in server/server/urls.py
. We want to match to patterns which begin after books/
:
...
urlpatterns = [
...
url(r'^api/books/?', include('books.api.urls', namespace='api-books')),
]
Now if you visit localhost:8000/api/books/1
it should display a single book record that matches to a book’s id
:
Now if you visit localhost:8000/api/books/1
it should display a single book record that matches to a book's id
:
Notice that we have access to the DELETE
, PUT
, PATCH
and other methods. These come with RetrieveUpdateDestroyAPIView
.
Notice that we have access to the DELETE
, PUT
, PATCH
and other methods. These come with RetrieveUpdateDestroyAPIView
.
With the JSONAPI
installed the back end should be sending back responses Ember can work with. Run both servers and visit localhost:4200/books
. We should get back real data from the back end and have the route display it. Success!
With the JSONAPI
installed the back end should be sending back responses Ember can work with. Run both servers and visit localhost:4200/books
. We should get back real data from the back end and have the route display it. 成功!
Take a look at the response coming through. It’s in the valid JSONAPI
format that Ember Data works with.
Take a look at the response coming through. It's in the valid JSONAPI
format that Ember Data works with.
We can now view the list of books from our database in the books
route. Next, let’s create a new route in the front-end client
. It will display individual books in detail with title
, author
, and description
data.
We can now view the list of books from our database in the books
route. Next, let's create a new route in the front-end client
. It will display individual books in detail with title
, author
, and description
data.
book
route (5.3.1 Create the book
route)Generate a new route for the individual book page:
Generate a new route for the individual book page:
ember g route book
In router.js
update the new route with the path ‘books/:book_id’
. This overrides the default path and takes in a book_id
parameter.
In router.js
update the new route with the path 'books/:book_id'
. This overrides the default path and takes in a book_id
parameter.
...
Router.map(function() {
this.route('books');
this.route('book', { path: 'books/:book_id' });
});
...
Next update the book
route client/app/routes/book.js
to retrieve a single book record from the database:
Next update the book
route client/app/routes/book.js
to retrieve a single book record from the database:
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default Route.extend({
store: service(),
model(book) {
return this.get('store').peekRecord('book', book.book_id);
}
});
As outlined in router.js
the book
route takes in the book_id
parameter. The parameter goes into the route’s model
hook and we use it to retrieve the book with the Ember Data store
.
As outlined in router.js
the book
route takes in the book_id
parameter. The parameter goes into the route's model
hook and we use it to retrieve the book with the Ember Data store
.
book
template (5.3.2 Update the book
template)Our client/app/templates/book.hbs
template should display the book data we get back from the store
. Get rid of {{outlet}}
and update it:
Our client/app/templates/book.hbs
template should display the book data we get back from the store
. Get rid of {{outlet}}
and update it:
{{model.title}}
{{model.description}}
Like in the books
template we access the model
attributes using dot notation.
Like in the books
template we access the model
attributes using dot notation .
books
template (5.3.3 Update the books
template)Finally, let’s update the books
template. We want to link to each individual book page as displayed in the book
route we created:
Finally, let's update the books
template. We want to link to each individual book page as displayed in the book
route we created:
{{#each model as |book|}}
{{#link-to 'book' book.id class="book"}}
{{book.title}}
{{/link-to}}
{{/each}}
Wrap the book.title
with the link-to
helper. It works like this:
Wrap the book.title
with the link-to
helper. 它是这样的:
creates a link to the book
route
creates a link to the book
route
takes in the book.id
as a parameter
takes in the book.id
as a parameter
takes a class
to style the <
;a> tag generated in the DOM.
takes a class
to style the <
;a> tag generated in the DOM.
Now check out localhost:4200/books
. We can click on our books to get a detailed view. Sweet!
Now check out localhost:4200/books
. We can click on our books to get a detailed view. 甜!
We’ve come to the end of Section 5 with the following steps completed:
We've come to the end of Section 5 with the following steps completed:
Updated the books
route template
Updated the books
route template
Created the book
route and template
Created the book
route and template
In this section we’ll add the following functionality to the front-end experience:
In this section we'll add the following functionality to the front-end experience:
That’s all we have to do to complete the rest of our application. We come a long way. Let’s push on to the end!
That's all we have to do to complete the rest of our application. We come a long way. Let's push on to the end!
We can now view all the books from the database and view individual book records in detail. It’s time to build the functionality to add a new book to the database. These are the steps we’ll take to make that happen:
We can now view all the books from the database and view individual book records in detail. It's time to build the functionality to add a new book to the database. These are the steps we'll take to make that happen:
The create-book
route handles the process of creating a new book and adding it to the database
The create-book
route handles the process of creating a new book and adding it to the database
The create-book
template will have a form with two inputs and a text area to take in a title
, author
, and description
The create-book
template will have a form with two inputs and a text area to take in a title
, author
, and description
The create-book
controller handles the data entered into the form
The create-book
controller handles the data entered into the form
Generate the create-book
route to handle new book creation:
Generate the create-book
route to handle new book creation:
ember g route create-book
Create a controller of the same name to hold form data:
Create a controller of the same name to hold form data:
ember g controller create-book
create-book
controller (6.1.2 Setup the create-book
controller)In client/app/controllers/create-book.js
create a computed property called form
. It will return an object with our book data attributes. This is where we capture the new book data entered in by the user. It’s empty by default.
In client/app/controllers/create-book.js
create a computed property called form
. It will return an object with our book data attributes. This is where we capture the new book data entered in by the user. It's empty by default.
import Controller from '@ember/controller';
import { computed } from '@ember/object';
export default Controller.extend({
form: computed(function() {
return {
title: '',
author: '',
description: ''
}
})
});
create-book
route (6.1.3 Setup the create-book
route)In client/app/routes/create-book.js
we do the following:
In client/app/routes/create-book.js
we do the following:
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default Route.extend({
store: service(),
setupController(controller, model) {
this._super(controller, model);
this.controller.set('form.title', '');
this.controller.set('form.author', '');
this.controller.set('form.description', '');
},
actions: {
create() {
const form = this.controller.get('form');
const store = this.get('store');
const newBook = store.createRecord('book', {
title: form.title,
author: form.author,
description: form.description
});
newBook.save()
.then(() => {
this.transitionTo('books');
});
},
cancel() {
this.transitionTo('books');
}
}
});
The setupController
hook allows us to reset the form’s values. This is so that they don’t persist when we go back and forth through pages. We don’t want to click away to another page without having completed the create book process. If we do, we’ll come back to see the unused data still sitting in our form.
The setupController
hook allows us to reset the form's values. This is so that they don't persist when we go back and forth through pages. We don't want to click away to another page without having completed the create book process. If we do, we'll come back to see the unused data still sitting in our form.
The create()
action will take the form data and create a new record with the Ember Data store
. It then persists it to the Django back end. Once complete it will transition the user back to the books
route.
The create()
action will take the form data and create a new record with the Ember Data store
. It then persists it to the Django back end. Once complete it will transition the user back to the books
route.
The cancel
button transitions the user back to the books
route.
The cancel
button transitions the user back to the books
route.
create-book
template (6.1.4 Setup the create-book
template)Next, in client/app/template/create-book.hbs
we build the form:
Next, in client/app/template/create-book.hbs
we build the form:
The form
uses the built-in {{input}}
helpers to:
The form
uses the built-in {{input}}
helpers to:
The {{text}}
area helper works in a similar way, with the addition of the number of rows.
The {{text}}
area helper works in a similar way, with the addition of the number of rows.
The actions div
contains the two buttons to create and cancel. Each button ties to its namesake action using the {{action}}
helper.
The actions div
contains the two buttons to create and cancel. Each button ties to its namesake action using the {{action}}
helper.
books
route template (6.1.5 Update the books
route template)The final piece of the create book puzzle is to add a button in the books
route. It will get us into the create-book
route and begin creating a new book.
The final piece of the create book puzzle is to add a button in the books
route. It will get us into the create-book
route and begin creating a new book.
Add on to the bottom of client/app/templates/books.hbs
:
Add on to the bottom of client/app/templates/books.hbs
:
...
{{#link-to 'create-book' class='btn btn-addBook'}}
Add Book
{{/link-to}}
Now if we go back and try to create a new book again we’ll find success. Click into the book to see a more detailed view:
Now if we go back and try to create a new book again we'll find success. Click into the book to see a more detailed view:
Now that we can add books to the database we should be able to delete them too.
Now that we can add books to the database we should be able to delete them too.
book
route template (6.2.1 Update the book
route template)First update the book
route’s template. Add on under book book--detail
:
First update the book
route's template. Add on under book book--detail
:
...
{{#if confirmingDelete}}
Are you sure you want to delete this book?
{{else}}
{{/if}}
The actions
div
contains the buttons and text for the book deletion process.
The actions
div
contains the buttons and text for the book deletion process.
We have a bool
called confirmingDelete
which will be set on the route’s controller
. confirmingDelete
adds the .u-justify-space-between
utility class on actions
when it’s true
.
We have a bool
called confirmingDelete
which will be set on the route's controller
. confirmingDelete
adds the .u-justify-space-between
utility class on actions
when it's true
.
When it’s true, it also displays a prompt with the utility class .u-text-danger
. This prompts the user to confirm deletion of the book. Two buttons show up. One to run delete
action in our route. The other uses the mut
helper to flip confirmingDelete
to false
.
When it's true, it also displays a prompt with the utility class .u-text-danger
. This prompts the user to confirm deletion of the book. Two buttons show up. One to run delete
action in our route. The other uses the mut
helper to flip confirmingDelete
to false
.
When confirmingDelete
is false
(the default state) a single delete
button display. Clicking it flips confirmingDelete
to true
. This then displays the prompt and the other two buttons.
When confirmingDelete
is false
(the default state) a single delete
button display. Clicking it flips confirmingDelete
to true
. This then displays the prompt and the other two buttons.
book
route (6.2.2 Update the book
route)Next update the book
route. Under the model
hook add:
Next update the book
route. Under the model
hook add:
setupController(controller, model) {
this._super(controller, model);
this.controller.set('confirmingDelete', false);
},
In setupController
we call this._super()
. This is so the controller goes through its usual motions before we do our business. Then we set confirmingDelete
to false
.
In setupController
we call this._super()
. This is so the controller goes through its usual motions before we do our business. Then we set confirmingDelete
to false
.
Why do we do this? Let’s say we start to delete a book, but leave the page without either cancelling the action or deleting the book. When we go to any book page confirmingDelete
would be set to true
as a leftover.
Why do we do this? Let's say we start to delete a book, but leave the page without either cancelling the action or deleting the book. When we go to any book page confirmingDelete
would be set to true
as a leftover.
Next let’s create an actions
object that will hold our route actions:
Next let's create an actions
object that will hold our route actions:
actions: {
delete(book) {
book.deleteRecord();
book.save().then(() => {
this.transitionTo('books');
});
}
}
The delete
action as referenced in our template takes in a book
. We run deleteRecord
on the book
and then save
to persist the change. Once that promise completes transitionTo
transitions to the books
route (our list view).
The delete
action as referenced in our template takes in a book
. We run deleteRecord
on the book
and then save
to persist the change. Once that promise completes transitionTo
transitions to the books
route (our list view).
Let’s check this out in action. Run the servers and select a book you want to delete.
Let's check this out in action. Run the servers and select a book you want to delete.
When you delete the book it redirects to the books
route.
When you delete the book it redirects to the books
route.
Last but not least we’ll add the functionality to edit an existing books information.
Last but not least we'll add the functionality to edit an existing books information.
book
route template (6.3.1 Update the book
route template)Open up the book
template and add a form to update book data:
Open up the book
template and add a form to update book data:
{{#if isEditing}}
{{else}}
...
...
{{/if}}
First let’s wrap the entire template in an if
statement. This corresponds to the isEditing
property which by default will be false
.
First let's wrap the entire template in an if
statement. This corresponds to the isEditing
property which by default will be false
.
Notice that the form is very almost identical to our create book form. The only real difference is that the actions update
runs the update
action in the book
route. The cancel
button also flips the isEditing
property to false
.
Notice that the form is very almost identical to our create book form. The only real difference is that the actions update
runs the update
action in the book
route. The cancel
button also flips the isEditing
property to false
.
Everything we had before gets nested inside the else
. We add the Edit
button to flip isEditing
to true and display the form.
Everything we had before gets nested inside the else
. We add the Edit
button to flip isEditing
to true and display the form.
book
controller to handle form values (6.3.2 Create a book
controller to handle form values)Remember the create-book
controller? We used it to hold the values that’s later sent to the server to create a new book record.
Remember the create-book
controller? We used it to hold the values that's later sent to the server to create a new book record.
We’ll use a similar method to get and display the book data in our isEditing
form. It will pre-populate the form with the current book’s data.
We'll use a similar method to get and display the book data in our isEditing
form. It will pre-populate the form with the current book's data.
Generate a book controller:
Generate a book controller:
ember g controller book
Open client/app/controllers/book.js
and create a form
computed property like before. Unlike before we’ll use the model
to pre-populate our form with the current book
data:
Open client/app/controllers/book.js
and create a form
computed property like before. Unlike before we'll use the model
to pre-populate our form with the current book
data:
import Controller from '@ember/controller';
import { computed } from '@ember/object';
export default Controller.extend({
form: computed(function() {
const model = this.get('model');
return {
title: model.get('title'),
author: model.get('author'),
description: model.get('description')
}
})
});
book
route (6.3.3 Update the book
route)We’ll have to update our route again:
We'll have to update our route again:
setupController(controller, model) {
...
this.controller.set('isEditing', false);
this.controller.set('form.title', model.get('title'));
this.controller.set('form.author', model.get('author'));
this.controller.set('form.description', model.get('description'));
},
Let’s add on to the setupController
hook. Set isEditing
to false
and reset all the form values to their defaults.
Let's add on to the setupController
hook. Set isEditing
to false
and reset all the form values to their defaults.
Next let’s create the update
action:
Next let's create the update
action:
actions: {
...
update(book) {
const form = this.controller.get('form');
book.set('title', form.title);
book.set('author', form.author);
book.set('description', form.description);
book.save().then(() => {
this.controller.set('isEditing', false);
});
}
}
It’s pretty straightforward. We get the form values, set those values on the book
and persist with save
. Once successful we flip isEditing
back to false
.
It's pretty straightforward. We get the form values, set those values on the book
and persist with save
. Once successful we flip isEditing
back to false
.
We’ve completed the following steps by the end of Section 6:
We've completed the following steps by the end of Section 6 :
That’s it. We’ve done it! We built a very basic full stack application using Django and Ember.
而已。 We've done it! We built a very basic full stack application using Django and Ember.
Let’s step back and think about what we’ve built for a minute. We have an application called my_library
that:
Let's step back and think about what we've built for a minute. We have an application called my_library
that:
As we built the application we learned about Django and how it’s used to administer the database. We created models, serializers, views, and URL patterns to work with the data. We used Ember to create a user interface to access and change the data through the API endpoints.
As we built the application we learned about Django and how it's used to administer the database. We created models, serializers, views, and URL patterns to work with the data. We used Ember to create a user interface to access and change the data through the API endpoints.
If you’ve gotten this far, you’ve finished the tutorial! The application is running with all the intended functionality. That’s a lot to be proud of. Software development, complicated? That’s an understatement. It can feel downright inaccessible even with all the resources available to us. I get that feeling all the time.
If you've gotten this far, you've finished the tutorial! The application is running with all the intended functionality. That's a lot to be proud of. Software development, complicated? 轻描淡写。 It can feel downright inaccessible even with all the resources available to us. I get that feeling all the time.
What works for me is to take frequent breaks. Get up and walk away from what you’re doing. Do something else. Then get back and break down your problems step by step into the smallest units. Fix and refactor until you get to where you want to be. There are no shortcuts to building your knowledge.
What works for me is to take frequent breaks. Get up and walk away from what you're doing. Do something else. Then get back and break down your problems step by step into the smallest units. Fix and refactor until you get to where you want to be. There are no shortcuts to building your knowledge.
Anyways, we’ve might have done a lot here for an introduction but we’re only scratching the surface. There is plenty more for you to learn about full stack development. Here are some examples to think about:
Anyways, we've might have done a lot here for an introduction but we're only scratching the surface. There is plenty more for you to learn about full stack development. Here are some examples to think about:
When I have time I’ll look into writing more on these topics myself.
When I have time I'll look into writing more on these topics myself.
I hope you found this tutorial useful. It’s intended to serve as a jump-off point for you to learn more about Django, Ember and full stack development. It was definitely a learning experience for me. Shoutout to my Closing Folders team for the support and encouragement. We’re hiring now so feel free to get in touch!
希望本教程对您有所帮助。 It's intended to serve as a jump-off point for you to learn more about Django, Ember and full stack development. It was definitely a learning experience for me. Shoutout to my Closing Folders team for the support and encouragement. We're hiring now so feel free to get in touch!
If you’d like to reach out you can contact me through the following channels:
If you'd like to reach out you can contact me through the following channels:
medium
medium
personal website
personal website
Writing this tutorial forced me confront the edges of my knowledge. Here are the resources that helped with my comprehension of the topics covered:
Writing this tutorial forced me confront the edges of my knowledge. Here are the resources that helped with my comprehension of the topics covered:
What is a full stack programmer?What is a web application?What is Django?What is EmberJS?What is version control?What is Git?How do I use Git with Github?How do I create a Git repository?How do I add a Git remote?What is a model?What is a view?What is a superuser?What is making a migration?What is migrating?What is SQLite?JSON Python Parsing: A Simple GuideHow to secure API keysWhat is Python?What is pip?What is virtualenv?Best practices for virtualenv and git repoWhat is an API?What are API endpoints?What is the Django REST Framework?What is __init__.py?What is a serializer?What are views?What are URLS?What is JSON?What are regular expressions?What does __init__.py do?What is REST?What is Node.js?What is NPM?What is EmberJS?What is Ember CLI?What is Ember-Data?What is a model?What is a route?What is a router?What is a template?What is an adapter?What is the Django REST Framework JSON API?What is the JSON API format?What is dot notation?
What is a full stack programmer? What is a web application? What is Django? What is EmberJS? What is version control? What is Git? How do I use Git with Github? How do I create a Git repository? How do I add a Git remote? What is a model? What is a view? What is a superuser? What is making a migration? What is migrating? What is SQLite? JSON Python Parsing: A Simple Guide How to secure API keys What is Python? What is pip? What is virtualenv? Best practices for virtualenv and git repo What is an API? What are API endpoints? What is the Django REST Framework? What is __init__.py? What is a serializer? What are views? What are URLS? What is JSON? What are regular expressions? What does __init__.py do? What is REST? What is Node.js? What is NPM? What is EmberJS? What is Ember CLI? What is Ember-Data? What is a model? What is a route? What is a router? What is a template? What is an adapter? What is the Django REST Framework JSON API? What is the JSON API format? What is dot notation?
翻译自: https://www.freecodecamp.org/news/eli5-full-stack-basics-breakthrough-with-django-emberjs-402fc7af0e3/
eli bendersky