Django-Admin: CharField como TextArea

87

yo tengo

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 )

¿Cómo asignar el widget TextArea al campo 'descr' en la interfaz de administración?

UPD:
En administración única interfaz!

Buena idea para utilizar ModelForm.

maxp
fuente
3
La respuesta aceptada es una exageración enorme con el propósito de modificar un solo campo. LAS PERSONAS SIGUEN ESTA RESPUESTA de Carl Meyer CON FINES DE SIMPLICIDAD: stackoverflow.com/questions/430592/…
andilabs

Respuestas:

99

Tendrá que crear un forms.ModelFormque describa cómo desea que se muestre el descrcampo y luego decirle admin.ModelAdminque use ese formulario. Por ejemplo:

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

El formatributo de admin.ModelAdminestá documentado en la documentación oficial de Django. Aquí hay un lugar para mirar .

ayaz
fuente
6
Para simplemente reemplazar un widget de formulario, no tiene que crear su propio formulario completo. Puede simplemente anular formfield_for_dbfieldsu ModelAdmin, vea mi respuesta a continuación.
Carl Meyer
1
Esto dadjango.core.exceptions.ImproperlyConfigured: Creating a ModelForm without either the 'fields' attribute or the 'exclude' attribute is prohibited
John Wu
4
A partir de Django 1.7, debe agregar también fields = '__all__'bajo class Meta:.
skoll
2
No es necesario que cree el formulario usted mismo, puede anular el get_formmétodo. Mira mi respuesta .
x-yuri
El enfoque de beeter es usar Meta class Meta: model = Message widgets = {'text': forms.Textarea (attrs = {'cols': 80, 'rows': 20}),}
Amulya Acharya
58

Para este caso, la mejor opción probablemente sea usar un TextField en lugar de CharField en su modelo. También puede anular el formfield_for_dbfieldmétodo de su ModelAdminclase:

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
fuente
¿Hay alguna desventaja en esto en comparación con la respuesta seleccionada? esto parece más limpio de implementar ya que no es necesario crear un nuevo ModelForm ...
Filipe Pina
3
reemplazado formfield.widget = forms.Textarea()con formfield.widget = forms.Textarea(attrs=formfield.widget.attrs)para mantener todos los atributos generados automáticamente para TextField, ¡Gracias!
Filipe Pina
2
No sé por qué se acepta la otra respuesta sobre esta; Creo que si todo lo que estás haciendo es reemplazar un widget, esto es más simple. Pero cualquiera funcionará bien.
Carl Meyer
Esto funcionó perfectamente, gracias. Sin embargo, la invocación de super () parece un poco detallada, ¿hay una forma más sencilla de hacerlo?
rjh
2
@rjh En py3 puedes eliminar todo lo que hay dentro de los parientes y solo usar super(). De otra manera no.
Carl Meyer
32

Ayaz tiene bastante razón, excepto por un ligero cambio (?!):

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)

Por lo tanto, no necesita redefinir un campo en ModelForm para cambiar su widget, simplemente configure el dict de widgets en Meta.

Gezim
fuente
1
Esto conserva más propiedades "automáticas" que la respuesta de Ayaz, pero ¿algún consejo sobre cómo mantener la validación de la longitud del campo también?
Filipe Pina
14

Puede subclasificar su propio campo con el formfieldmétodo necesario :

class CharFieldWithTextarea(models.CharField):

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

Esto afectará a todos los formularios generados.

Alex Koshelev
fuente
9

No es necesario que cree la clase de formulario usted mismo:

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
fuente
7

Si está intentando cambiar el Textarea en admin.py, esta es la solución que funcionó para mí:

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)

Si está utilizando una base de datos MySQL, la longitud de su columna generalmente se configurará automáticamente a 250 caracteres, por lo que querrá ejecutar ALTER TABLE para cambiar la longitud en su base de datos MySQL, de modo que pueda aprovechar el nuevo área de texto más grande que tiene en su sitio Admin Django.

Aaron Lelevier
fuente
6

En lugar de a models.CharField, usa models.TextFieldpara descr.

mipadi
fuente
4
Desafortunadamente, max_length = es totalmente ignorado por Django en un TextField, por lo que cuando necesita una validación de la longitud del campo (como permitir que las secretarias ingresen resúmenes de eventos), la única forma de obtenerlo es usar un CharField. Pero un CharField es demasiado corto para escribir 300 caracteres. De ahí la solución ayaz.
shacker
3
Esta no es una buena respuesta porque cambia la base de datos. El objetivo de cambiar el widget es cambiar la forma en que se muestra el campo, no cambiar el esquema de la base de datos
1
Es una gran respuesta para alguien (como yo) que está aprendiendo a usar Django y, en primer lugar, habría configurado la base de datos de manera diferente si lo hubiera sabido mejor.
Andrew Swift
5

Puede utilizar models.TextFieldpara este propósito:

class Sample(models.Model):
    field1 = models.CharField(max_length=128)
    field2 = models.TextField(max_length=1024*2)   # Will be rendered as textarea
código vk
fuente
3
Esto cambiará la estructura de la base de datos, así que tenga cuidado
elad silver
3

Quería ampliar la respuesta de Carl Meyer, que funciona perfectamente hasta la fecha.

Siempre uso en TextFieldlugar de CharField(con o sin opciones) e impongo límites de caracteres en el lado de la interfaz de usuario / API en lugar de en el nivel de base de datos. Para hacer que esto funcione dinámicamente:

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

Tengo un nombre de modelo Postdonde title, slug& stateare TextFieldsy statetiene opciones. La definición de administrador se ve así:

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

Pensé que otros que buscan respuestas podrían encontrar esto útil.

tarequeh
fuente
¿Está esto formfield_for_dbfielddocumentado?
x-yuri