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.


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 .


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

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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.