Style CSS dans les formulaires Django

150

Je voudrais styliser ce qui suit:

forms.py:

from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    email = forms.EmailField(required=False)
    message = forms.CharField(widget=forms.Textarea)

contact_form.html:

<form action="" method="post">
  <table>
    {{ form.as_table }}
  </table>
  <input type="submit" value="Submit">
</form>

Par exemple, comment puis-je créer une classe ou ID pour le subject, email, messagepour fournir une feuille de style externe à?

David542
la source

Réponses:

192

Tiré de ma réponse à: Comment baliser les champs de formulaire avec <div class = 'field_type'> dans Django

class MyForm(forms.Form):
    myfield = forms.CharField(widget=forms.TextInput(attrs={'class' : 'myfieldclass'}))

ou

class MyForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['myfield'].widget.attrs.update({'class' : 'myfieldclass'})

ou

class MyForm(forms.ModelForm):
    class Meta:
        model = MyModel
        widgets = {
            'myfield': forms.TextInput(attrs={'class': 'myfieldclass'}),
        }

--- EDIT ---
Ce qui précède est la modification la plus simple à apporter au code de la question d'origine qui accomplit ce qui a été demandé. Cela vous empêche également de vous répéter si vous réutilisez le formulaire ailleurs; vos classes ou autres attributs ne fonctionnent que si vous utilisez les méthodes de formulaire as_table / as_ul / as_p de Django. Si vous avez besoin d'un contrôle total pour un rendu entièrement personnalisé, cela est clairement documenté

- EDIT 2 ---
Ajout d'une nouvelle façon de spécifier un widget et des attrs pour un ModelForm.

shadfc
la source
27
Bien qu'il ne soit pas recommandé de mélanger la présentation avec la logique métier.
Torsten Engelbrecht
8
Comment est cette présentation? Vous donnez à l'élément une classe, qui n'est qu'un identifiant ou une catégorisation. Vous devez encore définir ce que cela fait ailleurs
shadfc
9
Oui et non. Les premières classes CSS sont par convention utilisées pour le style, si vous avez besoin d'un identifiant unique, il est préférable de l'utiliser id. Deuxièmement, c'est généralement la responsabilité du côté modèle de faire exactement cela, surtout si vous allez accéder à cette classe via des méthodes frontales (js, css). Je n'ai pas dit que votre réponse était fausse. À mon avis, c'est juste une mauvaise pratique (surtout lorsque vous travaillez dans une équipe avec des développeurs frontend et backend).
Torsten Engelbrecht
6
Cela semble ridicule, juste pour ajouter une classe, vous avez besoin d'autant de code? Il semble qu'il serait plus facile de coder en dur le HTML / CSS dans ces zones (en particulier pour un site riche en CSS).
David542
9
C'est fou, Django rend cela si gênant!
Bryce
103

Cela peut être fait à l'aide d'un filtre de modèle personnalisé. Pensez à rendre votre formulaire de cette façon:

<form action="/contact/" method="post">
  {{ form.non_field_errors }}
  <div class="fieldWrapper">
    {{ form.subject.errors }}
    {{ form.subject.label_tag }}
    {{ form.subject }}
    <span class="helptext">{{ form.subject.help_text }}</span>
  </div>
</form>

form.subjectest une instance BoundFielddont a la as_widget()méthode.

Vous pouvez créer un filtre personnalisé addclassdans my_app / templatetags / myfilters.py :

from django import template

register = template.Library()

@register.filter(name='addclass')
def addclass(value, arg):
    return value.as_widget(attrs={'class': arg})

Et puis appliquez votre filtre:

{% load myfilters %}

<form action="/contact/" method="post">
  {{ form.non_field_errors }}
  <div class="fieldWrapper">
    {{ form.subject.errors }}
    {{ form.subject.label_tag }}
    {{ form.subject|addclass:'MyClass' }}
    <span class="helptext">{{ form.subject.help_text }}</span>
  </div>
