Django-Admin: CharField comme TextArea

87

j'ai

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 )

comment attribuer le widget TextArea au champ 'descr' dans l'interface d'administration?

upd:
Dans l' interface d' administration uniquement!

Bonne idée d'utiliser ModelForm.

maxp
la source
3
La réponse acceptée est une énorme exagération dans le but de modifier un seul champ! LES GENS SUIVENT CETTE RÉPONSE par Carl Meyer POUR DES FINS DE SIMPLICITÉ: stackoverflow.com/questions/430592/…
andilabs

Réponses:

99

Vous devrez créer un forms.ModelFormqui décrira comment vous voulez que le descrchamp soit affiché, puis dites admin.ModelAdmind'utiliser ce formulaire. Par exemple:

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

L' formattribut de admin.ModelAdminest documenté dans la documentation officielle de Django. Voici un endroit à regarder .

ayaz
la source
6
Pour simplement remplacer un widget de formulaire, vous n'avez pas à créer votre propre formulaire entier. Vous pouvez simplement remplacer formfield_for_dbfieldvotre ModelAdmin, voir ma réponse ci-dessous.
Carl Meyer
1
Cela donnedjango.core.exceptions.ImproperlyConfigured: Creating a ModelForm without either the 'fields' attribute or the 'exclude' attribute is prohibited
John Wu
4
À partir de Django 1.7, vous devez également ajouter fields = '__all__'sous class Meta:.
skoll
2
Vous n'avez pas besoin de créer le formulaire vous-même, vous pouvez remplacer la get_formméthode. Voyez ma réponse .
x-yuri
L'approche Beeter consiste à utiliser la classe Meta Meta: model = Message widgets = {'text': forms.Textarea (attrs = {'cols': 80, 'rows': 20}),}
Amulya Acharya
58

Dans ce cas, la meilleure option est probablement simplement d'utiliser un TextField au lieu de CharField dans votre modèle. Vous pouvez également remplacer la formfield_for_dbfieldméthode de votre 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
la source
y a-t-il un inconvénient à cela par rapport à la réponse choisie? cela semble plus propre à mettre en œuvre car nous n'avons pas besoin de créer un nouveau ModelForm ...
Filipe Pina
3
remplacé formfield.widget = forms.Textarea()par formfield.widget = forms.Textarea(attrs=formfield.widget.attrs)pour conserver tous les attributs générés automatiquement pour le TextField, merci!
Filipe Pina
2
Je ne sais pas pourquoi l'autre réponse est acceptée par rapport à celle-ci; Je pense que si vous ne faites que remplacer un widget, c'est plus simple. Mais l'un ou l'autre fonctionnera bien.
Carl Meyer
Cela a parfaitement fonctionné, merci. L'appel super () semble un peu verbeux, existe-t-il un moyen plus simple de le faire?
rjh
2
@rjh Dans py3, vous pouvez éliminer tout ce qui se trouve à l'intérieur des parenthèses et simplement utiliser super(). Sinon non.
Carl Meyer
32

Ayaz a à peu près sa place, à l'exception d'un léger changement (?!):

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)

Donc, vous n'avez pas besoin de redéfinir un champ dans le ModelForm pour changer son widget, il suffit de définir les widgets dict dans Meta.

Gezim
la source
1
Cela conserve plus de propriétés "automatiques" que la réponse d'Ayaz, mais un conseil sur la façon de conserver la validation de la longueur du champ également?
Filipe Pina
14

Vous pouvez sous-classer votre propre champ avec la formfieldméthode nécessaire :

class CharFieldWithTextarea(models.CharField):

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

Cela prendra effet sur tous les formulaires générés.

Alex Koshelev
la source
9

Vous n'avez pas besoin de créer la classe de formulaire vous-même:

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)

Voir ModelAdmin.get_form .

x-yuri
la source
7

Si vous essayez de modifier Textarea sur admin.py, voici la solution qui a fonctionné pour moi:

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 vous utilisez une base de données MySQL, la longueur de votre colonne sera généralement définie automatiquement à 250 caractères, vous voudrez donc exécuter un ALTER TABLE pour modifier la longueur de votre base de données MySQL, afin de pouvoir profiter de la nouvelle zone de texte plus grande que vous avoir dans votre site Admin Django.

Aaron Lelevier
la source
6

Au lieu de a models.CharField, utilisez un models.TextFieldfor descr.

mipadi
la source
4
Malheureusement, max_length = est totalement ignoré par Django sur un TextField, donc lorsque vous avez besoin d'une validation de longueur de champ (comme laisser les secrétaires entrer des résumés d'événements), la seule façon de l'obtenir est d'utiliser un CharField. Mais un CharField est trop court pour taper 300 caractères. D'où la solution ayaz.
shacker
3
Ce n'est pas une bonne réponse car cela change la base de données. Le but de changer le widget est de changer la façon dont le champ est affiché, pas de changer le schéma de la base de données
1
C'est une excellente réponse pour quelqu'un (comme moi) qui apprend à utiliser Django et qui aurait configuré la base de données différemment si j'avais su mieux.
Andrew Swift
5

Vous pouvez utiliser models.TextFieldà cette fin:

class Sample(models.Model):
    field1 = models.CharField(max_length=128)
    field2 = models.TextField(max_length=1024*2)   # Will be rendered as textarea
code vk
la source
3
Cela changera la structure de la base de données, alors soyez
prudent
3

Je voulais développer la réponse de Carl Meyer, qui fonctionne parfaitement jusqu'à ce jour.

J'utilise toujours à la TextFieldplace de CharField(avec ou sans choix) et j'impose des limites de caractères côté interface utilisateur / API plutôt qu'au niveau DB. Pour que cela fonctionne de manière dynamique:

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

J'ai un nom de modèle Posttitle, slug& statesont TextFields et j'ai des statechoix. La définition de l'administrateur ressemble à:

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

Je pensais que d'autres personnes cherchant des réponses pourraient trouver cela utile.

tarequeh
la source
Est-ce formfield_for_dbfielddocumenté?
x-yuri