Django-Admin: CharField jako TextArea

88

mam

class Cab(models.Model):
    name  = models.CharField( max_length=20 )
    descr = models.CharField( max_length=2000 )

class Cab_Admin(admin.ModelAdmin):
    ordering     = ('name',)
    list_display = ('name','descr', )
    # what to write here to make descr using TextArea?

admin.site.register( Cab, Cab_Admin )

jak przypisać widżet TextArea do pola „descr” w interfejsie administratora?

UPD:
W Admin tylko interfejs!

Dobry pomysł na użycie ModelForm.

maxp
źródło
3
Zaakceptowaną odpowiedzią jest ogromna przesada w celu zmodyfikowania tylko jednego pola! LUDZIE ŚLEDZĄ TĘ ODPOWIEDŹ Carla Meyera W CELU
PROSTOŚCI

Odpowiedzi:

99

Będziesz musiał utworzyć, forms.ModelFormktóry będzie opisywał, jak chcesz, aby descrpole było wyświetlane, a następnie wskaż, admin.ModelAdminaby użyć tego formularza. Na przykład:

from django import forms
class CabModelForm( forms.ModelForm ):
    descr = forms.CharField( widget=forms.Textarea )
    class Meta:
        model = Cab

class Cab_Admin( admin.ModelAdmin ):
    form = CabModelForm

formAtrybutem admin.ModelAdminjest udokumentowana w oficjalnej dokumentacji Django. Oto jedno miejsce, na które warto spojrzeć .

ayaz
źródło
6
Aby po prostu zastąpić widżet formularza, nie musisz tworzyć własnego całego formularza. Możesz po prostu zastąpić formfield_for_dbfieldswoją ModelAdmin, zobacz moją odpowiedź poniżej.
Carl Meyer
1
To dajedjango.core.exceptions.ImproperlyConfigured: Creating a ModelForm without either the 'fields' attribute or the 'exclude' attribute is prohibited
John Wu
4
Począwszy od Django 1.7 musisz dodać również fields = '__all__'pod class Meta:.
skoll
2
Nie musisz samodzielnie tworzyć formularza, możesz zastąpić get_formmetodę. Zobacz moją odpowiedź .
x-yuri
podejście beeter polega na użyciu klasy Meta Meta: model = Message widgets = {'text': forms.Textarea (attrs = {'cols': 80, 'rows': 20}),}
Amulya Acharya
58

W tym przypadku najlepszą opcją jest prawdopodobnie użycie w modelu TextField zamiast CharField. Możesz także nadpisać formfield_for_dbfieldmetodę swojej ModelAdminklasy:

class CabAdmin(admin.ModelAdmin):
    def formfield_for_dbfield(self, db_field, **kwargs):
        formfield = super(CabAdmin, self).formfield_for_dbfield(db_field, **kwargs)
        if db_field.name == 'descr':
            formfield.widget = forms.Textarea(attrs=formfield.widget.attrs)
        return formfield
Carl Meyer
źródło
czy ma to jakiś minus w porównaniu z wybraną odpowiedzią? wygląda to na czystsze do wdrożenia, ponieważ nie musimy tworzyć nowego modelu ModelForm ...
Filipe Pina,
3
zastąpione formfield.widget = forms.Textarea()przez, formfield.widget = forms.Textarea(attrs=formfield.widget.attrs)aby zachować wszystkie atrybuty generowane automatycznie dla pola tekstowego.
Filipe Pina
2
Nie wiem, dlaczego inna odpowiedź jest akceptowana w porównaniu z tą; Myślę, że jeśli wszystko, co robisz, to wymiana widżetu, jest to prostsze. Ale albo będzie działać dobrze.
Carl Meyer
To działało doskonale, dzięki. Wywołanie super () wydaje się jednak nieco rozwlekłe, czy istnieje prostszy sposób na zrobienie tego?
rjh
2
@rjh W py3 możesz wyeliminować wszystko wewnątrz parenów i po prostu użyć super(). W przeciwnym razie nie.
Carl Meyer
33

Ayaz ma dość dobre miejsce, z wyjątkiem niewielkiej zmiany (?!):

class MessageAdminForm(forms.ModelForm):
    class Meta:
        model = Message
        widgets = {
            'text': forms.Textarea(attrs={'cols': 80, 'rows': 20}),
        }

class MessageAdmin(admin.ModelAdmin):
    form = MessageAdminForm
admin.site.register(Message, MessageAdmin)

Nie musisz więc redefiniować pola w ModelForm, aby zmienić jego widżet, po prostu ustaw widżety w Meta.

