One place for hosting & domains

      Construir

      Como construir um servidor Vault da Hashicorp usando o Packer e o Terraform na DigitalOcean [Início rápido]


      Introdução

      O Vault, da Hashicorp, é uma ferramenta de código aberto usada para armazenar segredos e dados confidenciais de maneira segura em ambientes dinâmicos em nuvem. O Packer e o Terraform, também desenvolvidos pelo Hashicorp, podem ser usados juntos para criar e implantar imagens do Vault.

      Neste tutorial, você usará o Packer para criar um snapshot imutável do sistema com o Vault instalado e orquestrar sua implantação usando o Terraform.

      Para obter uma versão mais detalhada deste tutorial, consulte Como construir um servidor Vault da Hashicorp usando o Packer e o Terraform na DigitalOcean.

      Pré-requisitos

      • O Packer instalado em sua máquina local. Para instruções, visite a documentação oficial.
      • O Terraform instalado em sua máquina local. Visite a documentação oficial para um guia.
      • Um token de acesso pessoal (chave API) com permissões de leitura e escrita para sua conta DigitalOcean. Visite Como criar um token de acesso pessoal para criar um.
      • Uma chave SSH que você usará para autenticar-se com os Droplets implantados pelo Vault, disponível em sua máquina local e adicionada em sua conta DigitalOcean. Você também precisará de suas digitais, que você pode copiar da página Security de sua conta assim que as tiver adicionado. Veja a documentação da DigitalOcean para instruções detalhadas ou o tutorial sobre Como configurar chaves SSH.

      Passo 1 — Como criar um template do Packer

      Crie e mova-se para o diretório ~/ ~/vault-orchestration para armazenar seus arquivos do Vault:

      • mkdir ~/vault-orchestration
      • cd ~/vault-orchestration

      Crie diretórios separados para a configuração do Packer e do Terraform, executando:

      Navegue até o diretório do Packer:

      Como usar variáveis do modelo

      Crie um variables.json em seu subdiretório packer para armazenar dados de sua variável privada:

      Adicione as linhas a seguir:

      ~/vault-orchestration/packer/variables.json

      {
        "do_token": "your_do_api_key",
        "base_system_image": "ubuntu-18-04-x64",
        "region": "nyc3",
        "size": "s-1vcpu-1gb"
      }
      

      Você usará essas variáveis no modelo que está prestes a criar. É possível editar o tamanho de imagem base, região e do Droplet, de acordo com a documentação do desenvolvedor.

      Substitua your_do_api_key pela sua chave API e, em seguida, salve e feche o arquivo.

      Como criar compiladores e provisionadores

      Crie seu modelo do Packer para o Vault em um arquivo chamado template.json:

      Adicione as linhas a seguir:

      ~/vault-orchestration/packer/template.json

      {
         "builders": [{
             "type": "digitalocean",
             "api_token": "{{user `do_token`}}",
             "image": "{{user `base_system_image`}}",
             "region": "{{user `region`}}",
             "size": "{{user `size`}}",
             "ssh_username": "root"
         }],
         "provisioners": [{
             "type": "shell",
             "inline": [
                 "sleep 30",
                 "sudo apt-get update",
                 "sudo apt-get install unzip -y",
                 "curl -L https://releases.hashicorp.com/vault/1.3.2/vault_1.3.2_linux_amd64.zip -o vault.zip",
                 "unzip vault.zip",
                 "sudo chown root:root vault",
                 "mv vault /usr/local/bin/",
                 "rm -f vault.zip"
             ]
      }]
      }
      

      Você define um único construtor digitalocean. O Packer criará um Droplet temporário do tamanho, imagem e região definidos usando a chave de API fornecida.

      O provisionador se conectará a ele usando o SSH com o nome de usuário especificado e executará sequencialmente todos os provisionadores definidos antes da criação de um Snapshot da DigitalOcean por meio do Droplet e da exclusão desse Snapshot.

      Ele é do tipo shell, que executará os comandos dados no destino. Os comandos no modelo esperam 30 segundos para o sistema inicializar e então baixam e descompactam o Vault 1.3.2. Verifique a página oficial de download do Vault para a versão mais atual do Linux.

      Salve e feche o arquivo.

      Verifique a validade do seu modelo:

      • packer validate -var-file=variables.json template.json

      Você verá o seguinte resultado:

      Output

      Template validated successfully.

      Passo 2 — Como compilar o Snapshot

      Compile seu snapshot com o comando build do Packer:

      • packer build -var-file=variables.json template.json

      Você verá um resultado extenso, que se parecerá com este:

      Output

      digitalocean: output will be in this color. ==> digitalocean: Creating temporary ssh key for droplet... ==> digitalocean: Creating droplet... ==> digitalocean: Waiting for droplet to become active... ==> digitalocean: Using ssh communicator to connect: ... ==> digitalocean: Waiting for SSH to become available... ==> digitalocean: Connected to SSH! ==> digitalocean: Provisioning with shell script: /tmp/packer-shell035430322 ... ==> digitalocean: % Total % Received % Xferd Average Speed Time Time Time Current ==> digitalocean: Dload Upload Total Spent Left Speed digitalocean: Archive: vault.zip ==> digitalocean: 100 45.5M 100 45.5M 0 0 154M 0 --:--:-- --:--:-- --:--:-- 153M digitalocean: inflating: vault ==> digitalocean: Gracefully shutting down droplet... ==> digitalocean: Creating snapshot: packer-1581537927 ==> digitalocean: Waiting for snapshot to complete... ==> digitalocean: Destroying droplet... ==> digitalocean: Deleting temporary ssh key... Build 'digitalocean' finished. ==> Builds finished. The artifacts of successful builds are: --> digitalocean: A snapshot was created: 'packer-1581537927' (ID: 58230938) in regions '...'

      A última linha contém o nome do snapshot (como packer-1581537927) e o ID dele entre parênteses, destacada aqui. Anote seu ID do snapshot, pois precisará dele no próximo passo.

      Se o processo de compilação falhar devido a erros da API, espere alguns minutos e, em seguida, tente novamente.

      Etapa 3 — Escrevendo a configuração do Terraform

      Navegue até o subdiretório terraform:

      • cd ~/vault-orchestration/terraform

      Crie um arquivo chamado do-provider.tf para armazenar o provedor:

      Adicione as linhas a seguir:

      ~/vault-orchestration/terraform/do-provider.tf

      variable "do_token" {
      }
      
      variable "ssh_fingerprint" {
      }
      
      variable "instance_count" {
      default = "1"
      }
      
      variable "do_snapshot_id" {
      }
      
      variable "do_name" {
      default = "vault"
      }
      
      variable "do_region" {
      }
      
      variable "do_size" {
      }
      
      variable "do_private_networking" {
      default = true
      }
      
      provider "digitalocean" {
      token = var.do_token
      }
      

      Este arquivo fornece uma chave de API ao provedor da digitalocean. Para especificar os valores dessas variáveis, você criará um arquivo de definições de variáveis, de maneira similar ao do Packer. O nome do arquivo deve terminar em .tfvars ou .tfvars.json.

      Salve e feche o arquivo.

      Crie um arquivo de definições de variáveis:

      Adicione as linhas a seguir:

      ~/vault-orchestration/terraform/definitions.tf

      do_token         = "your_do_api_key"
      ssh_fingerprint  = "your_ssh_key_fingerprint"
      do_snapshot_id   = your_do_snapshot_id
      do_name          = "vault"
      do_region        = "nyc3"
      do_size          = "s-1vcpu-1gb"
      instance_count   = 1
      

      Substitua your_do_api_key, your_ssh_key_fingerprint e your_do_snapshot_id (o ID do snapshot que você anotou no passo anterior). Os parâmetros do do_region e do_size devem ter os mesmos valores que no arquivo de variáveis do Packer.

      Salve e feche o arquivo.

      Crie o seguinte arquivo para armazenar a configuração de implantação do snapshot do Vault:

      Adicione as linhas a seguir:

      ~/vault-orchestration/terraform/deployment.tf

      resource "digitalocean_droplet" "vault" {
      count              = var.instance_count
      image              = var.do_snapshot_id
      name               = var.do_name
      region             = var.do_region
      size               = var.do_size
      private_networking = var.do_private_networking
      ssh_keys = [
        var.ssh_fingerprint
      ]
      }
      
      output "instance_ip_addr" {
      value = {
        for instance in digitalocean_droplet.vault:
        instance.id => instance.ipv4_address
      }
      description = "The IP addresses of the deployed instances, paired with their IDs."
      }
      

      Você define um único recurso do tipo digitalocean_droplet chamado vault. Então, define os parâmetros de acordo com os valores das variáveis e adiciona uma chave SSH (usando a digital dela) de sua conta DigitalOcean para o recurso do Droplet. Por fim, você usa output para transmitir os endereços IP de todas as instâncias recém-implantadas para o console.

      Salve e feche o arquivo.

      Inicialize o diretório como um projeto Terraform:

      Você verá o seguinte resultado:

      Output

      Initializing the backend... Initializing provider plugins... The following providers do not have any version constraints in configuration, so the latest version was installed. To prevent automatic upgrades to new major versions that may contain breaking changes, it is recommended to add version = "..." constraints to the corresponding provider blocks in configuration, with the constraint strings suggested below. * provider.digitalocean: version = "~> 1.14" Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.

      Passo 4 — Como implantar o Vault usando o Terraform

      Teste a validade da configuração:

      Você verá o seguinte resultado:

      Output

      Success! The configuration is valid.

      Execute o comando plan para ver o que o Terraform tentará fazer em relação ao provisionamento da infraestrutura:

      • terraform plan -var-file="definitions.tfvars"

      O resultado será semelhante a:

      Output

      Refreshing Terraform state in-memory prior to plan... The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage. ------------------------------------------------------------------------ An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.vault[0] will be created + resource "digitalocean_droplet" "vault" { ... } Plan: 1 to add, 0 to change, 0 to destroy. ------------------------------------------------------------------------ Note: You didn't specify an "-out" parameter to save this plan, so Terraform can't guarantee that exactly these actions will be performed if "terraform apply" is subsequently run.

      Execute o plano:

      • terraform apply -var-file="definitions.tfvars"

      O Droplet terminará o provisionamento e você verá um resultado semelhante a este:

      Output

      An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: + digitalocean_droplet.vault-droplet ... Plan: 1 to add, 0 to change, 0 to destroy. ... digitalocean_droplet.vault-droplet: Creating... ... Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Outputs: instance_ip_addr = { "181254240" = "your_new_server_ip" }

      Passo 5 — Como verificar seu Droplet implantado

      Para se conectar ao seu novo Droplet, execute o seguinte:

      Assim que estiver logado, execute o Vault com:

      Você verá o resultado “ajuda”:

      Output

      Usage: vault <command> [args] Common commands: read Read data and retrieves secrets write Write data, configuration, and secrets delete Delete secrets and configuration list List data or secrets login Authenticate locally agent Start a Vault agent server Start a Vault server status Print seal and HA status unwrap Unwrap a wrapped secret Other commands: audit Interact with audit devices auth Interact with auth methods debug Runs the debug command kv Interact with Vault's Key-Value storage lease Interact with leases namespace Interact with namespaces operator Perform operator-specific tasks path-help Retrieve API help for paths plugin Interact with Vault plugins and catalog policy Interact with policies print Prints runtime configurations secrets Interact with secrets engines ssh Initiate an SSH session token Interact with tokens

      Conclusão

      Agora, você tem um sistema automatizado para implantar o Vault da Hashicorp em Droplets da DigitalOcean usando o Terraform e o Packer. Para começar a usar o Vault, será necessário inicializá-lo e configurá-lo ainda mais. Para instruções sobre como fazer isso, visite os docs oficiais.

      Para outros tutoriais usando o Terraform, confira nossa página de conteúdo do Terraform.



      Source link

      Como construir loops for no Go


      Introdução

      Em programação de computadores, um loop é uma estrutura de código que faz um loop para executar repetidamente uma parte de um código, frequentemente até que alguma condição seja alcançada. O uso de loops em programação de computadores permite que você automatize e repita tarefas semelhantes várias vezes. Vamos supor que você tivesse uma lista de arquivos que precisasse processar, ou se quisesse contar o número de linhas em um artigo. Você poderia usar um loop em seu código para resolver esses tipos de problemas.

      Na linguagem Go, um loop for implementa a execução repetida de um código baseado em um contador ou uma variável de loop. Ao contrário do que ocorre com outras linguagens de programação que têm vários constructos de looping como o while, do etc., o go tem somente o loop for. Isso serve para tornar seu código mais claro e mais legível, considerando que você não precisa se preocupar com várias estratégias para chegar no mesmo constructo de looping. Essa legibilidade aprimorada e a redução da carga cognitiva durante o desenvolvimento também tornarão o seu código menos propenso a erros do que com outras linguagens.

      Neste tutorial, você aprenderá como o loop for do Go funciona, incluindo as três grandes variações de sua utilização. Vamos começar mostrando como criar diferentes tipos de loops for, seguido de como fazer o loop através de tipos de dados sequenciais em Go. Vamos terminar explicando como usar os loops aninhados

      Declarando o ForClause e os loops de condição

      Para atender a uma variedade de casos de uso, existem três maneiras diferentes de criar loops for em Go, cada qual com seus próprios recursos. Essas maneiras são para criar um loop for com uma Condition, uma ForClause, ou uma RangeClause. Nesta seção, explicaremos como declarar e usar as variantes ForClause e Condição.

      Vamos ver como podemos usar um loop for com o ForClause primeiro.

      Um ForClause loop é definido como tendo uma instrução inicial, seguida de uma condição e, depois, uma instrução de post. Eles são organizados com a seguinte sintaxe:

      for [ Initial Statement ] ; [ Condition ] ; [ Post Statement ] {
          [Action]
      }
      

      Para explicar o que os componentes anteriores fazem, vejamos um loop for que incrementa por meio de uma gama especificada de valores usando a sintaxe do ForClause:

      for i := 0; i < 5; i++ {
          fmt.Println(i)
      }
      

      Vamos desmembrar esse loop e identificar cada parte.

      A primeira parte do loop é i := 0. Esta é a instrução inicial:

      for i := 0; i < 5; i++ {
          fmt.Println(i)
      }
      

      Essa instrução afirma que estamos declarando uma variável chamada i e definindo o valor inicial para 0.

      Em seguida, temos a condição:

      for i := 0; i < 5; i++ {
          fmt.Println(i)
      }
      

      Nessa condição, afirmamos que enquanto o i for menor do que o valor 5, o loop deve continuar em looping.

      Por fim, temos a instrução post:

      for i := 0; i < 5; i++ {
          fmt.Println(i)
      }
      

      Na instrução post, incrementamos a variável do loop i em um toda vez que uma iteração ocorrer, usando o operador de incremento i++.

      Quando executamos esse programa, o resultado fica parecido com este:

      Output

      0 1 2 3 4

      O loop executou 5 vezes. Inicialmente, ele definiu i para 0 e, depois, verificou se o i era menor do que 5. Como o valor de i era menor que 5, o loop executou e a ação de fmt.Println(i) foi executada. Após o loop terminar, o instrução post i++ foi chamada e o valor i foi incrementado em 1.

      Nota: tenha em mente que, em programação, tendemos a começar no índice 0, o que explica por que, a despeito da impressão de 5 números, eles variam de 0 a 4.

      Não estamos limitados a iniciar no 0 ou concluir em um valor especificado. Podemos atribuir qualquer valor para nossa instrução inicial e parar em qualquer valor em nossa instrução post. Isso nos permite criar qualquer intervalo desejado para fazer o loop:

      for i := 20; i < 25; i++ {
          fmt.Println(i)
      }
      

      Aqui, a iteração vai de 20 (inclusive) até 25 (exclusive), de modo que o resultado fica parecido com este:

      Output

      20 21 22 23 24

      Também podemos usar nossa instrução post para incrementar em diferentes valores. Esse procedimento é parecido com o step de outras linguagens:

      Primeiro, vamos usar uma instrução post com um valor positivo:

      for i := 0; i < 15; i += 3 {
          fmt.Println(i)
      }
      

      Neste caso, o loop for foi configurado para que os números de 0 a 15 sejam impressos, mas a incrementos de 3, de modo a imprimir apenas um número a cada três, desta forma:

      Output

      0 3 6 9 12

      Também podemos usar um valor negativo para que o nosso argumento de instrução post faça a iteração retroativamente. Teremos, porém, que ajustar devidamente nossa instrução inicial e os argumentos de condição:

      for i := 100; i > 0; i -= 10 {
          fmt.Println(i)
      }
      

      Aqui, definimos i para um valor inicial de 100, usamos a condição i < 0 para parar em 0 e a instrução post para diminuir o valor em 10 com o operador -=. O loop começa em 100 e termina em 0, diminuindo o valor em 10 a cada iteração. Podemos ver isso ocorrer no resultado:

      Output

      100 90 80 70 60 50 40 30 20 10

      Também é possível excluir a instrução inicial e a instrução post da sintaxe for e usar apenas a condição. Trata-se de um loop de Condição:

      i := 0
      for i < 5 {
          fmt.Println(i)
          i++
      }
      

      Desta vez, declaramos a variável i separadamente do loop for na linha anterior do código. O loop tem apenas uma cláusula de condição que verifica se o i é menor do que 5. Desde que a condição avalie como true, o loop continuará a iterar.

      Às vezes, você pode não saber o número de iterações que serão necessárias para concluir uma determinada tarefa. Neste caso, é possível omitir todas as instruções e usar a palavra-chave break para sair da execução:

      for {
          if someCondition {
              break
          }
          // do action here
      }
      

      Como exemplo disso, vamos supor que estivéssemos lendo uma estrutura de tamanho indeterminado como a de um buffer e não soubéssemos quando terminaríamos a leitura:

      buffer.go

      package main
      
      import (
          "bytes"
          "fmt"
          "io"
      )
      
      func main() {
          buf := bytes.NewBufferString("onentwonthreenfourn")
      
          for {
              line, err := buf.ReadString('n')
              if err != nil {
                  if err == io.EOF {
      
                      fmt.Print(line)
                      break
                  }
                  fmt.Println(err)
                  break
              }
              fmt.Print(line)
          }
      }
      

      No código anterior, o buf :=bytes.NewBufferString("onentwonthreenfourn") declara um buffer com alguns dados. Como não sabemos quando o buffer irá terminar a leitura, criamos um loop for sem cláusula. Dentro do loop for, usamos line, err := buf.ReadString('n') para ler uma linha do buffer e verificamos se existe um erro ao ler do buffer. Se houver, lidamos com o erro e usamos a palavra-chave break para sair do loop for. Com esses pontos de break, não é necessário incluir uma condição para interromper o loop.

      Nesta seção, aprendemos como declarar um loop do ForClause e a usá-lo para iterar por meio de uma gama conhecida de valores. Também aprendemos a usar um loop de condição para iterar até que uma condição específica seja cumprida. Em seguida, vamos aprender como o RangeClause é usado para iterar através de tipos de dados sequenciais.

      Na linguagem Go, é comum o uso de loops for para iterar sobre os elementos de tipos de dados sequenciais ou dados de coleta sequencial ou tipos de dados de coleta, como fatias, matrizes e strings. Para facilitar esse processo, podemos utilizar um loop for com a sintaxe do RangeClause. Embora você possa fazer loops em tipos de dados sequenciais usando a sintaxe do ForClause, a RangeClause é mais clara e fácil de ler.

      Antes de examinarmos o uso da RangeClause, vejamos como podemos iterar por meio de uma fatia, usando a sintaxe do ForClause:

      main.go

      package main
      
      import "fmt"
      
      func main() {
          sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
      
          for i := 0; i < len(sharks); i++ {
              fmt.Println(sharks[i])
          }
      }
      

      Executar isso dará o seguinte resultado, imprimindo cada um dos elementos da fatia:

      Output

      hammerhead great white dogfish frilled bullhead requiem

      Agora, vamos usar a RangeClause para executar o mesmo conjunto de ações:

      main.go

      package main
      
      import "fmt"
      
      func main() {
          sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
      
          for i, shark := range sharks {
              fmt.Println(i, shark)
          }
      }
      

      Neste caso, estamos imprimindo cada item da lista. Embora tenhamos usado as variáveis i e shark, poderíamos ter chamado as variáveis por qualquer outro nome de variável válido e, ainda assim, obteríamos o mesmo resultado:

      Output

      0 hammerhead 1 great white 2 dogfish 3 frilled 4 bullhead 5 requiem

      Ao usar range em uma fatia, ele irá sempre retornar dois valores. O primeiro valor será o índice em que a iteração atual do loop está e o segundo será o valor naquele índice. Neste caso, para a primeira iteração, o índice era 0, e o valor era hammerhead.

      Às vezes, queremos apenas o valor dentro dos elementos da fatia, não do índice. Se alterarmos o código anterior para imprimir apenas o valor, no entanto, vamos receber um erro de tempo de compilação:

      main.go

      package main
      
      import "fmt"
      
      func main() {
          sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
      
          for i, shark := range sharks {
              fmt.Println(shark)
          }
      }
      

      Output

      src/range-error.go:8:6: i declared and not used

      Como o i foi declarado no loop for, mas nunca foi usado, o compilador responderá com o erro i declared and not used. Este é o mesmo erro que você receberá no Go sempre que for declarar uma variável e não a utilizar.

      Por isso, o Go tem o identificador em branco, que é um sublinhado (_). Em um loop for, é possível utilizar o identificador em branco para ignorar qualquer valor retornado da palavra-chave range. Neste caso, queremos ignorar o índice, que é o primeiro argumento retornado.

      main.go

      package main
      
      import "fmt"
      
      func main() {
          sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
      
          for _, shark := range sharks {
              fmt.Println(shark)
          }
      }
      

      Output

      hammerhead great white dogfish frilled bullhead requiem

      Esse resultado mostra que o loop for iterou por toda a fatia de strings e imprimiu cada item da fatia sem o índice.

      Também é possível usar o range para adicionar itens a uma lista:

      main.go

      package main
      
      import "fmt"
      
      func main() {
          sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}
      
          for range sharks {
              sharks = append(sharks, "shark")
          }
      
          fmt.Printf("%qn", sharks)
      }
      

      Output

      ['hammerhead', 'great white', 'dogfish', 'frilled', 'bullhead', 'requiem', 'shark', 'shark', 'shark', 'shark', 'shark', 'shark']

      Aqui, adicionamos uma string com espaço reservado de "shark"para cada item do comprimento da fatia sharks.

      Note que não precisamos usar o identificador em branco _ para ignorar nenhum dos valores retornados do operador range. O Go nos permite omitir toda a parte da instrução range se não precisarmos usar qualquer um do valores retornados.

      Também podemos usar o operador range para preencher valores de uma fatia:

      main.go

      package main
      
      import "fmt"
      
      func main() {
          integers := make([]int, 10)
          fmt.Println(integers)
      
          for i := range integers {
              integers[i] = i
          }
      
          fmt.Println(integers)
      }
      

      Neste exemplo, a fatia integers é inicializada com dez valores em branco, mas o loop for define todos os valores desta forma:

      Output

      [0 0 0 0 0 0 0 0 0 0] [0 1 2 3 4 5 6 7 8 9]

      A primeira vez que imprimimos o valor da fatia integers, vemos todos os zeros. Então, iteramos pro meio de cada índice e definimos o valor para o índice atual. Em seguida, imprimimos o valor de integers uma segunda vez, mostrando que todos eles agora têm um valor de 0 a 9.

      Também podemos usar o operador range para iterar por meio de cada caractere em uma string:

      main.go

      package main
      
      import "fmt"
      
      func main() {
          sammy := "Sammy"
      
          for _, letter := range sammy {
              fmt.Printf("%cn", letter)
          }
      }
      

      Output

      S a m m y

      Ao iterar por um mapa, o range retornará a key (chave) e o** value** (valor):

      main.go

      package main
      
      import "fmt"
      
      func main() {
          sammyShark := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
      
          for key, value := range sammyShark {
              fmt.Println(key + ": " + value)
          }
      }
      

      Output

      color: blue location: ocean name: Sammy animal: shark

      Nota: é importante notar que a ordem na qual um mapa retorna é aleatória. Cada vez que você executa esse programa, você pode obter um resultado diferente.

      Agora que aprendemos como iterar em dados sequenciais com loops for do tipo range, vamos ver como usar loops dentro de loops.

      Loops for aninhados

      Assim como acontece em outras linguagens de programação, os loops também podem ser aninhados em Go. Nesting (aninhamento) é quando temos um constructo dentro de outro. Neste caso, um loop aninhado é um loop que ocorre dentro de outro loop. Eles podem ser úteis quando o que se quer é uma ação em loop sendo realizada em cada elemento de um conjunto de dados.

      Os loops aninhados são estruturalmente semelhantes às instruções aninhadas if. Eles são construídos desta forma:

      for {
          [Action]
          for {
              [Action]  
          }
      }
      

      Primeiro, o programa encontra o loop externo, executando sua primeira iteração. Essa primeira iteração aciona o loop interno, o loop aninhado, o qual é executado até sua finalização. Em seguida, o programa retorna para o topo do loop externo, finalizando a segunda iteração e acionando novamente o loop aninhado. Novamente, o loop aninhado executa até sua finalização e o programa retorna para o topo do loop externo até a sequência estar completa ou que uma instrução break ou outra interrompa o processo.

      Vamos implementar um loop for aninhado para analisarmos melhor. Neste exemplo, o loop externo irá iterar por uma fatia de inteiros chamada de numList e o loop interno irá iterar por uma fatia de strings chamada alphaList.

      main.go

      package main
      
      import "fmt"
      
      func main() {
          numList := []int{1, 2, 3}
          alphaList := []string{"a", "b", "c"}
      
          for _, i := range numList {
              fmt.Println(i)
              for _, letter := range alphaList {
                  fmt.Println(letter)
              }
          }
      }
      

      Ao executarmos esse programa, vamos receber o seguinte resultado:

      Output

      1 a b c 2 a b c 3 a b c

      O resultado ilustra que o programa termina a primeira iteração do loop externo imprimindo 1, que, por sua vez, aciona a conclusão do loop interno, imprimindo a, b e c, consecutivamente. Assim que o loop interno for finalizado, o programa retorna para o topo do loop externo, imprime o número 2, e imprime novamente o loop interno em sua totalidade (a, b, c) etc.

      Os loops for aninhados podem ser úteis para iterar através de itens dentro de fatias compostas por fatias. Em uma fatia composta por fatias, se usarmos apenas um loop for, o programa dará como resultado cada lista interna como um item:

      main.go

      package main
      
      import "fmt"
      
      func main() {
          ints := [][]int{
              []int{0, 1, 2},
              []int{-1, -2, -3},
              []int{9, 8, 7},
          }
      
          for _, i := range ints {
              fmt.Println(i)
          }
      }
      

      Output

      [0 1 2] [-1 -2 -3] [9 8 7]

      Para acessar cada item individual das fatias internas, implementaremos um loop for aninhado:

      main.go

      package main
      
      import "fmt"
      
      func main() {
          ints := [][]int{
              []int{0, 1, 2},
              []int{-1, -2, -3},
              []int{9, 8, 7},
          }
      
          for _, i := range ints {
              for _, j := range i {
                  fmt.Println(j)
              }
          }
      }
      

      Output

      0 1 2 -1 -2 -3 9 8 7

      Ao usarmos um loop for aninhado aqui, podemos iterar com os itens individuais contidos nas fatias.

      Conclusão

      Neste tutorial, aprendemos a declarar e usar loops for para resolver tarefas repetitivas no Go. Também aprendemos as três diferentes variações de um loop for e quando usá-las. Para aprender mais sobre os loops for e como controlar o fluxo deles, leia o artigo Usando as instruções break e continue ao trabalhar com loops em Go.



      Source link

      Como Construir um Aplicativo Web Moderno para Gerenciar Informações de Clientes com Django e React no Ubuntu 18.04


      O autor selecionou a Open Sourcing Mental Ilness Ltd para receber uma doação como parte do programa Write for DOnations.

      Introdução

      As pessoas usam tipos diferentes de dispositivos para se conectar à internet e navegar pela Web. Por isso, os aplicativos precisam ser acessíveis de uma variedade de locais. Para sites tradicionais, ter uma interface responsiva geralmente é o suficiente, mas aplicativos mais complexos requerem muitas vezes o uso de outras técnicas e arquiteturas. Essas incluem ter aplicativos (separados) REST com back-end e front-end – que podem ser implementados como aplicativos Web do lado do cliente, Progressive Web Applications (PWAs – Aplicativos Progressivos para Web) ou aplicativos móveis nativos.

      Algumas ferramentas que você pode usar ao construir aplicativos mais complexos incluem:

      • O React, um framework JavaScript que permite que os desenvolvedores construam front-ends para Web e nativos para seus back-ends de API REST.
      • O Django, um framework Web gratuito e de código aberto, escrito em Python, que segue o padrão arquitetônico de software Model View Controller (MVC) [Modelo-Visão-Controle].
      • Framework Django REST, um kit de ferramentas eficaz e flexível para a criação de APIs REST no Django.

      Neste tutorial, você irá construir um aplicativo Web moderno com uma API REST separada com back-end e front-end, usando React, Django e o Django REST Framework. Ao usar o React com o Django, você conseguirá se beneficiar dos últimos avanços em JavaScript e desenvolvimento front-end. Em vez de construir um aplicativo Django que utilize um mecanismo de template interno, você usará o React como uma biblioteca de UI, beneficiando-se de sua abordagem informativa virtual Modelo de Documento por Objetos (do inglês Document Object Model – DOM) e de componentes que processam rapidamente alterações nos dados.

      O aplicativo Web que você construirá armazena registros sobre os clientes em um banco de dados, e você pode usá-lo como um ponto de partida para um aplicativo CRM. Quando você terminar, você conseguirá criar, ler, atualizar e excluir registros usando uma interface React estilizada com o Bootstrap 4.

      Pré-requisitos

      Para completar este tutorial, você precisará de:

      Passo 1 — Criando um Ambiente Virtual em Python e Instalando Dependências

      Neste passo, vamos criar um ambiente virtual e instalar as dependências necessárias para nosso aplicativo, incluindo o Django, o Django REST framework, e o django-cors-headers.

      Nosso aplicativo usará dois servidores diferentes para o Django e o React. Eles executarão em portas diferentes e funcionarão como dois domínios separados. Por isso, precisamos habilitar o compartilhamento de recursos com origens diferentes (CORS) para enviar pedidos HTTP do React para o Django sem sermos bloqueado pelo navegador.

      Navegue até seu diretório principal (home) e crie um ambiente virtual usando o módulo do Python 3 venv:

      • cd ~
      • python3 -m venv ./env

      Ative o ambiente virtual criado usando source:

      A seguir, instale as dependências do projeto com o pip. Essas incluem:

      • Django: o framework Web para o projeto.
      • Framework Django REST: um aplicativo de terceiros que constrói APIs REST com o Django.
      • django-cors-headers: um pacote que habilita o CORS.

      Instale o framework Django:

      • pip install django djangorestframework django-cors-headers

      Com as dependências do projeto instaladas, você pode criar o projeto Django e o front-end do React.

      Passo 2 — Criando o Projeto Django

      Neste passo, vamos gerar o projeto Django usando os comandos e utilitários a seguir:

      • **django-admin startproject project-name**: django-admin é um utilitário de linha de comando usado para realizar tarefas com o Django. O comando startproject cria um novo projeto Django.

      • **python manage.py startapp myapp**: manage.py é um script utilitário, adicionado automaticamente a cada projeto Django, que executa uma série de tarefas administrativas: criar novos aplicativos, migrar o banco de dados e atender ao projeto Django localmente. Seu comando startapp cria um aplicativo Django dentro do projeto Django. No Django, o termo aplicativo descreve um pacote Python que fornece alguns recursos em um projeto.

      Para começar, crie o projeto Django com django-admin startproject. Vamos chamar nosso projeto de djangoreactproject:

      • django-admin startproject djangoreactproject

      Antes de continuar, vamos ver a estrutura do diretório do nosso projeto Django usando o comando tree.

      Dica: tree é um comando útil para visualizar estruturas do arquivo e diretório da linha de comando. Você pode instalá-lo com o seguinte comando:

      • sudo apt-get install tree

      Para usá-lo, cd no diretório que você queira e digite tree ou forneça o caminho para o ponto inicial com tree /home/sammy/sammys-project.

      Navegue até a pasta djangoreactproject na raiz do seu projeto e execute o comando tree:

      • cd ~/djangoreactproject
      • tree

      Você verá o seguinte resultado:

      Output

      ├── djangoreactproject │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── manage.py

      A pasta ~/djangoreactproject é a raiz do projeto. Dentro dessa pasta, há vários arquivos que serão importantes para seu trabalho:

      • manage.py: o script utilitário que faz uma série de tarefas administrativas.
      • settings.py: o arquivo de configuração principal para o projeto Django onde você pode modificar as configurações do projeto. Essas configurações incluem variáveis como INSTALLED_APPS, uma lista de strings que designam os aplicativos habilitados para seu projeto. A documentação do Django tem mais informações sobre as configurações disponíveis.
      • urls.py: este arquivo contém uma lista de padrões de URL e visualizações relacionadas. Cada padrão mapeia uma conexão entre um URL e a função que deve ser chamada para aquele URL. Para obter mais informações sobre URLs e visualizações, consulte nosso tutorial em Como Criar Visualizações no Django.

      Nosso primeiro passo no desenvolvimento do projeto será configurar os pacotes que instalamos no passo anterior, incluindo o Django REST framework e o pacote Django CORS, adicionando-os às settings.py. Abra o arquivo com o nano ou com o seu editor favorito:

      • nano ~/djangoreactproject/djangoreactproject/settings.py

      Navegue até a configuração INSTALLED_APPS e adicione os aplicativos rest_framework e corsheaders no final da lista:

      ~/djangoreactproject/djangoreactproject/settings.py

      ...
      INSTALLED_APPS = [
          'django.contrib.admin',
          'django.contrib.auth',
          'django.contrib.contenttypes',
          'django.contrib.sessions',
          'django.contrib.messages',
          'django.contrib.staticfiles',
          'rest_framework',
          'corsheaders'
      ]
      

      A seguir, adicione o middleware corsheaders.middleware.CorsMiddleware do pacote CORS previamente instalado para a configuração do MIDDLEWARE. Esta configuração é uma lista de middlewares, uma classe Python que contém códigos processados cada vez que seu aplicativo Web lida com uma solicitação ou resposta:

      ~/djangoreactproject/djangoreactproject/settings.py

      ...
      
      MIDDLEWARE = [
      ...
      'django.contrib.messages.middleware.MessageMiddleware',
      'django.middleware.clickjacking.XFrameOptionsMiddleware',
      'corsheaders.middleware.CorsMiddleware'
      ]
      

      A seguir, você pode habilitar o CORS. A configuração CORS_ORIGIN_ALLOW_ALL especifica se você quer ou não permitir o CORS para todos os domínios e aCORS_ORIGIN_WHITELISTé uma tupla Python que contém URLs permitidos. No nosso caso, uma vez que o servidor de desenvolvimento para React estará executando em http://lochhost:3000, vamos adicionar as novas configurações CORS_ORIGIN_ALLOW_ALL = False e CORS_ORIGIN_WHHELIST('localhost:3000',) ao nosso arquivo settings.py. Adicione essas configurações em qualquer lugar do arquivo:

      ~/djangoreactproject/djangoreactproject/settings.py

      
      ...
      CORS_ORIGIN_ALLOW_ALL = False
      
      CORS_ORIGIN_WHITELIST = (
             'localhost:3000',
      )
      ...
      

      Você pode encontrar mais opções de configuração nos django-cors-headers docs.

      Salve o arquivo e saia do editor quando você terminar.

      Ainda no diretório ~/djangoreactproject, faça um novo aplicativo Django chamado customers:

      • python manage.py startapp customers

      Isso irá conter os modelos e exibições para gerenciar clientes. Modelos (models) definem os campos e comportamentos dos dados do aplicativo, enquanto exibições (views) habilitam nosso aplicativo para lidar corretamente com solicitações Web e retornar as respostas necessárias.

      A seguir, adicione este aplicativo na lista de aplicativos instalados no arquivo do seu projeto settings.py para que o Django o reconheça como parte do projeto. Abra as settings.py novamente:

      • nano ~/djangoreactproject/djangoreactproject/settings.py

      Adicione o aplicativo customers:

      ~/djangoreactproject/djangoreactproject/settings.py

      ...
      INSTALLED_APPS = [
          ...
          'rest_framework',
          'corsheaders',
          'customers'
      ]
      ...
      

      A seguir, migre o banco de dados e inicie o servidor local de desenvolvimento. Migrações consistem no modo que o Django tem de propagar as alterações que você faz nos modelos do esquema do seu banco de dados. Essas alterações podem incluir coisas como adicionar um campo ou excluir um modelo, por exemplo. Para saber mais sobre modelos e migrações, consulte o tópico Como Criar Modelos do Django.

      Migre o banco de dados:

      Inicie o servidor local de desenvolvimento:

      • python manage.py runserver

      Você verá um resultado similar ao seguinte:

      Output

      Performing system checks... System check identified no issues (0 silenced). October 22, 2018 - 15:14:50 Django version 2.1.2, using settings 'djangoreactproject.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.

      Seu aplicativo Web estará executando do endereço http://127.0.0.1:8000. Se você navegar até este endereço no seu navegador Web você verá a seguinte página:

      Django demo page

      Neste ponto, deixe o aplicativo funcionando e abra um novo terminal para continuar desenvolvendo o projeto.

      Passo 3 — Criando a Front-end React

      Nesta seção, vamos criar o aplicativo com front-end do nosso projeto usando o React.

      O React tem um utilitário oficial que permite que você crie rapidamente projetos React sem ter que configurar o Webpack diretamente. Webpack é um empacotador de módulos usado para empacotar ativos Web como o código JavaScript, CSS e imagens. Normalmente, antes de poder usar o Webpack você precisa definir várias opções de configuração, mas graças ao utilitário create-react-app você não precisa lidar com o Webpack diretamente até decidir que você precisa de mais controle. Para executar o create-react-app você pode usar o npx, uma ferramenta que executa pacotes npm binários.

      No seu segundo terminal, certifique-se de estar em seu diretório de projeto:

      Crie um projeto React chamado frontend usando o create-react-app e o npx:

      • npx create-react-app frontend

      A seguir, navegue dentro do seu aplicativo React e inicie o servidor de desenvolvimento:

      • cd ~/djangoreactproject/frontend
      • npm start

      Seu aplicativo estará executando a partir do endereço http://localhost:3000/:

      React demo page

      Deixe o servidor React de desenvolvimento funcionando e abra outra janela do terminal para prosseguir.

      Para ver a estrutura do diretório do projeto inteiro até o momento, navegue até a pasta raiz e execute novamente o tree:

      • cd ~/djangoreactproject
      • tree

      Você verá uma estrutura como essa:

      Output

      ├── customers │ ├── admin.py │ ├── apps.py │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── djangoreactproject │ ├── __init__.py │ ├── __pycache__ │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── frontend │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ ├── README.md │ ├── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ └── registerServiceWorker.js │ └── yarn.lock └── manage.py

      Nosso aplicativo usará o Bootstrap 4 para estilizar a interface do React. Assim,nós o incluiremos no arquivo frontend/src/App.css, que gerencia nossas configurações CSS. Abra o arquivo:

      • nano ~/djangoreactproject/frontend/src/App.css

      Adicione o seguinte import ao início do arquivo. Você pode excluir o conteúdo do arquivo existente, embora isso não seja necessário:

      ~/djangoreactproject/frontend/src/App.css

      @import  'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css';
      

      Aqui, @import é uma instrução CSS que é usada para importar regras de estilo de outras folhas de estilo.

      Agora que criamos os aplicativos com back-end e front-end, vamos criar o modelo Customer e alguns dados demonstrativos.

      Passo 4 — Criando o Modelo de Cliente e Dados Iniciais

      Após criar o aplicativo Django e o front-end React, nosso próximo passo será criar o modelo Customer, que representa a tabela do banco de dados que irá reter informações sobre os clientes. Você não precisa de nenhuma SQL (Structured Query Language, ou Linguagem de Consulta Estruturada), uma vez que o Object Relational Mapper (ORM) [Mapeamento Objeto-Relacional] do Django gerenciará as operações de banco de dados, mapeando as classes e variáveis em Python até as tabelas e colunas em SQL. Desta forma, o ORM do Django separa as interações em SQL com o banco de dados através de uma interface em Python.

      Ative seu ambiente virtual novamente:

      • cd ~
      • source env/bin/activate

      Vá até o diretório customers e abra o models.py, um arquivo Python que possui os modelos do seu aplicativo:

      • cd ~/djangoreactproject/customers/
      • nano models.py

      O arquivo terá o seguinte conteúdo:

      ~/djangoreactproject/customers/models.py

      from django.db import models
      # Create your models here.
      

      A API do modelo Customer já foi importada para o arquivo, graças à declaração de importação from django.db import models. Agora, você adicionará a classe Customer, que estende models.Model. Cada modelo no Django é uma classe em Python que estende o django.db.models.Model.

      O modelo Customer terá esses campos de banco de dados:

      • first_name — O primeiro nome do cliente.
      • last_name — O sobrenome do cliente.
      • email — O endereço de e-mail do cliente.
      • phone — O número de telefone do cliente.
      • address — O endereço do cliente.
      • description — A descrição do cliente.
      • createdAt — A data em que o cliente é adicionado.

      Também adicionaremos a função __str__(), que define como o modelo será exibido. No nosso caso, ela estará com o primeiro nome do cliente. Para saber mais sobre a construção de classes e definição de objetos, consulte o tópico pelo link How To Construct Classes and Define Objects in Python 3.

      Adicione o código a seguir ao arquivo:

      ~/djangoreactproject/customers/models.py

      from django.db import models
      
      class Customer(models.Model):
          first_name = models.CharField("First name", max_length=255)
          last_name = models.CharField("Last name", max_length=255)
          email = models.EmailField()
          phone = models.CharField(max_length=20)
          address =  models.TextField(blank=True, null=True)
          description = models.TextField(blank=True, null=True)
          createdAt = models.DateTimeField("Created At", auto_now_add=True)
      
          def __str__(self):
              return self.first_name
      

      A seguir, migre o banco de dados para criar as tabelas de banco de dados. O comando makemigrations cria os arquivos de migração onde as alterações do modelo serão adicionadas e o comando migrate aplica as alterações feitas nos arquivos de migrações ao banco de dados.

      Navegue novamente para a pasta raiz do projeto:

      Execute o que vem a seguir para criar os arquivos de migração:

      • python manage.py makemigrations

      Você receberá um resultado que se parece com este:

      Output

      customers/migrations/0001_initial.py - Create model Customer

      Aplique essas alterações ao banco de dados:

      Você verá o resultado indicando uma migração bem-sucedida:

      Output

      Operations to perform: Apply all migrations: admin, auth, contenttypes, customers, sessions Running migrations: Applying customers.0001_initial... OK

      Depois, você usará um arquivo de migração de dados para criar os dados iniciais do cliente. Um arquivo de migração de dados é uma migração que adiciona ou altera dados no banco de dados. Crie um arquivo de migração de dados vazio para o aplicativo customers:

      • python manage.py makemigrations --empty --name customers customers

      Você verá a seguinte confirmação com o nome do seu arquivo de migração:

      Output

      Migrations for 'customers': customers/migrations/0002_customers.py

      Note que o nome do seu arquivo de migração é 0002_customers.py.

      Na sequência, navegue dentro da pasta de migração do aplicativo customers:

      • cd ~/djangoreactproject/customers/migrations

      Abra o arquivo de migração criado:

      Este é o conteúdo inicial do arquivo:

      ~/djangoreactproject/customers/migrations/0002_customers.py

      from django.db import migrations
      
      class Migration(migrations.Migration):
          dependencies = [
              ('customers', '0001_initial'),
          ]
          operations = [
          ]        
      

      A instrução de importação importa a API migrations, uma API Django para criação de migrações, do django.db, um pacote integrado que contém classes para trabalhar com bancos de dados.

      A classe Migration é uma classe em Python que descreve as operações que são executadas durante a migração de bancos de dados. Esta classe estende migrations.Migration e tem duas listas:

      • dependencies: contém as migrações dependentes.
      • operations: contém as operações que serão executadas quando aplicarmos a migração.

      Na sequência, adicione um method para criar os dados de cliente da demonstração. Adicione o seguinte método antes da definição da classe Migration:

      ~/djangoreactproject/customers/migrations/0002_customers.py

      ...
      def create_data(apps, schema_editor):
          Customer = apps.get_model('customers', 'Customer')
          Customer(first_name="Customer 001", last_name="Customer 001", email="customer001@email.com", phone="00000000", address="Customer 000 Address", description= "Customer 001 description").save()
      
      ...
      

      Neste método, estamos pegando a classe Customer do nosso app customers e criando um cliente de demonstração para inserir no banco de dados.

      Para obter a classe Customer, que irá habilitar a criação de novos clientes, usamos o método get_model() do objeto apps. O objeto apps representa o registry dos aplicativos instalados e seus modelos de banco de dados.

      O objeto apps passará do método RunPython() quando o usamos para executar o create_data(). Adicione o método migrations.RunPython() à lista operations vazia:

      ~/djangoreactproject/customers/migrations/0002_customers.py

      
      ...
          operations = [
              migrations.RunPython(create_data),
          ]  
      

      O RunPython() faz parte da API com Migrations que permite que você execute códigos em Python personalizados em uma migração. Nossa lista operations especifica que este método será executado quando aplicarmos a migração.

      Este é o arquivo completo:

      ~/djangoreactproject/customers/migrations/0002_customers.py

      from django.db import migrations
      
      def create_data(apps, schema_editor):
          Customer = apps.get_model('customers', 'Customer')
          Customer(first_name="Customer 001", last_name="Customer 001", email="customer001@email.com", phone="00000000", address="Customer 000 Address", description= "Customer 001 description").save()
      
      class Migration(migrations.Migration):
          dependencies = [
              ('customers', '0001_initial'),
          ]
          operations = [
              migrations.RunPython(create_data),
          ]        
      

      Para obter mais informações sobre a migração de dados, consulte a documentação sobre migrações de dados no Django.

      Para migrar seu banco de dados, navegue primeiro de volta para a pasta raiz do seu projeto:

      Migre o seu banco de dados para criar os dados para demonstração:

      Você verá o resultado que confirma a migração:

      Output

      Operations to perform: Apply all migrations: admin, auth, contenttypes, customers, sessions Running migrations: Applying customers.0002_customers... OK

      Para obter mais detalhes sobre este processo, volte para o link How To Create Django Models.

      Com o modelo Customer e dados de demonstração criados, podemos continuar para a construção da API REST.

      Passo 5 — Criando a API REST

      Neste passo, vamos criar a API REST utilizando o Django REST Framework. Vamos criar várias *visualizações da API *diferentes. Uma visualização de API é uma função que lida com um pedido ou chamada de API, enquanto um *ponto de extremidade de API *é um URL único que representa um ponto de contato com o sistema REST. Por exemplo, quando o usuário envia um pedido GET para um ponto de extremidade de API, o Django chama a função correspondente ou a visualização de API para lidar com o pedido e retornar quaisquer resultados possíveis.

      Também vamos usar os serializers. Um serializador no Django REST Framework permite que as instâncias de modelos complexas e o QuerySets sejam convertidos em formato JSON para consumo de API. A classe de serializadores também pode funcionar na outra direção, fornecendo mecanismos para processar e desserializar dados em modelos Django e QuerySets.

      Nossos pontos de extremidade de API irão incluir:

      • api/customers: este ponto de extremidade é usado para criar clientes e retorna conjuntos de clientes paginados.
      • api/customers/<pk>: este ponto de extremidade é usado para obter, atualizar e excluir clientes únicos por chave ou ID primária.

      Também vamos criar URLs no arquivo urls.py do projeto para os pontos de extremidade correspondentes (ou seja, api/customers e <pk>).

      Vamos começar criando a classe de serializadores para nosso modelo Customer.

      Adicionando a Classe de Serializadores

      Criar uma classe de serializadores para nosso modelo Customer é necessário para transformar as instâncias de cliente e os QuerySets de e para JSON. Para criar a classe de serializadores, faça primeiro um arquivo serializers.py dentro do aplicativo customers:

      • cd ~/djangoreactproject/customers/
      • nano serializers.py

      Adicione o seguinte código para importar o API do serializador e o modelo Customer:

      ~/djangoreactproject/customers/serializers.py

      from rest_framework import serializers
      from .models import Customer
      

      A seguir, crie uma classe de serializadores que estende serializers.ModelSerializer e especifica os campos que serão serializados:

      ~/djangoreactproject/customers/serializers.py

      
      ...
      class CustomerSerializer(serializers.ModelSerializer):
      
          class Meta:
              model = Customer 
              fields = ('pk','first_name', 'last_name', 'email', 'phone','address','description')
      

      A classe Meta especifica o modelo e os campos para serializar: pk, first_name, last_name, email, phone, address, description.

      Este é o conteúdo completo do arquivo:

      ~/djangoreactproject/customers/serializers.py

      from rest_framework import serializers
      from .models import Customer
      
      class CustomerSerializer(serializers.ModelSerializer):
      
          class Meta:
              model = Customer 
              fields = ('pk','first_name', 'last_name', 'email', 'phone','address','description')
      

      Agora que criamos nossa classe de serializadores, podemos adicionar as visualizações da API.

      Adicionando as Visualizações da API

      Nesta seção, vamos criar as visualizações da API para nosso aplicativo que serão chamadas pelo Django quando o usuário visitar o ponto de extremidade correspondente à função da visualização.

      Abra ~/djangoreactproject/customers/views.py:

      • nano ~/djangoreactproject/customers/views.py

      Exclua o que estiver ali e adicione as seguintes importações:

      ~/djangoreactproject/customers/views.py

      from rest_framework.response import Response
      from rest_framework.decorators import api_view
      from rest_framework import status
      
      from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
      from .models import Customer 
      from .serializers import *
      

      Estamos importando o serializador que criamos, junto com o modelo Customer e as APIs com o Django e o Django REST Framework.

      Em seguida, adicione a visualização para processar os pedidos do POST e GET HTTP:

      ~/djangoreactproject/customers/views.py

      ...
      
      @api_view(['GET', 'POST'])
      def customers_list(request):
          """
       List  customers, or create a new customer.
       """
          if request.method == 'GET':
              data = []
              nextPage = 1
              previousPage = 1
              customers = Customer.objects.all()
              page = request.GET.get('page', 1)
              paginator = Paginator(customers, 10)
              try:
                  data = paginator.page(page)
              except PageNotAnInteger:
                  data = paginator.page(1)
              except EmptyPage:
                  data = paginator.page(paginator.num_pages)
      
              serializer = CustomerSerializer(data,context={'request': request} ,many=True)
              if data.has_next():
                  nextPage = data.next_page_number()
              if data.has_previous():
                  previousPage = data.previous_page_number()
      
              return Response({'data': serializer.data , 'count': paginator.count, 'numpages' : paginator.num_pages, 'nextlink': '/api/customers/?page=' + str(nextPage), 'prevlink': '/api/customers/?page=' + str(previousPage)})
      
          elif request.method == 'POST':
              serializer = CustomerSerializer(data=request.data)
              if serializer.is_valid():
                  serializer.save()
                  return Response(serializer.data, status=status.HTTP_201_CREATED)
              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
      

      Primeiramente, usamos o decorador @api_view(['GET', 'POST']) para criar uma visualização de API que possa aceitar solicitações GET e POST. Um decorator é uma função que assume outra função e a amplia de maneira dinâmica.

      No corpo do método, utilizamos a variável request.method para verificar o método HTTP atual e executar a lógica correspondente, dependendo do tipo de solicitação:

      • Se for uma solicitação GET, o método pagina os dados utilizando o Paginator do Django e retorna a primeira página de dados após a serialização, a contagem de clientes disponíveis, o número de páginas disponíveis, e os links para as páginas anteriores e as páginas posteriores. O Paginator é uma classe integrada do Django que organiza uma lista de dados em páginas e proporciona métodos para acessar os itens de cada página.
      • Se for uma solicitação POST, o método serializa os dados recebidos do cliente e chama então o método save() do objeto serializador. Então, ele retorna um objeto de Resposta, uma instância do HttpResponse, com um código de status 201. Cada visualização que você cria é responsável por retornar um objeto HttpResponse. O método save() salva os dados serializados no banco de dados.

      Para saber mais sobre HttpResponse e visualizações, leia esta discussão: creating view functions.

      Agora, adicione a visualização de API que será responsável por processar os pedidos GET, PUT e DELETE para obter, atualizar e excluir clientes por pk (chave primária):

      ~/djangoreactproject/customers/views.py

      
      ...
      @api_view(['GET', 'PUT', 'DELETE'])
      def customers_detail(request, pk):
       """
       Retrieve, update or delete a customer by id/pk.
       """
          try:
              customer = Customer.objects.get(pk=pk)
          except Customer.DoesNotExist:
              return Response(status=status.HTTP_404_NOT_FOUND)
      
          if request.method == 'GET':
              serializer = CustomerSerializer(customer,context={'request': request})
              return Response(serializer.data)
      
          elif request.method == 'PUT':
              serializer = CustomerSerializer(customer, data=request.data,context={'request': request})
              if serializer.is_valid():
                  serializer.save()
                  return Response(serializer.data)
              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
      
          elif request.method == 'DELETE':
              customer.delete()
              return Response(status=status.HTTP_204_NO_CONTENT)
      

      O método é decorado com @api_view(['GET', 'PUT', 'DELETE']) para indicara que se trata de uma visualização de API que pode aceitar solicitações GET, PUT e DELETE.

      A checagem no campo request.method verifica o método de solicitação e, dependendo do seu valor, chama a lógica correta:

      • Se for um pedido GET, os dados do cliente são serializados e enviados utilizando um objeto de Resposta.
      • Se for um pedido PUT, o método cria um serializador para novos dados do cliente. Em seguida, ele chama o método save() do objeto serializador criado. Finalmente, ele envia um objeto de Resposta com o cliente atualizado.
      • Se for um pedido DELETE, o método chama o método delete() do objeto cliente a excluir; depois, retorna um objeto de Resposta sem dados.

      O arquivo final se parece com este:

      ~/djangoreactproject/customers/views.py

      from rest_framework.response import Response
      from rest_framework.decorators import api_view
      from rest_framework import status
      
      from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
      from .models import Customer 
      from .serializers import *
      
      
      @api_view(['GET', 'POST'])
      def customers_list(request):
          """
       List  customers, or create a new customer.
       """
          if request.method == 'GET':
              data = []
              nextPage = 1
              previousPage = 1
              customers = Customer.objects.all()
              page = request.GET.get('page', 1)
              paginator = Paginator(customers, 5)
              try:
                  data = paginator.page(page)
              except PageNotAnInteger:
                  data = paginator.page(1)
              except EmptyPage:
                  data = paginator.page(paginator.num_pages)
      
              serializer = CustomerSerializer(data,context={'request': request} ,many=True)
              if data.has_next():
                  nextPage = data.next_page_number()
              if data.has_previous():
                  previousPage = data.previous_page_number()
      
              return Response({'data': serializer.data , 'count': paginator.count, 'numpages' : paginator.num_pages, 'nextlink': '/api/customers/?page=' + str(nextPage), 'prevlink': '/api/customers/?page=' + str(previousPage)})
      
          elif request.method == 'POST':
              serializer = CustomerSerializer(data=request.data)
              if serializer.is_valid():
                  serializer.save()
                  return Response(serializer.data, status=status.HTTP_201_CREATED)
              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
      
      @api_view(['GET', 'PUT', 'DELETE'])
      def customers_detail(request, pk):
          """
       Retrieve, update or delete a customer by id/pk.
       """
          try:
              customer = Customer.objects.get(pk=pk)
          except Customer.DoesNotExist:
              return Response(status=status.HTTP_404_NOT_FOUND)
      
          if request.method == 'GET':
              serializer = CustomerSerializer(customer,context={'request': request})
              return Response(serializer.data)
      
          elif request.method == 'PUT':
              serializer = CustomerSerializer(customer, data=request.data,context={'request': request})
              if serializer.is_valid():
                  serializer.save()
                  return Response(serializer.data)
              return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
      
          elif request.method == 'DELETE':
              customer.delete()
              return Response(status=status.HTTP_204_NO_CONTENT)
      

      Agora, podemos seguir em frente para criar nossos pontos de extremidade.

      Adicionando Pontos de extremidade de API

      Agora, vamos criar os pontos de extremidade de API: api/customers/, para consultar e criar clientes e api/customers/<pk>, para obter, atualizar ou excluir clientes únicos por pk.

      Abra ~/djangoreactproject/djangoreactproject/urls.py:

      • nano ~/djangoreactproject/djangoreactproject/urls.py

      Deixe o que está lá, mas adicione a importação às visualizações do customers no topo do arquivo:

      ~/djangoreactproject/djangoreactproject/urls.py

      from django.contrib import admin
      from django.urls import path
      from customers import views
      from django.conf.urls import url
      

      Em seguida, adicione os URLs api/customers/ e api/customers/<pk> à lista urlpatterns que contém os URLs do aplicativo:

      ~/djangoreactproject/djangoreactproject/urls.py

      ...
      
      urlpatterns = [
          path('admin/', admin.site.urls),
          url(r'^api/customers/$', views.customers_list),
          url(r'^api/customers/(?P<pk>[0-9]+)$', views.customers_detail),
      ]
      

      Com nossos pontos de extremidade de REST criados, vamos ver como podemos consumi-los.

      Neste passo, vamos instalar o Axios, o cliente HTTP que vamos usar para fazer chamadas de API. Também vamos criar uma classe para consumir os pontos de extremidade da API que criamos.

      Primeiramente, desative o seu ambiente virtual:

      Em seguida, navegue até sua pasta frontend:

      • cd ~/djangoreactproject/frontend

      Instale o axios a partir do npm utilizando:

      A opção --save adiciona a dependência do axios ao arquivo package.json do seu aplicativo.

      Em seguida, crie um arquivo JavaScript chamado CustomersService.js, que irá conter o código para chamar as APIs REST. Vamos fazer isso dentro da pasta src, onde o código do aplicativo para nosso projeto irá viver:

      • cd src
      • nano CustomersService.js

      Adicione o seguinte código, que contém métodos para se conectar à API REST do Django:

      ~/djangoreactproject/frontend/src/CustomersService.js

      import axios from 'axios';
      const API_URL = 'http://localhost:8000';
      
      export default class CustomersService{
      
          constructor(){}
      
      
          getCustomers() {
              const url = `${API_URL}/api/customers/`;
              return axios.get(url).then(response => response.data);
          }  
          getCustomersByURL(link){
              const url = `${API_URL}${link}`;
              return axios.get(url).then(response => response.data);
          }
          getCustomer(pk) {
              const url = `${API_URL}/api/customers/${pk}`;
              return axios.get(url).then(response => response.data);
          }
          deleteCustomer(customer){
              const url = `${API_URL}/api/customers/${customer.pk}`;
              return axios.delete(url);
          }
          createCustomer(customer){
              const url = `${API_URL}/api/customers/`;
              return axios.post(url,customer);
          }
          updateCustomer(customer){
              const url = `${API_URL}/api/customers/${customer.pk}`;
              return axios.put(url,customer);
          }
      }
      

      A classe CustomersService irá chamar os seguintes métodos do Axios:

      • getCustomers(): obtém a primeira página de clientes.
      • getCustomersByURL(): obtém clientes por URL. Isso possibilita obter-se as próximas páginas de clientes, atravessando-se links do tipo /api/customers/?page=2.
      • getCustomer(): obtém um cliente pela chave primária.
      • createCustomer(): cria um cliente.
      • updateCustomer(): atualiza um cliente.
      • deleteCustomer(): exclui um cliente.

      Agora, podemos exibir os dados de nossa API na nossa interface com a UI React criando um componente CustomersList.

      Passo 7 — Exibindo Dados da API no Aplicativo React

      Neste passo, vamos criar o componente do aplicativo React chamado CustomersList. Um componente do React representa uma parte da UI; ele também permite que você divida a UI em pedaços independentes e reutilizáveis.

      Inicie criando o CustomersList.js em frontend/src:

      • nano ~/djangoreactproject/frontend/src/CustomersList.js

      Inicie importando o React e o Component para criar um componente do React:

      ~/djangoreactproject/frontend/src/CustomersList.js

      import  React, { Component } from  'react';
      

      Em seguida, importe e crie a instância do módulo CustomersService que você criou no passo anterior, que proporciona métodos que interagem com o back-end da API REST:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      import  CustomersService  from  './CustomersService';
      
      const  customersService  =  new  CustomersService();
      

      Em seguida, crie um componente CustomersList que estende o Component para chamar a API REST. Um componente React deve estender ou subclassificar a classe Component. Para saber mais sobre as classes E6 e herança, consulte nossos tutorial Understanding Classes in JavaScript.

      Adicione o seguinte código para criar um componente do React que estende o react.Component:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      class  CustomersList  extends  Component {
      
          constructor(props) {
              super(props);
              this.state  = {
                  customers: [],
                  nextPageURL:  ''
              };
              this.nextPage  =  this.nextPage.bind(this);
              this.handleDelete  =  this.handleDelete.bind(this);
          }
      }
      export  default  CustomersList;
      

      Dentro do construtor, estamos inicializando o objeto de ](https://reactjs.org/docs/react-component.html#state)estado[. Isso mantém as variáveis de estado do nosso componente utilizando uma matriz de clientes vazia. Esta matriz conterá os clientes e um nextPageURL que irá reter o URL da próxima página a ser recuperada do back-end da API. Também estamos ligando os métodos nextPage() e o handleDelete() a este para que eles fiquem acessíveis a partir do código HTML.

      Em seguida, adicione o método componentDidMount() e uma chamada para o getCustomers() dentro da classe CustomersList, antes da chave de fechamento.

      O método componentDidMount() é um método de ciclo de vida do componente que é chamado quando o componente é criado e inserido no DOM. O getCustomers() chama o objeto Customers Service para obter a primeira página de dados e o link da página seguinte a partir do back-end do Django:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      componentDidMount() {
          var  self  =  this;
          customersService.getCustomers().then(function (result) {
              self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
          });
      }
      

      Agora, adicione o método handleDelete() que lida com a exclusão de um cliente, abaixo do componentDidMount():

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      handleDelete(e,pk){
          var  self  =  this;
          customersService.deleteCustomer({pk :  pk}).then(()=>{
              var  newArr  =  self.state.customers.filter(function(obj) {
                  return  obj.pk  !==  pk;
              });
              self.setState({customers:  newArr})
          });
      }
      

      O método handleDelete() chama o método deleteCustomer() para excluir um cliente utilizando sua pk (chave primária). Se a operação for bem-sucedida, a matriz de customers é filtrada em relação ao cliente removido.

      Em seguida, adicione um método nextPage() para obter os dados da próxima página e atualize o próximo link da página:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      nextPage(){
          var  self  =  this;
          customersService.getCustomersByURL(this.state.nextPageURL).then((result) => {
              self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
          });
      }
      

      O método nextPage() chama um método getCustomersByURL(), que recebe o próximo URL da página do objeto de estado, this.state.nextPageURL​​​ e atualiza a matriz de customers com os dados retornados.

      Finalmente, adicione o método render() do componente, que renderiza uma tabela de clientes a partir do estado do componente:

      ~/djangoreactproject/frontend/src/CustomersList.js

      
      ...
      render() {
      
          return (
          <div  className="customers--list">
              <table  className="table">
                  <thead  key="thead">
                  <tr>
                      <th>#</th>
                      <th>First Name</th>
                      <th>Last Name</th>
                      <th>Phone</th>
                      <th>Email</th>
                      <th>Address</th>
                      <th>Description</th>
                      <th>Actions</th>
                  </tr>
                  </thead>
                  <tbody>
                      {this.state.customers.map( c  =>
                      <tr  key={c.pk}>
                          <td>{c.pk}  </td>
                          <td>{c.first_name}</td>
                          <td>{c.last_name}</td>
                          <td>{c.phone}</td>
                          <td>{c.email}</td>
                          <td>{c.address}</td>
                          <td>{c.description}</td>
                          <td>
                          <button  onClick={(e)=>  this.handleDelete(e,c.pk) }> Delete</button>
                          <a  href={"/customer/" + c.pk}> Update</a>
                          </td>
                      </tr>)}
                  </tbody>
              </table>
              <button  className="btn btn-primary"  onClick=  {  this.nextPage  }>Next</button>
          </div>
          );
      }
      

      Este é o conteúdo completo do arquivo:

      ~/djangoreactproject/frontend/src/CustomersList.js

      import  React, { Component } from  'react';
      import  CustomersService  from  './CustomersService';
      
      const  customersService  =  new  CustomersService();
      
      class  CustomersList  extends  Component {
      
      constructor(props) {
          super(props);
          this.state  = {
              customers: [],
              nextPageURL:  ''
          };
          this.nextPage  =  this.nextPage.bind(this);
          this.handleDelete  =  this.handleDelete.bind(this);
      }
      
      componentDidMount() {
          var  self  =  this;
          customersService.getCustomers().then(function (result) {
              console.log(result);
              self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
          });
      }
      handleDelete(e,pk){
          var  self  =  this;
          customersService.deleteCustomer({pk :  pk}).then(()=>{
              var  newArr  =  self.state.customers.filter(function(obj) {
                  return  obj.pk  !==  pk;
              });
      
              self.setState({customers:  newArr})
          });
      }
      
      nextPage(){
          var  self  =  this;
          console.log(this.state.nextPageURL);        
          customersService.getCustomersByURL(this.state.nextPageURL).then((result) => {
              self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
          });
      }
      render() {
      
          return (
              <div  className="customers--list">
                  <table  className="table">
                  <thead  key="thead">
                  <tr>
                      <th>#</th>
                      <th>First Name</th>
                      <th>Last Name</th>
                      <th>Phone</th>
                      <th>Email</th>
                      <th>Address</th>
                      <th>Description</th>
                      <th>Actions</th>
                  </tr>
                  </thead>
                  <tbody>
                  {this.state.customers.map( c  =>
                      <tr  key={c.pk}>
                      <td>{c.pk}  </td>
                      <td>{c.first_name}</td>
                      <td>{c.last_name}</td>
                      <td>{c.phone}</td>
                      <td>{c.email}</td>
                      <td>{c.address}</td>
                      <td>{c.description}</td>
                      <td>
                      <button  onClick={(e)=>  this.handleDelete(e,c.pk) }> Delete</button>
                      <a  href={"/customer/" + c.pk}> Update</a>
                      </td>
                  </tr>)}
                  </tbody>
                  </table>
                  <button  className="btn btn-primary"  onClick=  {  this.nextPage  }>Next</button>
              </div>
              );
        }
      }
      export  default  CustomersList;
      

      Agora que criamos o componente CustomersList para exibir a lista de clientes, podemos adicionar o componente que lida com a criação e as atualizações do cliente.

      Passo 8 — Adicionando os Componentes Customer Create e Update do React

      Neste passo, vamos criar o componente CustomerCreateUpdate que irá lidar com a criação e atualização dos clientes. Ele irá fazer isso fornecendo um formulário que os usuários podem usar para digitar dados sobre um novo cliente ou atualizar um item existente.

      No frontend/src, crie um arquivo CustomerCreateUpdate.js:

      • nano ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      Adicione o seguinte código para criar um componente do React, import o React e o Component:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      import  React, { Component } from  'react';
      

      Além disso, podemos importar e instanciar a classe CustomersService que criamos no passo anterior, a qual proporciona métodos que interagem com o back-end da API REST:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      ...
      import  CustomersService  from  './CustomersService';
      
      const  customersService  =  new  CustomersService();
      

      Em seguida, crie um componente CustomerCreateUpdate que estende o Component para criar e atualizar os clientes:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      class  CustomerCreateUpdate  extends  Component {
      
          constructor(props) {
              super(props);
          }
      
      }
      export default CustomerCreateUpdate;
      

      Dentro da definição da classe, adicione o método render() do componente, que renderiza uma forma HTML que recebe informações sobre o cliente:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      render() {
              return (
                <form onSubmit={this.handleSubmit}>
                <div className="form-group">
                  <label>
                    First Name:</label>
                    <input className="form-control" type="text" ref='firstName' />
      
                  <label>
                    Last Name:</label>
                    <input className="form-control" type="text" ref='lastName'/>
      
                  <label>
                    Phone:</label>
                    <input className="form-control" type="text" ref='phone' />
      
                  <label>
                    Email:</label>
                    <input className="form-control" type="text" ref='email' />
      
                  <label>
                    Address:</label>
                    <input className="form-control" type="text" ref='address' />
      
                  <label>
                    Description:</label>
                    <textarea className="form-control" ref='description' ></textarea>
      
      
                  <input className="btn btn-primary" type="submit" value="Submit" />
                  </div>
                </form>
              );
        }
      

      Para cada elemento form input, o método adiciona uma propriedade ref para acessar e definir o valor do elemento do formulário.

      Em seguida, acima do método render(), defina um método handleSubmit(event) para que você tenha a funcionalidade apropriada quando um usuário clicar no botão para enviar:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      handleSubmit(event) {
          const { match: { params } } =  this.props;
          if(params  &&  params.pk){
              this.handleUpdate(params.pk);
          }
          else
          {
              this.handleCreate();
          }
          event.preventDefault();
      }
      
      ...
      

      O método handleSubmit(event) cuida do envio do formulário e, dependendo da rota, chama o método handleUpdate(pk) para atualizar o cliente com o método pk aprovado ou o método handleCreate() para criar um novo cliente. Vamos definir esses métodos em breve.

      De volta ao construtor do componente, conecte o método handleSubmit() recém-adicionado a este, para que você possa acessá-lo em seu formulário:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      ...
      class CustomerCreateUpdate extends Component {
      
      constructor(props) {
          super(props);
          this.handleSubmit = this.handleSubmit.bind(this);
      }
      ...
      

      Em seguida, defina o método handleCreate() para criar um cliente a partir dos dados do formulário. Acima do método handleSubmit(event) adicione o seguinte código:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      handleCreate(){
          customersService.createCustomer(
              {
              "first_name":  this.refs.firstName.value,
              "last_name":  this.refs.lastName.value,
              "email":  this.refs.email.value,
              "phone":  this.refs.phone.value,
              "address":  this.refs.address.value,
              "description":  this.refs.description.value
              }).then((result)=>{
                      alert("Customer created!");
              }).catch(()=>{
                      alert('There was an error! Please re-check your form.');
              });
      }
      
      ...
      

      O método handleCreate() será usado para criar um cliente a partir dos dados inseridos. Ele chama o método CustomersService.createCustomer() correspondente que faz a API real chamar o back-end para criar um cliente.

      Depois, abaixo do método handleCreate(), defina o método handleUpdate(pk) para implementar as atualizações:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      handleUpdate(pk){
      customersService.updateCustomer(
          {
          "pk":  pk,
          "first_name":  this.refs.firstName.value,
          "last_name":  this.refs.lastName.value,
          "email":  this.refs.email.value,
          "phone":  this.refs.phone.value,
          "address":  this.refs.address.value,
          "description":  this.refs.description.value
          }
          ).then((result)=>{
      
              alert("Customer updated!");
          }).catch(()=>{
              alert('There was an error! Please re-check your form.');
          });
      }
      

      O método updateCustomer() irá atualizar um cliente através da pk utilizando as novas informações do formulário de informações do cliente. Ele chama o método customersService.updateCustomer().

      Em seguida, adicione um método componentDidMount(). Se o usuário visitar uma rota customer/:pk, queremos preencher o formulário com informações relacionadas ao cliente, utilizando a chave primária do URL. Para fazer isso, podemos adicionar o método getCustomer(pk) após o componente ser montado no evento do ciclo de vida do componentDidMount(). Adicione o seguinte código abaixo do construtor do componente para adicionar este método:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      
      ...
      componentDidMount(){
          const { match: { params } } =  this.props;
          if(params  &&  params.pk)
          {
              customersService.getCustomer(params.pk).then((c)=>{
                  this.refs.firstName.value  =  c.first_name;
                  this.refs.lastName.value  =  c.last_name;
                  this.refs.email.value  =  c.email;
                  this.refs.phone.value  =  c.phone;
                  this.refs.address.value  =  c.address;
                  this.refs.description.value  =  c.description;
              })
          }
      }
      

      Este é o conteúdo completo do arquivo:

      ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

      import React, { Component } from 'react';
      import CustomersService from './CustomersService';
      
      const customersService = new CustomersService();
      
      class CustomerCreateUpdate extends Component {
          constructor(props) {
              super(props);
      
              this.handleSubmit = this.handleSubmit.bind(this);
            }
      
            componentDidMount(){
              const { match: { params } } = this.props;
              if(params && params.pk)
              {
                customersService.getCustomer(params.pk).then((c)=>{
                  this.refs.firstName.value = c.first_name;
                  this.refs.lastName.value = c.last_name;
                  this.refs.email.value = c.email;
                  this.refs.phone.value = c.phone;
                  this.refs.address.value = c.address;
                  this.refs.description.value = c.description;
                })
              }
            }
      
            handleCreate(){
              customersService.createCustomer(
                {
                  "first_name": this.refs.firstName.value,
                  "last_name": this.refs.lastName.value,
                  "email": this.refs.email.value,
                  "phone": this.refs.phone.value,
                  "address": this.refs.address.value,
                  "description": this.refs.description.value
              }          
              ).then((result)=>{
                alert("Customer created!");
              }).catch(()=>{
                alert('There was an error! Please re-check your form.');
              });
            }
            handleUpdate(pk){
              customersService.updateCustomer(
                {
                  "pk": pk,
                  "first_name": this.refs.firstName.value,
                  "last_name": this.refs.lastName.value,
                  "email": this.refs.email.value,
                  "phone": this.refs.phone.value,
                  "address": this.refs.address.value,
                  "description": this.refs.description.value
              }          
              ).then((result)=>{
                console.log(result);
                alert("Customer updated!");
              }).catch(()=>{
                alert('There was an error! Please re-check your form.');
              });
            }
            handleSubmit(event) {
              const { match: { params } } = this.props;
      
              if(params && params.pk){
                this.handleUpdate(params.pk);
              }
              else
              {
                this.handleCreate();
              }
      
              event.preventDefault();
            }
      
            render() {
              return (
                <form onSubmit={this.handleSubmit}>
                <div className="form-group">
                  <label>
                    First Name:</label>
                    <input className="form-control" type="text" ref='firstName' />
      
                  <label>
                    Last Name:</label>
                    <input className="form-control" type="text" ref='lastName'/>
      
                  <label>
                    Phone:</label>
                    <input className="form-control" type="text" ref='phone' />
      
                  <label>
                    Email:</label>
                    <input className="form-control" type="text" ref='email' />
      
                  <label>
                    Address:</label>
                    <input className="form-control" type="text" ref='address' />
      
                  <label>
                    Description:</label>
                    <textarea className="form-control" ref='description' ></textarea>
      
      
                  <input className="btn btn-primary" type="submit" value="Submit" />
                  </div>
                </form>
              );
            }  
      }
      
      export default CustomerCreateUpdate;
      

      Com o componente CustomerCreateUpdate criado, podemos atualizar o componente principal do App para adicionar links aos diferentes componentes que criamos.

      Passo 9 — Atualizando o Componente Principal do App

      Nesta seção, atualizaremos o componente App do nosso aplicativo para criar links para os componentes que criamos nos passos anteriores.

      A partir da pasta frontend, execute o seguinte comando para instalar o React Router, que permite que você adicione roteamento e navegação entre vários componentes do React:

      • cd ~/djangoreactproject/frontend
      • npm install --save react-router-dom

      Em seguida, abra ~/djangoreactproject/frontend/src/App.js:

      • nano ~/djangoreactproject/frontend/src/App.js

      Exclua tudo o que está lá e adicione o seguinte código para importar as classes necessárias para adicionar roteamentos. Esses incluem o BrowserRouter, que cria um componente Router e o Route, que cria um componente de rota:

      ~/djangoreactproject/frontend/src/App.js

      import  React, { Component } from  'react';
      import { BrowserRouter } from  'react-router-dom'
      import { Route, Link } from  'react-router-dom'
      import  CustomersList  from  './CustomersList'
      import  CustomerCreateUpdate  from  './CustomerCreateUpdate'
      import  './App.css';
      

      O BrowserRoutermantém a UI em sincronia com o URL utilizando a API de histórico HTML5.

      Na sequência, crie um layout básico que fornece o componente base a ser encapsulado pelo componente BrowserRouter:

      ~/djangoreactproject/frontend/src/App.js

      ...
      
      const  BaseLayout  = () => (
      <div  className="container-fluid">
          <nav  className="navbar navbar-expand-lg navbar-light bg-light">
              <a  className="navbar-brand"  href="https://www.digitalocean.com/#">Django React Demo</a>
              <button  className="navbar-toggler"  type="button"  data-toggle="collapse"  data-target="#navbarNavAltMarkup"  aria-controls="navbarNavAltMarkup"  aria-expanded="false"  aria-label="Toggle navigation">
              <span  className="navbar-toggler-icon"></span>
          </button>
          <div  className="collapse navbar-collapse"  id="navbarNavAltMarkup">
              <div  className="navbar-nav">
                  <a  className="nav-item nav-link"  href="/">CUSTOMERS</a>
                  <a  className="nav-item nav-link"  href="http://www.digitalocean.com/customer">CREATE CUSTOMER</a>
              </div>
          </div>
          </nav>
          <div  className="content">
              <Route  path="/"  exact  component={CustomersList}  />
              <Route  path="/customer/:pk"  component={CustomerCreateUpdate}  />
              <Route  path="/customer/"  exact  component={CustomerCreateUpdate}  />
          </div>
      </div>
      )
      

      Usamos o componente Route para definir as rotas do nosso aplicativo; o componente que o roteador deve carregar tão logo encontre um que seja compatível. Cada rota precisa de um path para especificar o caminho compatível e de um component para especificar o componente a carregar. A propriedade exact diz ao roteador para corresponder ao caminho exato.

      Finalmente, crie o componente App, o componente root ou o componente de nível superior do nosso aplicativo React:

      ~/djangoreactproject/frontend/src/App.js

      ...
      
      class  App  extends  Component {
      
      render() {
          return (
          <BrowserRouter>
              <BaseLayout/>
          </BrowserRouter>
          );
      }
      }
      export  default  App;
      

      Nós juntamos o componente BaseLayout ao componente BrowserRouter, uma vez que o nosso app deverá ser executado no navegador.

      O arquivo final se parece com este:

      ~/djangoreactproject/frontend/src/App.js

      import React, { Component } from 'react';
      import { BrowserRouter } from 'react-router-dom'
      import { Route, Link } from 'react-router-dom'
      
      import  CustomersList from './CustomersList'
      import  CustomerCreateUpdate  from './CustomerCreateUpdate'
      import './App.css';
      
      const BaseLayout = () => (
        <div className="container-fluid">
      <nav className="navbar navbar-expand-lg navbar-light bg-light">
        <a className="navbar-brand" href="https://www.digitalocean.com/#">Django React Demo</a>
        <button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
          <span className="navbar-toggler-icon"></span>
        </button>
        <div className="collapse navbar-collapse" id="navbarNavAltMarkup">
          <div className="navbar-nav">
            <a className="nav-item nav-link" href="/">CUSTOMERS</a>
            <a className="nav-item nav-link" href="http://www.digitalocean.com/customer">CREATE CUSTOMER</a>
      
          </div>
        </div>
      </nav>  
      
          <div className="content">
            <Route path="/" exact component={CustomersList} />
            <Route path="/customer/:pk"  component={CustomerCreateUpdate} />
            <Route path="/customer/" exact component={CustomerCreateUpdate} />
      
          </div>
      
        </div>
      )
      
      class App extends Component {
        render() {
          return (
            <BrowserRouter>
              <BaseLayout/>
            </BrowserRouter>
          );
        }
      }
      
      export default App;
      

      Após adicionar o roteamento ao nosso aplicativo, estamos agora prontos para testar o aplicativo. Navegue até o endereço http://localhost:3000. Você deve ver a primeira página do aplicativo:

      Application Home Page

      Com este aplicativo em funcionamento, agora você tem a base de um aplicativo de CRM.

      Conclusão

      Neste tutorial, você criou um aplicativo de demonstração utilizando o Django e o React. Você usou Django REST framework para construir a API REST, o Axios para consumir a API, e o Bootstrap 4 para estilizar o seu CSS. Você pode encontrar o código-fonte deste projeto neste repositório do GitHub.

      Esta configuração de tutorial usou apps separados de front-end e back-end. Para obter uma abordagem diferente para integrar o React ao Django, verifique este tutorial e este tutorial.

      Para saber mais sobre a construção de um aplicativo com o Django, você pode seguir as séries de desenvolvimento do Django. Você também pode considerar os documentos oficiais do Django.



      Source link