Redimensionner les champs dans Django Admin

109

Django a tendance à remplir un espace horizontal lors de l'ajout ou de la modification d'entrées sur l'administrateur, mais, dans certains cas, c'est une véritable perte d'espace, lorsque, c'est-à-dire, éditer un champ de date de 8 caractères de large, ou un CharField, également 6 ou 8 caractères de large, puis la zone d'édition va jusqu'à 15 ou 20 caractères.

Comment puis-je indiquer à l'administrateur la largeur d'une zone de texte ou la hauteur d'une zone d'édition TextField?

Andor
la source
9
De toute évidence, vous avez oublié de faire cela dans ce cas. Plus de deux ans plus tard. =)
casperOne
Le projet a été abandonné et je n'ai pas pu voir le code pendant un moment. Je pourrais commencer un nouveau projet le mois prochain, alors peut-être que je vais le revoir: D
Andor

Réponses:

211

Vous devez utiliser ModelAdmin.formfield_overrides .

Il est assez facile de admin.pydéfinir:

from django.forms import TextInput, Textarea
from django.db import models

class YourModelAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.CharField: {'widget': TextInput(attrs={'size':'20'})},
        models.TextField: {'widget': Textarea(attrs={'rows':4, 'cols':40})},
    }

admin.site.register(YourModel, YourModelAdmin)
jki
la source
1
C'était exactement ce que je recherchais sans avoir à faire des personnalisations de modèles ridicules. Merci!
Chris
2
Notez que cela ne fonctionnera pas si filter_horizontalou filter_verticalest défini pour les champs correspondants dans YourModelAdmin. J'ai passé du temps à comprendre cela.
Dennis Golomazov
Y a-t-il quelque chose de différent à cela sous une forme? Je n'ai pas trouvé de moyen de définir l'attribut attrs pour tous les Textareas.
Julian du
1
Je l'ai utilisé seulement models.TextField: {'widget': Textarea(attrs={'rows':4})}mais la largeur est devenue plus petite aussi.
Smit Johnth
Il est difficile de les avoir de la même taille apparemment.
Sören
45

Vous pouvez définir des attributs HTML arbitraires sur un widget en utilisant sa propriété "attrs" .

Vous pouvez le faire dans l'admin Django en utilisant formfield_for_dbfield:

class MyModelAdmin(admin.ModelAdmin):
  def formfield_for_dbfield(self, db_field, **kwargs):
    field = super(ContentAdmin, self).formfield_for_dbfield(db_field, **kwargs)
    if db_field.name == 'somefield':
      field.widget.attrs['class'] = 'someclass ' + field.widget.attrs.get('class', '')
    return field

ou avec une sous-classe de Widget personnalisée et le dictionnaire formfield_overrides :

class DifferentlySizedTextarea(forms.Textarea):
  def __init__(self, *args, **kwargs):
    attrs = kwargs.setdefault('attrs', {})
    attrs.setdefault('cols', 80)
    attrs.setdefault('rows', 5)
    super(DifferentlySizedTextarea, self).__init__(*args, **kwargs)

class MyModelAdmin(admin.ModelAdmin):
  formfield_overrides = { models.TextField: {'widget': DifferentlySizedTextarea}}
Carl Meyer
la source
30

Pour modifier la largeur d'un champ spécifique.

Fabriqué via ModelAdmin.get_form :

class YourModelAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        form = super(YourModelAdmin, self).get_form(request, obj, **kwargs)
        form.base_fields['myfield'].widget.attrs['style'] = 'width: 45em;'
        return form
alanjds
la source
À mon avis, c'est la meilleure réponse, car elle permet de modifier des champs individuels sans avoir à créer un nouveau formulaire.
rbennell
21

Une option rapide et sale consiste simplement à fournir un modèle personnalisé pour le modèle en question.

Si vous créez un modèle nommé, admin/<app label>/<class name>/change_form.htmll'administrateur utilisera ce modèle au lieu du modèle par défaut. Autrement dit, si vous avez un modèle nommé Persondans une application nommée people, vous créez un modèle nommé admin/people/person/change_form.html.

Tous les modèles d'administration ont un extraheadbloc que vous pouvez remplacer pour placer des éléments dans le <head>, et la dernière pièce du puzzle est le fait que chaque champ a un identifiant HTML id_<field-name>.

Ainsi, vous pouvez mettre quelque chose comme ce qui suit dans votre modèle:

{% extends "admin/change_form.html" %}

{% block extrahead %}
  {{ block.super }}
  <style type="text/css">
    #id_my_field { width: 100px; }
  </style>
{% endblock %}
jacobien
la source
Merci! Ceci et la réponse de alanjds sont très utiles pour changer la mise en page dans l'interface d'administration sur une base par champ, plutôt que par type de champ.
nealmcb
10

Si vous souhaitez modifier les attributs sur une instance par champ, vous pouvez ajouter la propriété "attrs" directement dans vos entrées de formulaire.

par exemple:

class BlogPostForm(forms.ModelForm):
    title = forms.CharField(label='Title:', max_length=128)
    body = forms.CharField(label='Post:', max_length=2000, 
        widget=forms.Textarea(attrs={'rows':'5', 'cols': '5'}))

    class Meta:
        model = BlogPost
        fields = ('title', 'body')

La propriété "attrs" transmet essentiellement le balisage HTML qui ajustera le champ du formulaire. Chaque entrée est un tuple de l'attribut que vous souhaitez remplacer et de la valeur avec laquelle vous souhaitez le remplacer. Vous pouvez entrer autant d'attributs que vous le souhaitez tant que vous séparez chaque tuple par une virgule.

Jonathan
la source
OMI, le meilleur moyen de modifier un seul champ et non tous les widgets TextInput à la fois (comme le fait la réponse acceptée)
ascripter
7

Le meilleur moyen que j'ai trouvé est quelque chose comme ceci:

class NotificationForm(forms.ModelForm):
    def __init__(self, *args, **kwargs): 
        super(NotificationForm, self).__init__(*args, **kwargs)
        self.fields['content'].widget.attrs['cols'] = 80
        self.fields['content'].widget.attrs['rows'] = 15
        self.fields['title'].widget.attrs['size'] = 50
    class Meta:
        model = Notification

C'est beaucoup mieux pour ModelForm que de remplacer les champs avec différents widgets, car il préserve nameet les help_textattributs ainsi que les valeurs par défaut des champs de modèle, vous n'avez donc pas à les copier dans votre formulaire.

Kurczak
la source
7

J'ai eu un problème similaire avec TextField. J'utilise Django 1.0.2 et je voulais changer la valeur par défaut des «lignes» dans la zone de texte associée. formfield_overrides n'existe pas dans cette version. Le remplacement de formfield_for_dbfield fonctionnait mais je devais le faire pour chacune de mes sous-classes ModelAdmin ou cela entraînerait une erreur de récursivité. Finalement, j'ai trouvé que l'ajout du code ci-dessous à models.py fonctionne:

from django.forms import Textarea

class MyTextField(models.TextField):
#A more reasonably sized textarea                                                                                                            
    def formfield(self, **kwargs):
         kwargs.update(
            {"widget": Textarea(attrs={'rows':2, 'cols':80})}
         )
         return super(MyTextField, self).formfield(**kwargs)

Utilisez ensuite MyTextField au lieu de TextField lors de la définition de vos modèles. Je l'ai adapté de cette réponse à une question similaire.

msdin
la source
5

C'est bien décrit dans la FAQ Django :

Q: Comment modifier les attributs d'un widget sur un champ de mon modèle?

R: Remplacez le formfield_for_dbfield dans la classe ModelAdmin / StackedInline / TabularInline

class MyOtherModelInline(admin.StackedInline):
    model = MyOtherModel
    extra = 1

    def formfield_for_dbfield(self, db_field, **kwargs):
        # This method will turn all TextFields into giant TextFields
        if isinstance(db_field, models.TextField):
            return forms.CharField(widget=forms.Textarea(attrs={'cols': 130, 'rows':30, 'class': 'docx'}))
        return super(MyOtherModelInline, self).formfield_for_dbfield(db_field, **kwargs)
