eli bendersky_ELI5 Full Stack基础知识:Django和EmberJS的突破

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.

这并不是要深入研究DjangoEmberJS 。 我不想让我们陷入太多的麻烦之中。 相反,其目的是显示基本的全栈开发的关键要素 。 如何将后端和前端缝合在一起,形成一个可以正常工作的应用程序。 我将详细介绍该过程中使用的软件,框架和工具。 本教程介绍了最终应用程序中每个终端命令运行和代码行。

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存储库下载最终项目文件

目录 (Table of Contents)

第1节:内容,方式和原因 (Section 1: The Whats, Hows, and Whys)

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节:深入后端 (Section 2: Diving into the Back End)

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节:先构建服务器,然后构建REST (Section 3: Build a Server, then REST)

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节:放下前端基础 (Section 4: Laying Down Front-end Foundations)

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节:正确的数据格式,处理单个记录 (Section 5: Correct data formats, deal with individual records)

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节:功能前端 (Section 6: Functional Front end)

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节:继续 (Section 7: Moving On)

7.1 What’s Next?7.2 Further Reading

7.1下一步是什么?7.2进一步阅读

第1节:内容,方式和原因 (Section 1: The Whats, Hows, and Whys)

1.1为什么我写了本教程 (1.1 Why I Wrote This Tutorial)

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 :注意高级开发人员,初级开发人员和设计师! 正在关闭文件夹,因此请随时与我们联系。

1.2后端,前端。 有什么不同? (1.2 Back End, Front End. What’s the Difference?)

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.

本文中找到有关差异的更多信息。

1.3概念:基本的图书馆应用 (1.3 The Concept: A Basic Library Application)

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_libraryWeb应用程序 。 该应用程序听起来像是一个数字图书图书馆。 不过,我们不会处理实际的书籍内容。 这些书将仅包含标题,作者和描述信息。 保持简单。

The application will have the following functionality:

该应用程序将具有以下功能:

  • View all books as a single list on the home page, ordered by title

    在首页上按列表查看所有书籍,按书名排序
  • View each book in detail, displaying its title, author, and description

    详细查看每本书,显示其标题,作者和描述
  • Add a new book with the fields title, author, and description

    添加带有标题,作者和描述字段的新书
  • Edit an existing book’s title, author, and description fields

    编辑现有书籍的标题,作者和描述字段
  • Delete an existing book

    删除现有书籍
1.3.1 my_library的最终设计和功能 (1.3.1 my_library’s final design and functionality)

Take a look at the screenshots below. They depict the application’s final look and functionality:

看看下面的截图。 它们描述了应用程序的最终外观和功能:

1.4项目目录结构 (1.4 Project Directory Structure)

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前端。

1.4.1后端 (1.4.1 Back End)
  • 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和视图。

1.4.2前端 (1.4.2 Front End)
  • 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

1.5项目目录设置 (1.5 Project Directory Setup)

