Définir la classe css dans django Forms

192

Supposons que j'ai un formulaire

class SampleClass(forms.Form):
    name = forms.CharField(max_length=30)
    age = forms.IntegerField()
    django_hacker = forms.BooleanField(required=False)

Existe-t-il un moyen pour moi de définir des classes css sur chaque champ de sorte que je puisse ensuite utiliser jQuery en fonction de la classe dans ma page rendue?

J'espérais ne pas avoir à créer manuellement le formulaire.

ashchristopher
la source
9
wow, tout le monde vote ça? docs.djangoproject.com/en/dev/ref/forms/widgets/…
Skylar Saveland
10
@skyl Je prendrais cela comme une indication que ce n'est pas facile à trouver dans la documentation django. J'ai parcouru et fait plusieurs recherches Google et je n'ai pas pu trouver cela, donc je suis heureux pour la question et elle obtient mon vote positif.
Nils
Je sais que c'est un vieux thread mais dans les champs de formulaire Django ont maintenant une classe id_fieldname.
ratsimihah
1
Voir cette réponse sur la façon de définir des classes pour un champ de formulaire à l'intérieur d'un modèle tout en respectant les classes de champ existantes
dimyG

Réponses:

182

Encore une autre solution qui ne nécessite pas de changements dans le code python et qui est donc meilleure pour les concepteurs et les changements de présentation ponctuels: django-widget-tweaks . J'espère que quelqu'un le trouvera utile.

Mikhail Korobov
la source
61
La seule solution sensée, je dois dire. Je vous remercie!. Le code Python, et en particulier dans la définition du formulaire, est le dernier endroit pour mettre des éléments de style - ceux-ci appartiennent définitivement aux modèles.
Boris Chervenkov
5
C'est une super bibliothèque! C'est dommage que cette réponse soit enterrée au fond.
Chris Lacasse
1
Idéal pour les designers! :-)
hobbes3
1
Excellent! J'avais développé des filtres pour faire ces choses, mais ce projet est beaucoup plus puissant. Merci!
msbrogli
1
en fait cela fonctionne aussi pour Jinja2 :-) J'ai changé l'ordre du fichier sécurisé et ajouté des parenthèses au lieu de deux-points {{myform.email | add_class ("css_class_1 css_class_2") | safe}} merci d'avoir écrit ceci. il devrait faire partie de Django.
David Dehghan
92

Répondu à ma propre question. Soupir

http://docs.djangoproject.com/en/dev/ref/forms/widgets/#django.forms.Widget.attrs

Je ne savais pas qu'il était passé dans le constructeur du widget.

ashchristopher
la source
2
cela implique-t-il qu'il ne peut pas être utilisé avec la classe ModelForm?
xster
2
Non, il vous suffirait de définir explicitement le champ du formulaire dans le formulaire modèle afin de pouvoir définir le widget. Ou utilisez la solution répertoriée ci-dessous pour éviter d'avoir à vous soucier du champ de formulaire.
Tom
78

Voici une autre solution pour ajouter des définitions de classe aux widgets après avoir déclaré les champs de la classe.

def __init__(self, *args, **kwargs):
    super(SampleClass, self).__init__(*args, **kwargs)
    self.fields['name'].widget.attrs['class'] = 'my_class'
ashchristopher
la source
4
Pour ModelForms, c'est souvent mieux car vous n'avez pas besoin de connaître le champ de formulaire par défaut utilisé et vous pouvez définir dynamiquement différentes classes en fonction des conditions d'exécution. Plus propre que les hacks de méta-codage ...
Daniel Naab
1
Cela suppose que vous souhaitiez une entrée pour chaque champ d'un formulaire, ce qui n'est souvent pas le cas. Un formulaire n'est pas nécessairement lié à un modèle - il peut être lié à de nombreux modèles. Dans le cas où les champs de modèle et les champs de formulaire ont une relation 1: 1, alors oui, ModelForm est un bien meilleur choix.
ashchristopher
44

Utilisez django-widget-tweaks , il est facile à utiliser et fonctionne plutôt bien.

Sinon, cela peut être fait à l'aide d'un filtre de modèle personnalisé.

