Personalizando o Site de Administração do Django

Beginner

Introdução

Este tutorial começa onde Adicionar uma Folha de Estilo e uma Imagem parou. Estamos continuando com a aplicação de enquete web e focaremos na personalização do site de administração gerado automaticamente pelo Django, que exploramos pela primeira vez em Configurar o Banco de Dados.

Personalizar o formulário de administração

Ao registrar o modelo Question com admin.site.register(Question), o Django foi capaz de construir uma representação de formulário padrão. Frequentemente, você desejará personalizar a aparência e o funcionamento do formulário de administração. Você fará isso informando ao Django as opções desejadas ao registrar o objeto.

Vamos ver como isso funciona reordenando os campos no formulário de edição. Substitua a linha admin.site.register(Question) por:

Edite o arquivo ~/project/mysite/polls/admin.py para que fique assim:

from django.contrib import admin

from .models import Question


class QuestionAdmin(admin.ModelAdmin):
    fields = ["pub_date", "question_text"]


admin.site.register(Question, QuestionAdmin)

Você seguirá este padrão -- crie uma classe de administração de modelo (model admin class), depois passe-a como o segundo argumento para admin.site.register() -- sempre que precisar alterar as opções de administração para um modelo.

Execute o servidor de desenvolvimento Django:

cd ~/project/mysite
python manage.py runserver

Abra http://127.0.0.1:8000/admin/ no Firefox ou no Ambiente de Trabalho (Desktop Environment) e clique no link "Questions". Você deve ver um formulário que se parece com este.

Esta alteração específica acima faz com que a "Data de publicação" (Publication date) venha antes do campo "Pergunta" (Question):

Admin form field reorder

Isso não é impressionante com apenas dois campos, mas para formulários de administração com dezenas de campos, escolher uma ordem intuitiva é um detalhe importante de usabilidade.

E falando em formulários com dezenas de campos, você pode querer dividir o formulário em fieldsets:

from django.contrib import admin

from .models import Question


class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {"fields": ["question_text"]}),
        ("Date information", {"fields": ["pub_date"]}),
    ]


admin.site.register(Question, QuestionAdmin)

O primeiro elemento de cada tupla em ~django.contrib.admin.ModelAdmin.fieldsets é o título do fieldset. Veja como nosso formulário se parece agora:

Admin form with fieldsets

Adicionando objetos relacionados

OK, temos nossa página de administração de Question, mas uma Question tem múltiplas Choices, e a página de administração não exibe as escolhas.

Ainda não.

Existem duas maneiras de resolver este problema. A primeira é registrar Choice com o admin, assim como fizemos com Question:

from django.contrib import admin

from .models import Choice, Question

## ...
admin.site.register(Choice)

Agora "Choices" é uma opção disponível no admin do Django. O formulário "Adicionar escolha" (Add choice) se parece com isto:

Add Choice form interface

Nesse formulário, o campo "Question" é uma caixa de seleção contendo todas as perguntas no banco de dados. O Django sabe que um ~django.db.models.ForeignKey deve ser representado no admin como uma caixa <select>. Em nosso caso, apenas uma pergunta existe neste ponto.

Observe também o link "Adicionar outra pergunta" (Add another question) ao lado de "Question". Cada objeto com um relacionamento ForeignKey com outro recebe isso gratuitamente. Quando você clica em "Adicionar outra pergunta", você obterá uma janela pop-up com o formulário "Adicionar pergunta". Se você adicionar uma pergunta nessa janela e clicar em "Salvar", o Django salvará a pergunta no banco de dados e a adicionará dinamicamente como a escolha selecionada no formulário "Adicionar escolha" que você está vendo.

Mas, na verdade, esta é uma maneira ineficiente de adicionar objetos Choice ao sistema. Seria melhor se você pudesse adicionar um monte de Choices diretamente quando criar o objeto Question. Vamos fazer isso acontecer.

Remova a chamada register() para o modelo Choice. Em seguida, edite o código de registro Question para ler:

from django.contrib import admin

from .models import Choice, Question


class ChoiceInline(admin.StackedInline):
    model = Choice
    extra = 3


class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None, {"fields": ["question_text"]}),
        ("Date information", {"fields": ["pub_date"], "classes": ["collapse"]}),
    ]
    inlines = [ChoiceInline]


admin.site.register(Question, QuestionAdmin)

Isso diz ao Django: "Objetos Choice são editados na página de administração de Question. Por padrão, forneça campos suficientes para 3 escolhas."

Carregue a página "Adicionar pergunta" para ver como isso fica:

Question admin with choices

Funciona assim: Existem três slots para Choices relacionadas -- conforme especificado por extra -- e cada vez que você volta para a página "Alterar" (Change) para um objeto já criado, você recebe mais três slots extras.