Gezim
źródło
1
To zachowuje więcej „automatycznych” właściwości niż odpowiedź Ayaza, ale jakaś wskazówka, jak zachować również walidację długości pola?
Filipe Pina
14

Możesz podklasować swoje własne pole wymaganą formfieldmetodą:

class CharFieldWithTextarea(models.CharField):

    def formfield(self, **kwargs):
        kwargs.update({"widget": forms.Textarea})
        return super(CharFieldWithTextarea, self).formfield(**kwargs)

Będzie to miało wpływ na wszystkie wygenerowane formularze.

Alex Koshelev
źródło
9

Nie musisz samodzielnie tworzyć klasy formularza:

from django.contrib import admin
from django import forms

class MyModelAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        kwargs['widgets'] = {'descr': forms.Textarea}
        return super().get_form(request, obj, **kwargs)

admin.site.register(MyModel, MyModelAdmin)

Zobacz ModelAdmin.get_form .

x-yuri
źródło
7

Zamiast a models.CharFieldużyj models.TextFieldfor descr.

mipadi
źródło
4
Niestety, max_length = jest całkowicie ignorowana przez Django w TextField, więc gdy potrzebujesz walidacji długości pola (na przykład zezwalając sekretarzom na wprowadzanie podsumowań zdarzeń) jedynym sposobem na to jest użycie CharField. Ale CharField jest zbyt krótki, aby wpisać 300 znaków w. Stąd rozwiązanie ayaz.
shacker
3
To nie jest dobra odpowiedź, ponieważ zmienia bazę danych. Celem zmiany widżetu jest zmiana sposobu wyświetlania pola, a nie zmiana schematu bazy danych
1
To świetna odpowiedź dla kogoś (takiego jak ja), który uczy się korzystania z Django i na początku inaczej skonfigurowałby bazę danych, gdybym wiedział lepiej.
Andrew Swift
7

Jeśli próbujesz zmienić obszar Textarea na admin.py, to jest rozwiązanie, które działało dla mnie:

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

from books.models import Book

class BookForm(forms.ModelForm):
    description = forms.CharField( widget=forms.Textarea(attrs={'rows': 5, 'cols': 100}))
    class Meta:
        model = Book

class BookAdmin(admin.ModelAdmin):
    form = BookForm

admin.site.register(Book, BookAdmin)

Jeśli używasz bazy danych MySQL, długość twojej kolumny będzie zwykle ustawiana automatycznie na 250 znaków, więc będziesz chciał uruchomić ALTER TABLE, aby zmienić długość w swojej bazie danych MySQL, abyś mógł skorzystać z nowego, większego obszaru Textarea, który możesz masz w sobie witrynę Admin Django.

Aaron Lelevier
źródło
5

Możesz użyć models.TextFieldw tym celu:

class Sample(models.Model):
    field1 = models.CharField(max_length=128)
    field2 = models.TextField(max_length=1024*2)   # Will be rendered as textarea
kod vk
źródło
3
To zmieni strukturę DB, więc bądź ostrożny
elad silver
3

Chciał rozwinąć odpowiedź Carla Meyera, która do dziś działa doskonale.

Zawsze używam TextFieldzamiast CharField(z opcjami lub bez) i narzucam limity znaków po stronie interfejsu użytkownika / API, a nie na poziomie bazy danych. Aby to działało dynamicznie:

from django import forms
from django.contrib import admin


class BaseAdmin(admin.ModelAdmin):
    """
    Base admin capable of forcing widget conversion
    """
    def formfield_for_dbfield(self, db_field, **kwargs):
        formfield = super(BaseAdmin, self).formfield_for_dbfield(
            db_field, **kwargs)

        display_as_charfield = getattr(self, 'display_as_charfield', [])
        display_as_choicefield = getattr(self, 'display_as_choicefield', [])

        if db_field.name in display_as_charfield:
            formfield.widget = forms.TextInput(attrs=formfield.widget.attrs)
        elif db_field.name in display_as_choicefield:
            formfield.widget = forms.Select(choices=formfield.choices,
                                            attrs=formfield.widget.attrs)

        return formfield

Mam nazwę modelu Postgdzie title, slugi stateto TextFields i statema wyborów. Definicja administratora wygląda następująco:

@admin.register(Post)
class PostAdmin(BaseAdmin):
    list_display = ('pk', 'title', 'author', 'org', 'state', 'created',)
    search_fields = [
        'title',
        'author__username',
    ]
    display_as_charfield = ['title', 'slug']
    display_as_choicefield = ['state']

Pomyślałem, że inni szukający odpowiedzi mogą uznać to za przydatne.

tarequeh
źródło
Czy to jest formfield_for_dbfieldudokumentowane?
x-yuri