Zmień rozmiar pól w Django Admin

109

Django ma tendencję do zapełniania poziomej przestrzeni podczas dodawania lub edytowania wpisów w panelu administracyjnym, ale w niektórych przypadkach jest to prawdziwa strata miejsca, gdy np. Edytowanie pola daty o szerokości 8 znaków lub CharField również 6 lub 8 znaków szerokich, a następnie pole edycji rozszerza się do 15 lub 20 znaków.

Jak mogę powiedzieć administratorowi, jak szerokie powinno być pole tekstowe lub jak wysokość pola edycji TextField?

Andor
źródło
9
Oczywiście zapomniałeś o tym zrobić w tym przypadku. Ponad dwa lata później. =)
casperOne
Projekt został porzucony i przez chwilę nie widziałem kodu. Mogę rozpocząć nowy projekt w przyszłym miesiącu, więc może jeszcze raz to zobaczę: D
Andor

Odpowiedzi:

211

Powinieneś użyć ModelAdmin.formfield_overrides .

To całkiem proste - admin.pyzdefiniuj:

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
źródło
1
To było dokładnie to, czego szukałem, bez konieczności dokonywania śmiesznych dostosowań szablonów. Dzięki!
Chris,
2
Zauważ, że to nie zadziała, jeśli filter_horizontallub filter_verticaljest ustawione dla odpowiednich pól w YourModelAdmin. Spędziłem trochę czasu, żeby to rozgryźć.
Dennis Golomazov
Czy jest coś dziwnego w formie? Nie znalazłem sposobu na ustawienie atrybutu attrs dla wszystkich Textareas.
Julian,
1
Użyłem tylko, models.TextField: {'widget': Textarea(attrs={'rows':4})}ale szerokość też się zmniejszyła.
Smit Johnth
Wydaje się, że trudno mieć je tej samej wielkości.
Sören
45

Możesz ustawić dowolne atrybuty HTML widżetu za pomocą jego właściwości „attrs” .

Możesz to zrobić w panelu administracyjnym Django za pomocą 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

lub z niestandardową podklasą Widget i słownikiem 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
źródło
30

Aby zmienić szerokość określonego pola.

Wykonane przez 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
źródło
Moim zdaniem to najlepsza odpowiedź, ponieważ pozwala na zmianę poszczególnych pól bez konieczności tworzenia nowego formularza.
rbennell,
21

Szybką i nieprzyjemną opcją jest po prostu zapewnienie niestandardowego szablonu dla danego modelu.

Jeśli utworzysz szablon o nazwie, admin/<app label>/<class name>/change_form.htmladministrator użyje tego szablonu zamiast domyślnego. Oznacza to, że jeśli masz model o nazwie Personw aplikacji o nazwie people, możesz utworzyć szablon o nazwie admin/people/person/change_form.html.

Wszystkie szablony administracyjne mają extraheadblok, który możesz nadpisać, aby umieścić rzeczy w <head>, a ostatnim elementem układanki jest fakt, że każde pole ma identyfikator HTML id_<field-name>.

Możesz więc umieścić w swoim szablonie coś takiego:

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

{% block extrahead %}
  {{ block.super }}
  <style type="text/css">
    #id_my_field { width: 100px; }
  </style>
{% endblock %}
jakobian
źródło
Dzięki! To i odpowiedź alanjds są bardzo pomocne przy zmianie układu w interfejsie administratora na podstawie poszczególnych pól, a nie poszczególnych typów pól.
nealmcb
10

Jeśli chcesz zmienić atrybuty instancji dla poszczególnych pól, możesz dodać właściwość „attrs” bezpośrednio do wpisów formularza.

na przykład:

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')

Właściwość „attrs” zasadniczo przekazuje kod HTML, który dostosuje pole formularza. Każdy wpis jest krotką atrybutu, który chcesz przesłonić, oraz wartością, którą chcesz zastąpić. Możesz wprowadzić dowolną liczbę atrybutów, pod warunkiem, że oddzielisz każdą krotkę przecinkiem.

