Configuração Frontend, Modelagem de Dados e API de Carrossel em Django REST Framework com Gestão de CORS

Configuração Essencial do Frontend Vue.js

Ao iniciar um novo projeto Vue.js, é fundamental realizar algumas configurações iniciais para garantir um ambiente de desenvolvimento limpo e eficiente. Os módulos de terceiros devem ser instalados via um gerenciador de pacotes como cnpm ou npm, e a verificação no arquivo package.json é uma boa prática para confirmar a instalação. É importante lembrar que projetos Vue.js, focados no frontend, não requerem ambientes virtuais como os projetos Python.

Estruturação do Projeto Vue.js

Para manter o projeto organizado e livre de excessos, é recomendado limpar os arquivos boilerplate gerados automaticamente:

Componente Raiz: App.vue

Remova quaisquer estilos ou elementos desnecessários, mantendo apenas o ponto de entrada para as rotas.

<template>
  <div id="app-container">
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'AppRoot',
};
</script>

<style>
/* Estilos globais ou reset podem ser importados aqui */
#app-container {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
}
</style>

Configuração de Rotas: router/index.js

Mantenha apenas as rotas essenciais para o início do projeto, removendo exemplos ou rotas não utilizadas.

import Vue from 'vue';
import VueRouter from 'vue-router';
import LandingPage from '../views/LandingPage.vue';

Vue.use(VueRouter);

const routes = [
  {
    path: '/',
    name: 'landing',
    component: LandingPage,
  },
];

const router = new VueRouter({
  mode: 'history', // Ou 'hash'
  base: process.env.BASE_URL,
  routes,
});

export default router;

Página de Exemplo: LandingPage.vue (ou similar)

Simplifique os componentes de página para conter apenas o conteúdo básico necessário.

<template>
  <div class="landing-page">
    <h1>Bem-vindo à Página Inicial</h1>
  </div>
</template>

<script>
export default {
  name: 'LandingPage',
};
</script>

<style scoped>
.landing-page {
  text-align: center;
  padding: 20px;
}
</style>

Estilos CSS Globais

É uma prática comum inicializar os estilos de um projeto web com um "reset" CSS, removendo margens e preenchimentos padrão dos navegadores para uma maior consistência visual.

  1. Crie um arquivo global.css dentro de uma pasta assets/css.
/* Arquivo: src/assets/css/global.css */
/* Define estilos globais e reinicia as propriedades padrão do navegador para elementos HTML comuns */

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
    margin: 0;
    padding: 0;
    border: 0;
    font-size: 100%;
    font: inherit;
    vertical-align: baseline;
    box-sizing: border-box; /* Adicionado para um modelo de caixa mais intuitivo */
}

/* Reset específico para alguns elementos */
body {
    line-height: 1;
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    color: #333;
}
ol, ul {
    list-style: none;
}
a {
    text-decoration: none;
    color: inherit;
}
table {
    border-collapse: collapse; /* Unifica bordas de tabelas */
    border-spacing: 0;
}
input, select, textarea, button {
    font-family: inherit;
    font-size: inherit;
}

  1. Importe este arquivo no main.js para que os estilos sejam aplicados globalmente.
// Arquivo: src/main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router';

// Importa os estilos CSS globais
import '@/assets/css/global.css'; 

Vue.config.productionTip = false;

new Vue({
  router,
  render: h => h(App),
}).$mount('#app-container');

Configurações JavaScript Globais

Para facilitar a manutenção de URLs de API e outras configurações que podem mudar entre ambientes (desenvolvimento, produção), é útil centralizá-las em um arquivo JavaScript global.

  1. Crie um arquivo api_settings.js dentro de assets/js.
// Arquivo: src/assets/js/api_settings.js
export default {
    BASE_API_URL: 'http://127.0.0.1:8000/api/v1', // URL base da API
    APP_NAME: 'Meu Projeto Web'
    // Outras configurações podem ser adicionadas aqui
};

  1. Importe este arquivo em main.js e o adicione ao protótipo do Vue para acessá-lo facilmente em qualquer componente.
// Arquivo: src/main.js (trecho relevante)
import Vue from 'vue';
// ... outras importações ...
import apiSettings from '@/assets/js/api_settings';

// Adiciona as configurações da API ao protótipo do Vue
Vue.prototype.$api = apiSettings;

new Vue({
  router,
  render: h => h(App),
}).$mount('#app-container');

  1. Acesse as configurações em qualquer componente Vue.
