Tutorial Express Parte 3: Usando um banco de dados (com Mongoose) - Aprendendo desenvolvimento web

Tutorial Express Parte 3: Usando um banco de dados (com Mongoose)
=================================================================

* Anterior
* Menu: Express Nodejs
* Próxima  (en-US)

Este artigo introduz brevemente bancos de dados e como usá-los com aplicativos Node/Express. Depois demonstra como podemos usar o Mongoose para prover acesso ao banco de dados para o website LocalLibrary. Explica como object schema e modelos são declarados, os principais tipos de campos e validações básicas. Também demonstra brevemente algumas das muitas maneiras em que se pode acessar os dados do modelo.

|  |  |
| --- | --- |
| Pré-requisitos: | Tutorial Express Parte 2: Criando o esqueleto de um website |
| Objetivo: | Ser capaz de projetar e criar seus próprios modelos usando Mongoose. |

Visão geral
-----------

A equipe da biblioteca usará o site da Biblioteca Local para gardar informações sobre livros e empréstimos, enquanto os membros da biblioteca irão utilizá-lo para navegar e pesquisar por livros, descobrir se há alguma cópia disponível, e então reservar ou emprestar eles. Para armazenar e obter informações eficientemente, nós guardaremos elas em um *banco de dados*.

Aplicativos *Express* podem usar muitos bancos de dados diferentes, e existem várias abordagens que você pode usar para fazer operações de Criar, Ler, Atualizar e Apagar (CRUD, na sigla em inglês). Esse tutorial provê uma curta visão geral de algumas das opções disponíveis e então irá mostrar em detalhes os mecanismos particulares selecionados.

### Quais bancos de dados eu posso usar?

Aplicativos *Express* podem usar qualquer banco de dados suportado pelo *Node* (O *Express* por si só não define nenhum requerimento ou comportamento adicional específico para gerenciamento de bancos de dados). Há muitas opções populares, incluindo PostgreSQL, MySQL, Redis, SQLite, and MongoDB.

Quando escolher um banco de dados, você deveria considerar coisas como o tempo-para-produtividade/curva de aprendizado, performance, facilidade de replicação/backup, custo, suporte da comunidade, etc. Enquanto não existe o "melhor" banco de dados, praticamente qualquer uma das soluções populares devem ser mais do que aceitáveis para um site de tamanho pequeno a médio como o da nossa Biblioteca Local.

Para mais informações sobre as opções veja Integração com o Banco de dados (documentação do Express).

### Qual o melhor jeito de interagir com um banco de dados?

There are two approaches for interacting with a database:

* Using the databases' native query language (e.g. SQL)
* Using an Object Data Model ("ODM") / Object Relational Model ("ORM"). An ODM/ORM represents the website's data as JavaScript objects, which are then mapped to the underlying database. Some ORMs are tied to a specific database, while others provide a database-agnostic backend.

The very best *performance* can be gained by using SQL, or whatever query language is supported by the database. ODM's are often slower because they use translation code to map between objects and the database format, which may not use the most efficient database queries (this is particularly true if the ODM supports different database backends, and must make greater compromises in terms of what database features are supported).

The benefit of using an ORM is that programmers can continue to think in terms of JavaScript objects rather than database semantics — this is particularly true if you need to work with different databases (on either the same or different websites). They also provide an obvious place to perform validation and checking of data.

**Nota:** **Tip:** Using ODM/ORMs often results in lower costs for development and maintenance! Unless you're very familiar with the native query language or performance is paramount, you should strongly consider using an ODM.

### Qual ORM/ODM eu devo usar?

There are many ODM/ORM solutions available on the NPM package manager site (check out the odm and orm tags for a subset!).

A few solutions that were popular at the time of writing are:

* Mongoose: Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment.
* Waterline: An ORM extracted from the Express-based Sails web framework. It provides a uniform API for accessing numerous different databases, including Redis, MySQL, LDAP, MongoDB, and Postgres.
* Bookshelf: Features both promise-based and traditional callback interfaces, providing transaction support, eager/nested-eager relation loading, polymorphic associations, and support for one-to-one, one-to-many, and many-to-many relations. Works with PostgreSQL, MySQL, and SQLite3.
* Objection: Makes it as easy as possible to use the full power of SQL and the underlying database engine (supports SQLite3, Postgres, and MySQL).
* Sequelize is a promise-based ORM for Node.js and io.js. It supports the dialects PostgreSQL, MySQL, MariaDB, SQLite, and MSSQL and features solid transaction support, relations, read replication and more.
* Node ORM2 is an Object Relationship Manager for NodeJS. It supports MySQL, SQLite, and Progress, helping to work with the database using an object-oriented approach.
* JugglingDB is cross-DB ORM for NodeJS, providing a common interface to access most popular database formats. Currently supporting MySQL, SQLite3, Postgres, MongoDB, Redis and js-memory-storage (self-written engine for test-usage only).

As a general rule, you should consider both the features provided and the "community activity" (downloads, contributions, bug reports, quality of documentation, etc.) when selecting a solution. At the time of writing Mongoose is by far the most popular ODM, and is a reasonable choice if you're using MongoDB for your database.

### Usando Mongoose e MongoDb para a LocalLibrary

Para o exemplo da *Local Library* (e para o resto do tópico) nós iremos usar o Mongoose ODM para acessar os dados da nossa aplicação. Mongoose funciona como uma interface para o MongoDB, um banco de dados de código aberto e NoSQL que usa um modelo de dados orientado a documentos. Uma "coleção" de "documentos", em uma base de dados do MongoDB, é semelhante a uma "tabela" com "linhas" em uma base dados relacional.

Esse ODM (Object Data Model) e banco de dados combinados são extremamente populares na comunidade do Node, particularmente porque os documentos armazenados e os métodos de consultas se parecem muito com JSON, que consequentemente são muito familiares aos desenvolvedores JavaScript.

**Nota:** **Dica:** Você não precisa conhecer o MongoDB antes de usar o Mongoose, apesar de que partes da documentação do Mongoose *são mais fáceis* de entender se você já está familiarizado com o MongoDB.

O resto desse tutorial mostra como definir e acessar os modelos e schemas no Mongoose para o nosso website da LocalLibrary.

Projetando os modelos da aplicação LocalLibrary
-----------------------------------------------

Antes de pularmos de cabeça na codificação dos modelos, vale a pena pensar uns minutinhos sobre quais dados precisamos armazenar e o relacionamento entre diferentes objetos.

Nós sabemos que precisamos armazenar informações sobre livros(título, resumo, autor, gênero, ISBN) e que nós podemos ter múltiplas cópias disponíveis (com ids globlamente únicos, status de disponibilidade, etc.). Nós também podemos armazenar mais informações sobre o autor do que apenas seu nome, e podem haver múltiplos autores com o mesmo nome ou um bem parecido. Nós queremos também ser capazes de ordernar informações baseadas no título do livro, autor, gênero e categoria.

Ao estruturar seus modelos é sempre bom separar modelos para cada "objeto"(um grupo de informações relacionadas). Neste caso, os nossos objetos mais evidentes são os de livros (books), pedidos de livros (book instances), e autores (authors).

Você pode usar também modelos para representar opções de listagens(por exemplo um menu drop-down com escolhas para o usuário), ao invés de implementar as escolhas diretamente em linhas de códigos dentro do seu website — isso é recomendado quando as escolhas não são conhecidas durante o desenvolvimento ou quando elas podem mudar. O candidato mais evidente para um modelo desse tipo é o de gênero (genre) de livros (por exemplo Ficção Ciêntífica, Poesia Clássica, etc.).

Uma vez que nós decidimos nossos modelos e os seus atributos, nós precisamos pensar sobre o relacionamento entre eles.

Com isso em mente, o diagrama UML a seguir mostra os modelos (as caixas) que iremos definir na nossa aplicação. Como discutido acima, criamos modelos para os livros ( com informações genéricas sobre o livro), pedidos de livros(status de cópias físicas de um livro específico disponíveis no sistema), e autor. E também decidimos ter um modelo para o gênero então esses valores poderão ser criados dinamicamente na aplicação. Outra decisão foi de não criarmos um modelo para `BookInstance:status` — nós deixaremos diretamente no código os valores aceitáveis para o status de pedidos porque nós não esperamos que eles mudem. Dentro de cada caixa, você pode ver o nome do modelo, o nome dos atributos e seus tipos, e também os métodos e seu tipo de retorno.

O diagrama também mostra o relacionamento entre modelos, incluindo sua cardinalidade. A cardinalidade são os números no diagrama próximos das linhas que conectam as caixas mostrando os números (máximo e mínimo) de cada modelo que pode estar presente no relacionamento. Por exemplo, as linhas que conectam as caixas `Book`e `Genre` mostram que as duas coleções têm uma relação. Os números próximos ao modelo `Book` mostra que Genre pode ter zero ou mais Book (quantos você quiser), enquanto no outro fim da linha de conexão próximo a `Genre` mostra que ele pode ter zero ou mais livros associados.

