Django-Admin: CharField como TextArea

87

eu tenho

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 )

como atribuir widget TextArea ao campo 'descr' na interface de administração?

upd: Somente
na interface Admin !

Boa ideia para usar ModelForm.

maxp
fonte
3
A resposta aceita é um enorme exagero para o propósito de modificação de apenas um campo! AS PESSOAS SEGUEM ESTA RESPOSTA de Carl Meyer PARA O PROPÓSITO DE SIMPLICIDADE: stackoverflow.com/questions/430592/…
andilabs

Respostas:

99

Você terá que criar um forms.ModelFormque descreva como deseja que o descrcampo seja exibido e, em seguida, diga admin.ModelAdminpara usar esse formulário. Por exemplo:

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

O formatributo de admin.ModelAdminestá documentado na documentação oficial do Django. Aqui está um lugar para olhar .

Ayaz
fonte
6
Para simplesmente substituir um widget de formulário, você não precisa criar seu próprio formulário inteiro. Você pode simplesmente substituir formfield_for_dbfieldno seu ModelAdmin, veja minha resposta abaixo.
Carl Meyer
1
Isso dádjango.core.exceptions.ImproperlyConfigured: Creating a ModelForm without either the 'fields' attribute or the 'exclude' attribute is prohibited
John Wu
4
A partir do Django 1.7, você precisa adicionar também fields = '__all__'em class Meta:.
skoll
2
Você não precisa criar o formulário sozinho, você pode substituir o get_formmétodo. Veja minha resposta .
x-yuri
melhor abordagem é usar Meta classe Meta: model = Message widgets = {'text': forms.Textarea (attrs = {'cols': 80, 'rows': 20}),}
Amulya Acharya
58

Para este caso, a melhor opção provavelmente é apenas usar um TextField em vez de CharField em seu modelo. Você também pode substituir o formfield_for_dbfieldmétodo de sua ModelAdminclasse:

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
fonte
há alguma desvantagem nisso em comparação com a resposta selecionada? parece mais limpo de implementar, pois não precisamos criar um novo ModelForm ...
Filipe Pina
3
substituído formfield.widget = forms.Textarea()por formfield.widget = forms.Textarea(attrs=formfield.widget.attrs)para manter todos os atributos gerados automaticamente para o TextField, obrigado!
Filipe Pina
2
Não sei por que a outra resposta é aceita sobre esta; Acho que se tudo o que você está fazendo é substituir um widget, isso é mais simples. Mas qualquer um vai funcionar bem.
Carl Meyer
Funcionou perfeitamente, obrigado. A invocação super () parece um pouco prolixa, existe uma maneira mais simples de fazer isso?
rjh
2
@rjh Em py3 você pode eliminar tudo dentro dos parênteses e apenas usar super(). Caso contrário, não.
Carl Meyer
32

Ayaz acertou em cheio, exceto por uma ligeira mudança (?!):

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)

Portanto, você não precisa redefinir um campo no ModelForm para alterar seu widget, apenas defina o dict dos widgets no Meta.

Gezim
fonte
1
Isso retém mais propriedades "automáticas" do que a resposta de Ayaz, mas alguma dica sobre como manter a validação do comprimento do campo também?
Filipe Pina
14

Você pode criar uma subclasse de seu próprio campo com o formfieldmétodo necessário :

class CharFieldWithTextarea(models.CharField):

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

Isso terá efeito em todos os formulários gerados.

Alex Koshelev
fonte
9

Você não precisa criar a classe de formulário sozinho:

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)

Consulte ModelAdmin.get_form .

x-yuri
fonte
7

Se você está tentando alterar a Textarea em admin.py, esta é a solução que funcionou para mim:

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)

Se você estiver usando um banco de dados MySQL, o comprimento da coluna geralmente será definido automaticamente para 250 caracteres, então você vai querer executar um ALTER TABLE para alterar o comprimento em seu banco de dados MySQL, para que você possa aproveitar a nova Textarea maior que você tem em seu site Admin Django.

Aaron Lelevier
fonte
6

Em vez de um models.CharField, use um models.TextFieldfor descr.

mipadi
fonte
4
Infelizmente, max_length = é totalmente ignorado pelo Django em um TextField, então quando você precisa da validação de comprimento de campo (como permitir que secretários entrem em resumos de eventos), a única maneira de obtê-lo é usar um CharField. Mas um CharField é uma forma curta de digitar 300 caracteres. Daí a solução ayaz.
shacker,
3
Esta não é uma boa resposta porque muda o banco de dados. O objetivo de mudar o widget é mudar a forma como o campo é exibido, não mudar o esquema do banco de dados
1
É uma ótima resposta para alguém (como eu) que está aprendendo a usar Django e teria configurado o banco de dados de maneira diferente se eu soubesse disso.
Andrew Swift
5

Você pode usar models.TextFieldpara esta finalidade:

class Sample(models.Model):
    field1 = models.CharField(max_length=128)
    field2 = models.TextField(max_length=1024*2)   # Will be rendered as textarea
vk-code
fonte
3
Isso mudará a estrutura do banco de dados, então
tome
3

Queria expandir a resposta de Carl Meyer, que funciona perfeitamente até hoje.

Eu sempre uso em TextFieldvez de CharField(com ou sem opções) e imponho limites de caracteres no lado da interface do usuário / API em vez de no nível do banco de dados. Para fazer isso funcionar de forma dinâmica:

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

Eu tenho um nome do modelo Post, onde title, sluge statesão TextFields e statetem escolhas. A definição do administrador se parece com:

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

Achei que outras pessoas em busca de respostas poderiam achar isso útil.

tarequeh
fonte
Isso está formfield_for_dbfielddocumentado?
x-yuri