Python

Django em Python: MVT, ORM, Admin e Estrutura de Projetos: Do Básico ao Avançado

20 min de leitura

Django em Python: MVT, ORM, Admin e Estrutura de Projetos: Do Básico ao Avançado

Entendendo o MVT: A Arquitetura do Django Django segue um padrão arquitetural chamado MVT (Model-View-Template), que é uma variação do popular MVC (Model-View-Controller). A diferença fundamental está na responsabilidade da "View": enquanto em MVC a View é apenas a apresentação visual, no Django a View é a lógica de negócio, e o Template é responsável pela renderização do HTML. Isso permite separação clara de responsabilidades e facilita a manutenção do código. O fluxo é simples: quando um cliente faz uma requisição, o Django a roteia para uma View através das URLs, a View interage com o banco de dados via Models, e retorna um Template renderizado com os dados. Cada componente tem um propósito bem definido, o que torna o desenvolvimento mais organizado e escalável. Models: Definindo sua Estrutura de Dados Os Models são classes Python que representam tabelas no banco de dados. Django converte essas classes em SQL através do ORM, eliminando a necessidade de escrever queries manualmente. Cada

<h2>Entendendo o MVT: A Arquitetura do Django</h2>

<p>Django segue um padrão arquitetural chamado <strong>MVT (Model-View-Template)</strong>, que é uma variação do popular MVC (Model-View-Controller). A diferença fundamental está na responsabilidade da &quot;View&quot;: enquanto em MVC a View é apenas a apresentação visual, no Django a View é a lógica de negócio, e o Template é responsável pela renderização do HTML. Isso permite separação clara de responsabilidades e facilita a manutenção do código.</p>

<p>O fluxo é simples: quando um cliente faz uma requisição, o Django a roteia para uma View através das URLs, a View interage com o banco de dados via Models, e retorna um Template renderizado com os dados. Cada componente tem um propósito bem definido, o que torna o desenvolvimento mais organizado e escalável.</p>

<h3>Models: Definindo sua Estrutura de Dados</h3>

<p>Os Models são classes Python que representam tabelas no banco de dados. Django converte essas classes em SQL através do ORM, eliminando a necessidade de escrever queries manualmente. Cada atributo da classe se torna uma coluna na tabela, e Django infere automaticamente os tipos de dados SQL apropriados.</p>

<pre><code class="language-python"># models.py

from django.db import models

class Autor(models.Model):

nome = models.CharField(max_length=100)

email = models.EmailField(unique=True)

data_criacao = models.DateTimeField(auto_now_add=True)

class Meta:

ordering = [&#039;-data_criacao&#039;]

verbose_name_plural = &#039;Autores&#039;

def __str__(self):

return self.nome

class Livro(models.Model):

GENEROS = [

(&#039;ficcao&#039;, &#039;Ficção&#039;),

(&#039;tecnico&#039;, &#039;Técnico&#039;),

(&#039;romance&#039;, &#039;Romance&#039;),

]

titulo = models.CharField(max_length=200)

autor = models.ForeignKey(Autor, on_delete=models.CASCADE, related_name=&#039;livros&#039;)

genero = models.CharField(max_length=20, choices=GENEROS)

paginas = models.IntegerField()

publicado_em = models.DateField()

ativo = models.BooleanField(default=True)

def __str__(self):

return self.titulo</code></pre>

<p>O <code>ForeignKey</code> cria um relacionamento um-para-muitos: cada livro pertence a um autor, mas um autor pode ter múltiplos livros. O parâmetro <code>on_delete=models.CASCADE</code> define que livros serão deletados quando seu autor for removido. O <code>related_name=&#039;livros&#039;</code> permite acessar os livros de um autor através de <code>autor.livros.all()</code>.</p>

<h3>Views: A Lógica da Sua Aplicação</h3>

<p>As Views são funções ou classes que recebem uma requisição HTTP e retornam uma resposta. Django oferece duas abordagens: views baseadas em funções (FBV) e views baseadas em classes (CBV). As CBVs são mais poderosas para operações CRUD complexas, enquanto FBVs são diretas para lógicas simples.</p>

<pre><code class="language-python"># views.py

from django.shortcuts import render, get_object_or_404

from django.views.generic import ListView, DetailView, CreateView

from django.urls import reverse_lazy

from .models import Livro, Autor

View baseada em função

def lista_livros(request):

livros = Livro.objects.filter(ativo=True).select_related(&#039;autor&#039;)

return render(request, &#039;livros/lista.html&#039;, {&#039;livros&#039;: livros})

def detalhe_livro(request, pk):

livro = get_object_or_404(Livro, pk=pk, ativo=True)

return render(request, &#039;livros/detalhe.html&#039;, {&#039;livro&#039;: livro})

Views baseadas em classes

class ListaAutores(ListView):

model = Autor

template_name = &#039;autores/lista.html&#039;

context_object_name = &#039;autores&#039;

paginate_by = 10

def get_queryset(self):

return Autor.objects.annotate(

total_livros=models.Count(&#039;livros&#039;)

).order_by(&#039;-total_livros&#039;)

class DetalheLivro(DetailView):

model = Livro

template_name = &#039;livros/detalhe.html&#039;

context_object_name = &#039;livro&#039;

class CriarLivro(CreateView):

model = Livro

fields = [&#039;titulo&#039;, &#039;autor&#039;, &#039;genero&#039;, &#039;paginas&#039;, &#039;publicado_em&#039;]

template_name = &#039;livros/criar.html&#039;

success_url = reverse_lazy(&#039;lista_livros&#039;)</code></pre>

<p>A diferença crucial: com <code>ListaAutores</code>, você herda toda a lógica de paginação, ordenação e renderização. O <code>select_related</code> na FBV é uma otimização que reduz consultas ao banco — sem ele, Django faria uma query para cada livro ao acessar o autor.</p>

<h3>Templates: Renderizando o HTML</h3>

<p>Templates são arquivos HTML com sintaxe especial do Django que permitem inserir dados dinâmicos. Você acessa variáveis com <code>{{ }}</code>, executa lógica com <code>{% %}</code>, e itera sobre listas com <code>{% for %}</code>.</p>

<pre><code class="language-html">&lt;!-- livros/lista.html --&gt;

{% extends &#039;base.html&#039; %}

{% block content %}

&lt;h1&gt;Livros Disponíveis&lt;/h1&gt;

{% if livros %}

&lt;table class=&quot;tabela&quot;&gt;

&lt;thead&gt;

&lt;tr&gt;

&lt;th&gt;Título&lt;/th&gt;

&lt;th&gt;Autor&lt;/th&gt;

&lt;th&gt;Gênero&lt;/th&gt;

&lt;th&gt;Páginas&lt;/th&gt;

&lt;/tr&gt;

&lt;/thead&gt;

&lt;tbody&gt;

{% for livro in livros %}

&lt;tr&gt;

&lt;td&gt;

&lt;a href=&quot;{% url &#039;detalhe_livro&#039; livro.pk %}&quot;&gt;

{{ livro.titulo }}

&lt;/a&gt;

&lt;/td&gt;

&lt;td&gt;{{ livro.autor.nome }}&lt;/td&gt;

&lt;td&gt;{{ livro.get_genero_display }}&lt;/td&gt;

&lt;td&gt;{{ livro.paginas }}&lt;/td&gt;

&lt;/tr&gt;

{% endfor %}

&lt;/tbody&gt;

&lt;/table&gt;

{% else %}

&lt;p&gt;Nenhum livro encontrado.&lt;/p&gt;

{% endif %}

{% endblock %}</code></pre>

<p>O <code>{% extends &#039;base.html&#039; %}</code> herda de um template pai, evitando duplicação de código HTML. O <code>{% url &#039;detalhe_livro&#039; livro.pk %}</code> gera URLs dinamicamente baseado no nome das rotas. O <code>get_genero_display</code> é um método automático que retorna o label legível de um campo <code>choices</code>.</p>

<h2>ORM Django: Consultas Sem SQL</h2>

<p>O ORM (Object-Relational Mapping) é a joia da coroa do Django. Você trabalha com objetos Python em vez de escrever SQL, mantendo o código independente do banco de dados e seguro contra injeção SQL. A sintaxe é intuitiva e expressiva, permitindo consultas complexas com encadeamento de métodos.</p>

<h3>Querysets e Métodos Fundamentais</h3>

<p>Um QuerySet é uma coleção de objetos do banco de dados. Ele é <strong>lazy</strong> — não executa a query até você realmente precisar dos dados (iterando, convertendo para lista, ou chamando métodos como <code>count()</code>). Isso melhora performance ao permitir otimizações automáticas.</p>

<pre><code class="language-python"># Exemplos de QuerySets

from django.db.models import Q, Count, Sum, F

Filtros simples

livros_ativos = Livro.objects.filter(ativo=True)

livros_tecnico = Livro.objects.filter(genero=&#039;tecnico&#039;)

Exclusões

livros_exceto_ficção = Livro.objects.exclude(genero=&#039;ficcao&#039;)

Obtendo um único objeto

primeiro_livro = Livro.objects.first()

livro_especifico = Livro.objects.get(pk=1) # Lança DoesNotExist se não encontrar

Ordenação

livros_ordenados = Livro.objects.all().order_by(&#039;-paginas&#039;, &#039;titulo&#039;)

Slicing (funciona como listas Python)

primeiros_cinco = Livro.objects.all()[:5]

Contagem

total_livros = Livro.objects.filter(ativo=True).count()

Verificação de existência

existe_livro = Livro.objects.filter(titulo__icontains=&#039;Django&#039;).exists()</code></pre>

<h3>Relacionamentos e Joins</h3>

<p>Trabalhar com relacionamentos é onde o ORM brilha. Você navega entre models intuitivamente, sem pensar em JOINs SQL.</p>

<pre><code class="language-python"># Acessando relacionamentos

autor = Autor.objects.get(pk=1)

livros_do_autor = autor.livros.all() # related_name definido no Model

Filtrando através de relacionamentos

livros_do_fulano = Livro.objects.filter(autor__nome=&#039;Fulano&#039;)

autores_com_livros_tecnicos = Autor.objects.filter(livros__genero=&#039;tecnico&#039;).distinct()

Anotações e agregações

autores_com_contagem = Autor.objects.annotate(

total_livros=Count(&#039;livros&#039;),

paginas_totais=Sum(&#039;livros__paginas&#039;)

)

for autor in autores_com_contagem:

print(f&quot;{autor.nome}: {autor.total_livros} livros, {autor.paginas_totais} páginas&quot;)

Queries complexas com Q

livros_especiais = Livro.objects.filter(

Q(genero=&#039;tecnico&#039;) | Q(paginas__gt=500)

).filter(ativo=True)

Expressões F para operações no banco

Livro.objects.all().update(paginas=F(&#039;paginas&#039;) + 10) # Incrementa páginas</code></pre>

<p>O <code>select_related()</code> otimiza queries ao fazer JOINs internos para relacionamentos ForeignKey. O <code>prefetch_related()</code> faz buscas separadas para ManyToMany e reverse ForeignKey, depois agrupa em Python — mais eficiente em alguns casos.</p>

<pre><code class="language-python"># Otimizações de performance

livros_otimizado = Livro.objects.select_related(&#039;autor&#039;).all()

autores_otimizado = Autor.objects.prefetch_related(&#039;livros&#039;).all()

Combinado

consulta_pesada = Livro.objects.select_related(&#039;autor&#039;).prefetch_related(

&#039;comentarios&#039;

).filter(ativo=True)</code></pre>

<h2>Admin Django: Interface Administrativa Automática</h2>

<p>O Django Admin é uma das maiores produtividades da framework. Com pouquíssimas linhas de código, você ganha uma interface web completa e segura para gerenciar seus dados — criar, ler, atualizar e deletar registros sem escrever uma linha de HTML ou JavaScript.</p>

<h3>Configuração Básica</h3>

<p>Para expor um Model no admin, basta registrá-lo em <code>admin.py</code>. Sem nenhuma customização, o Django já oferece uma interface funcional.</p>

<pre><code class="language-python"># admin.py

from django.contrib import admin

from .models import Autor, Livro

@admin.register(Autor)

class AutorAdmin(admin.ModelAdmin):

list_display = [&#039;nome&#039;, &#039;email&#039;, &#039;data_criacao&#039;]

list_filter = [&#039;data_criacao&#039;]

search_fields = [&#039;nome&#039;, &#039;email&#039;]

readonly_fields = [&#039;data_criacao&#039;]

fieldsets = (

(&#039;Informações Pessoais&#039;, {

&#039;fields&#039;: (&#039;nome&#039;, &#039;email&#039;)

}),

(&#039;Metadados&#039;, {

&#039;fields&#039;: (&#039;data_criacao&#039;,),

&#039;classes&#039;: (&#039;collapse&#039;,)

}),

)

@admin.register(Livro)

class LivroAdmin(admin.ModelAdmin):

list_display = [&#039;titulo&#039;, &#039;autor&#039;, &#039;genero&#039;, &#039;paginas&#039;, &#039;ativo&#039;]

list_filter = [&#039;genero&#039;, &#039;ativo&#039;, &#039;publicado_em&#039;]

search_fields = [&#039;titulo&#039;, &#039;autor__nome&#039;]

list_editable = [&#039;ativo&#039;]

readonly_fields = [&#039;data_criacao_display&#039;]

fieldsets = (

(&#039;Detalhes do Livro&#039;, {

&#039;fields&#039;: (&#039;titulo&#039;, &#039;autor&#039;, &#039;genero&#039;, &#039;paginas&#039;, &#039;publicado_em&#039;)

}),

(&#039;Status&#039;, {

&#039;fields&#039;: (&#039;ativo&#039;,)

}),

)

def data_criacao_display(self, obj):

return obj.publicado_em.strftime(&#039;%d/%m/%Y&#039;)

data_criacao_display.short_description = &#039;Publicado em&#039;</code></pre>

<p>O <code>list_display</code> define quais campos aparecem na listagem. <code>list_filter</code> adiciona filtros laterais. <code>search_fields</code> ativa busca por esses campos. <code>list_editable</code> permite editar campos diretamente da listagem sem entrar no detalhe. O decorador <code>@admin.register()</code> é equivalente a <code>admin.site.register(Livro, LivroAdmin)</code>, mas mais legível.</p>

<h3>Customizações Avançadas</h3>

<p>O admin é extremamente customizável. Você pode adicionar ações em massa, inline models para relacionamentos, validações personalizadas e até redirecionar após salvar.</p>

<pre><code class="language-python"># admin.py avançado

from django.utils.html import format_html

class LivroInline(admin.TabularInline):

model = Livro

extra = 1

fields = [&#039;titulo&#039;, &#039;genero&#039;, &#039;paginas&#039;, &#039;ativo&#039;]

@admin.register(Autor)

class AutorAdmin(admin.ModelAdmin):

list_display = [&#039;nome&#039;, &#039;total_livros_link&#039;, &#039;email&#039;]

inlines = [LivroInline]

def total_livros_link(self, obj):

count = obj.livros.count()

return format_html(

&#039;&lt;span style=&quot;color: green; font-weight: bold;&quot;&gt;{}&lt;/span&gt;&#039;,

count

)

total_livros_link.short_description = &#039;Livros&#039;

@admin.register(Livro)

class LivroAdmin(admin.ModelAdmin):

list_display = [&#039;titulo&#039;, &#039;autor&#039;, &#039;status_badge&#039;, &#039;paginas&#039;]

actions = [&#039;marcar_como_inativo&#039;, &#039;marcar_como_ativo&#039;]

def status_badge(self, obj):

if obj.ativo:

color = &#039;green&#039;

texto = &#039;Ativo&#039;

else:

color = &#039;red&#039;

texto = &#039;Inativo&#039;

return format_html(

&#039;&lt;span style=&quot;background-color: {}; color: white; padding: 3px 10px; &#039;

&#039;border-radius: 3px;&quot;&gt;{}&lt;/span&gt;&#039;,

color, texto

)

status_badge.short_description = &#039;Status&#039;

def marcar_como_inativo(self, request, queryset):

queryset.update(ativo=False)

marcar_como_inativo.short_description = &#039;Marcar selecionados como inativos&#039;

def marcar_como_ativo(self, request, queryset):

queryset.update(ativo=True)

marcar_como_ativo.short_description = &#039;Marcar selecionados como ativos&#039;</code></pre>

<p>O <code>TabularInline</code> permite editar Livros (relacionados via ForeignKey) diretamente dentro da página do Autor. Ações em massa economizam cliques quando você precisa atualizar múltiplos registros. O <code>format_html()</code> permite renderizar HTML customizado nas listas (evita XSS automaticamente).</p>

<h2>Estrutura de Projetos Django: Organização Profissional</h2>

<p>Um projeto Django bem estruturado cresce sem dor. A organização padrão separa preocupações por aplicações (apps), cada uma com responsabilidades específicas. Um projeto pode ter múltiplas apps, cada uma podendo ser reutilizável em outros projetos.</p>

<h3>Estrutura Recomendada</h3>

<pre><code>meu_projeto/

├── manage.py

├── requirements.txt

├── config/ # Configurações do projeto

│ ├── __init__.py

│ ├── settings.py

│ ├── urls.py

│ ├── asgi.py

│ └── wsgi.py

├── apps/

│ ├── livros/ # App de domínio

│ │ ├── migrations/

│ │ ├── __init__.py

│ │ ├── admin.py

│ │ ├── apps.py

│ │ ├── models.py

│ │ ├── views.py

│ │ ├── urls.py

│ │ ├── forms.py

│ │ └── tests.py

│ ├── usuarios/ # Outra app

│ │ ├── migrations/

│ │ ├── __init__.py

│ │ ├── admin.py

│ │ ├── models.py

│ │ ├── views.py

│ │ ├── urls.py

│ │ └── tests.py

│ └── comum/ # App com utilities reutilizáveis

│ ├── middleware.py

│ ├── decorators.py

│ └── utils.py

├── static/ # CSS, JS, imagens

│ └── css/

│ └── js/

├── media/ # Arquivos upload de usuários

├── templates/ # Templates globais

│ ├── base.html

│ ├── 404.html

│ └── 500.html

└── tests/ # Testes da aplicação

├── __init__.py

├── test_models.py

└── test_views.py</code></pre>

<p>O diretório <code>config</code> centraliza configurações. Cada app em <code>apps/</code> é independente e reutilizável. O <code>static/</code> contém assets que não mudam (você executa <code>collectstatic</code> em produção). O <code>media/</code> armazena arquivos enviados por usuários. Templates globais ficam na raiz, templates específicos de app dentro de <code>apps/app/templates/app/</code>.</p>

<h3>Configuração de URLs Modular</h3>

<p>URLs devem ser organizadas por app, não em um único arquivo monolítico. A configuração do projeto só importa rotas das apps.</p>

<pre><code class="language-python"># config/urls.py

from django.contrib import admin

from django.urls import path, include

from django.conf import settings

from django.conf.urls.static import static

urlpatterns = [

path(&#039;admin/&#039;, admin.site.urls),

path(&#039;livros/&#039;, include(&#039;apps.livros.urls&#039;)),

path(&#039;usuarios/&#039;, include(&#039;apps.usuarios.urls&#039;)),

]

if settings.DEBUG:

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

apps/livros/urls.py

from django.urls import path

from . import views

app_name = &#039;livros&#039;

urlpatterns = [

path(&#039;&#039;, views.lista_livros, name=&#039;lista&#039;),

path(&#039;&lt;int:pk&gt;/&#039;, views.detalhe_livro, name=&#039;detalhe&#039;),

path(&#039;criar/&#039;, views.CriarLivro.as_view(), name=&#039;criar&#039;),

path(&#039;&lt;int:pk&gt;/editar/&#039;, views.EditarLivro.as_view(), name=&#039;editar&#039;),

path(&#039;&lt;int:pk&gt;/deletar/&#039;, views.DeletarLivro.as_view(), name=&#039;deletar&#039;),

]

apps/usuarios/urls.py

from django.urls import path

from . import views

app_name = &#039;usuarios&#039;

urlpatterns = [

path(&#039;registro/&#039;, views.RegistroView.as_view(), name=&#039;registro&#039;),

path(&#039;login/&#039;, views.LoginView.as_view(), name=&#039;login&#039;),

path(&#039;logout/&#039;, views.LogoutView.as_view(), name=&#039;logout&#039;),

]</code></pre>

<p>O <code>app_name</code> evita conflitos de nomes entre apps. Na template, você usa <code>{% url &#039;livros:detalhe&#039; livro.pk %}</code> em vez de apenas <code>{% url &#039;detalhe&#039; livro.pk %}</code>.</p>

<h3>Configurações Seguras e Escaláveis</h3>

<p>Settings devem ser diferentes entre desenvolvimento, testes e produção. A abordagem profissional usa variáveis de ambiente.</p>

<pre><code class="language-python"># config/settings.py

import os

from pathlib import Path

from decouple import config

BASE_DIR = Path(__file__).resolve().parent.parent

SECRET_KEY = config(&#039;SECRET_KEY&#039;, default=&#039;dev-insecuro-change-in-production&#039;)

DEBUG = config(&#039;DEBUG&#039;, default=True, cast=bool)

ALLOWED_HOSTS = config(&#039;ALLOWED_HOSTS&#039;, default=&#039;localhost,127.0.0.1&#039;).split(&#039;,&#039;)

INSTALLED_APPS = [

&#039;django.contrib.admin&#039;,

&#039;django.contrib.auth&#039;,

&#039;django.contrib.contenttypes&#039;,

&#039;django.contrib.sessions&#039;,

&#039;django.contrib.messages&#039;,

&#039;django.contrib.staticfiles&#039;,

Terceiros

&#039;rest_framework&#039;,

&#039;corsheaders&#039;,

Apps locais

&#039;apps.livros&#039;,

&#039;apps.usuarios&#039;,

&#039;apps.comum&#039;,

]

MIDDLEWARE = [

&#039;django.middleware.security.SecurityMiddleware&#039;,

&#039;corsheaders.middleware.CorsMiddleware&#039;,

&#039;django.middleware.common.CommonMiddleware&#039;,

&#039;django.middleware.csrf.CsrfViewMiddleware&#039;,

&#039;django.contrib.sessions.middleware.SessionMiddleware&#039;,

&#039;django.contrib.auth.middleware.AuthenticationMiddleware&#039;,

&#039;django.contrib.messages.middleware.MessageMiddleware&#039;,

]

TEMPLATES = [

{

&#039;BACKEND&#039;: &#039;django.template.backends.django.DjangoTemplates&#039;,

&#039;DIRS&#039;: [BASE_DIR / &#039;templates&#039;],

&#039;APP_DIRS&#039;: True,

&#039;OPTIONS&#039;: {

&#039;context_processors&#039;: [

&#039;django.template.context_processors.debug&#039;,

&#039;django.template.context_processors.request&#039;,

&#039;django.contrib.auth.context_processors.auth&#039;,

&#039;django.contrib.messages.context_processors.messages&#039;,

],

},

},

]

DATABASES = {

&#039;default&#039;: {

&#039;ENGINE&#039;: &#039;django.db.backends.postgresql&#039;,

&#039;NAME&#039;: config(&#039;DB_NAME&#039;),

&#039;USER&#039;: config(&#039;DB_USER&#039;),

&#039;PASSWORD&#039;: config(&#039;DB_PASSWORD&#039;),

&#039;HOST&#039;: config(&#039;DB_HOST&#039;, default=&#039;localhost&#039;),

&#039;PORT&#039;: config(&#039;DB_PORT&#039;, default=&#039;5432&#039;),

}

}

AUTH_PASSWORD_VALIDATORS = [

{&#039;NAME&#039;: &#039;django.contrib.auth.password_validation.UserAttributeSimilarityValidator&#039;},

{&#039;NAME&#039;: &#039;django.contrib.auth.password_validation.MinimumLengthValidator&#039;},

{&#039;NAME&#039;: &#039;django.contrib.auth.password_validation.CommonPasswordValidator&#039;},

{&#039;NAME&#039;: &#039;django.contrib.auth.password_validation.NumericPasswordValidator&#039;},

]

LANGUAGE_CODE = &#039;pt-br&#039;

TIME_ZONE = &#039;America/Sao_Paulo&#039;

USE_I18N = True

USE_TZ = True

STATIC_URL = &#039;/static/&#039;

STATIC_ROOT = BASE_DIR / &#039;staticfiles&#039;

MEDIA_URL = &#039;/media/&#039;

MEDIA_ROOT = BASE_DIR / &#039;media&#039;

Segurança

if not DEBUG:

SECURE_SSL_REDIRECT = True

SESSION_COOKIE_SECURE = True

CSRF_COOKIE_SECURE = True

SECURE_BROWSER_XSS_FILTER = True

X_FRAME_OPTIONS = &#039;DENY&#039;</code></pre>

<p>Use um arquivo <code>.env</code> para variáveis sensíveis (nunca commita no Git):</p>

<pre><code>SECRET_KEY=sua-chave-aleatoria-super-secreta

DEBUG=False

ALLOWED_HOSTS=seudominio.com,www.seudominio.com

DB_ENGINE=django.db.backends.postgresql

DB_NAME=biblioteca_db

DB_USER=postgres

DB_PASSWORD=senha_super_secreta

DB_HOST=localhost

DB_PORT=5432</code></pre>

<p>E instale <code>python-decouple</code> para ler essas variáveis:</p>

<pre><code class="language-bash">pip install python-decouple</code></pre>

<h2>Conclusão</h2>

<p>Você aprendeu que <strong>Django MVT separa responsabilidades</strong> de forma clara: Models definem dados, Views contêm lógica, Templates renderizam HTML. Isso torna projetos organizados e fáceis de escalar. O <strong>ORM elimina SQL manual</strong> através de uma sintaxe Pythônica expressiva que protege contra injeção de dados e funciona com qualquer banco relacional. O <strong>Admin Django oferece CRUD automático</strong> sem você escrever uma linha de interface, economizando dias de desenvolvimento. Finalmente, uma <strong>estrutura modular com apps independentes</strong> permite que você reutilize componentes, organize código por domínio e trabalhe em equipe sem conflitos. Com esses pilares, você está pronto para construir aplicações web robustas em Django.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://docs.djangoproject.com/" target="_blank" rel="noopener noreferrer">Documentação Oficial do Django</a></li>

<li><a href="https://docs.djangoproject.com/en/stable/topics/db/models/" target="_blank" rel="noopener noreferrer">Django Models - Documentação Oficial</a></li>

<li><a href="https://realpython.com/django-orm/" target="_blank" rel="noopener noreferrer">Django ORM - Real Python</a></li>

<li><a href="https://www.feldroy.com/books/two-scoops-of-django-3-x" target="_blank" rel="noopener noreferrer">Two Scoops of Django - Daniel Roy Greenfeld e Audrey Roy Greenfeld</a></li>

<li><a href="https://lincolnloop.com/blog/" target="_blank" rel="noopener noreferrer">Django Best Practices - Lincoln Loop</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Python

Dominando Mypy em Python: Verificação Estática de Tipos no Projeto Real em Projetos Reais
Dominando Mypy em Python: Verificação Estática de Tipos no Projeto Real em Projetos Reais

Introdução ao Mypy: Por Que Type Checking Importa Quando você trabalha em pro...

Como Usar asyncio em Python: Event Loop, Coroutines e Tasks em Produção
Como Usar asyncio em Python: Event Loop, Coroutines e Tasks em Produção

Introdução ao Asyncio: O Coração da Programação Assíncrona em Python Asyncio...

Tipos Avançados em Python: Generic, Protocol, TypeVar e ParamSpec na Prática
Tipos Avançados em Python: Generic, Protocol, TypeVar e ParamSpec na Prática

Introdução aos Tipos Avançados em Python Python é uma linguagem dinamicamente...