**Nota:** Assim como discutido abaixo em Iniciando com Mongoose muitas vezes é melhor ter o atributo que define a relação entre os documentos/modelos em apenas um dos modelos( você ainda pode encontrar o relacionamento reverso pesquisando o `_id` associado no outro modelo). Abaixo nós escolhemos definir o modelo Book Schema para armazenar o relacionamento entre Book/Genre e Book/Author, e definimos BookInstance Schema para armazenar o relacionamento entre Book/BookInstance. Esta escolha foi um tanto arbitrária — nós poderíamos igualmente ter declarado esses atributos em outro schema.

![Mongoose Library Model with correct cardinality](/pt-BR/docs/Learn/Server-side/Express_Nodejs/mongoose/library_website_-_mongoose_express.png)

**Nota:** A próxima seção fornece um guia explicando como os modelos são definidos e usados. Ao ler, considere como iremos construir cada um dos modelos no diagrama acima.

Iniciando com Mongoose
----------------------

Esta seção fornece uma visão geral de como conectar o Mongoose a um banco de dados do MongoDB, como definir um schema e um modelo, e como fazer consultas básicas.

**Nota:** Esse guia é "bastante influenciado" pelo conteúdo encontrado no Mongoose quick start do *npm* e pela documentação oficial.

### Instalando Mongoose e MongoDB

O Mongoose é instalado no seu projeto (**package.json**) assim como outra dependência qualquer — usando NPM. Para instalá-lo, use a seguinte linha de comando dentro da pasta do seu projeto:

```bash
npm install mongoose
```

Installing *Mongoose* adds all its dependencies, including the MongoDB database driver, but it does not install MongoDB itself. If you want to install a MongoDB server then you can download installers from here for various operating systems and install it locally. You can also use cloud-based MongoDB instances.

**Nota:** For this tutorial, we'll be using the MongoDB Atlas cloud-based *database as a service* free tier to provide the database. This is suitable for development and makes sense for the tutorial because it makes "installation" operating system independent (database-as-a-service is also one approach you might well use for your production database).

### Conectando ao MongoDB

*Mongoose* requires a connection to a MongoDB database. You can `require()` and connect to a locally hosted database with `mongoose.connect()`, as shown below.

```js
//Import the mongoose module
var mongoose = require("mongoose");

//Set up default mongoose connection
var mongoDB = "mongodb://127.0.0.1/my\_database";
mongoose.connect(mongoDB, { useNewUrlParser: true });

//Get the default connection
var db = mongoose.connection;

//Bind connection to error event (to get notification of connection errors)
db.on("error", console.error.bind(console, "MongoDB connection error:"));
```

You can get the default `Connection` object with `mongoose.connection`. Once connected, the open event is fired on the `Connection` instance.

**Nota:** **Tip:** If you need to create additional connections you can use `mongoose.createConnection()`. This takes the same form of database URI (with host, database, port, options etc.) as `connect()` and returns a `Connection` object).

### Definindo e criando modelos

Models are *defined* using the `Schema` interface. The Schema allows you to define the fields stored in each document along with their validation requirements and default values. In addition, you can define static and instance helper methods to make it easier to work with your data types, and also virtual properties that you can use like any other field, but which aren't actually stored in the database (we'll discuss a bit further below).

Schemas are then "compiled" into models using the `mongoose.model()` method. Once you have a model you can use it to find, create, update, and delete objects of the given type.

**Nota:** Each model maps to a *collection* of *documents* in the MongoDB database. The documents will contain the fields/schema types defined in the model `Schema`.

#### Defining schemas

The code fragment below shows how you might define a simple schema. First you `require()` mongoose, then use the Schema constructor to create a new schema instance, defining the various fields inside it in the constructor's object parameter.

```js
//Require Mongoose
var mongoose = require("mongoose");

//Define a schema
var Schema = mongoose.Schema;

var SomeModelSchema = new Schema({
  a\_string: String,
  a\_date: Date,
});
```

In the case above we just have two fields, a string and a date. In the next sections, we will show some of the other field types, validation, and other methods.

#### Criando um modelo

Models are created from schemas using the `mongoose.model()` method:

```js
// Define schema
var Schema = mongoose.Schema;

var SomeModelSchema = new Schema({
  a\_string: String,
  a\_date: Date,
});

// Compile model from schema
var SomeModel = mongoose.model("SomeModel", SomeModelSchema);
```

The first argument is the singular name of the collection that will be created for your model (Mongoose will create the database collection for the above model *SomeModel* above), and the second argument is the schema you want to use in creating the model.

**Nota:** Once you've defined your model classes you can use them to create, update, or delete records, and run queries to get all records or particular subsets of records. We'll show you how to do this in the Using models section, and when we create our views.

#### Schema types (fields)

A schema can have an arbitrary number of fields — each one represents a field in the documents stored in *MongoDB*. An example schema showing many of the common field types and how they are declared is shown below.

```js
var schema = new Schema({
  name: String,
  binary: Buffer,
  living: Boolean,
  updated: { type: Date, default: Date.now() },
  age: { type: Number, min: 18, max: 65, required: true },
  mixed: Schema.Types.Mixed,
  \_someId: Schema.Types.ObjectId,
  array: [],
  ofString: [String], // You can also have an array of each of the other types too.
  nested: { stuff: { type: String, lowercase: true, trim: true } },
});
```

Most of the SchemaTypes (the descriptors after "type:" or after field names) are self-explanatory. The exceptions are:

* `ObjectId`: Represents specific instances of a model in the database. For example, a book might use this to represent its author object. This will actually contain the unique ID (`_id`) for the specified object. We can use the `populate()` method to pull in the associated information when needed.
* Mixed: An arbitrary schema type.
* []: An array of items. You can perform JavaScript array operations on these models (push, pop, unshift, etc.). The examples above show an array of objects without a specified type and an array of `String` objects, but you can have an array of any type of object.

The code also shows both ways of declaring a field:

* Field *name* and *type* as a key-value pair (i.e. as done with fields `name`, `binary` and `living`).
* Field *name* followed by an object defining the `type`, and any other *options* for the field. Options include things like:
    + default values.
    + built-in validators (e.g. max/min values) and custom validation functions.
    + Whether the field is required
    + Whether `String` fields should automatically be set to lowercase, uppercase, or trimmed (e.g. `{ type: String, lowercase: true, trim: true }`)

For more information about options see SchemaTypes (Mongoose docs).

#### Validação

Mongoose provides built-in and custom validators, and synchronous and asynchronous validators. It allows you to specify both the acceptable range or values and the error message for validation failure in all cases.

The built-in validators include:

* All SchemaTypes have the built-in required validator. This is used to specify whether the field must be supplied in order to save a document.
* Numbers have min and max validators.
* Strings have:
    + enum: specifies the set of allowed values for the field.
    + match: specifies a regular expression that the string must match.
    + maxlength and minlength for the string.

The example below (slightly modified from the Mongoose documents) shows how you can specify some of the validator types and error messages:

```js
var breakfastSchema = new Schema({
  eggs: {
    type: Number,
    min: [6, "Too few eggs"],
    max: 12,
    required: [true, "Why no eggs?"],
  },
  drink: {
    type: String,
    enum: ["Coffee", "Tea", "Water"],
  },
});
```

For complete information on field validation see Validation (Mongoose docs).

#### Propriedades virtuais

Virtual properties are document properties that you can get and set but that do not get persisted to MongoDB. The getters are useful for formatting or combining fields, while setters are useful for de-composing a single value into multiple values for storage. The example in the documentation constructs (and deconstructs) a full name virtual property from a first and last name field, which is easier and cleaner than constructing a full name every time one is used in a template.

**Nota:** We will use a virtual property in the library to define a unique URL for each model record using a path and the record's `_id` value.

For more information see Virtuals (Mongoose documentation).

#### Methods and query helpers

A schema can also have instance methods, static methods, and query helpers. The instance and static methods are similar, but with the obvious difference that an instance method is associated with a particular record and has access to the current object. Query helpers allow you to extend mongoose's chainable query builder API (for example, allowing you to add a query "byName" in addition to the `find()`, `findOne()` and `findById()` methods).

### Usando modelos

Once you've created a schema you can use it to create models. The model represents a collection of documents in the database that you can search, while the model's instances represent individual documents that you can save and retrieve.

We provide a brief overview below. For more information see: Models (Mongoose docs).

#### Criando e modificando documentos

To create a record you can define an instance of the model and then call `save()`. The examples below assume SomeModel is a model (with a single field "name") that we have created from our schema.

```js
// Create an instance of model SomeModel
var awesome_instance = new SomeModel({ name: "awesome" });

// Save the new model instance, passing a callback
awesome_instance.save(function (err) {
  if (err) return handleError(err);
  // saved!
});
```

Creation of records (along with updates, deletes, and queries) are asynchronous operations — you supply a callback that is called when the operation completes. The API uses the error-first argument convention, so the first argument for the callback will always be an error value (or null). If the API returns some result, this will be provided as the second argument.

