Sempre soube que é possível de ocultar os campos, mas e os benditos inlines? Todas as soluções que encontrei até hoje não resolviam isso. Ajudavam bastante, ou eram tão complicadas, que eram inviáveis de serem colocadas em prática. O que me salvou foi esta dica aqui que me deu as orientações de que precisava.
A dica explicava como sumir com os inlines no admin para os usuários sem privilégios de administrador. Basta deixar a propriedade inline_instances do ModelAdmin vazia, que os inlines somem sem causar nenhum problema. Esta foi a solução que ele utilizou:
def get_readonly_fields(self, request, obj=None): if request.user.is_superuser: return () else: self.inline_instances = [] return ()
Depois de vários testes, funcionou parcialmente no meu caso. O comportamente ficou estranho. Meio aleatório: hora funcionava, hora não. Nisso, acabei revirando o arquivo onde esse método se encontra na declaração da classe ModelAdmin no arquivo /django/contrib/admin/options.py do Django. Lá dentro encontrei um método relacionado ao get_readonly_fields, este aqui:
def get_fieldsets(self, request, obj=None): "Hook for specifying fieldsets for the add form." if self.declared_fieldsets: return self.declared_fieldsets form = self.get_form(request, obj) fields = form.base_fields.keys() + list(self.get_readonly_fields(request, obj)) return [(None, {'fields': fields})]
E foi ele quem resolveu meu problema.
Como disse, no meu caso eu queria sumir com os inlines quando estava na página de inserção e exibi-los quando estava na página de edição/inserção de um novo elemento. Então eu fiz uma verificação, não muito bonita, mas que funcionou muito bem:
class CategoriaAdmin(admin.ModelAdmin): """ Options for the admin interface """ inlines = [PaginaInline, ] list_display = ['categoria', 'ordem_exibicao', 'permalink'] def get_fieldsets(self, request, obj=None): "Hook for specifying fieldsets for the add form." if self.declared_fieldsets: return self.declared_fieldsets form = self.get_form(request, obj) fields = form.base_fields.keys() + list(self.get_readonly_fields(request, obj)) try: int(request.get_full_path().rsplit('/', 2)[1]) except: self.inline_instances = [] return [(None, {'fields': fields})]
Como dá pra ver, eu extrai o id do objeto da url do admin. Se tudo correr bem, é porque é uma edição. Caso ocorrer uma exceção, é uma inserção e eu sumo com os inlines.
Depois, que a inserção é feita, os inlines são exibidos. Porém, cada um desses inlines tem uma caixa de seleção (ForeignKey) e eu queria filtrá-la para exibir somente os objetos referentes ao objeto pai sendo editado. Esse caso é bem mais simples:
class PaginaInline(admin.StackedInline): """ Allows profile to be added when creating user """ model = Pagina extra = 1 #max_num = 1 formset = RequireOneFormSet def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name.startswith('item'): try: id = int(request.get_full_path().rsplit('/', 2)[1]) kwargs["queryset"] = Item.objects.filter( categoria__id=id ) except: kwargs["queryset"] = Item.objects.none() return super(PaginaInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Bem, o método formfield_for_foreignkey já é bem conhecido por aí. Então basta dizer que é o método chamado para recuperar os valores a serem exibidos em um campo que seja uma chave estrangeira. Dentro dele, editei kwargs["queryset"] que é a variável que armazena a consulta que vai retornar os valores desejados.
Eu ia terminar por aqui, mas acho que você deve estar se perguntando o que é esse formset chamado RequireOneFormSet. Se trata de um snippet muito útil que achei aqui. Ele serve para obrigar o usuário a cadastrar pelo menos um item, mesmo que nenhum seja obrigatório. No caso, o meu modelo inline tinha 6 campos não obrigatórios, mas não fazia sentido criá-lo sem ter ao menos um preenchido. Se o mínimo forem dois, é só editar o código desse formset e aumentar o valor no final (ali no "if completed < 1:"). Dê uma olhada nele:
from django import forms from django.forms.models import BaseInlineFormSet from django.utils.translation import ugettext as _ __all__ = ('RequireOneFormSet', ) class RequireOneFormSet(BaseInlineFormSet): """Require at least one form in the formset to be completed.""" def clean(self): """Check that at least one form has been completed.""" super(RequireOneFormSet, self).clean() for error in self.errors: if error: return completed = 0 for cleaned_data in self.cleaned_data: # form has data and we aren't deleting it. if cleaned_data and not cleaned_data.get('DELETE', False): completed += 1 if completed < 1: raise forms.ValidationError( _("At least one %s is required." % self.model._meta.object_name.lower()) )
Bem, por hoje é só pessoal! ;)
Nenhum comentário:
Postar um comentário