Django-Admin: CharField как TextArea

87

у меня есть

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 )

как назначить виджет TextArea полю "descr" в интерфейсе администратора?

UPD:
В Администратор только интерфейс!

Хорошая идея использовать ModelForm.

maxp
источник
3
Принятый ответ - огромное излишество для модификации только одного поля! ЛЮДИ ПОСЛЕДУЮТ ЭТОМ ОТВЕТУ Карла Мейера ДЛЯ ПРОСТОТЫ: stackoverflow.com/questions/430592/…
andilabs

Ответы:

99

Вам нужно будет создать форму, forms.ModelFormкоторая будет описывать, как вы хотите, чтобы descrполе отображалось, а затем указать, что admin.ModelAdminнужно использовать эту форму. Например:

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

formАтрибут admin.ModelAdminдокументирована в официальной документации Django. Вот одно место, на которое можно посмотреть .

аяз
источник
6
Чтобы просто заменить виджет формы, вам не нужно создавать свою собственную форму целиком. Вы можете просто переопределить formfield_for_dbfieldсвой ModelAdmin, см. Мой ответ ниже.
Карл Мейер,
1
Это даетdjango.core.exceptions.ImproperlyConfigured: Creating a ModelForm without either the 'fields' attribute or the 'exclude' attribute is prohibited
Джон Ву
4
Начиная с Django 1.7 вам нужно добавить еще и fields = '__all__'под class Meta:.
skoll
2
Вам не нужно создавать форму самостоятельно, вы можете переопределить get_formметод. Смотрите мой ответ .
x-yuri
Подход beeter заключается в использовании мета-класса Meta: model = Message widgets = {'text': forms.Textarea (attrs = {'cols': 80, 'rows': 20}),}
Amulya Acharya
58

В этом случае лучшим вариантом, вероятно, будет просто использовать TextField вместо CharField в вашей модели. Вы также можете переопределить formfield_for_dbfieldметод своего ModelAdminкласса:

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
Карл Мейер
источник
есть ли в этом обратная сторона по сравнению с выбранным ответом? это выглядит более чистым для реализации, поскольку нам не нужно создавать новую ModelForm ...
Филипе Пина,
3
заменить formfield.widget = forms.Textarea()с , formfield.widget = forms.Textarea(attrs=formfield.widget.attrs)чтобы сохранить все атрибуты автоматически сгенерированные для TextField, спасибо!
Filipe Pina
2
Я не знаю, почему принимается другой ответ вместо этого; Я думаю, что если все, что вы делаете, это заменяете виджет, это проще. Но оба варианта будут работать нормально.
Карл Мейер,
Это сработало отлично, спасибо. Вызов super () кажется немного многословным, есть ли более простой способ сделать это?
rjh
2
@rjh В py3 вы можете удалить все, что находится внутри скобок, и просто использовать super(). В противном случае нет.
Карл Мейер
32

Аяз в значительной степени подходит, за исключением небольшого изменения (?!):

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)

Итак, вам не нужно переопределять поле в ModelForm, чтобы изменить его виджет, просто установите dict виджетов в Meta.

Гезим
источник
1
Это сохраняет больше "автоматических" свойств, чем ответ Аяза, но есть ли подсказки о том, как сохранить проверку длины поля?
Филипе Пина
14

Вы можете подклассифицировать собственное поле с помощью необходимого formfieldметода:

class CharFieldWithTextarea(models.CharField):

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

Это повлияет на все сгенерированные формы.

Алексей Кошелев
источник
9

Самостоятельно создавать класс формы не нужно:

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)

См. ModelAdmin.get_form .

x-yuri
источник
7

Если вы пытаетесь изменить Textarea на admin.py, это решение, которое сработало для меня:

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)

Если вы используете базу данных MySQL, длина вашего столбца обычно будет автоматически установлена ​​на 250 символов, поэтому вам нужно будет запустить ALTER TABLE, чтобы изменить длину в вашей базе данных MySQL, чтобы вы могли воспользоваться преимуществами нового большего Textarea, которое вы есть у вас админский сайт Django.

Аарон Лелевье
источник
6

Вместо a models.CharFieldиспользуйте models.TextFieldfor descr.

мипади
источник
4
К сожалению, max_length = полностью игнорируется Django в TextField, поэтому, когда вам нужна проверка длины поля (например, разрешение секретарям вводить сводки событий), единственный способ получить это - использовать CharField. Но CharField - это способ сократить до 300 символов. Отсюда и решение аяз.
shacker
3
Это плохой ответ, потому что он меняет базу данных.
1
Это отличный ответ для кого-то (вроде меня), который учится использовать Django и, если бы я знал лучше, изначально настроил бы базу данных по-другому.
Эндрю Свифт
5

models.TextFieldДля этого можно использовать :

class Sample(models.Model):
    field1 = models.CharField(max_length=128)
    field2 = models.TextField(max_length=1024*2)   # Will be rendered as textarea
vk-код
источник
3
Это изменит структуру базы данных, так что будьте осторожны
elad silver
3

Хотел расширить ответ Карла Мейера, который отлично работает до сих пор.

Я всегда использую TextFieldвместо CharField(с выбором или без) и налагаю ограничения на количество символов на стороне UI / API, а не на уровне БД. Чтобы это работало динамически:

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

У меня есть название модели Postгде title, slugи stateэто TextFieldс и stateимеет выбор. Определение администратора выглядит так:

@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']

Подумал, что другие, ищущие ответы, могут найти это полезным.

Тареке
источник
Это formfield_for_dbfieldзадокументировано?
x-yuri