Considérant que vous rendez votre formulaire de cette façon:

<form action="/contact/" method="post">
    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="id_subject">Email subject:</label>
        {{ form.subject }}
    </div>
</form>

form.subject est une instance de BoundField qui a la méthode as_widget .

vous pouvez créer un filtre personnalisé "addcss" dans "my_app / templatetags / myfilters.py"

from django import template

register = template.Library()

@register.filter(name='addcss')
def addcss(value, arg):
    css_classes = value.field.widget.attrs.get('class', '').split(' ')
    if css_classes and arg not in css_classes:
        css_classes = '%s %s' % (css_classes, arg)
    return value.as_widget(attrs={'class': css_classes})

Et puis appliquez votre filtre:

{% load myfilters %}
<form action="/contact/" method="post">
    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="id_subject">Email subject:</label>
        {{ form.subject|addcss:'MyClass' }}
    </div>
</form>

form.subjects sera alors rendu avec la classe css "MyClass".

J'espère que cette aide.

MODIFIER 1

  • Filtre à jour selon dimyG la réponse «

  • Ajouter un lien django-widget-tweak

MODIFIER 2

  • Filtre à jour selon Bhyd le commentaire «
Charlesthk
la source
3
C'est bien! C'est DRY et il sépare la couche d'affichage de la couche de contrôle. +1 de moi!
alekwisnia
1
Cependant, j'ai maintenant remarqué un inconvénient. Si je définis la classe dans l'attrs de widget, alors elles sont remplacées par ce filtre 'addcss'. Avez-vous des idées pour fusionner cela?
alekwisnia
1
Je veux dire, as_widget () remplace les attrs. Comment s'assurer qu'il utilise les attrs existants et les étend avec de nouveaux?
alekwisnia
Voir cette réponse sur la façon de respecter les classes de champs existantes
dimyG
get('class', None).split(' ')échouera si la balise n'a pas l'attribut class, tel qu'il Noneest renvoyé. Changer cela en get('class', '').split(' ')fonctionne
dtasev
32

Extension de la méthode pointée sur docs.djangoproject.com:

class MyForm(forms.Form): 
    comment = forms.CharField(
            widget=forms.TextInput(attrs={'size':'40'}))

Je pensais qu'il était difficile de connaître le type de widget natif pour chaque champ, et j'ai trouvé drôle de remplacer la valeur par défaut juste pour mettre un nom de classe sur un champ de formulaire. Cela semble fonctionner pour moi:

class MyForm(forms.Form): 
    #This instantiates the field w/ the default widget
    comment = forms.CharField()

    #We only override the part we care about
    comment.widget.attrs['size'] = '40'

Cela me semble un peu plus propre.

Dave
la source
C'est précisément ce que d'autres ont dit, il y a très longtemps, sauf que vous le faites avec «taille» plutôt que «classe» qui a été demandé.
Chris Morgan
3
@Chris Morgan En fait, il est le premier à suggérer d'utiliser "comment.widget.attrs" dans la déclaration Form elle-même (au lieu de jouer avec init ou de le faire dans la vue).
DarwinSurvivor
29

Si vous voulez que tous les champs du formulaire héritent d'une certaine classe, vous définissez simplement une classe parent, qui hérite de forms.ModelForm, puis en hérite

class BaseForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(BaseForm, self).__init__(*args, **kwargs)
        for field_name, field in self.fields.items():
            field.widget.attrs['class'] = 'someClass'


class WhateverForm(BaseForm):
    class Meta:
        model = SomeModel

Cela m'a aidé à ajouter 'form-control'automatiquement la classe à tous les champs de tous les formulaires de mon application, sans ajouter de réplication de code.

Oleg Belousov
la source
1
s'il vous plaît commencer les noms de classe avec des majuscules (aussi, BaseForm semble un nom beaucoup mieux pour cela)
Alvaro
16

Voici un moyen simple de modifier la vue. ajoutez ci-dessous dans la vue juste avant de le passer dans le modèle.

form = MyForm(instance = instance.obj)
form.fields['email'].widget.attrs = {'class':'here_class_name'}
Homme araignée
la source
14