</form>

form.subjectssera alors rendu avec la MyClassclasse CSS.

Charlesthk
la source
5
C'est l'une des solutions les plus propres et faciles à mettre en œuvre
utilisateur
5
Cette réponse devrait être la meilleure réponse !!! C'est vraiment plus propre que la solution proposée par Django! Bravo @Charlesthk
David D.
4
Super utile. Ce n'était pas évident pour moi au début, mais vous pouvez également l'utiliser pour ajouter plusieurs classes:{{ form.subject|addclass:'myclass1 myclass2' }}
smg
J'aime que cela permette de conserver les classes HTML dans les fichiers HTML. Lorsque je travaille avec le style, je saute entre les feuilles de style et la structure, pas les modèles et / ou les formulaires.
Kevin
29

Si vous ne souhaitez pas ajouter de code au formulaire (comme mentionné dans les commentaires de la réponse de @ shadfc), c'est certainement possible, voici deux options.

Tout d'abord, il vous suffit de référencer les champs individuellement dans le HTML, plutôt que l'ensemble du formulaire à la fois:

<form action="" method="post">
    <ul class="contactList">
        <li id="subject" class="contact">{{ form.subject }}</li>
        <li id="email" class="contact">{{ form.email }}</li>
        <li id="message" class="contact">{{ form.message }}</li>
    </ul>
    <input type="submit" value="Submit">
</form>

(Notez que je l'ai également changé en une liste non triée .)

Deuxièmement, notez dans la documentation sur la sortie de formulaires au format HTML , Django:

L'ID de champ est généré en ajoutant «id_» au nom du champ. Les attributs et balises id sont inclus dans la sortie par défaut.

Tous vos champs de formulaire ont déjà un identifiant unique . Vous feriez donc référence à id_subject dans votre fichier CSS pour styliser le champ sujet . Je dois noter que voici comment le formulaire se comporte lorsque vous prenez la valeur par défaut HTML , ce qui nécessite simplement d'imprimer le formulaire, pas les champs individuels:

<ul class="contactList">
    {{ form }}  # Will auto-generate HTML with id_subject, id_email, email_message 
    {{ form.as_ul }} # might also work, haven't tested
</ul>

Voir le lien précédent pour d'autres options lors de la sortie de formulaires (vous pouvez faire des tableaux, etc.).

Remarque - Je me rends compte que ce n'est pas la même chose que d'ajouter une classe à chaque élément (si vous avez ajouté un champ au formulaire, vous devrez également mettre à jour le CSS) - mais il est assez facile de référencer tous les champs par id dans votre CSS comme ceci:

#id_subject, #id_email, #email_message 
{color: red;}
Jean C
la source
J'ai essayé votre deuxième solution mais cela n'a pas fonctionné. J'ai créé une classe pour id_email et cela n'a produit aucun résultat.
presque un débutant
@almostabeginner une chose que je peux suggérer pour le débogage - une fois que vous voyez la page dans un navigateur, utilisez Afficher la source de la page (généralement en cliquant avec le bouton droit de la souris) et regardez la page complète réelle que Django génère. Vérifiez si les champs existent, avec l' identifiant ou l' identifiant de classe que vous attendez. De plus, la plupart des navigateurs (éventuellement en installant un plugin) peuvent exécuter un débogueur qui vous montre le css qui est appliqué à une page, également utile pour voir ce qui se passe.
John C
@almostabeginner note également, j'ai ajouté un peu d'exemple de code. Au cas où cela ne serait pas clair à partir du texte seul - vous devez référencer le formulaire lui-même, pas des champs individuels, à quel point le formulaire génère automatiquement du HTML qui contient des identifiants , comme décrit. Espérons que cela aide.
John C
1
Merci pour l'aide, le problème n'était pas du tout mon css, le problème était lié au cache. Donc, mon ancien css était stocké, donc aucune des modifications ne s'afficherait. Je viens de vider le cache de Chrome et toutes les mises à jour ont commencé à apparaître.
presque un débutant
15

Selon cet article de blog, vous pouvez ajouter des classes css à vos champs à l'aide d'un filtre de modèle personnalisé.

from django import template
register = template.Library()

@register.filter(name='addcss')
def addcss(field, css):
    return field.as_widget(attrs={"class":css})

Placez-le dans le dossier templatetags / de votre application et vous pouvez maintenant le faire

{{field|addcss:"form-control"}}
aashanand
la source
2
cela aurait dû être accepté comme la vraie réponse à ce message :)
mvdb
La meilleure solution de loin.
Mods Vs Rockers
1
Génial, merci! N'oubliez pas de charger réellement le tag. De plus, dans Django 2.1, le seul moyen pour que Django trouve le modèle était d'ajouter une option dans settings.py: 'bibliothèques': {'add_css': 'app.templatetags.tag_name',}
simonbogarde
11