You can also use `create()` to define the model instance at the same time as you save it. The callback will return an error for the first argument and the newly-created model instance for the second argument.

```js
SomeModel.create({ name: "also\_awesome" }, function (err, awesome\_instance) {
  if (err) return handleError(err);
  // saved!
});
```

Every model has an associated connection (this will be the default connection when you use `mongoose.model()`). You create a new connection and call `.model()` on it to create the documents on a different database.

You can access the fields in this new record using the dot syntax, and change the values. You have to call `save()` or `update()` to store modified values back to the database.

```js
// Access model field values using dot notation
console.log(awesome_instance.name); //should log 'also\_awesome'

// Change record by modifying the fields, then calling save().
awesome_instance.name = "New cool name";
awesome_instance.save(function (err) {
  if (err) return handleError(err); // saved!
});
```

#### Pesquisando por registros

You can search for records using query methods, specifying the query conditions as a JSON document. The code fragment below shows how you might find all athletes in a database that play tennis, returning just the fields for athlete *name* and *age*. Here we just specify one matching field (sport) but you can add more criteria, specify regular expression criteria, or remove the conditions altogether to return all athletes.

```js
var Athlete = mongoose.model("Athlete", yourSchema);

// find all athletes who play tennis, selecting the 'name' and 'age' fields
Athlete.find({ sport: "Tennis" }, "name age", function (err, athletes) {
  if (err) return handleError(err);
  // 'athletes' contains the list of athletes that match the criteria.
});
```

If you specify a callback, as shown above, the query will execute immediately. The callback will be invoked when the search completes.

**Nota:** All callbacks in Mongoose use the pattern `callback(error, result)`. If an error occurs executing the query, the `error` parameter will contain an error document and `result` will be null. If the query is successful, the `error` parameter will be null, and the `result` will be populated with the results of the query.

**Nota:** It is important to remember that not finding any results is **not an error** for a search —but it may be a fail-case in the context of your application. If your application expects a search to find a value you can either check the result in the callback (`results==null`) or daisy chain the method orFail() on the query.

If you don't specify a callback then the API will return a variable of type Query. You can use this query object to build up your query and then execute it (with a callback) later using the `exec()` method.

```js
// find all athletes that play tennis
var query = Athlete.find({ sport: "Tennis" });

// selecting the 'name' and 'age' fields
query.select("name age");

// limit our results to 5 items
query.limit(5);

// sort by age
query.sort({ age: -1 });

// execute the query at a later time
query.exec(function (err, athletes) {
  if (err) return handleError(err);
  // athletes contains an ordered list of 5 athletes who play Tennis
});
```

Above we've defined the query conditions in the `find()` method. We can also do this using a `where()` function, and we can chain all the parts of our query together using the dot operator (.) rather than adding them separately. The code fragment below is the same as our query above, with an additional condition for the age.

```js
Athlete.find()
  .where("sport")
  .equals("Tennis")
  .where("age")
  .gt(17)
  .lt(50) //Additional where query
  .limit(5)
  .sort({ age: -1 })
  .select("name age")
  .exec(callback); // where callback is the name of our callback function.
```

The find() method gets all matching records, but often you just want to get one match. The following methods query for a single record:

* `findById()`: Finds the document with the specified `id` (every document has a unique `id`).
* `findOne()`: Finds a single document that matches the specified criteria.
* `findByIdAndRemove()`, `findByIdAndUpdate()`, `findOneAndRemove()`, `findOneAndUpdate()`: Finds a single document by `id` or criteria and either update or remove it. These are useful convenience functions for updating and removing records.

**Nota:** There is also a `count()` method that you can use to get the number of items that match conditions. This is useful if you want to perform a count without actually fetching the records.

There is a lot more you can do with queries. For more information see: Queries (Mongoose docs).

#### Working with related documents — population

You can create references from one document/model instance to another using the `ObjectId` schema field, or from one document to many using an array of `ObjectIds`. The field stores the id of the related model. If you need the actual content of the associated document, you can use the `populate()` method in a query to replace the id with the actual data.

For example, the following schema defines authors and stories. Each author can have multiple stories, which we represent as an array of `ObjectId`. Each story can have a single author. The "ref" (highlighted in bold below) tells the schema which model can be assigned to this field.

```js
var mongoose = require("mongoose"),
  Schema = mongoose.Schema;

var authorSchema = Schema({
  name: String,
  stories: [{ type: Schema.Types.ObjectId, ref: "Story" }],
});

var storySchema = Schema({
  author: { type: Schema.Types.ObjectId, ref: "Author" },
  title: String,
});

var Story = mongoose.model("Story", storySchema);
var Author = mongoose.model("Author", authorSchema);
```

We can save our references to the related document by assigning the `_id` value. Below we create an author, then a story, and assign the author id to our stories author field.

```js
var bob = new Author({ name: "Bob Smith" });

bob.save(function (err) {
  if (err) return handleError(err);

  //Bob now exists, so lets create a story
  var story = new Story({
    title: "Bob goes sledding",
    author: bob._id, // assign the \_id from the our author Bob. This ID is created by default!
  });

  story.save(function (err) {
    if (err) return handleError(err);
    // Bob now has his story
  });
});
```

Our story document now has an author referenced by the author document's ID. In order to get the author information in the story results we use `populate()`, as shown below.

```js
Story.findOne({ title: "Bob goes sledding" })
  .populate("author") //This populates the author id with actual author information!
  .exec(function (err, story) {
    if (err) return handleError(err);
    console.log("The author is %s", story.author.name);
    // prints "The author is Bob Smith"
  });
```

**Nota:** Astute readers will have noted that we added an author to our story, but we didn't do anything to add our story to our author's `stories` array. How then can we get all stories by a particular author? One way would be to add our author to the stories array, but this would result in us having two places where the information relating authors and stories needs to be maintained.

A better way is to get the `_id` of our *author*, then use `find()` to search for this in the author field across all stories.

```js
Story.find({ author: bob._id }).exec(function (err, stories) {
  if (err) return handleError(err);
  // returns all stories that have Bob's id as their author.
});
```

This is almost everything you need to know about working with related items *for this tutorial*. For more detailed information see Population (Mongoose docs).

### One schema/model per file

While you can create schemas and models using any file structure you like, we highly recommend defining each model schema in its own module (file), exporting the method to create the model. This is shown below:

```js
// File: ./models/somemodel.js

//Require Mongoose
var mongoose = require("mongoose");

//Define a schema
var Schema = mongoose.Schema;

var SomeModelSchema = new Schema({
  a\_string: String,
  a\_date: Date,
});

//Export function to create "SomeModel" model class
module.exports = mongoose.model("SomeModel", SomeModelSchema);
```

You can then require and use the model immediately in other files. Below we show how you might use it to get all instances of the model.

```js
//Create a SomeModel model just by requiring the module
var SomeModel = require("../models/somemodel");

// Use the SomeModel object (model) to find all SomeModel records
SomeModel.find(callback_function);
```

Configurando o banco de dados MongoDB
-------------------------------------

Now that we understand something of what Mongoose can do and how we want to design our models, it's time to start work on the *LocalLibrary* website. The very first thing we want to do is set up a MongoDb database that we can use to store our library data.

For this tutorial, we're going to use the MongoDB Atlas free cloud-hosted sandbox database. This database tier is not considered suitable for production websites because it has no redundancy, but it is great for development and prototyping. We're using it here because it is free and easy to set up, and because MongoDB Atlas is a popular *database as a service* vendor that you might reasonably choose for your production database (other popular choices at the time of writing include Compose, ScaleGrid and ObjectRocket).

**Nota:** If you prefer you can set up a MongoDb database locally by downloading and installing the appropriate binaries for your system. The rest of the instructions in this article would be similar, except for the database URL you would specify when connecting.

You will first need to create an account with MongoDB Atlas (this is free, and just requires that you enter basic contact details and acknowledge their terms of service).

After logging in, you'll be taken to the home screen:

1. Click **Buid a Cluster** button in the Clusters Overview section.

 ![Create a cluster on MongoDB Atlas.](/pt-BR/docs/Learn/Server-side/Express_Nodejs/mongoose/mongodb_atlas_-_createcluster.jpg)
2. This will open the *Create New Cluster* screen.

 ![Choose a cloud provider when using MongoDB Atlas.](/pt-BR/docs/Learn/Server-side/Express_Nodejs/mongoose/mongodb_atlas_-_chooseproviderregion.jpg)
    * Select any provider from the *Cloud Provider & Region* section. Different providers offer different regions.
    * Select any region marked "FREE TIER AVAILABLE".
    * Click the **Create Cluster** button (creation of the cluster will take some minutes).
3. You will return to the *Cluster Overview* screen.

 ![Setup a collection on MongoDB Atlas.](/pt-BR/docs/Learn/Server-side/Express_Nodejs/mongoose/mongodb_atlas_-_createcollection.jpg)
    * Click the **Collections** button.
