Stile CSS in forme Django


150

Vorrei modellare quanto segue:

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>

Per esempio, come faccio a impostare una classe o ID per il subject, email, messageper fornire un foglio di stile esterno?

Risposte:


192

Tratto dalla mia risposta a: Come contrassegnare i campi modulo con <div class = 'field_type'> in Django

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

o

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'})

o

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

--- MODIFICA ---
Quanto sopra è la modifica più semplice da apportare al codice della domanda originale che realizza ciò che è stato chiesto. Ti impedisce anche di ripetere te stesso se riutilizzi il modulo in altri luoghi; le tue classi o altri attributi funzionano solo se usi i metodi del modulo as_table / as_ul / as_p di Django. Se è necessario il controllo completo per un rendering completamente personalizzato, questo è chiaramente documentato

- EDIT 2 ---
Aggiunto un nuovo modo per specificare widget e attr per un ModelForm.


27
Sebbene non sia consigliabile mescolare la presentazione con la logica aziendale.
Torsten Engelbrecht,

8
Come è questa presentazione? Stai dando all'elemento una classe, che è solo un identificatore o una categorizzazione. Devi ancora definire cosa succede altrove
shadfc,

9
Sì e no. Le prime classi CSS sono per convenzione utilizzate per lo stile, se hai bisogno di un identificatore univoco è meglio usarlo id. In secondo luogo è di solito la responsabilità del lato template di fare esattamente questo, specialmente se si accederà a questa classe tramite metodi frontend (js, css). Non ho detto che la tua risposta è sbagliata. Secondo me è solo una cattiva pratica (soprattutto quando lavori in una squadra con sviluppatori frontend e backend).
Torsten Engelbrecht,

6
Sembra ridicolo, solo per aggiungere una classe hai bisogno di così tanto codice? Sembra che sarebbe più semplice codificare l'HTML / CSS in queste aree (specialmente per un sito con CSS elevato).
David542,

9
È un folle django che lo rende così imbarazzante!
Bryce,

103

Questo può essere fatto usando un filtro template personalizzato. Prendi in considerazione il rendering del modulo in questo modo:

<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.subjectè un'istanza di BoundFieldcui ha il as_widget()metodo

Puoi creare un filtro personalizzato addclassin 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})

E quindi applicare il filtro:

{% 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.subjectsverrà quindi eseguito il rendering con la MyClassclasse CSS.


5
Questa è una delle soluzioni più pulite e facili da implementare
utente

5
Questa risposta dovrebbe essere la risposta migliore !!! È molto più pulito della soluzione proposta da Django! Ben fatto @Charlesthk
David D.

4
Super utile. All'inizio non era ovvio per me, ma puoi usarlo anche per aggiungere più classi:{{ form.subject|addclass:'myclass1 myclass2' }}
smg

Mi piace che questo consenta di mantenere le classi HTML nei file HTML. Quando lavoro con lo styling, salto avanti e indietro tra fogli di stile e struttura, non modelli e / o forme.
Kevin,

29

Se non vuoi aggiungerne nessuno codice al modulo (come menzionato nei commenti alla risposta di @ shadfc), è certamente possibile, qui ci sono due opzioni.

Innanzitutto, fai semplicemente riferimento ai campi singolarmente nell'HTML, anziché all'intero modulo contemporaneamente:

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

(Nota che l'ho anche cambiato in un elenco non ordinato .)

In secondo luogo, nota nei documenti sull'output di moduli come HTML , Django:

L'ID campo viene generato anteponendo "id_" al nome del campo. Gli attributi e i tag id sono inclusi nell'output per impostazione predefinita.

Tutti i campi del modulo hanno già un ID univoco . Quindi faresti riferimento a id_subject nel tuo file CSS per dare uno stile al campo dell'oggetto . Dovrei notare, questo è come si comporta il modulo quando si prende l' HTML predefinito , che richiede solo la stampa del modulo, non i singoli campi:

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

Vedi il link precedente per altre opzioni durante la produzione di moduli (puoi fare tabelle, ecc.).

Nota: mi rendo conto che questo non è lo stesso dell'aggiunta di una classe a ciascun elemento (se hai aggiunto un campo al modulo, dovresti aggiornare anche il CSS) - ma è abbastanza facile fare riferimento a tutti i campi per id nel tuo CSS in questo modo:

#id_subject, #id_email, #email_message 
{color: red;}

Ho provato la tua seconda soluzione ma non ha funzionato. Ho creato la classe per id_email e non è stato possibile ottenere risultati.
Quasi un principiante il

@almostabeginner una cosa che posso suggerire per il debug: una volta che vedi la pagina in un browser, usa Visualizza sorgente pagina (generalmente facendo clic con il tasto destro) e guarda la pagina intera che sta generando Django. Verifica se i campi esistono, con l' id o l' identificatore di classe che ti aspetti. Inoltre la maggior parte dei browser (possibilmente installando un plug-in) può eseguire un debugger che ti mostra il CSS che viene applicato a una pagina, utile anche per vedere cosa sta succedendo.
Giovanni C

@almostabeginner nota anche che ho aggiunto un po 'di codice di esempio. Nel caso in cui non fosse chiaro solo dal testo - devi fare riferimento al modulo stesso, non ai singoli campi, a quel punto il modulo genera automaticamente HTML che contiene ID , come descritto. Spero che questo aiuti.
Giovanni C

1
Grazie per l'aiuto, il problema non era affatto il mio CSS, il problema era legato alla cache. Quindi il mio vecchio CSS è stato archiviato, pertanto nessuna delle modifiche sarebbe stata visualizzata. Ho appena cancellato la cache da Chrome e tutti gli aggiornamenti hanno iniziato a mostrare.
Quasi un principiante il

15

Per questo post di blog, puoi aggiungere classi CSS ai tuoi campi utilizzando un filtro modello personalizzato.

from django import template
register = template.Library()

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

Inseriscilo nella cartella / templatetags della tua app e ora puoi farlo

{{field|addcss:"form-control"}}

2
questo avrebbe dovuto essere accettato come la vera risposta a questo post :)
mvdb

La migliore soluzione di gran lunga.
Mods Vs Rockers

1
Fantastico, grazie! Non dimenticare di caricare effettivamente il tag. Inoltre, in Django 2.1 l'unico modo per convincere Django a trovare il modello era aggiungendo un'opzione in settings.py: 'libraries': {'add_css': 'app.templatetags.tag_name',}
simonbogarde

11

Puoi fare così:

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

Spero che funzioni.

Ignas


Grazie ignas. Risposta precisa!
Rudresh Ajgaonkar,

9

È possibile utilizzare questa libreria: https://pypi.python.org/pypi/django-widget-tweaks

Ti permette di fare quanto segue:

{% load widget_tweaks %}
<!-- add 2 extra css classes to field element -->
{{ form.title|add_class:"css_class_1 css_class_2" }}

1
Dai un'occhiata alla soluzione di Charlesthk, è la stessa senza aggiungere una libreria aggiuntiva :)
David D.

@DavidW .: Sì, ma Widget Tweaks ha molti più filtri, come render_field.
mrdaliri,

5

Tu puoi fare:

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

Quindi puoi aggiungere classi / ID ad esempio al <td>tag. Ovviamente puoi usare qualsiasi altro tag che desideri. Controlla Lavorare con i moduli Django come esempio di ciò che è disponibile per ciascuno fieldnel modulo ( {{field}}ad esempio è solo l'output del tag di input, non dell'etichetta e così via).


3

Una soluzione è utilizzare JavaScript per aggiungere le classi CSS richieste dopo che la pagina è pronta. Ad esempio, lo stile dell'output del modulo django con le classi bootstrap (jQuery usato per brevità):

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

Questo evita la bruttezza di mescolare le specifiche di stile con la tua logica aziendale.


3

Scrivi il tuo modulo come:

    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")

Nel tuo campo HTML fai qualcosa del tipo:

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

Quindi nel tuo CSS scrivi qualcosa di simile:

.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 */
}

Spero che questa risposta meriti una prova! Nota che devi aver scritto le tue visualizzazioni per eseguire il rendering del file HTML che contiene il modulo.


Grazie. ma come posso scrivere un testo specifico per l'etichetta?
Gakeko Betsi,

2

Potrebbe non essere necessario sovrascrivere la classe del modulo " __init__, poiché Django imposta namee idattributi negli HTML input. Puoi avere CSS come questo:

form input[name='subject'] {
    font-size: xx-large;
}

1
Per aggiungere a questo. Dato "subject = forms ...", id = "id_subject" e name = "subject" è la convenzione di Django per questi attributi. Quindi dovresti anche essere in grado di fare #id_subject {...}
solartic

@solartic: hai ragione, grazie. Non ho menzionato questo perché il idcampo creato da Django per i formets diventa piuttosto peloso ...
tehfink

2

Non l'ho visto davvero ...

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

Uscita più granulare

I metodi as_p (), as_ul () e as_table () sono semplicemente scorciatoie per sviluppatori pigri - non sono l'unico modo in cui un oggetto modulo può essere visualizzato.

class BoundField Utilizzato per visualizzare HTML o accedere ad attributi per un singolo campo di un'istanza Form.

Il metodo str () ( unicode su Python 2) di questo oggetto visualizza l'HTML per questo campo.

Per recuperare un singolo BoundField, utilizzare la sintassi di ricerca del dizionario nel modulo utilizzando il nome del campo come chiave:

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

Per recuperare tutti gli oggetti BoundField, ripetere il modulo:

>>> 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" />

L'output specifico del campo rispetta l'impostazione auto_id dell'oggetto modulo:

>>> 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" />

2

C'è uno strumento molto facile da installare e ottimo per Django che uso per lo styling e può essere utilizzato per ogni framework front-end come Bootstrap, Materialise, Foundation, ecc. Si chiama widget-tweaks Documentazione: Widget Tweaks

  1. Puoi usarlo con le viste generiche di Django
  2. O con le tue forme:

dai moduli di importazione Django

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

Invece di utilizzare l'impostazione predefinita:

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

Puoi modificarlo a modo tuo usando l'attributo render_field che ti dà un modo più simile a html di disegnarlo come in questo esempio:

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>

Questa libreria ti dà l'opportunità di avere il tuo front-end ben separato dal tuo back-end


1

In Django 1.10 (forse anche prima) puoi farlo come segue.

Modello:

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

Modulo:

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

Modello:

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

0

Modifica: un altro modo (leggermente migliore) di fare ciò che sto suggerendo è la risposta qui: stile del campo di input del modulo Django

Tutte le opzioni di cui sopra sono fantastiche, ho pensato di aggiungere questo perché è diverso.

Se si desidera uno stile personalizzato, classi, ecc. Nei moduli, è possibile inserire un input HTML nel modello che corrisponda al campo del modulo. Per un CharField, ad esempio, (widget predefinito è TextInput), supponiamo che tu voglia un input di testo dall'aspetto bootstrap. Faresti qualcosa del genere:

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

E finché metti il ​​nome del campo modulo corrisponde nameall'attributo html (e probabilmente il widget deve corrispondere anche al tipo di input) Django eseguirà tutti gli stessi validatori su quel campo quando esegui validateo form.is_valid()e

Lo stile di altre cose come etichette, messaggi di errore e testo di aiuto non richiede molta soluzione perché puoi fare qualcosa di simile form.field.error.as_texte modellarli come preferisci. I campi effettivi sono quelli che richiedono un po 'di armeggiamento.

Non so se questo è il modo migliore, o il modo in cui lo consiglierei, ma è un modo, e potrebbe essere giusto per qualcuno.

Ecco un'utile procedura dettagliata delle forme di stile e include la maggior parte delle risposte elencate su SO (come usare l'attr sui widget e le modifiche al widget). https://simpleisbetterthancomplex.com/article/2017/08/19/how-to-render-django-form-manually.html


0

Styling delle istanze del widget

Se si desidera che un'istanza del widget sia diversa da un'altra, sarà necessario specificare ulteriori attributi nel momento in cui l'oggetto widget viene istanziato e assegnato a un campo modulo (e forse aggiungere alcune regole ai file CSS).

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

Per fare ciò, utilizzare l'argomento Widget.attrs durante la creazione del 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'}))

Puoi anche modificare un widget nella definizione del modulo:

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')

Oppure se il campo non è dichiarato direttamente sul modulo (come i campi modulo modello), è possibile utilizzare l'attributo 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 includerà quindi gli attributi extra nell'output renderizzato:

>>> 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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.