// Exemplo de uso em um componente Vue
export default {
  methods: {
    fetchData() {
      // Usando o Axios (ainda a ser instalado)
      this.$axios.get(`${this.$api.BASE_API_URL}/produtos`).then(response => {
        console.log(response.data);
      }).catch(error => {
        console.error('Erro ao buscar dados:', error);
      });
    }
  }
};

Instalação e Configuração de Módulos Essenciais

Integração com Axios para Requisições HTTP

Axios é uma biblioteca popular para fazer requisições HTTP, amplamente utilizada em projetos Vue.js.

  1. Instale o Axios: cnpm install axios -S
  2. Configure-o globalmente no main.js.
// Arquivo: src/main.js (trecho relevante)
import Vue from 'vue';
import axios from 'axios';
// ... outras importações ...

Vue.prototype.$axios = axios;

// Opcional: Configurações padrão para o Axios
axios.defaults.headers.common['Accept'] = 'application/json';
axios.defaults.withCredentials = true; // Para enviar cookies automaticamente

new Vue({
  // ...
}).$mount('#app-container');

  1. Utilize o Axios em seus componentes.
// Exemplo em um componente Vue
export default {
  methods: {
    async loginUser(credentials) {
      try {
        const response = await this.$axios.post(`${this.$api.BASE_API_URL}/auth/login`, credentials);
        console.log('Login bem-sucedido:', response.data);
        // Salvar token, redirecionar, etc.
      } catch (error) {
        console.error('Falha no login:', error.response ? error.response.data : error.message);
      }
    }
  }
};

Gerenciamento de Cookies com Vue-Cookies

Para armazenar tokens de autenticação ou outras informações no navegador, vue-cookies é uma solução prática.

  1. Instale vue-cookies: cnpm install vue-cookies -S
  2. Configure-o globalmente no main.js.
// Arquivo: src/main.js (trecho relevante)
import Vue from 'vue';
import VueCookies from 'vue-cookies';
// ... outras importações ...

Vue.use(VueCookies); // Para Vue 2
// Ou, se precisar de acesso direto ao objeto:
// Vue.prototype.$cookies = VueCookies;

new Vue({
  // ...
}).$mount('#app-container');

  1. Use vue-cookies em seus componentes.
// Exemplo em um componente Vue
export default {
  methods: {
    saveAuthToken(token) {
      this.$cookies.set('authToken', token, '1d'); // Salva o token por 1 dia
    },
    getAuthToken() {
      return this.$cookies.get('authToken');
    },
    removeAuthToken() {
      this.$cookies.remove('authToken');
    }
  }
};

Integração da Biblioteca de Componentes Element UI

Element UI oferece um conjunto rico de componentes de interface do usuário, acelerando o desenvolvimento frontend.

  1. Instale o Element UI: cnpm install element-ui -S
  2. Configure-o no main.js.
// Arquivo: src/main.js (trecho relevante)
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css'; // Importa o tema CSS
// ... outras importações ...

Vue.use(ElementUI);

new Vue({
  // ...
}).$mount('#app-container');

  1. Agora você pode usar os componentes do Element UI em seus modelos.
<template>
  <div>
    <el-button type="primary" @click="handleClick">Meu Botão</el-button>
    <el-alert
      title="Sucesso"
      type="success"
      description="Operação realizada com êxito.">
    </el-alert>
  </div>
</template>

<script>
export default {
  methods: {
    handleClick() {
      this.$message('Botão clicado!');
    }
  }
};
</script>

Configurando jQuery e Bootstrap (para projetos legados ou específicos)

Embora Vue.js e Element UI forneçam soluções modernas, pode ser necessário integrar jQuery e Bootstrap (especialmente a versão 3) para compatibilidade com sistemas existentes ou requisitos específicos. Este processo requer configuração do Webpack.

  1. Instale as dependências: cnpm install jquery -S cnpm install bootstrap@3 -S
  2. Configure o Bootstrap no main.js.
// Arquivo: src/main.js (trecho relevante)
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap'; // Importa o JavaScript do Bootstrap
// ...

  1. Configure o jQuery através do vue.config.js para que esteja disponível globalmente.
// Arquivo: vue.config.js
const webpack = require('webpack');

