Upload de Arquivos
O upload de arquivos no Django é o processo de permitir que os usuários enviem arquivos para o servidor por meio de um formulário HTML. O Django possui ferramentas integradas para lidar com arquivos enviados e armazená-los de maneira eficiente no servidor.
Quando temos uma entrada de arquivo em HTML input type="file" precisamos informar isso ao navegador com a propriedade enctype="multipart/form-data" para que ele envie arquivos junto com outros dados do formulário. O Django nos dá a possibilidade de acessarmos os arquivos enviados (pelo html por exemplo) através da propriedade especial FILES(um dicionário python) da requisição, assim como todo o conteúdo em POST.
O método chunks() faz parte da classe UploadedFile, que o Django utiliza para manipular arquivos enviados por formulários, ou seja, todo formulário é um objeto do tipo UploadedFile. Chunks divide o arquivo em partes menores (chunks) para processamento. Quando o arquivo é grande, processar tudo de uma vez pode causar problemas de memória. Dividir o arquivo em partes menores permite manipulá-lo de maneira mais eficiente e evita que o servidor consuma toda a memória disponível.
| Exemplo Upload de Arquivos
Leve em consideração a seguinte estrutura:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Criar um Perfil</title>
</head>
<body>
<form action="/perfis/" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="Imagem" />
<button>Upload!</button>
</form>
</body>
</html>
from django.shortcuts import render
from django.views import View
from django.http import HttpResponseRedirect
def armazenar_arquivo(file):
# Abre o arquivo em modo binário
with open("temp/image.jpeg", 'wb+') as destination:
# Escreve/Salva o Arquivo em partes pois pode ser pesado
for chunk in file.chunks():
destination.write(chunk)
class CriarPerfilView(View):
def get(self, request):
return render(request, "perfis/criar_perfil_upload.html")
def post(self, request):
# Recupera o arquivo imagem enviado no formulário e chama a função para salvar
armazenar_arquivo(request.FILES['imagem'])
return HttpResponseRedirect("/perfis")
A função armazenar_arquivo está no mesmo arquivo (views.py) apenas para facilitar o entendimento. temp/ estará na pasta principal do projeto.
from django.urls import path
from . import views
urlpatterns = [
path("", views.CriarPerfilView.as_view())
]
| Exemplo Upload com Forms
from django import forms
class PerfilForm(forms.Form):
usuario_imagem = forms.FileField()
from django.shortcuts import render
from django.views import View
from django.http import HttpResponseRedirect
from .forms import PerfilForm
def armazenar_arquivo(file):
with open("temp/image.jpeg", 'wb+') as destination:
for chunk in file.chunks():
destination.write(chunk)
class CriarPerfilView(View):
def get(self, request):
form = PerfilForm()
return render(request, "perfis/criar_perfil_upload.html",{
"form": form
})
def post(self, request):
formulario_enviado = PerfilForm(request.POST,request.FILES)
if formulario_enviado.is_valid():
armazenar_arquivo(request.FILES['usuario_imagem'])
return HttpResponseRedirect("/perfis")
return render(request, "perfis/criar_perfil_upload.html",{
"form": formulario_enviado
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Criar um Perfil</title>
</head>
<body>
<form action="/perfis" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{form}}
<button>Upload!</button>
</form>
</body>
</html>
| Exemplo Upload com Models
FileField cria um campo para armazenar o caminho do arquivo no banco de dados. E quando criado também move os arquivos automaticamente para o local/pasta designada no parametro upload_to (renomeando automáticamente o arquivo se já existir), onde ele busca a pasta na raiz do sistema e não do projeto. Para que não haja problemas, nas configurações da pasta projeto (setting.py) é necessário informar ao django onde devem ficar os arquivos upados na variável MEDIA_ROOT. Internamente, o FileField salva apenas o caminho do arquivo no banco de dados, enquanto o arquivo em si é salvo no local configurado por MEDIA_ROOT.

from django.db import models
class PerfilUsuario(models.Model):
# Cria uma pasta imagens_perfil dentro da pasta definida como MEDIA_ROOT (uploads)
imagem = models.FileField(upload_to="imagens_perfil")
# Se colocar /%Y/%m/%d/ o django automaticamente interpreta a data e cria uma pasta com a data atual o que facilita a busca e organiza melhor.
imagem = models.FileField(upload_to="imagens_perfil/%Y/%m/%d/")
# Define um caminho absoluto para armazenar os arquivos com base na pasta raiz.
MEDIA_ROOT = BASE / "uploads"
from django.shortcuts import render
from django.views import View
from django.http import HttpResponseRedirect
from .forms import PerfilForm
from .models import PerfilUsuario
class CriarPerfilView(View):
def get(self, request):
form = PerfilForm()
return render(request, "perfis/criar_perfil_upload.html",{
"form": form
})
def post(self, request):
formulario_enviado = PerfilForm(request.POST,request.FILES)
if formulario_enviado.is_valid():
perfil = PerfilUsuario(imagem=request.FILES['usuario_imagem'])
perfil.save()
return HttpResponseRedirect("/perfis")
return render(request, "perfis/criar_perfil_upload.html",{
"form": formulario_enviado
})
Um objeto do tipo modelo (PerfilUsuario) está sendo criado com o valor do arquivo enviado pelo formulário (request.FILES['usuario_imagem']) atribuído ao campo imagem desse modelo.
from django.views.generic import CreateView
from django.http import HttpResponseRedirect
from .models import PerfilUsuario
class CriarPerfilView(CreateView):
model = PerfilUsuario
template_name = "perfis/criar_perfil_upload.html"
fields = "__all__"
success_url = "/perfis"
Toda a nossa view, desse exemplo, pode ser substituída por essa CBV CreateView. Sem precisar do forms.py.
No exemplo acima usamos no campo o FileField mas para aceitar apenas imagens use ImageField. E para isso é nessário instalar um pacote extra
Instalar ImageField
$ python -m pip install Pillow
Arquivos Estáticos (Statics Files)
Os arquivos estáticos (statics) no Django são arquivos que não mudam frequentemente e são servidos diretamente para o navegador do cliente. Exemplos típicos incluem: Arquivos CSS, JavaScript, Imagens, Fontes. Geralmente, os arquivos estáticos são armazenados em um diretório chamado static no seu projeto ou dentro de cada aplicativo. Veja mais: Arquivos Estáticos.

STATIC_URL: URL base para os arquivos estáticos que informa o django onde ele deve SERVIR os arquivos staticos de seu projeto. o Django usa essa URL como base para encontrar o arquivo estatico referenciado no html.STATICFILES_DIRS: Diretórios extras com arquivos estáticos no sistema de arquivos. Durante o desenvolvimento, se você tem arquivos estáticos que não estão dentro de um app (por exemplo, arquivos CSS globais em uma pastastatic/no nível do projeto), você deve informar ao Django onde encontrá-los.STATIC_ROOT: Diretório onde os arquivos estáticos serão coletados para uso em ambiente de produção, Deve ser feita uma coleta de arquivos estáticos, onde, todos os arquivos estáticos são coletados em um único diretório especificado porSTATIC_ROOT. O comandocollectstaticcoleta toda a estrutura de diretórios dostatic/. preservando a estrutura demeu_app/static/meu_app.
Durante o desenvolvimento, o Django serve arquivos estáticos automaticamente. No entanto, em produção, é mais eficiente que o servidor web cuide disso (como nginx ou apache), distribuindo através do diretório definiddo em STATIC_ROOT.
Coleta de Arquivos Estáticos
A coleta de arquivos estáticos é feita através desse comando, no ambiente de produção, que transfere todos os arquivos para o diretório especificado em STATIC_ROOT configurado em settings.py. Ao aplicar esse comando, toda seus arquivos serão copiados para a pasta definida em settings.py, e toda vez que fizer alguma alteração, é necessário rodar o comando novamente, onde ele só copiará/substituirá arquivos que forem alterados.
Mas ATENÇÃO. Se os arquivos estiverem em pastas com a mesma estrutura relativa, mas em apps diferentes, o último arquivo processado pelo Django sobrescreverá o anterior. Para evitar problemas, mantenha os arquivos organizados com nomes únicos ou dentro de subpastas específicas de cada app.
$ python manage.py collectstatic
# Define o prefixo da URL para acessar os arquivos estáticos no navegador.
STATIC_URL = 'static/'
# instrui o Django a buscar arquivos estáticos também na pasta na raiz do projeto.
STATICFILES_DIRS = [
BASE_DIR / 'static/'
]
# Quando a aplicação está em produção, define a pasta onde todos os arquivos estáticos estarão
STATIC_ROOT = BASE_DIR / 'staticfiles/'
<!-- Carrega arquivo estáticos para pagina -->
{% load static %}
<!-- Mostra aonde carrega-los -->
<link rel="stylesheet" href="{% static 'css/header.css' %}">
Carregue o arquivo estático para página!
Servindo Arquivos
URL VS PATH
A propriedade url difere de path, onde a url representa a URL pública do arquivo, ou seja, o caminho completo acessível ao navegador ou cliente HTTP e deve ser usada para exibir o arquivo em um template ou fornecer um link público. Já path representa o caminho absoluto no sistema de arquivos do servidor onde o arquivo foi salvo, e deve ser usado em situações onde você precisa manipular diretamente o arquivo no servidor, como abrir, editar, deletar e executar operações específicas (ex.: redimensionar imagens, processar dados, etc.) no arquivo.
O Django automaticamente bloqueia o acesso a todas as pastas do projeto (menos a estática que ele configura uma url pública automáticamente, ou seja, ele serve css, imagens estáticas da pasta static apenas no ambiente de desenvolvimento), sendo assim, os arquivos de imagens ficam bloqueados para serem exibidos. Por isso a propriedade MEDIA_URL o arquivo settings.py deverá ser configurada.
MEDIA_URL é uma configuração que funciona como uma representação pública (URL) do caminho físico definido em MEDIA_ROOT. Ela não tem existência física no sistema de arquivos, mas serve para mapear as solicitações HTTP para os arquivos de mídia armazenados no diretório especificado por MEDIA_ROOT. Sendo assim MEDIA_URL como funciona como uma "ponte" que liga o mundo web ao sistema de arquivos físico.
| Configuração | Descrição | Exemplo |
|---|---|---|
MEDIA_ROOT |
Define onde os arquivos de mídia estão armazenados fisicamente no servidor. | /home/user/meuprojeto/media/ |
MEDIA_URL |
Define o caminho público para acessar esses arquivos pela web. | http://localhost:8000/media/ ou /media/ |
Além disso, no aquivo urls.py (do projeto principal) deve-se usar a função static(), para informar ao Django a url que deve ser usada para mostrar os arquivos publicamente ( MEDIA_URL) o caminho físico/path onde eles se encontram (MEDIA_ROOT). O operador + no contexto do Python soma listas, ou seja, ele concatena duas ou mais listas em uma só. No caso do Django, urlpatterns é uma lista de rotas, e static() retorna outra lista que contém as configurações para servir arquivos de mídia. O + junta essas duas listas.
Por padrão, segurança e desempenho a seguinte estrutura deve ser levada em consideração, definindo MEDIA_ROOT = BASE_DIR/"media/" e MEDIA_URL = "/media/" .Evite usar a pasta static/ para uploads de usuários já que é destinado a arquivos estáticos que não mudam (CSS, JS, imagens fixas). Já os uploads de usuários podem ser alterados frequentemente e exigem um caminho dinâmico, tornando o uso de media/ mais adequado.

| Exemplo Expor Arquivos
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Perfil do Usuário</title>
</head>
<body>
<h1>Perfis dos Usuários</h1>
<ul>
{% for perfil in perfis %}
<li><img src="{{ perfil.imagem.url }}" alt="Imagem do perfil"></li>
{% endfor %}
</ul>
</body>
</html>
from django.views.generic import ListView
from .models import PerfilUsuario
class PerfilView(ListView):
model = PerfilUsuario
template_name = "perfis/usuario_perfil.html"
context_object_name = "perfis"
from django.contrib import admin
from django.urls import path, include
# Importa o método static que é usado para informar a Django os arquivos que serão expostos
from django.conf.urls.static import static
# Importa MEDIA_URL (url pública) e MEDIA_ROOT (path físico)
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
path("perfis/", include("perfis.urls")),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
A função static cria uma rota de URL baseada em MEDIA_URL e faz o Django servir arquivos do diretório especificado em MEDIA_ROOT.
from django.urls import path
from . import views
urlpatterns = [
path("lista", views.PerfilView.as_view()),
]
MEDIA_ROOT = BASE_DIR / 'uploads'
# Configura acesso externo
MEDIA_URL = '/midias-usuarios/'
| Servir Arquivos Estáticos Pelo Django
Essa opção server para servidores pequenos sem muitas requisições.
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
path("perfis/", include("perfis.urls")),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \
# Essa linha configura o Django para servir os Arquivos estáticos
+ static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
# Também pode ser feito como abaixo
urlpatteners += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)