1.5.1创建主项目文件夹:my_library (1.5.1 Create the main project folder: 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存储库作为项目起点。

1.5.2安装Git进行版本控制 (1.5.2 Install Git for version control)

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
1.5.3创建一个新的项目存储库 (1.5.3 Create a new project repository)

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:

git@github.com:username/repo_name.git
1.5.4提交更改并将其推送到项目存储库 (1.5.4 Commit and push your changes to the project repository)

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 git@github.com: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:

注意 :从现在开始,我将不再进一步讨论提交。 下面的审查和提交指示符将在适当时通知您:

1.6结论 (1.6 Conclusion)

We’ve come to the end of Section 1 with the following steps completed:

我们已经完成了以下步骤,到了第1节的结尾:

  • Got a feel for what we’re building and how it will work

    对我们正在构建的东西以及它将如何工作有一种感觉
  • Created the my_library main project directory

    创建了my_library主项目目录

  • Installed git and created a remote project repository on Github

    安装了git并在Github上创建了一个远程项目存储库

  • Initialized the local repository and set the remote URL

    初始化本地存储库并设置远程URL
  • Created aREADME.md file, then committed and pushed all changes

    创建一个README.md文件,然后提交并推送所有更改

第2节:深入后端 (Section 2: Diving into the Back End)

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网站来管理数据库,并开始将其与书籍数据一起播种。

2.1安装所需的软件 (2.1 Install Required Software)

Before we begin our back end project we’ll need to install some software:

在开始我们的后端项目之前,我们需要安装一些软件:

2.1.1 Python (2.1.1 Python)

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.73.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
2.1.2点 (2.1.2 pip)

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.

完整的安装文档可在此处获得

2.1.3 virtualenv (2.1.3 virtualenv)

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
2.1.4 Django 1.11(LTS) (2.1.4 Django 1.11 (LTS))

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:

示例包括:

  • a management panel

    管理面板
  • a way to handle user authentication

    处理用户身份验证的方法

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.

完整的安装文档可在此处获得

2.2启动Django项目:服务器 (2.2 Start a Django Project: server)

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关闭服务器。

2.2.1创建超级用户帐户 (2.2.1 Create the Superuser account)

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.

填写UsernameEmail 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关闭服务器。

2.2.2保护我们的秘密 (2.2.2 Protecting Our Secrets)

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.pySECRET_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!

现在,当您提交更改时,不会添加上面列出的文件和文件夹。 我们的秘密是安全的,我们的存储库不会包含不必要的额外文件!

2.3启动Django应用:书籍 (2.3 Start a Django App: books)

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'
]

2.4描述书籍模型 (2.4 Describe the Book model)

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()

2.5向管理员注册Book模型 (2.5 Register the Book model with the admin)

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项目的创建而预先安装。 就本教程而言,无需对数据库做任何额外的工作。

2.6结论 (2.6 Conclusion)

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模型

  • Added books data into the database

    将图书数据添加到数据库中

第3节:先构建服务器,然后构建REST (Section 3: Build a Server, then REST)

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与后端进行交互。

3.1 Django REST框架 (3.1 Django REST Framework)

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的开发人员已经确定了序列化器和视图的通用模式。 由于我们的数据和用户可以执行的操作很简单,因此我们将使用内置的序列化器和视图。 请记住,我们的图书数据只有三个字段titleauthordescription 。 用户能够创建书籍的新记录,编辑和删除现有记录。 此功能完全在基本通用模式范围内。 内置的序列化器和视图很好地支持它们。 我们不必从头开始构建它们。

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.

对于更复杂的项目,您将需要覆盖默认值或自行创建。 再次,为了简单起见,我们将使用开箱即用的内容,而无需进行不适当的修改。

3.1.1安装Django REST框架 (3.1.1 Install Django REST Framework)

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的只读访问权限”在此处查找有关权限的更多信息。

3.2创建books API文件夹 (3.2 Create the books API folder)

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。 我将在下面的各个部分中介绍这些术语的含义。

3.3创建书籍序列化器 (3.3 Create a book serializer)

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格式。 这样可以确保前端可以理解。

进口货 (Imports)

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
这本书Serializer类 (The bookSerializer Class)

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类。 序列化程序应映射到模型字段的titleauthordescription 。 我们可以使用ModelSerializer做到这ModelSerializer 。 根据文档:

The ModelSerializer class is the same as a regular Serializer class, except that:

ModelSerializer类与常规Serializer类相同,除了:

  • It will generate a set of fields for you, based on the model.

    它将根据模型为您生成一组字段。
  • It will generate validators for the serializer, such as unique_together validators.

    它将为序列化器生成验证器,例如unique_together验证器。
  • 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',
    )

3.4创建一个GET和POST图书数据视图 (3.4 Create a view to GET and POST books data)

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)
进口货 (Imports)

First we import generics and mixins from DRF. Then the Book model from our books app and the bookSerializer that we created.

首先,我们从DRF导入genericsmixins 。 然后是来自我们的books应用程序和我们创建的bookSerializerBook模型。

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是“ 提供用于提供基本视图行为的操作 ”的类。 我们的书模型很简单。 它仅具有titleauthordescription属性,因此它们为我们提供了所需的基础知识。