module.exports = {
  configureWebpack: {
    plugins: [
      new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery',
        'window.jQuery': 'jquery',
        'window.$': 'jquery',
        Popper: ['popper.js', 'default'], // Para Bootstrap 4+, mas pode ser útil
      }),
    ],
  },
};

Design da Tabela de Módulos da Página Inicial do Backend

Com base na análise do design da página inicial, identificamos a necessidade de uma API para gerenciar os banners rotativos.

  • API de Carrossel (Banners)
  • (Outras APIs, como cursos recomendados, professores, depoimentos de alunos, podem ser planejadas para o futuro).

Para isso, criaremos um novo aplicativo Django chamado homepage.

python manage.py startapp homepage

Passos para a Criação de Tabelas

1. Definição de um Modelo Base Comum (common_models.py)

Para padronizar campos como datas de criação/atualização, status de exclusão e visibilidade, criamos um modelo abstrato.

# Arquivo: utils/common_models.py
from django.db import models

class BaseModel(models.Model):
    data_criacao = models.DateTimeField(auto_now_add=True, verbose_name='Data de Criação')
    data_atualizacao = models.DateTimeField(auto_now=True, verbose_name='Última Atualização')
    excluido = models.BooleanField(default=False, verbose_name='Registro Excluído')
    ativo = models.BooleanField(default=True, verbose_name='Visível no Site')
    prioridade = models.IntegerField(default=0, verbose_name='Ordem de Exibição')

    class Meta:
        abstract = True  # Este modelo não será criado no banco de dados, apenas herdado
        ordering = ['prioridade', '-data_criacao'] # Ordem padrão

2. Criação do Modelo Banner no Aplicativo homepage (models.py)

Este modelo representará cada item no carrossel da página inicial.

# Arquivo: homepage/models.py
from django.db import models
from utils.common_models import BaseModel # Importe do caminho correto do projeto

class Banner(BaseModel):
    titulo = models.CharField(max_length=64, unique=True, verbose_name='Título do Banner')
    imagem_url = models.ImageField(upload_to='banners/', verbose_name='URL da Imagem')
    url_destino = models.CharField(max_length=255, verbose_name='Link de Destino')
    descricao = models.TextField(blank=True, verbose_name='Descrição Detalhada')

    class Meta:
        db_table = 'app_banners'  # Nome da tabela no banco de dados
        verbose_name = 'Banner do Carrossel'
        verbose_name_plural = 'Banners do Carrossel'

    def __str__(self):
        return self.titulo

3. Execução das Migrações

python manage.py makemigrations homepage
python manage.py migrate homepage

Observação sobre ModelForm vs. Form e ModelSerializer vs. Serializer:

A principal diferença reside na vinculação com modelos de banco de dados. Form e Serializer são mais genéricos, usados para renderizar campos HTML, validar dados e exibir erros. Já ModelForm e ModelSerializer são especificamente projetados para interagir com modelos Django, facilitando a criação, atualização e serialização de instâncias de modelo, pois inferem automaticamente os campos e validações do modelo associado.

Solução para RuntimeError: Model class ... doesn't declare an explicit app_label:

Esse erro geralmente ocorre quando um modelo está em um aplicativo que não foi corretamente registrado em INSTALLED_APPS ou quando o caminho de importação é ambíguo. Certifique-se de que seu aplicativo (ex: homepage) esteja em INSTALLED_APPS e que as importações dentro dos seus modelos e serializers usem caminhos absolutos baseados no diretório raiz do projeto ou no próprio nome do aplicativo.

API de Carrossel para a Página Inicial

1. Implementação da Viewset

Utilizaremos o Django REST Framework para criar uma ViewSet de leitura (somente listagem) para os banners.

# Arquivo: homepage/views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from homepage.models import Banner
from homepage.serializers import BannerSerializer
from utils.response_handler import APIResponse # Classe de resposta customizada

class BannerAPIView(GenericViewSet, ListModelMixin):
    queryset = Banner.objects.filter(excluido=False, ativo=True).order_by('prioridade')
    serializer_class = BannerSerializer

    def list(self, request, *args, **kwargs):
        # Chama o método list da ListModelMixin para obter os dados serializados
        response_data = super().list(request, *args, **kwargs)
        # Retorna uma resposta customizada com o formato padronizado
        return APIResponse(data=response_data.data)

2. Definição do Serializer

O Serializer converterá as instâncias do modelo Banner em representações de dados (JSON) e vice-versa.

# Arquivo: homepage/serializers.py
from rest_framework import serializers
from homepage.models import Banner

class BannerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Banner
        fields = ['id', 'imagem_url', 'url_destino', 'titulo'] # Campos a serem expostos

3. Configuração das Rotas

Registramos a ViewSet com um router simples do DRF para gerar automaticamente as rotas para a API.

# Arquivo: project_name/urls.py (ou um arquivo de urls específico para a API)
from django.urls import path, include
from rest_framework.routers import SimpleRouter
from homepage import views

# Cria um router para registrar ViewSets
api_router = SimpleRouter()

# Registra a ViewSet de banners
# A URL será: /api/v1/banners/
api_router.register('banners', views.BannerAPIView, basename='banner')

urlpatterns = [
    # ... outras urls do projeto ...
    path('api/v1/', include(api_router.urls)), # Inclui as urls geradas pelo router
]

Inserção de Dados via Painel Administrativo

Para facilitar a gestão de conteúdo, utilizaremos o painel administrativo do Django, que pode ser aprimorado com temas como o SimpleUI.

  1. Instale o SimpleUI (opcional, mas recomendado para uma melhor experiência): pip install django-simpleui
  2. Adicione simpleui e homepage aos seus INSTALLED_APPS em settings.py.
# Arquivo: project_name/settings.py
INSTALLED_APPS = [
    'simpleui', # Adicionar antes de 'django.contrib.admin'
    'django.contrib.admin',
    # ... outros apps do Django ...
    'homepage', # Seu aplicativo de página inicial
    # ...
]

Dica sobre criação de superusuários: Se você definir um campo como unique=True (ex: mobile para números de telefone) em seu modelo de usuário, certifique-se de fornecer um valor diferente para cada superusuário criado ou deixar o campo nulo se permitido, caso contrário, tentativas subsequentes de criar superusuários sem preencher esse campo podem falhar devido à violação da unicidade.

  1. Acesse o painel administrativo em http://127.0.0.1:8000/admin, faça login com um superusuário e insira os dados dos banners.

Detalhamento do Problema de Cross-Origin (CORS)

Ao fazer requisições AJAX de um frontend para um backend que estão em origens diferentes, você encontrará o problema de Cross-Origin Resource Sharing (CORS). Isso ocorre devido à Política de Mesma Origem (Same-Origin Policy) imposta pelos navegadores, uma medida de segurança fundamental para prevenir interações maliciosas entre diferentes sites.

  • Origem é definida pela combinação de protocolo (http/https), domínio (ex: exemplo.com) e porta (ex: 80, 443, 3000). Se qualquer um desses três componentes for diferente, as origens são consideradas distintas.
  • Mesmo que a requisição seja bem-sucedida no servidor, o navegador pode bloquear o acesso à resposta se a política de mesma origem não for satisfeita, resultando em um erro CORS.

Observação sobre o arquivo hosts: O arquivo hosts do sistema operacional (localizado em C:\Windows\System32\drivers\etc\hosts no Windows) permite mapear nomes de domínio para endereços IP localmente. Por exemplo, adicionar 127.0.0.1 meudominio.com fará com que seu navegador resolva meudominio.com para o seu próprio computador. Isso é útil para testes de desenvolvimento, mas não resolve diretamente problemas de CORS entre origens diferentes na prática.

Soluções para Problemas de CORS

Existem várias abordagens para contornar restrições de CORS:

  • CORS (Cross-Origin Resource Sharing): A solução mais comum e recomendada. Envolve o backend enviando cabeçalhos HTTP específicos na resposta para informar ao navegador que a requisição cross-origin é permitida.
  • Proxy Reverso (Nginx): Um servidor como Nginx pode atuar como um proxy, redirecionando requisições do frontand para o backend, fazendo com que o frontend e o proxy pareçam ter a mesma origem.
  • Proxy Node.js: Similar ao Nginx, um servidor Node.js pode ser configurado para receber requisições do frontend e reencaminhá-las ao backend.
  • JSONP (JSON with Padding): Uma técnica mais antiga e limitada, que explora a capacidade da tag <script> de carregar scripts de qualquer domínio. Funciona apenas para requisições GET e não é considerada segura ou flexível o suficiente para a maioria dos casos modernos.

Fluxo Básico de Requisições CORS:

Os navegadores categorizam requisições CORS em dois tipos:

  • Requisições Simples: São requisições HTTP que atendem a critérios específicos (métodos GET, HEAD, POST; cabeçalhos limitados a Accept, Accept-Language, Content-Language, Last-Event-ID, Content-Type [apenas application/x-www-form-urlencoded, multipart/form-data, text/plain]). Nesses casos, o navegador envia a requisição diretamente com um cabeçalho Origin.
  • Requisições Não-Simples: Qualquer requisição que não se enquadre nos critérios de requisição simples (ex: métodos PUT, DELETE, ou cabeçalhos customizados, ou Content-Type: application/json). Para estas, o navegador envia uma "requisição de pré-checagem" (preflight request) com o método OPTIONS antes da requisição real. Esta pré-checagem pergunta ao servidor se a requisição real é permitida. Somente se a pré-checagem for bem-sucedida, a requisição real é enviada.

Implementando a Solução CORS (Manual)

Você pode criar um middleware personalizado no Django para adicionar os cabeçalhos CORS necessários nas respostas.

# Arquivo: utils/custom_middleware.py
from django.utils.deprecation import MiddlewareMixin

class MiddlewareCORSPersonalizado(MiddlewareMixin):
    def process_response(self, request, response):
        # Permite todas as origens (ideal para desenvolvimento, para produção, use domínios específicos)
        response["Access-Control-Allow-Origin"] = "*"
        
        # Lida com requisições de pré-checagem (OPTIONS)
        if request.method == "OPTIONS":
            response["Access-Control-Allow-Headers"] = "Content-Type, Authorization, X-Requested-With, token, client-id"
            response["Access-Control-Allow-Methods"] = "DELETE, GET, OPTIONS, PATCH, POST, PUT"
            response["Access-Control-Max-Age"] = "86400" # Cache da pré-checagem por 24 horas

        return response

Em seguida, adicione este middleware ao seu settings.py, preferencialmente no início da lista MIDDLEWARE.

# Arquivo: project_name/settings.py
MIDDLEWARE = [
    'utils.custom_middleware.MiddlewareCORSPersonalizado', # Seu middleware personalizado
    'django.middleware.security.SecurityMiddleware',
    # ... outros middlewares
]

Implementando a Solução CORS (com django-cors-headers)

A forma mais robusta e recomendada de gerenciar CORS no Django é usando a biblioteca django-cors-headers.

  1. Instale a biblioteca: pip install django-cors-headers
  2. Adicione corsheaders aos seus INSTALLED_APPS em settings.py.
# Arquivo: project_name/settings.py
INSTALLED_APPS = [
    # ...
    'corsheaders', # Deve ser adicionado
    # ...
]

  1. Adicione o middleware de CORS. Ele deve vir antes de qualquer middleware que possa gerar respostas (como CommonMiddleware ou CsrfViewMiddleware) e depois de qualquer middleware que lida com codificação.
# Arquivo: project_name/settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'corsheaders.middleware.CorsMiddleware', # Adicionado aqui
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

  1. Configure os cabeçalhos e métodos permitidos em settings.py.
# Arquivo: project_name/settings.py
# Permite requisições de todas as origens. Em produção, considere usar CORS_ALLOWED_ORIGINS
CORS_ORIGIN_ALLOW_ALL = True 

# Lista de métodos HTTP permitidos para requisições cross-origin
CORS_ALLOW_METHODS = (
    'DELETE',
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
)

# Lista de cabeçalhos HTTP que podem ser incluídos em requisições cross-origin
# Inclua quaisquer cabeçalhos customizados que seu frontend possa enviar (ex: 'token', 'client-id')
CORS_ALLOW_HEADERS = (
    'accept',
    'accept-encoding',
    'authorization',
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
    'pragma',
    'token',      # Exemplo de cabeçalho customizado
    'client-id',  # Outro exemplo
)

# Se CORS_ORIGIN_ALLOW_ALL for False, use CORS_ALLOWED_ORIGINS para listar origens específicas
# CORS_ALLOWED_ORIGINS = [
#     "http://localhost:8080",
#     "http://meufrontend.com",
# ]

# Para permitir o envio de credenciais (cookies, cabeçalhos de autorização)
CORS_ALLOW_CREDENTIALS = True

Lembre-se de sempre listar todos os cabeçalhos customizados que seu frontend envia (como token ou name) na configuração CORS_ALLOW_HEADERS para garantir que as requisições não sejam bloqueadas.

Tags: Vue.js Django REST Framework CORS Axios Element UI

Publicado em 8-20 21:27