4. This will open the *Collections* section.

 ![Create a database on MongoDB Atlas.](/pt-BR/docs/Learn/Server-side/Express_Nodejs/mongoose/mongodb_atlas_-_createdatabase.jpg)
    * Click the **Create Database** button.
5. This will open the *Create Database* screen.

 ![Details during database creation on MongoDB Atlas.](/pt-BR/docs/Learn/Server-side/Express_Nodejs/mongoose/mongodb_atlas_-_databasedetails.jpg)
    * Enter the name for the new database as `local_library`.
    * Enter the name of the collection as Collection0.
    * Click the **Create** button to create the database.
6. You will return to the Collection screen with your database created.

 ![Database creation confirmation on MongoDB Atlas.](/pt-BR/docs/Learn/Server-side/Express_Nodejs/mongoose/mongodb_atlas_-_databasecreated.jpg)
    * Click the *Overview* tab to return the cluster overview.
7. From the Cluster0 Overview screen click the **Connect** button.

 ![Configure a connection when after setting up a cluster in MongoDB Atlas.](/pt-BR/docs/Learn/Server-side/Express_Nodejs/mongoose/mongodb_atlas_-_connectbutton.jpg)
8. This will open the Connect to Cluster screen.

 ![Setup a connection when using MongoDB Atlas.](/pt-BR/docs/Learn/Server-side/Express_Nodejs/mongoose/mongodb_atlas_-_connectcluster.jpg)
    * Click the **Add a Different IP Address** button, enter `0.0.0.0/0` for the IP Address and click **Add IP Address** button.

    **Nota:** It is a best practice to limit the IP addresses that can connect to your database and other resources. Here we allow a connection from anywhere because we don't know where the request will come from after deployment.
    * Enter a username and password and click **Create MongoDB User** button.

    **Nota:** Avoid using special characters in your MongoDB user password as mongoose may not parse the connection string properly.
    * If you have completed the 2 previous steps, the button **Choose a connection method** will turn green.
    * Click the **Choose a connection method** button.
9. This will access the *Choose a connection* method tab.

 ![Choose a connection type when connecting with MongoDB Atlas.](/pt-BR/docs/Learn/Server-side/Express_Nodejs/mongoose/mongodb_atlas_-_chooseaconnectionmethod.jpg)
    * Click the **Connect Your Application** option.
10. This will open the *Connect* screen.

 ![Choose the Short SRV connection when settinup a connection on MongoDB Atalas.](/pt-BR/docs/Learn/Server-side/Express_Nodejs/mongoose/mongodb_atlas_-_connectforshortsrv.jpg)
    * Click the **Short SRV connection string** option to copy the connection string.
11. This will open the connection string URL.

 ![Copy the Short SRV connection string when setting up a connection on MongoDB Atlas](/pt-BR/docs/Learn/Server-side/Express_Nodejs/mongoose/mongodb_atlas_-_copyshortsrv.jpg)
    * Choose **Copy** button to copy the string.
    * Save this string somewhere safe.
    * Update the password with your users password.
    * Replace test with `local_library`.

You have now created the database, and have an URL (with username and password) that can be used to access it. This will look something like: `mongodb+srv://your_user_name:your_password@cluster0-mbdj7.mongodb.net/local_library?retryWrites=true`

Instalando Mongoose
-------------------

Open a command prompt and navigate to the directory where you created your skeleton Local Library website. Enter the following command to install Mongoose (and its dependencies) and add it to your **package.json** file, unless you have already done so when reading the Mongoose Primer above.

```bash
npm install mongoose
```

Conectando ao MongoDB
---------------------

Open **/app.js** (in the root of your project) and copy the following text below where you declare the *Express application object* (after the line `var app = express();`). Replace the database url string ('*insert\_your\_database\_url\_here*') with the location URL representing your own database (i.e. using the information from *mongoDB Atlas*).

```js
//Set up mongoose connection
var mongoose = require("mongoose");
var mongoDB = "insert\_your\_database\_url\_here";
mongoose.connect(mongoDB, { useNewUrlParser: true });
var db = mongoose.connection;
db.on("error", console.error.bind(console, "MongoDB connection error:"));
```

As discussed in the Mongoose primer above, this code creates the default connection to the database and binds to the error event (so that errors will be printed to the console).

Defining the LocalLibrary Schema
--------------------------------

We will define a separate module for each model, as discussed above. Start by creating a folder for our models in the project root (**/models**) and then create separate files for each of the models:
```

/express-locallibrary-tutorial  //the project root
  /models
    author.js
    book.js
    bookinstance.js
    genre.js
```

### Author model

Copy the `Author` schema code shown below and paste it into your **./models/author.js** file. The scheme defines an author has having `String` SchemaTypes for the first and family names, that are required and have a maximum of 100 characters, and `Date` fields for the date of birth and death.

```js
var mongoose = require("mongoose");

var Schema = mongoose.Schema;

var AuthorSchema = new Schema({
  first\_name: { type: String, required: true, max: 100 },
  family\_name: { type: String, required: true, max: 100 },
  date\_of\_birth: { type: Date },
  date\_of\_death: { type: Date },
});

// Virtual for author's full name
AuthorSchema.virtual("name").get(function () {
  return this.family_name + ", " + this.first_name;
});

// Virtual for author's lifespan
AuthorSchema.virtual("lifespan").get(function () {
  return (
    this.date_of_death.getYear() - this.date_of_birth.getYear()
  ).toString();
});

// Virtual for author's URL
AuthorSchema.virtual("url").get(function () {
  return "/catalog/author/" + this._id;
});

//Export model
module.exports = mongoose.model("Author", AuthorSchema);
```

We've also declared a virtual for the AuthorSchema named "url" that returns the absolute URL required to get a particular instance of the model — we'll use the property in our templates whenever we need to get a link to a particular author.

**Nota:** Declaring our URLs as a virtual in the schema is a good idea because then the URL for an item only ever needs to be changed in one place.
 At this point, a link using this URL wouldn't work, because we haven't got any routes handling code for individual model instances. We'll set those up in a later article!

At the end of the module, we export the model.

### Book model

Copy the `Book` schema code shown below and paste it into your **./models/book.js** file. Most of this is similar to the author model — we've declared a schema with a number of string fields and a virtual for getting the URL of specific book records, and we've exported the model.

```js
var mongoose = require("mongoose");

var Schema = mongoose.Schema;

var BookSchema = new Schema({
  title: { type: String, required: true },
  author: { type: Schema.Types.ObjectId, ref: "Author", required: true },
  summary: { type: String, required: true },
  isbn: { type: String, required: true },
  genre: [{ type: Schema.Types.ObjectId, ref: "Genre" }],
});

// Virtual for book's URL
BookSchema.virtual("url").get(function () {
  return "/catalog/book/" + this._id;
});

//Export model
module.exports = mongoose.model("Book", BookSchema);
```

The main difference here is that we've created two references to other models:

* author is a reference to a single `Author` model object, and is required.
* genre is a reference to an array of `Genre` model objects. We haven't declared this object yet!
### BookInstance model

Finally, copy the `BookInstance` schema code shown below and paste it into your **./models/bookinstance.js** file. The `BookInstance` represents a specific copy of a book that someone might borrow and includes information about whether the copy is available or on what date it is expected back, "imprint" or version details.

```js
var mongoose = require("mongoose");

var Schema = mongoose.Schema;

var BookInstanceSchema = new Schema({
  book: { type: Schema.Types.ObjectId, ref: "Book", required: true }, //reference to the associated book
  imprint: { type: String, required: true },
  status: {
    type: String,
    required: true,
    enum: ["Available", "Maintenance", "Loaned", "Reserved"],
    default: "Maintenance",
  },
  due\_back: { type: Date, default: Date.now },
});

// Virtual for bookinstance's URL
BookInstanceSchema.virtual("url").get(function () {
  return "/catalog/bookinstance/" + this._id;
});

//Export model
module.exports = mongoose.model("BookInstance", BookInstanceSchema);
```

The new things we show here are the field options:

* `enum`: This allows us to set the allowed values of a string. In this case, we use it to specify the availability status of our books (using an enum means that we can prevent mis-spellings and arbitrary values for our status)
* `default`: We use default to set the default status for newly created bookinstances to maintenance and the default `due_back` date to `now` (note how you can call the Date function when setting the date!)

Everything else should be familiar from our previous schema.

### Genre model - challenge!

Open your **./models/genre.js** file and create a schema for storing genres (the category of book, e.g. whether it is fiction or non-fiction, romance or military history, etc).

The definition will be very similar to the other models:

* The model should have a `String` SchemaType called `name` to describe the genre.
* This name should be required and have between 3 and 100 characters.
* Declare a virtual for the genre's URL, named `url`.
* Export the model.
Testando — criando alguns itens
-------------------------------

That's it. We now have all models for the site set up!

In order to test the models (and to create some example books and other items that we can use in our next articles) we'll now run an *independent* script to create items of each type:

1. Download (or otherwise create) the file populatedb.js inside your *express-locallibrary-tutorial* directory (in the same level as `package.json`).

**Nota:** You don't need to know how populatedb.js works; it just adds sample data into the database.
2. Enter the following commands in the project root to install the *async* module that is required by the script (we'll discuss this in later tutorials, )

```bash
npm install async
```

3. Run the script using node in your command prompt, passing in the URL of your *MongoDB* database (the same one you replaced the *insert\_your\_database\_url\_here* placeholder with, inside `app.js` earlier):

```bash
node populatedb <your mongodb url>
```

**Nota:** **Note for Windows operating system users**: If the above command results in the error `DeprecationWarning: current URL string parser is deprecated`, change the `mongoose.connect(mongoDB);` line in `populatedb.js` file with `mongoose.connect(mongoDB, { useNewUrlParser:true });`
4. The script should run through to completion, displaying items as it creates them in the terminal.

**Nota:** **Tip:** Go to your database on mongoDB Atlas (in the *Collections* tab). You should now be able to drill down into individual collections of Books, Authors, Genres and BookInstances, and check out individual documents.

Resumo
------

In this article, we've learned a bit about databases and ORMs on Node/Express, and a lot about how Mongoose schema and models are defined. We then used this information to design and implement `Book`, `BookInstance`, `Author` and `Genre` models for the *LocalLibrary* website.

Last of all we tested our models by creating a number of instances (using a standalone script). In the next article we'll look at creating some pages to display these objects.

Veja também
-----------

* Database integration (Express docs)
* Mongoose website (Mongoose docs)
* Mongoose Guide (Mongoose docs)
* Validation (Mongoose docs)
* Schema Types (Mongoose docs)
* Models (Mongoose docs)
* Queries (Mongoose docs)
* Population (Mongoose docs)
* Anterior
* Menu: Express Nodejs
* Próxima  (en-US)


Django Tutorial Parte 5: Criando nossa home page - Aprendendo desenvolvimento web

Django Tutorial Parte 5: Criando nossa home page
================================================

* Anterior
* Menu: Django
* Próxima

Agora estamos prontos para adicionar o código que exibe nossa primeira página completa - uma home page do site LocalLibrary. A página inicial mostrará o número de registros que temos para cada tipo de modelo e fornecerá links de navegação na barra lateral para nossas outras páginas. Ao longo do caminho, obteremos experiência prática ao escrever mapas e visualizações básicos de URL, obter registros do banco de dados e usar modelos.

|  |  |
| --- | --- |
| Pré-requisitos: |
 Leia a
 Introdução ao Django. Conclua os tópicos do tutorial anterior (incluindo
 Django Tutorial Part 4: Django admin site).
  |
| Objetivo: |
 Aprender a criar mapas e visualizações de URL simples (onde nenhum dado
 é codificado no URL), obtenha dados de modelos e crie modelos.
  |

Visão Global
------------

Depois de definirmos nossos modelos e criarmos alguns registros iniciais da biblioteca para trabalhar, é hora de escrever o código que apresenta essas informações aos usuários. A primeira coisa que precisamos fazer é determinar quais informações queremos exibir em nossas páginas e definir os URLs a serem usados para retornar esses recursos. Em seguida, criaremos um mapeador de URLs, visualizações e modelos para exibir as páginas.

O diagrama a seguir descreve o fluxo de dados principal e os componentes necessários ao manipular solicitações e respostas HTTP. Como já implementamos o modelo, os principais componentes que criaremos são:

* Mapeadores de URL para encaminhar os URLs suportados (e qualquer informação codificada nos URLs) para as funções de exibição apropriadas.
* View functions para obter os dados solicitados dos modelos, crie páginas HTML que exibem os dados e retorne as páginas ao usuário para visualização no navegador.
* Templates para usar ao renderizar dados nas visualizações.

![](/pt-BR/docs/Learn/Server-side/Django/Home_page/basic-django.png)

Como você verá na próxima seção, temos 5 páginas para exibir, o que é muita informação para documentar em um único artigo. Portanto, este artigo se concentrará em como implementar a página inicial e abordaremos as outras páginas em um artigo subsequente. Isso deve fornecer uma boa compreensão completa de como os mapeadores, visualizações e modelos de URL funcionam na prática.

Definindo os URLs do recurso
----------------------------

Como esta versão do LocalLibrary é essencialmente somente leitura para usuários finais, precisamos fornecer uma página de destino para o site (uma página inicial) e páginas que exibam visualizações de lista e detalhes de livros e autores.

As URLs que iremos precisar na nossa página são:

* `catalog/` — A página inicial (index).
* `catalog/books/` — Uma lista de todos os livros.
* `catalog/authors/` — Uma lista de todos os autores.
* `catalog/book/<id>` — A exibição de detalhes de um livro específico, com uma chave primária de campo `<id>` (o padrão). Por exemplo, o URL do terceiro livro adicionado à lista será `/catalog/book/3`.
* `catalog/author/<id>` — A exibição de detalhes para o autor específico com um campo de chave primária de `<id>`. Por exemplo, o URL do 11º autor adicionado à lista será `/catalog/author/11`.

Os três primeiros URLs retornarão a página de índice, a lista de livros e a lista de autores. Esses URLs não codificam nenhuma informação adicional e as consultas que buscam dados no banco de dados sempre serão as mesmas. No entanto, os resultados retornados pelas consultas dependerão do conteúdo do banco de dados.

Por outro lado, os dois URLs finais exibirão informações detalhadas sobre um livro ou autor específico. Esses URLs codificam a identidade do item a ser exibido (representado por `<id>` acima). O mapeador de URLs extrairá as informações codificadas e as passará para a visualização, e a visualização determinará dinamicamente quais informações serão obtidas do banco de dados. Ao codificar as informações no URL, usaremos um único conjunto de mapeamento de URL, uma visualização e um modelo para lidar com todos os livros (ou autores).

**Nota:** Com o Django, você pode construir suas URLs da maneira que desejar - você pode codificar informações no corpo da URL, como mostrado acima, ou incluir `GET` parâmetros no URL, por exemplo `/book/?id=6`. Qualquer que seja a abordagem usada, os URLs devem ser mantidos limpos, lógicos e legíveis, conforme recomendado pelo W3C.
 A documentação do Django recomenda informações de codificação no corpo da URL para obter um melhor design da URL.

Conforme mencionado na visão geral, o restante deste artigo descreve como construir a página index.

Criando a página index
----------------------

A primeira página que criaremos é a página index (`catalog/`). A pagina index incluirá algum HTML estático, juntamente com "contagens" geradas de diferentes registros no banco de dados. Para fazer isso funcionar, criaremos um mapeamento de URL, uma visualização e um modelo.

**Nota:** Vale a pena prestar um pouco de atenção extra nesta seção. A maioria das informações também se aplica às outras páginas que criaremos.

### Mapeamento de URL

Quando criamos o esqueleto do website, atualizamos o arquivo **locallibrary/urls.py** para garantir que sempre que um URL que comece com `catalog/` é recebido, o módulo URLConf `catalog.urls` processará a substring restante.

O seguinte snippet de código de **locallibrary/urls.py** inclui o modulo `catalog.urls`:
```

urlpatterns += [
    path('catalog/', include('catalog.urls')),
]
```

**Nota:** Sempre que o Django encontra a função de importação `django.urls.include()`, divide a string da URL no caractere final designado e envia a subsequência restante para o módulo URLconf incluído para processamento adicional.

Também criamos um arquivo de espaço reservado para o modulo *URLConf*, chamado **/catalog/urls.py**. Adicione as seguintes linhas a esse arquivo:

```python
urlpatterns = [
    path('', views.index, name='index'),
]
```

A função `path()` define o seguinte:

* Um padrão de URL, que é uma sequência vazia: `''`. Discutiremos detalhadamente os padrões de URL ao trabalhar em outras visualizações.
* A view function that will be called if the URL pattern is detected: `views.index`, which is the function named `index()` in the **views.py** file.

A função `path()` também especifica um parâmetro de nome, que é um identificador exclusivo para esse mapeamento de URL específico. Você pode usar o nome para "reverter" o mapeador, ou seja, para criar dinamicamente um URL que aponte para o recurso que o mapeador foi projetado para manipular. Por exemplo, podemos usar o parâmetro name para vincular à nossa home page a partir de qualquer outra página adicionando o seguinte link em um modelo:

```html
<a href="{% url 'index' %}">Home</a>.
```

**Nota:** Podemos codificar o link como em `<a href="/catalog/">Home</a>`), mas se alterarmos o padrão da nossa página inicial, por exemplo, para `/catalog/index`) os modelos não serão mais vinculados corretamente. Usar um mapeamento de URL invertido é muito mais flexível e robusto.

### View (function-based)

Uma view é uma função que processa uma solicitação HTTP, busca os dados necessários no banco de dados, renderiza os dados em uma página HTML usando um modelo HTML e, em seguida, retorna o HTML gerado em uma resposta HTTP para exibir a página ao usuário. A visualização do índice segue esse modelo - ele busca informações sobre o número de `Book`, `BookInstance`, disponibilidade de `BookInstance` e registros de `Author` que temos no banco de dados e passa essas informações para um modelo para exibição.