from rest_framework import generics, mixins
from books.models import Book
from .serializers import  bookSerializer
bookAPI视图 (The bookAPI View)

We then create a bookAPIView which takes in the CreateModelMixin and ListAPIView.

然后,我们创建一个bookAPIView ,它接受CreateModelMixinListAPIView

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
功能 (Functions)

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)

3.5创建URL以访问图书数据 (3.5 Create URLs to access books data)

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'),
]
进口货 (Imports)

We import our view bookAPIView and url. We’ll use url to create a list of url instances.

我们导入视图bookAPIViewurl 。 我们将使用url创建url实例列表。

from .views import bookAPIView
from django.conf.urls import url
booksAPI URL模式 (booksAPI URL patterns)

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'),
]
服务器网址格式 (server URL patterns)

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更多信息。

3.5.1演示:浏览书籍API (3.5.1 Demonstration: Browsing the books API)

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应该返回以下内容:

3.6结论 (3.6 Conclusion)

Awesome, we’re getting going now. By the end of Section 3 we’ve completed the following steps:

太好了,我们现在要开始。 在第3节结束之前,我们已完成以下步骤:

  • Installed Django REST Framework into our project

    将Django REST Framework安装到我们的项目中
  • Started building the books API

    开始构建books API

  • Created a serializer for books

    创建书籍的serializer

  • Created a view for books

    创建书籍view

  • Created URLs for books

    为书籍创建URLs

  • Browsed the books API that returns book data from the back end

    浏览了图书API,该API从后端返回图书数据

第4节:放下前端基础 (Section 4: Laying Down Front-end Foundations)

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路线。 在继续从后端访问真实数据之前,我们还将加载假书数据以进行演示。

4.1安装所需的软件 (4.1 Install Required Software)

To begin front-end development we need to install some software:

要开始前端开发,我们需要安装一些软件:

4.1.1 NodeJS和NPM (4.1.1 NodeJS and NPM)

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
4.1.2 Ember CLI (4.1.2 Ember CLI)

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

4.2开始一个Ember项目:客户端 (4.2 Start an Ember Project: client)

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关闭服务器。

4.2.1使用Ember排除项更新.gitignore (4.2.1 Update .gitignore with Ember exclusions)

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

4.3显示图书数据 (4.3 Displaying books data)

4.3.1设置DOM (4.3.1 Setup the DOM)

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 (我们将在稍后创建它):

<div class="nav">
  {{#link-to 'books' class="nav-item"}}Home{{/link-to}}
</div>

Wrap everything including the{{outlet}} in a div with the .container class. Each route template will render inside {{outlet}}:

使用.container类将所有内容(包括{{outlet}}包装在div 。 每个路线模板都将在{{outlet}}内部呈现:

<div class="container">
  <div class="nav">
    {{#link-to 'books' class="nav-item"}}Home{{/link-to}}
  </div>
{{outlet}}
</div>

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将始终在屏幕上可见。

4.3.2创建样式 (4.3.2 Create styles)

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)
}

4.4书本路线 (4.4 The books route)

4.4.1创建图书路线 (4.4.1 Create the books route)

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;
4.4.2在模型挂钩中加载假数据 (4.4.2 Load fake data in the model hook)

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从数据库加载实际数据。

4.4.3更新图书路径模板 (4.4.3 Update the books route template)

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 。 我们要显示模型中返回的书。

<div class="book-list">
  {{#each model as |book|}}
    <div class="book">
      {{book.title}}
    </div>
  {{/each}}
</div>

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}}访问并显示其标题信息。

4.4.4演示:书籍路线加载和显示虚假数据 (4.4.4 Demonstration: books route loading and displaying fake data)

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

4.4.5创建用于重定向的应用程序路由 (4.4.5 Create application route for redirect)

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

4.5在书籍路径中显示真实数据 (4.5 Displaying real data in the books route)

4.5.1创建应用程序适配器 (4.5.1 Create an application adapter)

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

4.5.2 Create the book model (4.5.2 Create the book model)

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.

4.5.3 Update the 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');
  }
});
4.5.4 Demonstration: books has a CORS issue (4.5.4 Demonstration: books has a CORS issue)

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.

