terça-feira, 26 de março de 2013

Django admin: inlines obrigatórios

Um problema recorrente nos projetos em que trabalhei com Django foi tornar inlines obrigatórios no admin. Fiz diversas gambiarras, conforme a situação,  para tentar resolver isso, mas nunca havia encontrado uma forma satisfatória de resolver esse problema, até agora. Pois sabemos como os usuários são: você avisa que se não fizer de tal modo vai dar problema. O que acontece? Eles ignoram, isso causa um monte de problema e, claro, no final nós desenvolvedores somos os culpados. Enfim.

Depois de bastante tempo de procura, acabei caindo nesta discussão sobre o assunto que me levou a um snippet, um fragmento de código, que resolve o problema. O código a seguir foi obtido aqui. Ele altera o comportamento padrão da classe BaseInlineFormSet que é usada pelo admin para montar seus formulários, fazendo com que obrigue o usuário a salvar ao menos um inline.
Veja:

from django import forms
from django.forms.models import BaseInlineFormSet
from django.utils.translation import ugettext as _

__all__ = ('RequireOneFormSet',)

class RequireOneFormSet(BaseInlineFormSet):
    """Exige que ao menos um formulario do conjunto seja salvo."""
    def clean(self):
        """Verifica se ao menos um inline foi salvo."""
        super(RequireOneFormSet, self).clean()
        for error in self.errors:
            if error:
                return
        inlines = 0
        for cleaned_data in self.cleaned_data:
            #Percorre todos os inlines sendo salvos.
            if cleaned_data and not cleaned_data.get('DELETE', False):
                inlines += 1

        if inlines < 1:
            raise forms.ValidationError( _("At least one %s is required." %
                self.model._meta.object_name.lower()) )


Note que podemos mudar o valor na comparação no final para obrigar que mais inlines sejam salvos, ao invés de um só. Bem, agora só faltou responder como utilizar esse código, não é mesmo? É bem simples. Vá até o admin.py onde está a sua classe inline e atribua essa nova classe que criamos ao atributo formset. Desta forma:
class ImagemInline(admin.StackedInline):
    model = Imagem
    extra = 1
    formset = RequireOneFormSet

Pronto! Quando for tentar salvar um objeto no admin que tenha inlines desta classe um erro de validação vai acontecer e exigir que ao menos um inline seja criado. Isso vai evitar que o usuário deixe de salvar informações importantes que os inlines poderiam conter e te salvar de muitas dores de cabeça, como me salvou. ;)

Até a próxima!





Nenhum comentário: