Redimensionar campos no Django Admin

107

Django tende a preencher espaço horizontal ao adicionar ou editar entradas no admin, mas, em alguns casos, é um verdadeiro desperdício de espaço, quando, ou seja, editar um campo de data, de 8 caracteres de largura, ou um CharField, também de 6 ou 8 caracteres de largura, e a caixa de edição sobe para 15 ou 20 caracteres.

Como posso dizer ao administrador a largura de uma caixa de texto ou a altura de uma caixa de edição TextField?

Andor
fonte
9
Obviamente, você se esqueceu de fazer isso neste caso. Mais de dois anos depois. =)
casperOne
O projeto foi descartado e eu não consegui ver o código por um tempo. Posso começar um novo projeto no mês que vem, então talvez veja novamente: D
Andor

Respostas:

209

Você deve usar ModelAdmin.formfield_overrides .

É muito fácil - em admin.py, defina:

from django.forms import TextInput, Textarea
from django.db import models

class YourModelAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.CharField: {'widget': TextInput(attrs={'size':'20'})},
        models.TextField: {'widget': Textarea(attrs={'rows':4, 'cols':40})},
    }

admin.site.register(YourModel, YourModelAdmin)
jki
fonte
1
Isso era exatamente o que eu estava procurando sem ter que fazer algumas personalizações ridículas de modelo. Obrigado!
Chris,
2
Observe que isso não funcionará se filter_horizontalou filter_verticalfor definido para os campos correspondentes em YourModelAdmin. Passei algum tempo tentando descobrir isso.
Dennis Golomazov,
Existe algum problema com isso no formulário? Eu não encontrei uma maneira de definir o atributo attrs para todas as Textareas.
Juliano,
1
Usei apenas, models.TextField: {'widget': Textarea(attrs={'rows':4})}mas a largura também ficou menor.
Smit Johnth de
É difícil tê-los do mesmo tamanho aparentemente.
Sören
44

Você pode definir atributos HTML arbitrários em um widget usando sua propriedade "attrs" .

Você pode fazer isso no administrador do Django usando formfield_for_dbfield:

class MyModelAdmin(admin.ModelAdmin):
  def formfield_for_dbfield(self, db_field, **kwargs):
    field = super(ContentAdmin, self).formfield_for_dbfield(db_field, **kwargs)
    if db_field.name == 'somefield':
      field.widget.attrs['class'] = 'someclass ' + field.widget.attrs.get('class', '')
    return field

ou com uma subclasse de widget customizada e o dicionário formfield_overrides :

class DifferentlySizedTextarea(forms.Textarea):
  def __init__(self, *args, **kwargs):
    attrs = kwargs.setdefault('attrs', {})
    attrs.setdefault('cols', 80)
    attrs.setdefault('rows', 5)
    super(DifferentlySizedTextarea, self).__init__(*args, **kwargs)

class MyModelAdmin(admin.ModelAdmin):
  formfield_overrides = { models.TextField: {'widget': DifferentlySizedTextarea}}
Carl Meyer
fonte
29

Para alterar a largura de um campo específico.

Feito via ModelAdmin.get_form :

class YourModelAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        form = super(YourModelAdmin, self).get_form(request, obj, **kwargs)
        form.base_fields['myfield'].widget.attrs['style'] = 'width: 45em;'
        return form
Alanjds
fonte
Na minha opinião, esta é a melhor resposta, pois permite alterar campos individuais sem ter que criar um novo formulário.
rbennell
20

Uma opção rápida e suja é simplesmente fornecer um modelo personalizado para o modelo em questão.

Se você criar um modelo denominado admin/<app label>/<class name>/change_form.html, o administrador usará esse modelo em vez do padrão. Ou seja, se você tiver um modelo nomeado Personem um aplicativo denominado people, você criará um modelo denominado admin/people/person/change_form.html.

Todos os modelos de administração têm um extraheadbloco que você pode sobrescrever para colocar coisas no <head>, e a peça final do quebra-cabeça é o fato de que cada campo tem um ID HTML de id_<field-name>.

Então, você pode colocar algo como o seguinte em seu modelo:

{% extends "admin/change_form.html" %}

{% block extrahead %}
  {{ block.super }}
  <style type="text/css">
    #id_my_field { width: 100px; }
  </style>
{% endblock %}
jacobiano
fonte
Obrigado! Esta e a resposta por alanjds são muito úteis para alterar o layout na interface de administração por campo, em vez de por tipo de campo.
nealmcb
10

Se desejar alterar os atributos em uma instância por campo, você pode adicionar a propriedade "attrs" diretamente nas entradas do formulário.

por exemplo:

class BlogPostForm(forms.ModelForm):
    title = forms.CharField(label='Title:', max_length=128)
    body = forms.CharField(label='Post:', max_length=2000, 
        widget=forms.Textarea(attrs={'rows':'5', 'cols': '5'}))

    class Meta:
        model = BlogPost
        fields = ('title', 'body')

A propriedade "attrs" basicamente transmite a marcação HTML que ajustará o campo do formulário. Cada entrada é uma tupla do atributo que você gostaria de substituir e o valor que você gostaria de substituir. Você pode inserir quantos atributos desejar, contanto que separe cada tupla com uma vírgula.

Jonathan
fonte
IMO a melhor maneira de modificar apenas um único campo e não todos os widgets TextInput de uma vez (como a resposta aceita faz)
ascripter
7

A melhor maneira que encontrei é algo assim:

class NotificationForm(forms.ModelForm):
    def __init__(self, *args, **kwargs): 
        super(NotificationForm, self).__init__(*args, **kwargs)
        self.fields['content'].widget.attrs['cols'] = 80
        self.fields['content'].widget.attrs['rows'] = 15
        self.fields['title'].widget.attrs['size'] = 50
    class Meta:
        model = Notification