4.5.5 Resolve the Cross-Origin Resource Sharing (CORS) issue (4.5.5 Resolve the Cross-Origin Resource Sharing (CORS) issue)

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
4.5.6 Demonstration: CORS issue resolved, incompatible data format (4.5.6 Demonstration: CORS issue resolved, incompatible data format)

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.

4.6 Conclusion (4.6 Conclusion)

We’ve completed the following steps in Section 4:

We've completed the following steps in Section 4 :

  • Installed NodeJS and NPM

    Installed NodeJS and NPM
  • Installed the Ember CLI and created a new client project

    Installed the Ember CLI and created a new client project
  • Basic DOM setup

    Basic DOM setup
  • Created a books route and template to load and display books

    Created a books route and template to load and display books

  • Demonstrated the app running with fake data

    Demonstrated the app running with fake data
  • Created an application adapter to connect to the back end and receive data

    Created an application adapter to connect to the back end and receive data
  • 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

  • Demonstrated that the back-end data isn’t structured in the way that Ember Data expects it to be

    Demonstrated that the back-end data isn't structured in the way that Ember Data expects it to be

Section 5: Correct data formats, deal with individual records (Section 5: Correct data formats, deal with individual records)

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!

5.1 Install the Django REST Framework JSON API (5.1 Install the Django REST Framework JSON API)

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.

5.2 Working with individual book records (5.2 Working with individual book records)

5.2.1 Create a view (5.2.1 Create a view)

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.

5.2.2 Update the book API URLs (5.2.2 Update the book API URLs)

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<id>\d+)', bookRudView.as_view(), name='book-rud')
]

Import bookRudView, match it to the pattern r'^(?P<id>;\d+)', and give it the name book-rud.

Import bookRudView , match it to the pattern r'^(?P<id> ;\d+)', and give it the name book-rud .

5.2.3 Update the server URLs (5.2.3 Update the server URLs)

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')),
]
5.2.4 Demonstration: Access a single book record (5.2.4 Demonstration: Access a single book record)

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 .

5.2.5 Demonstration: Capturing and displaying data from the back end in the correct format (5.2.5 Demonstration: Capturing and displaying data from the back end in the correct format)

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.

5.3 The book Route (5.3 The book Route)

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.

5.3.1 Create the 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 .

5.3.2 Update the 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:

<div class="book book--detail">
  <div class="book-title">
    {{model.title}}
  </div>
  <div class="book-author">
    {{model.author}}
  </div>
  <div class="book-description">
    {{model.description}}
  </div>
</div>

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 .

5.3.3 Update the 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:

<div class="book-list">
  {{#each model as |book|}}
    {{#link-to 'book' book.id class="book"}}
      {{book.title}}
    {{/link-to}}
  {{/each}}
</div>

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.

5.3.4 Demonstration: Select book to view detailed information (5.3.4 Demonstration: Select book to view detailed information)

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. 甜!

5.4 Conclusion (5.4 Conclusion)

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:

  • Identified the problem with the data coming from Django

    Identified the problem with the data coming from Django
  • Installed the Django REST Framework JSON API

    Installed the Django REST Framework JSON API
  • Updated the books route template

    Updated the books route template

  • Created the book route and template

    Created the book route and template

Section 6: Functional Front end (Section 6: Functional Front end)

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:

  • Add a new book with the fields title, author, and description

    Add a new book with the fields title, author, and description
  • Edit an existing book’s title, author, and description fields

    Edit an existing book's title, author, and description fields
  • Delete an existing book

    Delete an existing book

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!

6.1 Adding a new book to the database (6.1 Adding a new book to the database)

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

6.1.1 Create the create-book route and controller (6.1.1 Create the create-book route and controller)

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
6.1.2 Setup the 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: ''
    }
  })
});
6.1.3 Setup the 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:

  • create actions to confirm creation of a new book

    create actions to confirm creation of a new book
  • cancel the creation process

    cancel the creation process
  • use a route hook to clear the form data upon entering the route:

    use a route hook to clear the form data upon entering the route:
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.