No final dos três slots atuais, você encontrará um link "Adicionar outra Choice" (Add another Choice). Se você clicar nele, um novo slot será adicionado. Se você quiser remover o slot adicionado, você pode clicar no X no canto superior direito do slot adicionado. Esta imagem mostra um slot adicionado:

Additional slot added dynamically

Um pequeno problema, no entanto. Leva muito espaço na tela para exibir todos os campos para inserir objetos Choice relacionados. Por essa razão, o Django oferece uma maneira tabular de exibir objetos relacionados inline. Para usá-lo, altere a declaração ChoiceInline para ler:

class ChoiceInline(admin.TabularInline):
    ...

Com esse TabularInline (em vez de StackedInline), os objetos relacionados são exibidos em um formato mais compacto, baseado em tabela:

Tabular inline choices display

Observe que há uma coluna extra "Excluir?" (Delete?) que permite remover linhas adicionadas usando o botão "Adicionar outra Choice" e linhas que já foram salvas.

Personalizar a lista de alterações do admin

Agora que a página de administração de Question está com boa aparência, vamos fazer alguns ajustes na página de "lista de alterações" (change list) -- aquela que exibe todas as perguntas no sistema.

Veja como ela se parece neste ponto:

Polls change list page

Por padrão, o Django exibe o str() de cada objeto. Mas, às vezes, seria mais útil se pudéssemos exibir campos individuais. Para fazer isso, use a opção de administração ~django.contrib.admin.ModelAdmin.list_display, que é uma tupla de nomes de campos a serem exibidos, como colunas, na página de lista de alterações do objeto:

class QuestionAdmin(admin.ModelAdmin):
    ## ...
    list_display = ["question_text", "pub_date"]

Para garantir, vamos também incluir o método was_published_recently() de **Configurar o Banco de Dados**:

class QuestionAdmin(admin.ModelAdmin):
    ## ...
    list_display = ["question_text", "pub_date", "was_published_recently"]

Agora, a página de lista de alterações da pergunta se parece com isto:

Question change list view

Você pode clicar nos cabeçalhos das colunas para classificar por esses valores -- exceto no caso do cabeçalho was_published_recently, porque a classificação pela saída de um método arbitrário não é suportada. Observe também que o cabeçalho da coluna para was_published_recently é, por padrão, o nome do método (com sublinhados substituídos por espaços), e que cada linha contém a representação de string da saída.

Você pode melhorar isso usando o decorador ~django.contrib.admin.display nesse método (em polls/models.py), da seguinte forma:

from django.contrib import admin


class Question(models.Model):
    ## ...
    @admin.display(
        boolean=True,
        ordering="pub_date",
        description="Published recently?",
    )
    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now

Para obter mais informações sobre as propriedades configuráveis por meio do decorador, consulte ~django.contrib.admin.ModelAdmin.list_display.

Edite seu arquivo polls/admin.py novamente e adicione uma melhoria à página de lista de alterações de Question: filtros usando ~django.contrib.admin.ModelAdmin.list_filter. Adicione a seguinte linha a QuestionAdmin:

list_filter = ["pub_date"]

Isso adiciona uma barra lateral "Filtro" (Filter) que permite que as pessoas filtrem a lista de alterações pelo campo pub_date:

Admin list filter sidebar

O tipo de filtro exibido depende do tipo de campo que você está filtrando. Como pub_date é um ~django.db.models.DateTimeField, o Django sabe dar opções de filtro apropriadas: "Qualquer data", "Hoje", "Últimos 7 dias", "Este mês", "Este ano".

Isso está tomando forma. Vamos adicionar alguma capacidade de pesquisa:

search_fields = ["question_text"]

Isso adiciona uma caixa de pesquisa na parte superior da lista de alterações. Quando alguém insere termos de pesquisa, o Django pesquisará o campo question_text. Você pode usar quantos campos quiser -- embora, como ele usa uma consulta LIKE nos bastidores, limitar o número de campos de pesquisa a um número razoável tornará mais fácil para seu banco de dados fazer a pesquisa.

Agora também é um bom momento para observar que as listas de alterações fornecem paginação gratuita. O padrão é exibir 100 itens por página. Paginação da lista de alterações <django.contrib.admin.ModelAdmin.list_per_page>, caixas de pesquisa <django.contrib.admin.ModelAdmin.search_fields>, filtros <django.contrib.admin.ModelAdmin.list_filter>, hierarquias de data <django.contrib.admin.ModelAdmin.date_hierarchy> e ordenação de cabeçalho de coluna <django.contrib.admin.ModelAdmin.list_display> funcionam juntos como você pensa que deveriam.

Personalizar a aparência e a sensação do admin

Claramente, ter "Administração do Django" no topo de cada página de administração é ridículo. É apenas texto de espaço reservado.