Abra **catalog/views.py** e observe que o arquivo já importa o render() da função shortcuts para gerar arquivo HTML usando um modelo e dados:

```python
from django.shortcuts import render

# Create your views here.
```

Cole as seguintes linhas na parte inferior do arquivo:

```python
from catalog.models import Book, Author, BookInstance, Genre

def index(request):
    """View function for home page of site."""

    # Generate counts of some of the main objects
    num_books = Book.objects.all().count()
    num_instances = BookInstance.objects.all().count()

    # Available books (status = 'a')
    num_instances_available = BookInstance.objects.filter(status__exact='a').count()

    # The 'all()' is implied by default.
    num_authors = Author.objects.count()

    context = {
        'num\_books': num_books,
        'num\_instances': num_instances,
        'num\_instances\_available': num_instances_available,
        'num\_authors': num_authors,
    }

    # Render the HTML template index.html with the data in the context variable
    return render(request, 'index.html', context=context)
```

A primeira linha importa as classes de models que usaremos para acessar dados em todas as nossas visualizações.

A primeira parte da função view busca o número de registros usando o atributo `objects.all()` nas classes de modelo. Também recebe uma lista de objetos de `BookInstance` que possuem um valor de 'a' (Disponibilidade) no campo status. Você pode encontrar mais informações sobre como acessar os dados do modelo em nosso tutorial anterior Django Tutorial Part 3: Using models > Searching for records.

No final da função view chamamos a função `render()` para criar uma página HTML e retornar a página como resposta. essa função de atalho envolve várias outras funções para simplificar um caso de uso muito comum. A função `render()` aceita os seguintes parâmetros:

* o objeto `request` original, que é um `HttpRequest`.
* um modelo HTML com espaços reservados para os dados.
* uma variável `context`, que é um dicionário Python, contendo os dados a serem inseridos nos espaços reservados.

Falaremos mais sobre modelos e variáveis `context` na próxima seção. Vamos criar nosso template para que possamos exibir algo para o usuário!

### Template

Um template é um arquivo de texto que define a estrutura ou o layout de um arquivo (como uma página HTML), usa espaços reservados para representar o conteúdo real.

Django irá procurar automaticamente templates na pasta chamada '**templates**' em sua aplicação. Por exemplo, na exibição de index que acabamos de adicionar, a função `render()` espera encontrar o arquivo **index.html** em **/locallibrary/catalog/templates/** e gera um erro se o arquivo não estiver presente.

Você pode verificar isso salvando as alterações anteriores e acessando `127.0.0.1:8000` no seu navegador - ele exibirá uma mensagem de erro bastante intuitiva: "`TemplateDoesNotExist at /catalog/`", e outros detalhes.

**Nota:** Com base no arquivo de configurações do seu projeto, o Django procurará templates em vários locais, pesquisando nos aplicativos instalados por padrão. Você pode descobrir mais sobre como o Django encontra templates e quais formatos ele suporta no the Templates section of the Django documentation.

#### Estendendo templates

O index template precisará de marcação HTML padrão para head e a body, juntamente com as seções de navegação para criar um link para as outras páginas do site (que ainda não criamos) e para as seções que exibem dados introdutórios de texto e livro.

Grande parte da estrutura HTML e de navegação será a mesma em todas as páginas do nosso site. Em vez de duplicar o código padrão em todas as páginas, você pode usar a linguagem de modelagem do Django para declarar um modelo base e depois estendê-lo para substituir apenas os bits que são diferentes para cada página específica.

O seguinte snippet de código é um template base de amostra de um arquivo **base\_generic.html**. Em breve, criaremos o modelo para a LocalLibrary. O exemplo abaixo inclui HTML comum com seções para um título, uma barra lateral e o conteúdo principal marcado com as template tags de nome `block` e `endblock`, mostrado em negrito. Você pode deixar os blocos vazios ou incluir o conteúdo padrão a ser usado ao renderizar páginas derivadas do modelo.

**Nota:** Template *tags* são funções que você pode usar em um modelo para percorrer as listas, executar operações condicionais com base no valor de uma variável e assim por diante. Além das template tags, a sintaxe template permite que você faça referência a variáveis que são passadas para a template a partir da view e use filtros de template para formatar variáveis (por exemplo, para converter uma sequência em minúscula).

```django
<!DOCTYPE html>
<html lang="en">
  <head>
    {% block title %}<title>Local Library</title>{% endblock %}
  </head>
  <body>
    {% block sidebar %}<!-- insert default navigation text for every page -->{% endblock %}
    {% block content %}<!-- default content text (typically empty) -->{% endblock %}
  </body>
</html>
```

Ao definir um template para uma visualização específica, primeiro especificamos o template base usando a template tag `extends` — veja o exemplo de código abaixo. Em seguida, declaramos quais seções do template queremos substituir (se houver), usando seções `block`/`endblock` como no template base.

Por exemplo, o trecho de código abaixo mostra como usar a template tag `extends` e substituir o block `content`. O HTML gerado incluirá o código e a estrutura definidos no template base, incluindo o conteúdo padrão que você definiu no block `title`, mas o novo block `content` no lugar do padrão.

```django
{% extends "base\_generic.html" %}

{% block content %}
  <h1>Local Library Home</h1>
  <p>Welcome to LocalLibrary, a website developed by <em>Mozilla Developer Network</em>!</p>
{% endblock %}
```

#### O template base LocalLibrary

Usaremos o seguinte snippet de código como modelo básico para o site *LocalLibrary*. Como você pode ver, ele contém algum código HTML e define blocos para `title`, `sidebar`, e `content`. Temos um título padrão e uma barra lateral padrão com links para listas de todos os livros e autores, ambos colocados em blocos para serem facilmente alterados no futuro.

**Nota:** Também introduzimos duas template tags adicionais: `url` e `load static`. Essas tags serão explicadas nas próximas seções.

Crie um novo arquivo \*\**base\_generic.html* \*\*em **/locallibrary/catalog/templates/** e cole o seguinte código no arquivo:

```django
<!doctype html>
<html lang="en">
  <head>
    {% block title %}<title>Local Library</title>{% endblock %}
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link
 rel="stylesheet"
 href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
 integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
 crossorigin="anonymous" />
    <!-- Add additional CSS in static file -->
    {% load static %}
    <link rel="stylesheet" href="{% static 'css/styles.css' %}" />
  </head>
  <body>
    <div class="container-fluid">
      <div class="row">
        <div class="col-sm-2">
          {% block sidebar %}
            <ul class="sidebar-nav">
              <li><a href="{% url 'index' %}">Home</a></li>
              <li><a href="">All books</a></li>
              <li><a href="">All authors</a></li>
            </ul>
          {% endblock %}
        </div>
        <div class="col-sm-10">{% block content %}{% endblock %}</div>
      </div>
    </div>
  </body>
</html>
```

O template inclui CSS de Bootstrap para melhorar o layout e a apresentação da página HTML. O uso do Bootstrap (ou outra estrutura da Web do lado do cliente) é uma maneira rápida de criar uma página atraente que é exibida bem em diferentes tamanhos de tela.

O template base também faz referência a um arquivo css local (**styles.css**) que fornece estilo adicional. Criar um arquivo **styles.css** em **/locallibrary/catalog/static/css/** e cole o seguinte código no arquivo:

```css
.sidebar-nav {
  margin-top: 20px;
  padding: 0;
  list-style: none;
}
```

#### O template index

Crie um novo arquivo HTML \*\**index.html* \*\*em **/locallibrary/catalog/templates/** e cole o seguinte código no arquivo Esse código estende nosso modelo base na primeira linha e substitui o padrão block `content` para o template.

```django
{% extends "base\_generic.html" %}

{% block content %}
  <h1>Local Library Home</h1>
  <p>
    Welcome to LocalLibrary, a website developed by
    <em>Mozilla Developer Network</em>!
  </p>
  <h2>Dynamic content</h2>
  <p>The library has the following record counts:</p>
  <ul>
    <li><strong>Books:</strong> {{ num\_books }}</li>
    <li><strong>Copies:</strong> {{ num\_instances }}</li>
    <li><strong>Copies available:</strong> {{ num\_instances\_available }}</li>
    <li><strong>Authors:</strong> {{ num\_authors }}</li>
  </ul>
{% endblock %}
```

Na seção *Dynamic content*, declaramos espaços reservados (*variáveis de template*) para as informações da exibição que queremos incluir. As variáveis são colocadas entre chaves (guiador), como mostrado em negrito no exemplo de código.

**Nota:** Você pode reconhecer facilmente variáveis de template e template tags (funções) - as variáveis são colocadas entre chaves (`{{ num_books }}`), e as tags são colocadas em chaves simples com sinais de porcentagem (`{% extends "base_generic.html" %}`).

O importante a ser observado aqui é que as variáveis são nomeadas com as *chaves* que passamos para o dicionário `context` na função `render()` da nossa view (veja a amostra abaixo). As variáveis serão substituídas pelos *valores* associados quando o modelo for renderizado.

```python
context = {
    'num\_books': num_books,
    'num\_instances': num_instances,
    'num\_instances\_available': num_instances_available,
    'num\_authors': num_authors,
}

return render(request, 'index.html', context=context)
```

#### Referenciando arquivos estáticos nos templates

É provável que seu projeto use recursos estáticos, incluindo JavaScript, CSS e imagens. Como a localização desses arquivos pode não ser conhecida (ou pode mudar), o Django permite que você especifique a localização em seus modelos em relação a configuração global `STATIC_URL`. O site padrão do esqueleto define o valor de `STATIC_URL` para '`/static/`', mas você pode optar por hospedá-los em uma rede de entrega de conteúdo ou em outro local.

Dentro do template que você chama primeiro na template tag `load` especificando "static" para adicionar a biblioteca de modelos, conforme mostrado no exemplo de código abaixo. Você pode então usar a template tag `static` e especifique o URL relativo ao arquivo necessário.

```django
<!-- Add additional CSS in static file -->
{% load static %}
<link rel="stylesheet" href="{% static 'css/styles.css' %}" />
```

Você pode adicionar uma imagem à página de maneira semelhante, por exemplo:

```django
{% load static %}
<img
 src="{% static 'catalog/images/local\_library\_model\_uml.png' %}"
 alt="UML diagram"
 style="width:555px;height:540px;" />
```

**Nota:** Os exemplos acima especificam onde os arquivos estão localizados, mas o Django não os serve por padrão. Configuramos o servidor da web de desenvolvimento para exibir arquivos modificando o mapeador de URL global (**/locallibrary/locallibrary/urls.py**) quando criamos o esqueleto do website, mas ainda precisamos ativar a veiculação de arquivos na produção. Veremos isso mais tarde.

