Destaques do Django: disputando ativos estáticos e arquivos de mídia (Parte 4)
Publicados: 2022-03-10Os sites Django envolvem muitos arquivos. Não é apenas o código-fonte para a configuração, modelos, visualizações e modelos, mas também ativos estáticos: CSS e JavaScript, imagens, ícones. Como se isso já não bastasse, às vezes os usuários chegam e querem fazer upload de seus próprios arquivos para o seu site. É o suficiente para deixar qualquer desenvolvedor incrédulo. Arquivos em todos os lugares!
Aqui é onde eu gostaria de poder dizer (sem ressalvas): “Não se preocupe, o Django te protege!” Mas, infelizmente, ao lidar com ativos estáticos e arquivos de mídia, há muitas ressalvas com as quais lidar.
Hoje, abordaremos o armazenamento e a entrega de arquivos para implantações de servidor único e escaláveis, considerando fatores como compactação, armazenamento em cache e disponibilidade. Também discutiremos os custos e benefícios de CDNs e soluções de armazenamento de arquivos dedicados.
Nota : Este não é um tutorial sobre como implantar um site Django em qualquer plataforma específica. Em vez disso, como os outros artigos da série Django Highlights (veja abaixo), ele pretende ser um guia para desenvolvedores e designers de front-end entenderem outras partes do processo de criação de um aplicativo da web. Hoje, estamos nos concentrando no que acontece depois que o hotfix de estilo ou o belo gráfico que você acabou de terminar é forçado a dominar. Juntos, desenvolveremos uma intuição para as estratégias disponíveis para os desenvolvedores do Django para servir esses arquivos para usuários em todo o mundo de maneira segura, eficiente e econômica.
Partes anteriores da série:
- Parte 1: Modelos de usuário e autenticação
- Parte 2: A modelagem salva as linhas
- Parte 3: Modelos, administração e aproveitamento do banco de dados relacional
Definições
A maioria desses termos é bastante direta, mas vale a pena dedicar um momento para estabelecer um vocabulário compartilhado para esta discussão.
Os três tipos de arquivos em um aplicativo Django ao vivo são:
- Código fonte
Os arquivos Python e HTML que são criados com o framework Django. Esses arquivos são o núcleo do aplicativo. Os arquivos de código-fonte geralmente são bem pequenos, medidos em kilobytes. - Arquivos estáticos
Também chamados de “ativos estáticos”, esses arquivos incluem CSS e JavaScript, ambos escritos pelo desenvolvedor do aplicativo e bibliotecas de terceiros, bem como PDFs, instaladores de software, imagens, músicas, vídeos e ícones. Esses arquivos são usados apenas no lado do cliente. Os arquivos estáticos variam de alguns kilobytes de CSS a gigabytes de vídeo. - Arquivos de mídia
Qualquer arquivo carregado por um usuário, de fotos de perfil a documentos pessoais, é chamado de arquivo de mídia. Esses arquivos precisam ser armazenados e recuperados de forma segura e confiável para o usuário. Os arquivos de mídia podem ser de qualquer tamanho, o usuário pode enviar alguns kilobytes de texto simples para alguns gigabytes de vídeo. Se você estiver no último extremo dessa escala, provavelmente precisará de conselhos mais especializados do que este artigo está preparado para dar.
Os dois tipos de implantações do Django são:
- Servidor Único
Uma implantação de servidor único do Django é exatamente o que parece: tudo vive em um único servidor. Essa estratégia é muito simples e se parece muito com o ambiente de desenvolvimento, mas não pode lidar com quantidades grandes ou inconsistentes de tráfego de forma eficaz. A abordagem de servidor único é aplicável apenas para projetos de aprendizado ou demonstração, não para aplicativos reais que exigem tempo de atividade confiável. - Escalável
Existem muitas maneiras diferentes de implantar um projeto Django que permite dimensioná-lo para atender à demanda do usuário. Essas estratégias geralmente envolvem a ativação e desativação de vários servidores e o uso de ferramentas como balanceadores de carga e bancos de dados gerenciados. Felizmente, podemos agrupar efetivamente tudo mais complexo do que uma implantação de servidor único nessa categoria para os propósitos deste artigo.
Opção 1: Django padrão
Pequenos projetos se beneficiam de uma arquitetura simples. O manuseio padrão do Django de ativos estáticos e arquivos de mídia é exatamente isso: simples. Para cada um, você tem uma pasta raiz que armazena os arquivos e fica ao lado do código-fonte no servidor. Simples. Essas pastas raiz são geradas e gerenciadas principalmente por meio da configuração yourproject/settings.py .
Ativos estáticos
A coisa mais importante para entender ao trabalhar com arquivos estáticos no Django é o comando python manage.py collectstatic
. Este comando vasculha a pasta estática de cada aplicativo no projeto Django e copia todos os ativos estáticos para a pasta raiz. A execução deste comando é uma parte importante da implantação de um projeto Django. Considere a seguinte estrutura de diretórios:
- project - project - settings.py - urls.py - ... - app1 - static/ - app1 - style.css - script.js - img.jpg - templates/ - views.py - ... - app2 - static/ - app2 - style.css - image.png - templates/ - views.py - ...
Assuma também as seguintes configurações em project/settings.py :
STATIC_URL = "/static/" STATIC_ROOT = "/path/on/server/to/djangoproject/static"
A execução do comando python manage.py collectstatic
criará a seguinte pasta no servidor:
- /path/on/server/to/djangoproject/static - app1 - style.css - script.js - img.jpg - app2 - style.css - image.png
Observe que dentro de cada pasta estática, há outra pasta com o nome do aplicativo. Isso é para evitar conflitos de namespace depois que os arquivos estáticos são coletados; como você pode ver na estrutura de arquivos acima, isso mantém app1/style.css e app2/style.css distintos. A partir daqui, o aplicativo procurará arquivos estáticos nessa estrutura no STATIC_ROOT
durante a produção. Como tal, faça referência a arquivos estáticos da seguinte maneira em um modelo em app1/templates/ :
{% load static %} <link rel="stylesheet" type="text/css" href="{% static "app1/style.css" %}">
O Django descobre automaticamente de onde obter o arquivo estático em desenvolvimento para modelar esse comportamento, você não precisa executar o collectstatic
durante o desenvolvimento.
Para mais detalhes, veja a documentação do Django.
Arquivos de mídia
Imagine um site de rede profissional com um banco de dados de usuários. Cada um desses usuários teria um perfil associado, que poderia conter, entre outras coisas, uma imagem de avatar e um documento de currículo. Aqui está um pequeno modelo de exemplo dessas informações:
from django.db import models from django.contrib.auth.models import User def avatar_path(instance, filename): return "avatar_{}_{}".format(instance.user.id, filename) class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) resume = models.FileField(upload_to="path/string") avatar = models.ImageField(upload_to=avatar_path)
Para que isso funcione, você precisa das seguintes opções em project/settings.py , como com ativos estáticos:
MEDIA_URL = "/media/" MEDIA_ROOT = "/path/on/server/to/media"
Um ImageField
herda de FileField
, portanto, compartilha os mesmos parâmetros e recursos. Ambos os campos têm um argumento upload_to
opcional, que pega uma string que é um caminho e a anexa ao MEDIA_ROOT
para armazenar o arquivo, que é então acessível pelo mesmo caminho no topo de MEDIA_URL
. O argumento upload_to
também pode receber uma função que retorna uma string, conforme demonstrado com a função avatar_path
.
Certifique-se de omitir o diretório de arquivos de mídia e seu conteúdo do controle de versão. Seu conteúdo pode entrar em conflito quando dois desenvolvedores testam o mesmo aplicativo em máquinas diferentes e, ao contrário dos ativos estáticos, não faz parte do aplicativo Django implementável.
Opção 2: Django com serviços
Minha filosofia orientadora é usar ferramentas para o que elas fazem de melhor. O Django é uma estrutura incrível e fornece ótimas ferramentas prontas para autenticação de usuário, renderização do lado do servidor, trabalhar com modelos e formulários, funções administrativas e dezenas de outros aspectos essenciais da construção de aplicativos da web. No entanto, suas ferramentas para lidar com ativos estáticos e arquivos de mídia não são, na minha opinião, adequadas para produção em sites escaláveis. Os desenvolvedores principais do Django reconhecem que muitas pessoas escolhem abordagens alternativas para lidar com esses arquivos em produção; a estrutura é muito boa em sair do seu caminho quando você faz isso. A maioria dos sites Django destinados ao uso geral desejará incorporar ativos estáticos e manipular arquivos de mídia usando essas abordagens não específicas do Django.
Ativos estáticos em uma CDN
Enquanto projetos de pequeno a médio porte podem sair sem um, uma CDN (rede de entrega de conteúdo) é fácil de usar e melhora o desempenho de aplicativos de qualquer tamanho. Uma CDN é uma rede de servidores, geralmente em todo o mundo, que distribui e serve conteúdo da web, principalmente ativos estáticos. CDNs populares incluem Cloudflare CDN, Amazon CloudFront e Fastly. Para usar um CDN, você carrega seus arquivos estáticos e, em seguida, em seu aplicativo, referencie-os da seguinte maneira:
<link rel="stylesheet" type="text/css" href="https://cdn.example.com/path/to/your/files/app1/style.css">
Esse processo é fácil de integrar com seus scripts de implantação do Django. Depois de executar o comando python manage.py collectstatic
, copie o diretório gerado para seu CDN (um processo que varia substancialmente com base no serviço que você está usando) e remova os ativos estáticos do pacote de implantação do Django.
No desenvolvimento, você desejará acessar cópias diferentes de seus ativos estáticos do que na produção. Dessa forma, você pode fazer alterações localmente sem afetar o site de produção. Você pode usar ativos locais ou executar uma segunda instância da CDN para entregar os arquivos. Configure yourproject/settings.py com alguma variável personalizada, como CDN_URL
, e use esse valor em seus modelos para garantir que você esteja usando a versão correta dos ativos em desenvolvimento e produção.
Uma observação final é que muitas bibliotecas para CSS e JavaScript têm CDNs gratuitos que a maioria dos sites pode usar. Se estiver carregando, digamos, Bootstrap 4 ou underscore.js, você pode pular o incômodo de usar sua própria cópia em desenvolvimento e a despesa de servir suas próprias cópias em produção usando essas CDNs públicas.
Arquivos de mídia com um armazenamento de arquivos dedicado
Nenhum site Django de produção deve armazenar arquivos de usuário em uma pasta /media/ simples em algum lugar no servidor que executa o site. Aqui estão três das muitas razões pelas quais não:
- Se você precisar escalar o site adicionando vários servidores, precisará de alguma forma de copiar e sincronizar os arquivos carregados nesses servidores.
- Se um servidor travar, é feito backup do código-fonte em seu sistema de controle de versão, mas os arquivos de mídia não são copiados por padrão, a menos que você tenha configurado seu servidor para isso, mas para esse esforço seria melhor usar um armazenamento de arquivos.
- Em caso de atividade maliciosa, é um pouco melhor manter os arquivos carregados pelo usuário em um servidor separado daquele que executa o aplicativo, embora isso de forma alguma remova a necessidade de validar os arquivos carregados pelo usuário.
Integrar um terceiro para armazenar seus arquivos enviados pelo usuário é muito fácil. Você não precisa alterar nada em seu código, exceto talvez remover ou modificar o valor upload_to
de FileField
s em seus modelos e definir algumas configurações. Por exemplo, se você planeja armazenar seus arquivos no AWS S3, faça o seguinte, que é muito semelhante ao processo de armazenamento de arquivos com Google Cloud, Azure, Backblaze ou serviços concorrentes semelhantes.
Primeiro, você precisará instalar as bibliotecas boto3
e django-storages
. Em seguida, você precisa configurar um bucket e uma função do IAM na AWS, o que está fora do escopo deste artigo, mas você pode ver as instruções aqui. Depois que tudo isso estiver configurado, você precisa adicionar três variáveis ao seu project/settings.py :
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage" AWS_STORAGE_BUCKET_NAME = "BUCKET_NAME" AWS_S3_REGION_NAME = "us-east-2"
Além disso, você precisará configurar o acesso de credencial ao seu bucket da AWS. Alguns tutoriais demonstrarão como adicionar um ID e uma chave secreta ao seu arquivo de configurações ou como variáveis de ambiente, mas essas são práticas inseguras. Em vez disso, use django-storages
com a AWS CLI para configurar as chaves, conforme descrito aqui. Você também pode estar interessado na documentação do django-storages
.
Você não quer que arquivos de mídia de desenvolvimento ou teste sejam misturados com uploads de usuários reais. Evitar isso é bem simples: configure vários buckets, um para desenvolvimento (ou um para cada desenvolvedor), um para teste e outro para produção. Em seguida, tudo o que você precisa alterar é a configuração AWS_STORAGE_BUCKET_NAME
por ambiente e pronto.
Desempenho e disponibilidade
Existem vários fatores que afetam o desempenho e a confiabilidade do seu site. Aqui estão alguns importantes ao considerar arquivos estáticos e de mídia que importam, independentemente de qual abordagem você adota para gerenciá-los.
Custo
Servir arquivos a um usuário custa dinheiro por dois motivos: armazenamento e largura de banda. Você tem que pagar ao provedor de hospedagem para armazenar os arquivos para você, mas também tem que pagá-los para servir os arquivos. A largura de banda é substancialmente mais cara do que o armazenamento (por exemplo, o AWS S3 cobra 2,3 centavos por gigabyte para armazenamento versus 9 centavos por gigabyte de transferência de dados para a Internet no momento da redação). A economia de um armazenamento de arquivos como o S3 ou um CDN é diferente da economia de um host generalizado como um droplet Digital Ocean. Aproveite a especialização e as economias de escala movendo arquivos caros para serviços projetados para eles. Além disso, muitos armazenamentos de arquivos e CDNs oferecem planos gratuitos para que sites que podem ser pequenos o suficiente para sair sem usá-los possam fazê-lo e colher os benefícios sem custos adicionais de infraestrutura.
Compressão e Transcodificação
A maioria dos problemas causados por ativos estáticos, como fotos e vídeos, ocorre porque são arquivos grandes. Naturalmente, os desenvolvedores resolvem isso tentando tornar esses arquivos menores. Há várias maneiras de fazer isso usando uma combinação de compactação e transcodificação em duas categorias gerais: sem perdas e com perdas. A compactação sem perdas mantém a qualidade original dos ativos, mas fornece reduções relativamente modestas no tamanho do arquivo. A compactação com perdas, ou transcodificação em um formato com perdas, permite tamanhos de arquivo muito menores à custa de perder parte da qualidade do artefato original. Um exemplo disso é a transcodificação de vídeo para uma taxa de bits mais baixa. Para obter detalhes, confira este artigo sobre como otimizar a entrega de vídeo. Ao servir arquivos grandes pela Web, as velocidades de largura de banda geralmente exigem que você forneça artefatos altamente compactados, exigindo compactação com perdas.
A menos que você seja o YouTube, a compactação e a transcodificação não acontecem na hora. Os ativos estáticos devem ser formatados adequadamente antes da implantação, e você pode aplicar restrições básicas de tipo de arquivo e tamanho de arquivo em uploads de usuários para garantir compactação suficiente e formatação apropriada nos arquivos de mídia de seus usuários.
Minificação
Embora os arquivos de JavaScript e CSS geralmente não sejam tão grandes quanto imagens, eles geralmente podem ser compactados para compactar em menos bytes. Esse processo é chamado de minificação. A minificação não altera a codificação dos arquivos, eles ainda são texto e um arquivo minificado ainda precisa ser um código válido para seu idioma original. Os arquivos reduzidos mantêm suas extensões originais.
A principal coisa removida em um arquivo minificado é o espaço em branco desnecessário e, da perspectiva do computador, quase todo o espaço em branco em CSS e JavaScript é desnecessário. Esquemas de minificação também encurtam nomes de variáveis e removem comentários.
A minificação por padrão ofusca o código; como desenvolvedor, você deve trabalhar exclusivamente com arquivos não minificados. Alguma etapa automática durante o processo de implantação deve reduzir os arquivos antes de serem armazenados e servidos. Se você estiver usando uma biblioteca fornecida por uma CDN de terceiros, verifique se está usando a versão reduzida dessa biblioteca, se disponível. Arquivos HTML podem ser reduzidos, mas como o Django usa renderização do lado do servidor, o custo de processamento de fazê-lo em tempo real provavelmente superaria a pequena diminuição no tamanho da página.
Disponibilidade Global
Assim como leva menos tempo para enviar uma carta ao seu vizinho do que para enviá-la pelo país, leva menos tempo para transmitir dados nas proximidades do que em todo o mundo. Uma das maneiras pelas quais uma CDN melhora o desempenho da página é copiando ativos em servidores em todo o mundo. Então, quando um cliente faz uma solicitação, ele recebe os ativos estáticos do servidor mais próximo (geralmente chamado de nó de borda), diminuindo os tempos de carregamento. Uma das vantagens de usar uma CDN com um site Django é desacoplar a distribuição global de seus ativos estáticos da distribuição global de seu código.
Cache do lado do cliente
O que é melhor do que ter um arquivo estático em um servidor próximo ao seu usuário? Ter o arquivo estático já armazenado no dispositivo do seu usuário! Cache é o processo de armazenar os resultados de uma computação ou solicitação para que possam ser acessados repetidamente com mais rapidez. Assim como uma folha de estilo CSS pode ser armazenada em cache em todo o mundo em uma CDN, ela pode ser armazenada em cache no navegador do cliente na primeira vez que ele carrega uma página do seu site. Em seguida, a folha de estilo fica disponível no próprio dispositivo nas solicitações subsequentes, de modo que o cliente está fazendo menos solicitações, melhorando o tempo de carregamento da página e diminuindo o uso da largura de banda.
Os navegadores executam suas próprias operações de cache, mas se o seu site tiver tráfego substancial, você pode otimizar o comportamento de cache do lado do cliente usando a estrutura de cache do Django.
Para concluir
Novamente, minha filosofia orientadora é usar as ferramentas para o que elas fazem de melhor. Projetos de servidor único e pequenas implantações escaláveis com apenas ativos estáticos leves podem usar o gerenciamento de ativos estáticos integrado do Django, mas a maioria dos aplicativos deve separar os ativos a serem servidos em uma CDN.
Se o seu projeto for destinado a qualquer tipo de uso de palavra real, não armazene arquivos de mídia com o método padrão do Django, em vez disso, use um serviço. Com tráfego suficiente, onde “tráfego suficiente” é um número relativamente pequeno na escala da Internet, as complicações adicionais à arquitetura, ao processo de desenvolvimento e à implantação valem a pena pelo desempenho, confiabilidade e economia de custos de usar um CDN e solução de armazenamento de arquivos separados para arquivos estáticos e de mídia, respectivamente.
Leitura recomendada
- Parte 1: Modelos de usuário e autenticação
- Parte 2: A modelagem salva as linhas
- Parte 3: Modelos, administração e aproveitamento do banco de dados relacional