Styl CSS w formularzach Django

150

Chciałbym nadać następujący styl:

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>

Na przykład, w jaki sposób ustawić klasę lub identyfikator dla subject, email, messagew celu zapewnienia zewnętrznego arkusza stylów do?

David542
źródło

Odpowiedzi:

192

Zaczerpnięte z mojej odpowiedzi do: Jak oznaczyć pola formularza za pomocą <div class = 'field_type'> w Django

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

lub

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

lub

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

--- EDYTUJ ---
Powyższa zmiana jest najłatwiejszą do wprowadzenia w kodzie oryginalnego pytania, która spełnia to, co zostało zadane. Uniemożliwia to również powtórzenie się w przypadku ponownego użycia formularza w innych miejscach; Twoje klasy lub inne atrybuty działają po prostu, jeśli używasz metod formularza as_table / as_ul / as_p Django. Jeśli potrzebujesz pełnej kontroli nad całkowicie niestandardowym renderowaniem, jest to wyraźnie udokumentowane

- EDYCJA 2 ---
Dodano nowszy sposób określania widżetu i atrybutów dla ModelForm.

shadfc
źródło
27
Chociaż nie zaleca się mieszania prezentacji z logiką biznesową.
Torsten Engelbrecht
8
Jak wygląda ta prezentacja? Nadajesz elementowi klasę, która jest tylko identyfikatorem lub kategoryzacją. Nadal musisz zdefiniować, co to robi gdzie indziej
shadfc
9
Tak i nie. Pierwsze klasy CSS są zgodnie z konwencją używane do stylizacji, jeśli potrzebujesz unikalnego identyfikatora, lepiej go użyć id. Po drugie, zwykle jest to odpowiedzialne za zrobienie tego po stronie szablonu, zwłaszcza jeśli masz zamiar uzyskać dostęp do tej klasy za pomocą metod frontendu (js, css). Nie powiedziałem, że twoja odpowiedź jest błędna. Moim zdaniem to po prostu zła praktyka (szczególnie gdy pracujesz w zespole z programistami frontend i backend).
Torsten Engelbrecht
6
Wygląda to absurdalnie, wystarczy dodać klasę, której potrzebujesz tyle kodu? Wydaje się, że łatwiej byłoby po prostu na stałe zakodować HTML / CSS w tych obszarach (szczególnie w przypadku strony z dużą ilością CSS).
David542,
9
To szalone, że django sprawia, że ​​jest to takie niezręczne!
Bryce,
103

Można to zrobić za pomocą niestandardowego filtru szablonu. Rozważ renderowanie swojego formularza w ten sposób:

<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.subjectjest instancją, BoundFieldktóra ma as_widget()metodę.

Możesz utworzyć niestandardowy filtr addclassw 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})

Następnie zastosuj swój filtr:

{% 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.subjectszostanie wyrenderowany z MyClassklasą CSS.

Charlesthk
źródło
5
Jest to jedno z bardziej przejrzystych i łatwych do wdrożenia rozwiązań
użytkownik
5
Ta odpowiedź powinna być najlepszą odpowiedzią !!! Jest naprawdę czystszy niż rozwiązanie proponowane przez Django! Dobra robota @Charlesthk
David D.
4
Super pomocny. Na początku nie było to dla mnie oczywiste, ale możesz użyć tego, aby dodać wiele klas:{{ form.subject|addclass:'myclass1 myclass2' }}
smg
Podoba mi się, że pozwala to na przechowywanie klas HTML w plikach HTML. Podczas pracy nad stylizacją przeskakuję w tę iz powrotem między arkuszami stylów i strukturą, a nie modelami i / lub formularzami.
Kevin
29

Jeśli nie chcesz dodawać żadnego kodu do formularza (jak wspomniano w komentarzach do odpowiedzi @ shadfc), z pewnością jest to możliwe, oto dwie opcje.

Po pierwsze, odwołujesz się do poszczególnych pól w kodzie HTML, a nie do całego formularza na raz:

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

(Zwróć uwagę, że zmieniłem ją również na listę niesortowaną ).

Po drugie, zwróć uwagę w dokumentach na tworzenie formularzy jako HTML , Django:

Identyfikator pola jest generowany przez dołączenie „id_” do nazwy pola. Atrybuty i tagi id są domyślnie uwzględniane w danych wyjściowych.

Wszystkie pola formularza mają już unikalny identyfikator . Więc możesz odwołać się do id_subject w pliku CSS, aby nadać styl polu tematu . Powinienem zauważyć, że tak zachowuje się formularz, gdy bierzesz domyślny HTML, który wymaga tylko wydrukowania formularza, a nie poszczególnych pól:

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

Zobacz poprzedni link, aby uzyskać inne opcje podczas tworzenia formularzy (możesz tworzyć tabele itp.).

Uwaga - zdaję sobie sprawę, że to nie to samo, co dodanie klasy do każdego elementu (jeśli dodałeś pole do formularza, musisz również zaktualizować CSS) - ale łatwo jest odwołać się do wszystkich pól według identyfikatora w swoim CSS w ten sposób:

#id_subject, #id_email, #email_message 
{color: red;}
John C.
źródło
Wypróbowałem twoje drugie rozwiązanie, ale nie zadziałało. Utworzyłem klasę dla id_email i nie przyniosło to żadnych wyników.
prawie początkujący
@almostabeginner Jedna rzecz, którą mogę zasugerować do debugowania - kiedy zobaczysz stronę w przeglądarce, użyj opcji Wyświetl źródło strony (zwykle klikając prawym przyciskiem myszy) i spójrz na rzeczywistą pełną stronę, którą generuje Django. Sprawdź, czy pola istnieją, z oczekiwanym identyfikatorem lub identyfikatorem klasy . Również większość przeglądarek (prawdopodobnie poprzez zainstalowanie wtyczki) może uruchomić debugger, który pokazuje css, który jest stosowany na stronie, co pomaga również zobaczyć, co się dzieje.
John C
@almostabeginner również zauważ, że dodałem trochę przykładowego kodu. W przypadku, gdy nie było to jasne z samego tekstu - musisz odwołać się do samego formularza, a nie do poszczególnych pól, w którym to momencie formularz automatycznie generuje kod HTML zawierający identyfikatory , zgodnie z opisem. Mam nadzieję, że to pomoże.
John C
1
Dzięki za pomoc, problem w ogóle nie był moim css, problem był związany z cache. Więc mój stary css został zapisany, więc żadna ze zmian nie była wyświetlana. Właśnie wyczyściłem pamięć podręczną z Chrome i wszystkie aktualizacje zaczęły się wyświetlać.
prawie początkujący
15

W tym poście na blogu możesz dodać klasy css do swoich pól, używając niestandardowego filtru szablonu.

from django import template
register = template.Library()

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

Umieść to w folderze templatetags / swojej aplikacji i możesz to zrobić

{{field|addcss:"form-control"}}
aashanand
źródło
2
to powinno zostać zaakceptowane jako prawdziwa odpowiedź na ten post :)
mvdb
Zdecydowanie najlepsze rozwiązanie.
Mods Vs Rockers
1
Świetnie, dzięki! Nie zapomnij załadować tagu. Ponadto w Django 2.1 jedynym sposobem na znalezienie szablonu przez Django było dodanie opcji w settings.py: 'libraries': {'add_css': 'app.templatetags.tag_name',}
simonbogarde
11

Możesz zrobić tak:

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

Mam nadzieję, że to zadziała.

Ignas

Ignas Butėnas
źródło
Dzięki, ignas. Dokładna odpowiedź!
Rudresh Ajgaonkar
9

Możesz użyć tej biblioteki: https://pypi.python.org/pypi/django-widget-tweaks

Umożliwia wykonanie następujących czynności:

{% load widget_tweaks %}
<!-- add 2 extra css classes to field element -->
{{ form.title|add_class:"css_class_1 css_class_2" }}
Eamonn Faherty
źródło
1
Spójrz na rozwiązanie Charlesthk, jest to to samo bez dodawania dodatkowej biblioteki :)
David D.
@DavidW .: Tak, ale Widget Tweaks ma dużo więcej filtrów, takich jak render_field.
mrdaliri
5

Możesz to zrobić:

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

Następnie możesz dodać klasy / id na przykład do <td>tagu. Możesz oczywiście użyć dowolnych innych tagów. Sprawdź jako przykład Praca z formularzami Django, co jest dostępne dla każdego fieldw formularzu ( {{field}}na przykład wyprowadza tylko znacznik wejściowy, a nie etykietę i tak dalej).

Torsten Engelbrecht
źródło
3

Jednym z rozwiązań jest użycie JavaScript do dodania wymaganych klas CSS po przygotowaniu strony. Na przykład stylizowanie wyjścia formularza django za pomocą klas ładowania początkowego (jQuery używane do zwięzłości):

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

Pozwala to uniknąć brzydoty mieszania specyfiki stylizacji z logiką biznesową.

Simon Feltman
źródło
3

Napisz swój formularz jak:

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

W swoim polu HTML zrób coś takiego:

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

Następnie w swoim CSS napisz coś takiego:

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

Mam nadzieję, że warto spróbować tej odpowiedzi! Zauważ, że musisz napisać swoje widoki, aby renderować plik HTML zawierający formularz.

Aula
źródło
Dzięki. ale jak mogę napisać konkretny tekst etykiety?
gakeko betsi
2

Być może nie będziesz musiał nadpisywać swojej klasy formularza __init__, ponieważ Django ustawia name& idatrybuty w plikach HTML input. Możesz mieć CSS w ten sposób:

form input[name='subject'] {
    font-size: xx-large;
}
tehfink
źródło
1
Aby dodać do tego. Biorąc pod uwagę "subject = formularze ...", id = "id_subject" i name = "subject" to konwencja Django dla tych atrybutów. Dlatego też powinieneś być w stanie zrobić #id_subject {...}
solartic
@solartic: Masz rację, dzięki. Nie wspomniałem o tym, ponieważ idpole utworzone przez Django dla zestawów formularzy robi się dość owłosione…
tehfink
2

Tak naprawdę nie widziałem tego ...

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

Bardziej szczegółowe wyniki

Metody as_p (), as_ul () i as_table () to po prostu skróty dla leniwych programistów - nie są one jedynym sposobem wyświetlania obiektu formularza.

class BoundField Służy do wyświetlania HTML lub dostępu do atrybutów dla pojedynczego pola instancji Form.

Metoda str () ( unicode w Pythonie 2) tego obiektu wyświetla kod HTML dla tego pola.

Aby pobrać pojedyncze BoundField, użyj składni wyszukiwania w słowniku w swoim formularzu, używając nazwy pola jako klucza:

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

Aby pobrać wszystkie obiekty BoundField, przeprowadź iterację formularza:

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

Dane wyjściowe specyficzne dla pola uwzględniają ustawienie auto_id obiektu formularza:

>>> 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
źródło
2

Istnieje bardzo łatwe w instalacji i świetne narzędzie stworzone dla Django, którego używam do stylizacji i może być używane w każdym frameworku frontendowym, takim jak Bootstrap, Materialize, Foundation, itp. Nazywa się ono poprawkami do widgetów Dokumentacja: Widget Tweaks

  1. Możesz go używać z ogólnymi widokami Django
  2. Lub za pomocą własnych formularzy:

z formularzy importu django

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

Zamiast używać domyślnych:

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

Możesz edytować go na swój własny sposób, korzystając z atrybutu render_field, który zapewnia stylizację bardziej zbliżoną do HTML, jak w tym przykładzie:

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>

Ta biblioteka daje Ci możliwość dobrego oddzielenia interfejsu użytkownika od zaplecza

Alam Téllez
źródło
1

W Django 1.10 (prawdopodobnie również wcześniej) możesz to zrobić w następujący sposób.

Model:

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

Formularz:

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

Szablon:

<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
źródło
0

Edycja: Odpowiedź na inny (nieco lepszy) sposób robienia tego, co sugeruję, znajduje się tutaj: Stylizacja pól wejściowych formularza w Django

Wszystkie powyższe opcje są niesamowite, pomyślałem, że dorzucę tę, ponieważ jest inna.

Jeśli chcesz mieć niestandardowe style, klasy itp. W swoich formularzach, możesz wprowadzić dane wejściowe HTML w swoim szablonie, które pasują do pola formularza. Na przykład dla CharField (domyślnym widżetem jest TextInput), powiedzmy, że chcesz wprowadzić tekst w stylu bootstrap. Zrobiłbyś coś takiego:

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

Dopóki nazwa pola formularza będzie zgodna z nameatrybutem html (i widżet prawdopodobnie będzie również musiał pasować do typu danych wejściowych), Django uruchomi wszystkie te same walidatory w tym polu po uruchomieniu validatelubform.is_valid() i

Stylizowanie innych rzeczy, takich jak etykiety, komunikaty o błędach i tekst pomocy, nie wymaga dużego obejścia, ponieważ możesz zrobić coś takiego form.field.error.as_text i nadać im styl, jak chcesz. Rzeczywiste pola to te, które wymagają trochę zabawy.

Nie wiem, czy to najlepszy sposób, czy polecam, ale to jest sposób i może być odpowiedni dla kogoś.

Oto przydatny przewodnik po stylach formularzy i zawiera większość odpowiedzi wymienionych w SO (np. Użycie atr w widżetach i poprawkach widżetów). https://simpleisbetterthancomplex.com/article/2017/08/19/how-to-render-django-form-manually.html

kimbo
źródło
0

Stylizowanie instancji widżetów

Jeśli chcesz, aby jedna instancja widżetu wyglądała inaczej niż druga, musisz określić dodatkowe atrybuty w momencie tworzenia instancji obiektu widżetu i przypisywania go do pola formularza (i być może dodać kilka reguł do plików CSS).

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

Aby to zrobić, podczas tworzenia widgetu użyj argumentu Widget.attrs:

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

Możesz także zmodyfikować widget w definicji formularza:

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

Lub jeśli pole nie jest zadeklarowane bezpośrednio w formularzu (na przykład pola formularza modelu), możesz użyć atrybutu 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 umieści wtedy dodatkowe atrybuty w wyrenderowanym wyniku:

>>> 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
źródło