Jonathan
źródło
IMO to najlepszy sposób na modyfikację tylko jednego pola, a nie wszystkich widżetów TextInput naraz (tak jak robi to zaakceptowana odpowiedź)
ascripter
7

Najlepszy sposób, jaki znalazłem, to coś takiego:

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

Jest o wiele lepiej niż na ModelForm przesłanianie pól z różnych widgetów, jak zachowuje namei help_textatrybuty, a także domyślne wartości pól modelu, więc nie trzeba je skopiować do formularza.

kurczak
źródło
7

Miałem podobny problem z TextField. Używam Django 1.0.2 i chciałem zmienić domyślną wartość dla „wierszy” w powiązanym obszarze tekstowym. formfield_overrides nie istnieje w tej wersji. Zastępowanie formfield_for_dbfield działało, ale musiałem to zrobić dla każdej z moich podklas ModelAdmin lub spowodowałoby to błąd rekursji. Ostatecznie stwierdziłem, że dodanie poniższego kodu do models.py działa:

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)

Następnie podczas definiowania modeli użyj MyTextField zamiast TextField. Zaadaptowałem to na podstawie tej odpowiedzi do podobnego pytania.

msdin
źródło
5

Jest to dobrze opisane w Django FAQ :

P: Jak zmienić atrybuty widgetu w polu w moim modelu?

Odp .: Zastąp formfield_for_dbfield w klasie 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
źródło
Kiedy używam tej techniki, tekst pomocy nie pojawia się, a jeśli jest to TabularInline, nagłówek kolumny zmienia się na „Brak”.
Steven T. Snyder
1
To nie jest dobre podejście, ponieważ usuwasz wszystkie inne ustawienia w CharField. Na przykład domyślnie inteligentnie wykryje, czy pole formularza powinno być wymagane, ale ten kod to niszczy. Zamiast tego powinieneś ustawić widget na wartość zwracaną przez wywołanie super ().
Cerin
5

Zawsze możesz ustawić rozmiary pól w niestandardowym arkuszu stylów i powiedzieć Django, aby użył tego dla Twojej klasy ModelAdmin:

class MyModelAdmin(ModelAdmin):
    class Media:
        css = {"all": ("my_stylesheet.css",)}
Gonzalo
źródło
najlepsza odpowiedź! bez majstrowania przy polach formularzy i atrybutach (i możliwych efektach ubocznych), po prostu plik css, który może - z istniejącymi identyfikatorami / klasami w formularzach administracyjnych django - łatwo kierować tylko niektóre lub nawet wszystkie pola. wielki!
benzkji
2

dla wersji 1.6, używając formularzy musiałem określić atrybuty obszaru tekstowego wewnątrz pola charfield:

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

Taka sama odpowiedź jak msdin, ale z TextInput zamiast 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
źródło
1

Oto proste, ale elastyczne rozwiązanie. Użyj niestandardowego formularza, aby zastąpić niektóre widżety.

# 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

Widgety podane w ElephantForm zastąpią domyślne. Klucz to ciąg znaków reprezentujący pole. Pola niewyszczególnione w formularzu będą używały domyślnego widżetu.

Zauważ, że chociaż agejest to widżet, IntegerFieldmożemy użyć TextInputwidżetu, ponieważ w przeciwieństwie do NumberInput, TextInputakceptuje sizeatrybut.

To rozwiązanie jest opisane w tym artykule .

Eerik Sven Puudist
źródło
0

Jeśli pracujesz z polem ForeignKey, które obejmuje opcje / opcje / menu rozwijane, możesz nadpisać formfield_for_foreignkeyw instancji administratora:

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)

Więcej informacji na temat tego wzoru tutaj i tutaj .

mih
źródło
-1

I jeszcze jeden przykład:

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

Jeśli chcesz edytować tylko określony rozmiar pól, możesz tego użyć.

mevaka
źródło