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.


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ć .


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

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.


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.


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 .


7

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


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.


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

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.


Czy to jest formfield_for_dbfieldudokumentowane?
x-yuri
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.