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.


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 .


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

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


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.


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 .


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.


6

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


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

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.


¿Está esto formfield_for_dbfielddocumentado?
x-yuri
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.