Questions difficiles
la source
Lorsque j'utilise cette technique, le texte d'aide n'apparaîtra pas, et s'il s'agit d'un TabularInline, l'en-tête de colonne devient «Aucun».
Steven T.Snyder
1
Ce n'est pas une bonne approche, car vous effacez tous les autres paramètres du CharField. Par exemple, par défaut, il détectera intelligemment si le champ de formulaire doit être requis ou non, mais ce code le détruit. Vous devez à la place définir le widget sur la valeur renvoyée par votre appel super ().
Cerin
5

Vous pouvez toujours définir la taille de vos champs dans une feuille de style personnalisée et dire à Django de l'utiliser pour votre classe ModelAdmin:

class MyModelAdmin(ModelAdmin):
    class Media:
        css = {"all": ("my_stylesheet.css",)}
Gonzalo
la source
meilleure réponse! pas de bidouillage avec les champs de formulaire et les attributs (et les effets secondaires possibles), juste un fichier css, qui peut - avec les ids / classes existants dans les formulaires d'administration de django - cibler facilement seulement certains, voire tous les champs. dédicace!
benzkji
2

pour 1.6, en utilisant des formulaires, je devais spécifier les attributs de la zone de texte à l'intérieur du champ de caractères:

test1 = forms.CharField(max_length=400, widget=forms.Textarea( attrs={'rows':'2', 'cols': '10'}),  initial='', help_text=helptexts.helptxt['test'])
deddu
la source
1

Même réponse que msdin mais avec TextInput au lieu de TextArea:

from django.forms import TextInput

class ShortTextField(models.TextField):
    def formfield(self, **kwargs):
         kwargs.update(
            {"widget": TextInput(attrs={'size': 10})}
         )
         return super(ShortTextField, self).formfield(**kwargs)
dan-klasson
la source
1

Voici une solution simple mais flexible. Utilisez un formulaire personnalisé pour remplacer certains widgets.

# models.py
class Elephant(models.Model):
    name = models.CharField(max_length=25)
    age = models.IntegerField()

# forms.py
class ElephantForm(forms.ModelForm):

    class Meta:
        widgets = {
            'age': forms.TextInput(attrs={'size': 3}),
        }

# admin.py
@admin.register(Elephant)
class ElephantAdmin(admin.ModelAdmin):
    form = ElephantForm

Les widgets donnés dans ElephantFormremplaceront ceux par défaut. La clé est la représentation sous forme de chaîne du champ. Les champs non spécifiés dans le formulaire utiliseront le widget par défaut.

Notez que bien que ce agesoit un, IntegerFieldnous pouvons utiliser le TextInputwidget, car contrairement à NumberInput, TextInputaccepte l' sizeattribut.

Cette solution est décrite dans cet article .

Eerik Sven Puudist
la source
0

Si vous travaillez avec un champ ForeignKey qui implique des choix / options / un menu déroulant, vous pouvez remplacer formfield_for_foreignkeydans l'instance Admin:

class YourNewAdmin(admin.ModelAdmin):
    ...

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == 'your_fk_field':
            """ For your FK field of choice, override the dropdown style """
            kwargs["widget"] = django.forms.widgets.Select(attrs={
                'style': 'width: 250px;'
            })

        return super().formfield_for_foreignkey(db_field, request, **kwargs)

Plus d'informations sur ce modèle ici et ici .

mih
la source
-1

Et encore un exemple:

class SecenekInline(admin.TabularInline):
   model = Secenek
   # classes = ['collapse']
   def formfield_for_dbfield(self, db_field, **kwargs):
       field = super(SecenekInline, self).formfield_for_dbfield(db_field, **kwargs)
       if db_field.name == 'harf':
           field.widget = TextInput(attrs={'size':2})
       return field
   formfield_overrides = {
       models.TextField: {'widget': Textarea(attrs={'rows':2})},
   }
   extra = 2

Si vous souhaitez modifier uniquement une taille de champ spécifique, vous pouvez l'utiliser.

mevaka
la source