6.1.4 Setup the 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:

<form class="form">
  <div class="header">
    Add a new book
  </div>
  {{input
    value=form.title
    name="title"
    placeholder="Title"
    autocomplete='off'
  }}
  {{input
    value=form.author
    name="author"
    placeholder="Author"
    autocomplete='off'
  }}
  {{textarea
    value=form.description
    name="description"
    placeholder="Description"
    rows=10
  }}
</form>
<div class="actions">
  <div>
    <button {{action 'create'}}>
      Create
    </button>
    <button {{action 'cancel'}}>
      Cancel
    </button>
  </div>
</div>

The form uses the built-in {{input}} helpers to:

The form uses the built-in {{input}} helpers to:

  • take in values

    take in values
  • display placeholders

    display placeholders
  • turn autocomplete off.

    turn autocomplete off.

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.

6.1.5 Update the 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}}
6.1.6 Demonstration: Can add a new book (6.1.6 Demonstration: Can add a new book)

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:

6.2 Deleting a book from the database (6.2 Deleting a book from the database)

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.

6.2.1 Update the 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 :

...
<div class="actions {{if confirmingDelete
                         'u-justify-space-between'}}">
  {{#if confirmingDelete}}
    <div class="u-text-danger">
      Are you sure you want to delete this book?
    </div>
    <div>
      <button {{action 'delete' model}}>Delete</button>
      <button {{action (mut confirmingDelete)false}}>
        Cancel
      </button>
    </div>
  {{else}}
    <div>
      <button {{action (mut confirmingDelete) true}}>Delete</button>
    </div>
  {{/if}}
</div>

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.

6.2.2 Update the 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).

6.2.3 Demonstration: Can delete an existing book (6.2.3 Demonstration: Can delete an existing book)

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.

6.3 Editing a book in the database (6.3 Editing a book in the database)

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.

6.3.1 Update the 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}}
  <form class="form">
    <div class="header">Edit</div>
    {{input
      value=form.title
      placeholder="Title"
      autocomplete='off'
    }}
    {{input
      value=form.author
      placeholder="Author"
      autocomplete='off'
    }}
    {{textarea
      value=form.description
      placeholder="Description"
      rows=10
    }}
  </form>
  <div class="actions">
    <div>
      <button {{action 'update' model}}>Update</button>
      <button {{action (mut isEditing) false}}>Cancel</button>
    </div>
  </div>
{{else}}
  ...
  <div>
    <button {{action (mut isEditing) true}}>Edit</button>
    <button {{action (mut confirmingDelete) true}}>Delete</button>
  </div>
  ...
{{/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.

6.3.2 Create a 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')
    }
  })
});
6.3.3 Update the 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 .

6.3.4 Demonstration: Can edit information of an existing book (6.3.4 Demonstration: Can edit information of an existing book)

6.4 Conclusion (6.4 Conclusion)

We’ve completed the following steps by the end of Section 6:

We've completed the following steps by the end of Section 6 :

  • Identified the problem with the data coming from Django

    Identified the problem with the data coming from Django
  • Installed JSON API into Django

    Installed JSON API into Django
  • Updated the Books Route Template

    Updated the Books Route Template
  • Created the book detail route and template

    Created the book detail route and template
  • Can view, add, edit, and delete database records from the EmberJS client

    Can view, add, edit, and delete database records from the EmberJS client

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:

  • lists books from a database

    lists books from a database
  • allows users to view each book in more detail

    allows users to view each book in more detail
  • add a new book

    add a new book
  • edit an existing book

    edit an existing book
  • delete a book

    delete a book

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.

Section 7: Moving On (Section 7: Moving On)

7.1 What's Next (7.1 What’s Next)

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:

  • user accounts with authentication

    user accounts with authentication
  • testing functionality of the application

    testing functionality of the application
  • deploying the application to the web

    deploying the application to the web
  • writing the REST API from scratch

    writing the REST API from scratch

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:

7.2 Further Reading (7.2 Further Reading)

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值