É muito melhor para ModelForm do que substituir campos com widgets diferentes, pois preserva namee help_textatribui os valores padrão dos campos do modelo, então você não precisa copiá-los para o seu formulário.

Kurczak
fonte
7

Tive um problema semelhante com TextField. Estou usando Django 1.0.2 e queria alterar o valor padrão para 'linhas' na textarea associada. formfield_overrides não existe nesta versão. Substituir formfield_for_dbfield funcionou, mas eu tive que fazer isso para cada uma das minhas subclasses ModelAdmin ou resultaria em um erro de recursão. Por fim, descobri que adicionar o código abaixo a models.py funciona:

from django.forms import Textarea

class MyTextField(models.TextField):
#A more reasonably sized textarea                                                                                                            
    def formfield(self, **kwargs):
         kwargs.update(
            {"widget": Textarea(attrs={'rows':2, 'cols':80})}
         )
         return super(MyTextField, self).formfield(**kwargs)

Em seguida, use MyTextField em vez de TextField ao definir seus modelos. Eu adaptei desta resposta a uma pergunta semelhante.

msdin
fonte
5

Está bem descrito no FAQ do Django :

Q: Como altero os atributos de um widget em um campo em meu modelo?

R: Substitua o formfield_for_dbfield na classe ModelAdmin / StackedInline / TabularInline

class MyOtherModelInline(admin.StackedInline):
    model = MyOtherModel
    extra = 1

    def formfield_for_dbfield(self, db_field, **kwargs):
        # This method will turn all TextFields into giant TextFields
        if isinstance(db_field, models.TextField):
            return forms.CharField(widget=forms.Textarea(attrs={'cols': 130, 'rows':30, 'class': 'docx'}))
        return super(MyOtherModelInline, self).formfield_for_dbfield(db_field, **kwargs)
HardQuestions
fonte
Quando eu uso essa técnica, o texto de ajuda não aparece e, se for um TabularInline, o cabeçalho da coluna se torna 'Nenhum'.
Steven T. Snyder
1
Esta não é uma boa abordagem, já que você está apagando todas as outras configurações do CharField. Por exemplo, por padrão, ele detectará de forma inteligente se o campo do formulário deve ou não ser obrigatório, mas este código destrói isso. Em vez disso, você deve definir o widget no valor retornado por sua chamada super ().
Cerin
5

Você sempre pode definir os tamanhos dos seus campos em uma folha de estilo personalizada e dizer ao Django para usar isso para a sua classe ModelAdmin:

class MyModelAdmin(ModelAdmin):
    class Media:
        css = {"all": ("my_stylesheet.css",)}
Gonzalo
fonte
melhor resposta! sem mexer com campos de formulário e atributos (e possíveis efeitos colaterais), apenas um arquivo css, que pode - com ids / classes existentes em formulários de administração django - facilmente direcionar apenas alguns, ou mesmo todos os campos. engrandecer!
benzkji
2

para 1.6, usando formulários eu tive que especificar os atributos da textarea dentro do charfield:

test1 = forms.CharField(max_length=400, widget=forms.Textarea( attrs={'rows':'2', 'cols': '10'}),  initial='', help_text=helptexts.helptxt['test'])
deddu
fonte
1

Mesma resposta que msdin, mas com TextInput em vez de TextArea:

from django.forms import TextInput

class ShortTextField(models.TextField):
    def formfield(self, **kwargs):
         kwargs.update(
            {"widget": TextInput(attrs={'size': 10})}
         )
         return super(ShortTextField, self).formfield(**kwargs)
dan-klasson
fonte
1

Aqui está uma solução simples, mas flexível. Use um formulário personalizado para substituir alguns widgets.

# models.py
class Elephant(models.Model):
    name = models.CharField(max_length=25)
    age = models.IntegerField()

# forms.py
class ElephantForm(forms.ModelForm):

    class Meta:
        widgets = {
            'age': forms.TextInput(attrs={'size': 3}),
        }

# admin.py
@admin.register(Elephant)
class ElephantAdmin(admin.ModelAdmin):
    form = ElephantForm

Os widgets fornecidos em ElephantForm irão substituir os padrões. A chave é a representação em string do campo. Os campos não especificados no formulário usarão o widget padrão.

Observe que embora ageseja um IntegerField, podemos usar o TextInputwidget, porque, ao contrário de NumberInput, TextInputaceita o sizeatributo.

Esta solução é descrita neste artigo .

Eerik Sven Puudist
fonte
0

Se você estiver trabalhando com um campo ForeignKey que envolve escolhas / opções / um menu suspenso, você pode substituir formfield_for_foreignkeyna instância de Admin:

class YourNewAdmin(admin.ModelAdmin):
    ...

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == 'your_fk_field':
            """ For your FK field of choice, override the dropdown style """
            kwargs["widget"] = django.forms.widgets.Select(attrs={
                'style': 'width: 250px;'
            })

        return super().formfield_for_foreignkey(db_field, request, **kwargs)

Mais informações sobre este padrão aqui e aqui .

mih
fonte
-1

E mais um exemplo também:

class SecenekInline(admin.TabularInline):
   model = Secenek
   # classes = ['collapse']
   def formfield_for_dbfield(self, db_field, **kwargs):
       field = super(SecenekInline, self).formfield_for_dbfield(db_field, **kwargs)
       if db_field.name == 'harf':
           field.widget = TextInput(attrs={'size':2})
       return field
   formfield_overrides = {
       models.TextField: {'widget': Textarea(attrs={'rows':2})},
   }
   extra = 2

Se você deseja editar apenas um tamanho de campo específico, você pode usar isso.

Mevaka
fonte