Você pode alterá-lo, no entanto, usando o sistema de templates do Django. O admin do Django é alimentado pelo próprio Django, e suas interfaces usam o sistema de templates do Django.

Personalizando os templates do seu projeto

Crie um diretório templates no diretório do seu projeto (aquele que contém manage.py). Os templates podem residir em qualquer lugar no seu sistema de arquivos que o Django possa acessar. (O Django é executado como qualquer usuário que seu servidor execute.) No entanto, manter seus templates dentro do projeto é uma boa convenção a seguir.

Abra seu arquivo de configurações (mysite/settings.py, lembre-se) e adicione uma opção DIRS <TEMPLATES-DIRS> na configuração TEMPLATES:

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

DIRS <TEMPLATES-DIRS> é uma lista de diretórios do sistema de arquivos a serem verificados ao carregar templates do Django; é um caminho de pesquisa.

Organizando templates

Assim como os arquivos estáticos, poderíamos ter todos os nossos templates juntos, em um grande diretório de templates, e funcionaria perfeitamente bem. No entanto, os templates que pertencem a uma aplicação específica devem ser colocados no diretório de templates dessa aplicação (por exemplo, polls/templates) em vez do projeto (templates). Discutiremos em mais detalhes no tutorial de aplicativos reutilizáveis </intro/reusable-apps> por que fazemos isso.

Agora, crie um diretório chamado admin dentro de templates e copie o template admin/base_site.html de dentro do diretório de templates padrão do admin do Django no código-fonte do próprio Django (django/contrib/admin/templates) para esse diretório.

Onde estão os arquivos-fonte do Django?

Se você tiver dificuldade em encontrar onde os arquivos-fonte do Django estão localizados em seu sistema, execute o seguinte comando:

python -c "import django; print(django.__path__)"
['/home/labex/.local/lib/python3.10/site-packages/django']

Em seguida, edite o arquivo e substitua {{ site_header|default:_('Django administration') }} (incluindo as chaves) pelo nome do seu próprio site, conforme achar adequado. Você deve acabar com uma seção de código como:

{% block branding %}
<div id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a><div>
{% endblock %}

Usamos essa abordagem para ensinar como substituir templates. Em um projeto real, você provavelmente usaria o atributo django.contrib.admin.AdminSite.site_header para fazer essa personalização específica com mais facilidade.

Este arquivo de template contém muito texto como {% block branding %} e {{ title }}. As tags {% e {{ fazem parte da linguagem de templates do Django. Quando o Django renderiza admin/base_site.html, essa linguagem de templates será avaliada para produzir a página HTML final, assim como vimos em **Criando as Visualizações da Interface Pública**.

Observe que qualquer um dos templates de administração padrão do Django pode ser substituído. Para substituir um template, faça a mesma coisa que você fez com base_site.html -- copie-o do diretório padrão para seu diretório personalizado e faça alterações.

Personalizando os templates da sua aplicação

Leitores astutos perguntarão: Mas se DIRS <TEMPLATES-DIRS> estivesse vazio por padrão, como o Django estava encontrando os templates de administração padrão? A resposta é que, como APP_DIRS <TEMPLATES-APP_DIRS> está definido como True, o Django procura automaticamente um subdiretório templates/ dentro de cada pacote de aplicação, para uso como fallback (não se esqueça que django.contrib.admin é uma aplicação).

Nossa aplicação de enquetes não é muito complexa e não precisa de templates de administração personalizados. Mas se ela se tornasse mais sofisticada e exigisse a modificação dos templates de administração padrão do Django para alguma de suas funcionalidades, seria mais sensato modificar os templates da aplicação, em vez daqueles do projeto. Dessa forma, você poderia incluir a aplicação de enquetes em qualquer novo projeto e ter a certeza de que ela encontraria os templates personalizados de que precisa.

Consulte a documentação de carregamento de templates <template-loading> para obter mais informações sobre como o Django encontra seus templates.

Personalizar a página de índice do admin

De forma semelhante, você pode querer personalizar a aparência e a sensação da página de índice do admin do Django.

Por padrão, ela exibe todos os aplicativos em INSTALLED_APPS que foram registrados com o aplicativo admin, em ordem alfabética. Você pode querer fazer alterações significativas no layout. Afinal, o índice é provavelmente a página mais importante do admin, e deve ser fácil de usar.

O template a ser personalizado é admin/index.html. (Faça o mesmo que com admin/base_site.html na seção anterior -- copie-o do diretório padrão para seu diretório de templates personalizado). Edite o arquivo e você verá que ele usa uma variável de template chamada app_list. Essa variável contém todos os aplicativos Django instalados. Em vez de usar isso, você pode codificar links para páginas de administração específicas de objetos da maneira que achar melhor.

Resumo

Parabéns! Você concluiu o laboratório de Personalização do Site de Administração do Django. Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.