Ajoutez simplement les classes à votre formulaire comme suit.

class UserLoginForm(forms.Form):
    username = forms.CharField(widget=forms.TextInput(
        attrs={
        'class':'form-control',
        'placeholder':'Username'
        }
    ))
    password = forms.CharField(widget=forms.PasswordInput(
        attrs={
        'class':'form-control',
        'placeholder':'Password'
        }
    ))
paulc
la source
5

Voici une variante de ce qui précède qui donnera à tous les champs la même classe (par exemple jquery jolis coins arrondis).

  # Simple way to assign css class to every field
  def __init__(self, *args, **kwargs):
    super(TranslatedPageForm, self).__init__(*args, **kwargs)
    for myField in self.fields:
      self.fields[myField].widget.attrs['class'] = 'ui-state-default ui-corner-all'
timlinux
la source
5

Vous pouvez essayer ceci.

class SampleClass(forms.Form):
  name = forms.CharField(max_length=30)
  name.widget.attrs.update({'class': 'your-class'})
...

Vous pouvez voir plus d'informations dans: Django Widgets

Andres Quiroga
la source
2

Si vous souhaitez ajouter une classe au champ d'un formulaire dans un modèle (pas dans view.py ou form.py), par exemple dans le cas où vous souhaitez modifier des applications tierces sans remplacer leurs vues, puis un filtre de modèle comme décrit dans la réponse de Charlesthk est très pratique. Mais dans cette réponse, le filtre de modèle remplace toutes les classes existantes que le champ pourrait avoir.

J'ai essayé d'ajouter ceci en tant que modification, mais il a été suggéré d'écrire comme une nouvelle réponse.

Alors, voici une balise template qui respecte les classes existantes du champ:

from django import template

register = template.Library()


@register.filter(name='addclass')
def addclass(field, given_class):
    existing_classes = field.field.widget.attrs.get('class', None)
    if existing_classes:
        if existing_classes.find(given_class) == -1:
            # if the given class doesn't exist in the existing classes
            classes = existing_classes + ' ' + given_class
        else:
            classes = existing_classes
    else:
        classes = given_class
    return field.as_widget(attrs={"class": classes})
dimyG
la source
Merci pour votre suggestion. J'ai édité ma réponse en conséquence avec une approche plus «pythonique».
Charlesthk
super !!, c'est ce que je cherchais
ugali soft
1

En fait, vous pouvez le faire dans le constructeur de formulaire ( fonction init ) ou après le lancement de la classe de formulaire. Ceci est parfois nécessaire si vous n'écrivez pas votre propre formulaire et que ce formulaire vient d'ailleurs -

def some_view(request):
    add_css_to_fields = ['list','of','fields']
    if request.method == 'POST':
        form = SomeForm(request.POST)
        if form.is_valid():
            return HttpResponseRedirect('/thanks/')
    else:
        form = SomeForm()

    for key in form.fields.keys():
        if key in add_css_to_fields:
            field = form.fields[key]
            css_addition = 'css_addition '
            css = field.widget.attrs.get('class', '')
            field.widget.attrs['class'] = css_addition + css_classes

    return render(request, 'template_name.html', {'form': form})
Niki.py
la source
1

Vous pouvez également utiliser Django Crispy Forms , c'est un excellent outil pour définir des formulaires au cas où vous souhaiteriez utiliser un framework CSS comme Bootstrap ou Foundation. Et il est facile de spécifier des classes pour vos champs de formulaire.

Votre classe de formulaire aimerait alors ceci:

from django import forms

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Div, Submit, Field
from crispy_forms.bootstrap import FormActions

class SampleClass(forms.Form):
    name = forms.CharField(max_length=30)
    age = forms.IntegerField()
    django_hacker = forms.BooleanField(required=False)

    helper = FormHelper()
    helper.form_class = 'your-form-class'
    helper.layout = Layout(
        Field('name', css_class='name-class'),
        Field('age', css_class='age-class'),
        Field('django_hacker', css-class='hacker-class'),
        FormActions(
            Submit('save_changes', 'Save changes'),
        )
    )
Dmitrii Mikhailov
la source