Vous pouvez faire comme ceci:

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    subject.widget.attrs.update({'id' : 'your_id'})

J'espère que ça marche.

Ignas

Ignas Butėnas
la source
Merci ignas. Réponse précise!
Rudresh Ajgaonkar
9

Vous pouvez utiliser cette bibliothèque: https://pypi.python.org/pypi/django-widget-tweaks

Il vous permet d'effectuer les opérations suivantes:

{% load widget_tweaks %}
<!-- add 2 extra css classes to field element -->
{{ form.title|add_class:"css_class_1 css_class_2" }}
Eamonn Faherty
la source
1
Jetez un œil à la solution Charlesthk, c'est pareil sans ajouter de bibliothèque supplémentaire :)
David D.
@DavidW: Oui, mais Widget Tweaks a beaucoup plus de filtres, tels que render_field.
mrdaliri
5

Tu peux faire:

<form action="" method="post">
    <table>
        {% for field in form %}
        <tr><td>{{field}}</td></tr>
        {% endfor %}
    </table>
    <input type="submit" value="Submit">
</form>

Ensuite, vous pouvez ajouter des classes / id à, par exemple, la <td>balise. Vous pouvez bien sûr utiliser toutes les autres balises de votre choix. Cochez Travailler avec les formulaires Django comme exemple de ce qui est disponible pour chacun fielddans le formulaire ( {{field}}par exemple, il s'agit simplement de sortir la balise d'entrée, pas l'étiquette, etc.).

Torsten Engelbrecht
la source
3

Une solution consiste à utiliser JavaScript pour ajouter les classes CSS requises une fois la page prête. Par exemple, styliser la sortie du formulaire django avec des classes bootstrap (jQuery utilisé par souci de concision):

<script type="text/javascript">
    $(document).ready(function() {
        $('#some_django_form_id').find("input[type='text'], select, textarea").each(function(index, element) {
            $(element).addClass("form-control");
        });
    });
</script>

Cela évite la laideur de mélanger les spécificités du style avec votre logique métier.

Simon Feltman
la source
3

Écrivez votre formulaire comme:

    class MyForm(forms.Form):
         name = forms.CharField(widget=forms.TextInput(attr={'class':'name'}),label="Your Name")
         message = forms.CharField(widget=forms.Textarea(attr={'class':'message'}), label="Your Message")

Dans votre champ HTML, faites quelque chose comme:

{% for field in form %}
      <div class="row">
        <label for="{{ field.name}}">{{ field.label}}</label>{{ field }}
     </div>
{% endfor %}

Ensuite, dans votre CSS, écrivez quelque chose comme:

.name{
      /* you already have this class so create it's style form here */
}
.message{
      /* you already have this class so create it's style form here */
}
label[for='message']{
      /* style for label */
}

J'espère que cette réponse vaut la peine d'être essayée! Notez que vous devez avoir écrit vos vues pour rendre le fichier HTML qui contient le formulaire.

Aula
la source
Merci. mais comment puis-je écrire un texte d'étiquette spécifique?
gakeko betsi
2

Vous n'aurez peut-être pas besoin de remplacer votre classe de formulaire __init__, car Django définit nameet idattribue des attributs dans les HTML input. Vous pouvez avoir CSS comme ceci:

form input[name='subject'] {
    font-size: xx-large;
}
tehfink
la source
1
Pour ajouter à cela. Étant donné "subject = forms ...", id = "id_subject" et name = "subject" est la convention Django pour ces attributs. Par conséquent, vous devriez également être capable de faire #id_subject {...}
solartic
@solartic: Vous avez raison, merci. Je n'ai pas mentionné cela parce que le idchamp créé par Django pour les ensembles de formulaires devient assez poilu…
tehfink
2

Je n'ai pas vraiment vu celui-ci ...

https://docs.djangoproject.com/en/1.8/ref/forms/api/#more-granular-output

Sortie plus granulaire

Les méthodes as_p (), as_ul () et as_table () sont simplement des raccourcis pour les développeurs paresseux - elles ne sont pas la seule façon d'afficher un objet de formulaire.

class BoundField Utilisé pour afficher le HTML ou accéder aux attributs d'un seul champ d'une instance de formulaire.

La méthode str () ( unicode sur Python 2) de cet objet affiche le code HTML de ce champ.

Pour récupérer un seul BoundField, utilisez la syntaxe de recherche de dictionnaire sur votre formulaire en utilisant le nom du champ comme clé:

>>> form = ContactForm()
>>> print(form['subject'])
<input id="id_subject" type="text" name="subject" maxlength="100" />

Pour récupérer tous les objets BoundField, parcourez le formulaire:

>>> form = ContactForm()
>>> for boundfield in form: print(boundfield)
<input id="id_subject" type="text" name="subject" maxlength="100" />
<input type="text" name="message" id="id_message" />
<input type="email" name="sender" id="id_sender" />
<input type="checkbox" name="cc_myself" id="id_cc_myself" />

La sortie spécifique au champ respecte le paramètre auto_id de l'objet de formulaire:

>>> f = ContactForm(auto_id=False)
>>> print(f['message'])
<input type="text" name="message" />
>>> f = ContactForm(auto_id='id_%s')
>>> print(f['message'])
<input type="text" name="message" id="id_message" />
BilliAm
la source
2

Il y a un très facile à installer et un excellent outil conçu pour Django que j'utilise pour le style et il peut être utilisé pour tous les frameworks frontaux tels que Bootstrap, Materialize, Foundation, etc. Il s'appelle widget-tweaks Documentation: Widget Tweaks

  1. Vous pouvez l'utiliser avec les vues génériques de Django
  2. Ou avec vos propres formulaires:

depuis les formulaires d'importation django

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    email = forms.EmailField(required=False)
    message = forms.CharField(widget=forms.Textarea)

Au lieu d'utiliser default:

{{ form.as_p }} or {{ form.as_ul }}

Vous pouvez le modifier à votre guise en utilisant l'attribut render_field qui vous donne une manière plus html de le styliser comme cet exemple:

template.html

{% load widget_tweaks %}

<div class="container">
   <div class="col-md-4">
      {% render_field form.subject class+="form-control myCSSclass" placeholder="Enter your subject here" %}
   </div>
   <div class="col-md-4">
      {% render_field form.email type="email" class+="myCSSclassX myCSSclass2" %}
   </div>
   <div class="col-md-4">
      {% render_field form.message class+="myCSSclass" rows="4" cols="6" placeholder=form.message.label %}
   </div>
</div>

Cette bibliothèque vous donne la possibilité d'avoir votre front-end bien séparé de votre backend

Alam Téllez
la source
1

Dans Django 1.10 (peut-être plus tôt également), vous pouvez le faire comme suit.

Modèle:

class Todo(models.Model):
    todo_name = models.CharField(max_length=200)
    todo_description = models.CharField(max_length=200, default="")
    todo_created = models.DateTimeField('date created')
    todo_completed = models.BooleanField(default=False)

    def __str__(self):
        return self.todo_name

Forme:

class TodoUpdateForm(forms.ModelForm):
    class Meta:
        model = Todo
        exclude = ('todo_created','todo_completed')

Modèle:

<form action="" method="post">{% csrf_token %}
    {{ form.non_field_errors }}
<div class="fieldWrapper">
    {{ form.todo_name.errors }}
    <label for="{{ form.name.id_for_label }}">Name:</label>
    {{ form.todo_name }}
</div>
<div class="fieldWrapper">
    {{ form.todo_description.errors }}
    <label for="{{ form.todo_description.id_for_label }}">Description</label>
    {{ form.todo_description }}
</div>
    <input type="submit" value="Update" />
</form>
MadPhysicist
la source
0

Edit: Une autre façon (légèrement meilleure) de faire ce que je suggère est répondue ici: Style de champ d'entrée de formulaire Django

Toutes les options ci-dessus sont géniales, j'ai juste pensé ajouter celle-ci parce qu'elle est différente.

Si vous voulez un style personnalisé, des classes, etc. sur vos formulaires, vous pouvez créer une entrée html dans votre modèle qui correspond à votre champ de formulaire. Pour un CharField, par exemple, (le widget par défaut est TextInput), disons que vous voulez une entrée de texte de type bootstrap. Vous feriez quelque chose comme ceci:

<input type="text" class="form-control" name="form_field_name_here">

Et tant que vous mettez le nom du champ de formulaire correspond à l' nameattribut html , (et le widget doit probablement correspondre également au type d'entrée) Django exécutera tous les mêmes validateurs sur ce champ lorsque vous exécutez validateouform.is_valid() et