Para obter mais informações sobre como Trabalhar com arquivos estaticos, consulte Gerenciando arquivos estáticos na documentação do Django.

#### Vinculando as URLs

O template base abaixo introduziu a template tag `url`.

```python
<li><a href="{% url 'index' %}">Home</a></li>
```

Essa tag aceita o nome de uma função `path()` chamado em **urls.py** e os valores para quaisquer argumentos que a view associada receberá dessa função e retorna um URL que você pode usar para vincular ao recurso.

#### Configurando onde encontrar os templates

Você precisa dizer ao Django para procurar seus templates na pasta de templates. Para fazer isso, adicione o diretório de templates ao objeto TEMPLATES editando o arquivo **settings.py**, como mostrado em negrito, no seguinte exemplo de código:

```python
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
        ],
        'APP\_DIRS': True,
        'OPTIONS': {
            'context\_processors': [
                'django.template.context\_processors.debug',
                'django.template.context\_processors.request',
                'django.contrib.auth.context\_processors.auth',
                'django.contrib.messages.context\_processors.messages',
            ],
        },
    },
]
```

Com o que se parece?
--------------------

Neste ponto, criamos todos os recursos necessários para exibir a página index. Execute o servidor (`python3 manage.py runserver`) e abra `http://127.0.0.1:8000/` no seu navegador. Se tudo estiver configurado corretamente, seu site deverá ter a seguinte captura de tela.

![Index page for LocalLibrary website](/pt-BR/docs/Learn/Server-side/Django/Home_page/index_page_ok.png)

**Nota:** Os links **All books** e **All authors** ainda não funcionarão porque os caminhos, visualizações e modelos para essas páginas não estão definidos. Acabamos de inserir espaços reservados para esses links no template `base_generic.html`.

Desafie-se
----------

Temos duas tarefas para testar a sua familiaridade com as consultas de modelos, views e templates

1. O modelo de base da BibliotecaLocal inclui um bloco de `título`. Substitua este bloco no modelo de índice e crie um novo título para a página.
2. **Nota:** **Dica:** A seção Extendendo Templates explica como criar blocos e extender um bloco em outro template.
3. Modifique a view para gerar contagens para gêneros e livros que contenham uma palavra específica (case insensitive), e passe o resultado para o `contexto`. Isso é feito de maneira semelhante à criação e uso de `num_books` e `num_instances_available`. Em seguida, atualize o template do index para incluir essas variáveis.
Resumo
------

Acabamos de criar a página inicial do nosso site - uma página HTML que exibe uma série de registros do banco de dados e links para outras páginas ainda a serem criadas. Ao longo do caminho, aprendemos informações fundamentais sobre mapeadores de url, views, consulta do banco de dados com modelos, passagem de informações para um modelo a partir de uma view e criação e extensão de templates.

No próximo artigo, continuaremos sobre esse conhecimento para criar as quatro páginas restantes de nosso site.

Veja também
-----------

* Escrevendo sua primeira aplicação Django, parte 3: View e Templates (documentação do Django)
* Despachante de URL (Django docs)
* Funções das Views (DJango docs)
* Templates (Django docs)
* Gerenciando arquivos estáticos (Django docs)
* Funções de atalho do Django (Django docs)
* Anterior
* Menu: Django
* Próxima
Nesse Módulo
------------

* Introdução ao Django
* Configurando um ambiente de desenvolvimento Django
* Tutorial Django: Website de uma Biblioteca Local
* Tutorial Django Parte 2: Criando a base do website
* Tutorial Django Parte 3: Utilizando models
* Tutorial Django Parte 4: Django admin site
* Tutorial Django Parte 5: Criando nossa página principal
* Tutorial Django Parte 6: Lista genérica e detail views
* Tutorial Django Parte 7: Sessões de Framework
* Tutorial Django Parte 9: Trabalhando com formulários
* Tutorial Django Parte 10: Testando uma aplicação web Django
* Tutorial Django Parte 11: Implantando Django em produção
* Segurança de aplicações web Django
* DIY Django mini blog (en-US)


Segurança de aplicações web Django - Aprendendo desenvolvimento web

Segurança de aplicações web Django
==================================

* Anterior
* Menu: Django
* Próxima  (en-US)

Proteger dados do usuário é uma parte essencial de qualquer projeto de website. Anteriormente, explicamos algumas das ameaças de segurança mais comuns no artigo Web security — esse artigo fornece uma demonstração prática de como as proteções internas de Django lidam com essas ameaças.

|  |  |
| --- | --- |
| Pré-requisitos: |
 Ler o tópico "Website security" de Programação Server-side. Conclua os tópicos do tutorial Django
 tutorial até (e incluindo) pelos menos
 Tutorial Django Parte 9: Trabalhando com formulários.
  |
| Objective: |
 Para entender as principais coisas que você precisa fazer (ou não fazer)
 para proteger seu aplicatico web Django.
  |

Visão geral
-----------

O tópico Website security fornece uma visão geral do que a segurança de website siginifica para o projeto server-side e algumas das ameaças mais comuns contra as quais você deve se proteger. Uma das mensagens chave nesse artigo é que quase todos os ataques são bem sucedidos quando a aplicação web confia nos dados do navegador.

**Aviso:** **Importante:** A lição mais importante que você pode aprender sobre segurança de website é **nunca confiar nos dados do navegador**. Isso inclui dados de requisição GET em parâmetros de URL, dados `POST`, cabeçalhos HTTP e cookies, arquivos enviados por usuários, etc. Sempre verifique e "desinfete" todos os dados recebidos. Sempre assuma o pior.

A boa notícia para usuários Django é que muitas das ameaças mais comuns são tratadas pelo framework! O artigo Segurança no Django (Django docs) explica os recursos de segurança e como proteger um website desenvolvido pelo Django.

Ameaças/proteções comuns
------------------------

Em vez de duplicar a documentação do Django aqui, neste artigo demonstraremos apenas alguns dos recursos de segurança no contexto do nosso tutorial Django da LocalLibrary.

### Cross site scripting (XSS)

XSS é um termo utilizado para descrever uma classe de ataques que permitem um invasor injetar scripts no lado cliente, através do website, no navegador de outros usuários. Issi geralmente é conseguido armazenando scripts maliciosos no banco de dados onde eles podem ser recuperado e exibidos para outros usuários, ou fazendo com que usuários cliquem em um link que fará com que o JavaScript do invasor seja executado pelo navegador do usuário.

O sistema de *templates* do Django protege você da maioria dos ataques XSS escapando de caracteres específicos que são "perigosos" em HTML. Podemos demonstrar isso tentando injetar algum JavaScript em nosso website LocalLibrary usando o *form* Create-author que configuramos em Django Tutorial Parte 9: Trabalhando com formulários.

1. Inicie o website usando o servidor de desenvolvimento (`python3 manage.py runserver`).
2. Abra o site em seu navegador local e faça login em sua conta de superusuário.
3. Navegue até a página de criação do autor (que deve estar na URL: `http://127.0.0.1:8000/catalog/author/create/`).
4. Digite os nomes e os detalhes de data para um novo usuário, e então acrescente o seguinte texto no campo Last Name :
 `<script>alert('Test alert');</script>`.

 ![Author Form XSS test](/pt-BR/docs/Learn/Server-side/Django/web_application_security/author_create_form_alert_xss.png)