Le style d'autres éléments tels que les étiquettes, les messages d'erreur et le texte d'aide ne nécessite pas beaucoup de solution de contournement, car vous pouvez faire quelque chose comme form.field.error.as_textet les styliser comme vous le souhaitez. Les champs réels sont ceux qui nécessitent quelques manipulations.

Je ne sais pas si c'est le meilleur moyen ou celui que je recommanderais, mais c'est un moyen, et cela pourrait convenir à quelqu'un.

Voici une procédure pas à pas utile des formulaires de style et elle comprend la plupart des réponses répertoriées sur SO (comme l'utilisation de l'attr sur les widgets et les ajustements de widgets). https://simpleisbetterthancomplex.com/article/2017/08/19/how-to-render-django-form-manually.html

Kimbo
la source
0

Styliser les instances de widget

Si vous souhaitez rendre une instance de widget différente d'une autre, vous devrez spécifier des attributs supplémentaires au moment où l'objet widget est instancié et assigné à un champ de formulaire (et peut-être ajouter des règles à vos fichiers CSS).

https://docs.djangoproject.com/en/2.2/ref/forms/widgets/

Pour ce faire, vous utilisez l'argument Widget.attrs lors de la création du widget:

class CommentForm(forms.Form):
    name = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}))
    url = forms.URLField()
    comment = forms.CharField(widget=forms.TextInput(attrs={'size': '40'}))

Vous pouvez également modifier un widget dans la définition du formulaire:

class CommentForm(forms.Form):
    name = forms.CharField()
    url = forms.URLField()
    comment = forms.CharField()

    name.widget.attrs.update({'class': 'special'})
    comment.widget.attrs.update(size='40')

Ou si le champ n'est pas déclaré directement sur le formulaire (comme les champs de formulaire de modèle), vous pouvez utiliser l'attribut Form.fields:

class CommentForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['name'].widget.attrs.update({'class': 'special'})
        self.fields['comment'].widget.attrs.update(size='40')

Django inclura alors les attributs supplémentaires dans la sortie rendue:

>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" class="special" required></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40" required></td></tr>
Freman Zhang
la source