**Nota:** Este é um script inofensivo que, se executado, exibirá uma caixa de alerta em seu navegador. Se o alerta é exibido quando você submeter o registro então o site está vulnerável a ameaças XSS.
5. Pressione **Submit** para salvar o registro.
6. Quando você salvar o autor, ele será exibido como mostrado abaixo. Por causa das proteções XSS o `alert()` não deve ser executado. Em vez disso o script é exibido como texto simples.
 ![Author detail view XSS test](/pt-BR/docs/Learn/Server-side/Django/web_application_security/author_detail_alert_xss.png)

Se você visualizar o código fonte da página HTML, poderá ver que os carcteres perigosos para as tags de script foram trasnformadoes em seus equivalentes de código de escape inofensivos (ex. `>` agora é `>`)

```html
<h1>
  Author: Boon<script>alert('Test alert');</script>, David
  (Boonie)
</h1>
```

Usar templates Django protege você contra a maioria dos ataques de XSS. No entanto, é possível desativar esta proteção, e a proteção não é automaticamente aplicada a todas as tags que normalmente não seriam preenchidas pela entrada do usuário (por exemplo, o `help_text` em um campo de formulário normalmente não é preechido pelo usuário, então Django não escapa esses valores).

Também é possível que os ataques XSS se originem de outra fonte de dados não confiável, como cookies, webservices ou upload de arquivos (sempre que os dados não forem suficientemente limpos antes de serem incluídos em uma página). Se estiver exibindo dados dessas fontes, então pode ser necessário adicionar seu próprio código de limpeza.

### Proteção contra Cross site request forgery (CSRF)

Ataques CSRF permitem que um usuário malicioso execute ações usando as credenciais de outro usuário sem o conhecimento ou consentimento desse usuário. Por exemplo, considere o caso em que temos um hacker que quer criar autores adicionais para nossa LocalLibrary.

**Nota:** Obviamente nosso hacker não está nisso por dinheiro! Um hacker mais ambicioso poderia usar a mesma abordagem em outros sites para realizar tarefas muito mais prejudiciais (ex. transferir dinheiro para suas prórpias contas, etc.)

Para fazer isso, eles podem criar um arquivo HTML como o abaixo, que contém um form de criação de autor (como o que usamos na seção anterior) que é enviado assim que o arquivo é carregado. Eles então enviariam o arquivo para todos os bibliotecários e sugeririam que eles abrissem o arquivo (ele contém algumas informações inofensivas, honestamente!). Se o arquivo for aberto por qualquer bibliotecário logado, o formulário será enviado com suas credenciais e um novo autor será criado.

```html
<html>
  <body onload="document.EvilForm.submit()">
    <form
 action="http://127.0.0.1:8000/catalog/author/create/"
 method="post"
 name="EvilForm">
      <table>
        <tr>
          <th><label for="id\_first\_name">First name:</label></th>
          <td>
            <input
 id="id\_first\_name"
 maxlength="100"
 name="first\_name"
 type="text"
 value="Mad"
 required />
          </td>
        </tr>
        <tr>
          <th><label for="id\_last\_name">Last name:</label></th>
          <td>
            <input
 id="id\_last\_name"
 maxlength="100"
 name="last\_name"
 type="text"
 value="Man"
 required />
          </td>
        </tr>
        <tr>
          <th><label for="id\_date\_of\_birth">Date of birth:</label></th>
          <td>
            <input id="id\_date\_of\_birth" name="date\_of\_birth" type="text" />
          </td>
        </tr>
        <tr>
          <th><label for="id\_date\_of\_death">Died:</label></th>
          <td>
            <input
 id="id\_date\_of\_death"
 name="date\_of\_death"
 type="text"
 value="12/10/2016" />
          </td>
        </tr>
      </table>
      <input type="submit" value="Submit" />
    </form>
  </body>
</html>
```

Execute o servidor web de desenvolvimento e faça login com a conta de superusuário. Copie o texto acima em um arquivo e abra-o no navegado. Você deve obter um erro de CSRF, porque Django tem protteção contra esse tipo de coisa!

A forma como a proteção é habilitada é incluindo a tag de template `{% csrf_token %}` em sua definição de formulário. Esse token é então renderizado em seu HTML como mostrado abaixo com um valor que é específico para o usuário no navegador atual.

```html
<input
 type="hidden"
 name="csrfmiddlewaretoken"
 value="0QRWHnYVg776y2l66mcvZqp8alrv4lb8S8lZ4ZJUWGZFA5VHrVfL2mpH29YZ39PW" />
```

Django gera uma chave específica de usuário/navegador e irá rejeitar formulários que não contenham o campo, ou que contenham um valor de campo incorreto para o usuário/navegador.

Para usar esse tipo de ataque o hacker agora precisa descobrir e incluir a chave CSRF para o usuário alvo específico. Eles também não podem usar a abordagem "scattergun" de enviar um arquivo malicioso para todos bibliotecários e esperar que um deles abra, já que a chave CSRF é específica do navegador.

A proteção CSRF do Django é ativada por padrão. Você deve sempre usar a tag de template `{% csrf_token %}` em seus formulários e utilizar `POST` para requisições que podem alterar ou adicionar dados ao banco de dados.

### Outras proteções

Django also provides other forms of protection (most of which would be hard or not particularly useful to demonstrate):

Proteção contra Injeção de SQL

As vulnerabilidades de injeção de SQL (SQL injection) permitem usuários mal-intencionados executarem código SQL arbitrário em um banco de dados, permitindo que dados sejam acessados, modificados ou apagados independentemente das permissões do usuário. Em quase todos os casos você acessará o banco de dados usando querysets/models do Django, de mdo que o SQL resultante será devidamente escapado pelo driver de banco de dados subjacente. Se você precisa escrever consultas brutas ou SQL customizado precisará pensar explicitamente sobre como previnir injeção de SQL.

Proteção contra Clickjacking

Nesse ataque, um usuário malicioso sequestra clicks destinados a um site de visível no nível superior e os encaminha para uma página oculta abaixo. Essa técnica pode ser usada, por exemplo, para exibir um site de banco legítimo, mas capturar as credenciais de login em um `<iframe>` invisível, controlado pelo atacante. O Django possui proteção contra clickjacking na forma do `X-Frame-Options middleware` que, em um navegador de suporte, pode impedir que um site seja renderizado dentro de um frame.

Aplicação de SSL/HTTPS

SSL/HTTPS pode ser habilitado no servidor web para criptografar todo o tráfego entre o site e o navegador, incluindo credenciais de autenticação que seriam enviadas em texto simples (habilitar HTTPS é altamente recomendado). Se HTTPS estiver habilitado o Django fornece uma série de outras proteções que você pode utilizar:

* `SECURE_PROXY_SSL_HEADER` pode ser utilizado para verificar se o conteúdo é seguro, mesmo se for recebido de um proxy não HTTP.
* `SECURE_SSL_REDIRECT` é usado para redirecionar todas as requisições HTTP para HTTPS.
* Usar HTTP Strict Transport Security (HSTS). Este é um cabeçalho HTTP que informa ao navegador que todas as conexões futuras com um determinado site devem sempre utilizar HTTPS. Combinada com o redirecionamento de requisições HTTP para HTTPS, essa configuração garante que HTTPS é sempre usado depois que uma conexão bem-sucedida ocorrer. HSTS pode ser configurado com `SECURE_HSTS_SECONDS` e `SECURE_HSTS_INCLUDE_SUBDOMAINS` ou no servidor web.
* Usar cookies "seguros" definindo `SESSION_COOKIE_SECURE` e `CSRF_COOKIE_SECURE` como `True`. Isso garantirá que os cookies sejam enviados apenas por HTTPS.

Validação de cabeçalho de host

Usar `ALLOWED_HOSTS` para aceitar apenas requisições de hosts confiáveis.

Existem muitas outras proteções, e ressalvas para o uso dos mecanismos acima. Embora esperamos que isso tenha dado a você uma visão geral do que o Django oferece, você ainda deve ler a documentação de segurança de Django.

Resumo
------

Django tem proteções eficazes contra uma série de ameaças comuns, incluindo ataques XSS e CSRF. Neste artigo demonstramos como essas ameaças específicas são tratadas pelo Django em nosso website *LocalLibrary*. Também fornecemos uma breve visão geral de algumas das outras proteções.

Esta foi uma incursão muito breve em segurança web. Nós recomendamos fortemente que você leia Segurança no Django para obter um entendimento mais profundo.

A próxima e última etapa neste módulo sobre Django é concluir a tarefa de avaliação (en-US).

Veja também
-----------

* Segurança no Django (Django docs)
* Segurança de website no lado do servidor (MDN)
* Segurança web (MDN)
* Protegendo seu site (MDN)
* Anterior
* Menu: Django
* Próxima  (en-US)