Estilo CSS em formulários Django

150

Gostaria de estilizar o seguinte:

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>

Por exemplo, como faço para definir uma classe ou ID para a subject, email,message para fornecer uma folha de estilo externa para?

David542
fonte

Respostas:

192

Retirado da minha resposta para: Como marcar campos de formulário com <div class = 'field_type'> no 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'}),
        }

--- EDITAR ---
A descrição acima é a alteração mais fácil de fazer no código da pergunta original que realiza o que foi solicitado. Também impede que você se repita se você reutilizar o formulário em outros lugares; suas classes ou outros atributos funcionam apenas se você usar os métodos de formulário as_table / as_ul / as_p do Django. Se você precisar de controle total para uma renderização totalmente personalizada, isso está claramente documentado

- EDIT 2 ---
Adicionada uma nova maneira de especificar o widget e os atributos para um ModelForm.

shadfc
fonte
27
Embora não seja recomendável misturar apresentação com lógica de negócios.
Torsten Engelbrecht
8
Como é esta apresentação? Você está dando ao elemento uma classe, que é apenas um identificador ou categorização. Você ainda tem que definir o que é que faz em outros lugares
shadfc
9
Sim e não. As primeiras classes CSS são usadas por convenção para estilizar, se você precisar de um identificador exclusivo, é melhor usá-lo id. Em segundo lugar, geralmente é a responsabilidade do lado do modelo fazer isso exatamente, principalmente se você deseja acessar essa classe por meio de métodos de front-end (js, css). Eu não disse que sua resposta está errada. Na minha opinião, é apenas uma prática ruim (especialmente quando você trabalha em uma equipe com desenvolvedores de front-end e back-end).
Torsten Engelbrecht
6
Isso parece ridículo, apenas para adicionar uma classe, você precisa de tanto código? Parece que seria mais fácil codificar o HTML / CSS nessas áreas (especialmente para sites pesados ​​em CSS).
precisa saber é o seguinte
9
É insano django torna isso tão estranho!
Bryce
103

Isso pode ser feito usando um filtro de modelo personalizado. Considere renderizar seu formulário da seguinte maneira:

<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é uma instância da BoundFieldqual possui o as_widget()método

Você pode criar um filtro personalizado addclassem 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 depois aplique seu 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.subjectsserá renderizado com a MyClassclasse CSS.

Charlesthk
fonte
5
Esta é uma das soluções mais limpas e fáceis de implementar
usuário
5
Esta resposta deve ser a principal resposta !!! É realmente mais limpo que a solução proposta pelo Django! Bem feito @Charlesthk
David D.
4
Super útil. Não foi óbvio para mim no começo, mas você pode usar isso para adicionar várias classes também:{{ form.subject|addclass:'myclass1 myclass2' }}
smg
Eu gosto que isso permita manter as classes HTML nos arquivos HTML. Ao trabalhar com estilo, pulo para frente e para trás entre folhas de estilo e estrutura, não modelos e / ou formulários.
26417 Kevin
29

Se você não quiser adicionar nenhum código ao formulário (como mencionado nos comentários da resposta do @ shadfc), é certamente possível, aqui estão duas opções.

Primeiro, basta referenciar os campos individualmente no HTML, em vez de todo o formulário de uma vez:

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

(Observe que eu também mudei para uma lista não classificada .)

Segundo, observe nos documentos sobre saída de formulários como HTML , Django:

A identificação do campo é gerada adicionando 'id_' ao nome do campo. Os atributos e tags de identificação estão incluídos na saída por padrão.

Todos os seus campos de formulário já têm um ID exclusivo . Portanto, você faria referência ao id_subject em seu arquivo CSS para estilizar o campo de assunto . Devo observar que é assim que o formulário se comporta quando você usa o HTML padrão , o que requer apenas a impressão do formulário, não os campos individuais:

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

Veja o link anterior para outras opções ao imprimir formulários (você pode criar tabelas, etc.).

Nota - Eu sei que isso não é o mesmo que adicionar uma classe a cada elemento (se você adicionou um campo ao formulário, você também precisará atualizar o CSS) - mas é fácil o suficiente referenciar todos os campos por id no seu CSS assim:

#id_subject, #id_email, #email_message 
{color: red;}
John C
fonte
Eu tentei sua segunda solução, mas não funcionou. Eu criei a classe para o id_email e ele não conseguiu produzir nenhum resultado.
quase um iniciante
@almostabeginner uma coisa que posso sugerir para depuração - depois de ver a página em um navegador, use Exibir fonte da página (geralmente clicando com o botão direito do mouse) e veja a página inteira que o Django está gerando. Veja se os campos existem, com o ID ou identificador de classe que você espera. Além disso, a maioria dos navegadores (possivelmente instalando um plug-in) pode executar um depurador que mostra o css aplicado a uma página, também útil para ver o que está acontecendo.
John C
@almostabeginner também observe, eu adicionei um pouco de código de exemplo. Caso não esteja claro apenas no texto - é necessário fazer referência ao próprio formulário, não aos campos individuais; nesse momento, o formulário gera automaticamente HTML que contém IDs , conforme descrito. Espero que ajude.
John C
1
Obrigado pela ajuda, o problema não era meu CSS, o problema estava relacionado ao cache. Portanto, meu CSS antigo foi armazenado, portanto, nenhuma das alterações seria exibida. Acabei de limpar o cache do chrome e todas as atualizações começaram a aparecer.
quase um iniciante
15

Por esta postagem do blog, você pode adicionar classes css aos seus campos usando um filtro de modelo personalizado.

from django import template
register = template.Library()

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

Coloque isso nas etiquetas / pasta do modelo do seu aplicativo e agora você pode

{{field|addcss:"form-control"}}
aashanand
fonte
2
isto deveria ter sido aceito como a verdadeira resposta a este post :)
mvdb
Melhor solução de longe.
Mods Vs Rockers
1
Brilhante, obrigado! Não se esqueça de realmente carregar a tag. Além disso, no Django 2.1, a única maneira de conseguir que o Django encontrasse o modelo foi adicionando uma opção em settings.py: 'libraries': {'add_css': 'app.templatetags.tag_name',}
simonbogarde
11

Você pode fazer assim:

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

Espero que funcione.

Ignas

Ignas Butėnas
fonte
Obrigado ignas. Resposta precisa!
Rudresh Ajgaonkar
9

Você poderia usar esta biblioteca: https://pypi.python.org/pypi/django-widget-tweaks

Ele permite que você faça o seguinte:

{% load widget_tweaks %}
<!-- add 2 extra css classes to field element -->
{{ form.title|add_class:"css_class_1 css_class_2" }}
Eamonn Faherty
fonte
1
Dê uma olhada na solução Charlesthk, é o mesmo sem a adição de uma biblioteca extra :)
David D.
@DavidW .: Sim, mas o Widget Tweaks tem muito mais filtros, como render_field .
Mdaliri #
5

Você pode fazer:

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

Em seguida, você pode adicionar classes / IDs, por exemplo, ao <td> tag. Obviamente, você pode usar outras tags que desejar. Marque Trabalhando com formulários Django como um exemplo do que está disponível para cada um fieldno formulário ( {{field}}por exemplo, está apenas emitindo a tag de entrada, não a etiqueta e assim por diante).

Torsten Engelbrecht
fonte
3

Uma solução é usar o JavaScript para adicionar as classes CSS necessárias depois que a página estiver pronta. Por exemplo, estilizando a saída do formulário django com classes de inicialização (jQuery usado por questões de brevidade):

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

Isso evita a feiura de misturar detalhes de estilo com sua lógica de negócios.

Simon Feltman
fonte
3

Escreva seu formulário como:

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

No seu campo HTML, faça algo como:

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

Então, no seu CSS, escreva algo como:

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

Espero que esta resposta valha a pena tentar! Observe que você deve ter escrito suas visualizações para renderizar o arquivo HTML que contém o formulário.

Aula
fonte
Obrigado. mas como posso escrever um texto de etiqueta específico?
gakeko Betsi
2

Você pode não precisar substituir sua classe de formulário ' __init__, porque o Django define namee idatributos nos HTML inputs. Você pode ter CSS como este:

form input[name='subject'] {
    font-size: xx-large;
}
tehfink
fonte
1
Para adicionar a isso. Dado "subject = forms ...", id = "id_subject" e name = "subject" é a convenção do Django para esses atributos. Portanto, você também deve ser capaz de fazer #id_subject {...}
solartic
@ sololartic: Você está certo, obrigado. Eu não mencionei isso porque o idcampo criado por Django para formsets fica bastante peludo ...
tehfink
2

Não vi esse realmente ...

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

Saída mais granular

Os métodos as_p (), as_ul () e as_table () são simplesmente atalhos para desenvolvedores preguiçosos - não são a única maneira que um objeto de formulário pode ser exibido.

classe BoundField Usado para exibir HTML ou acessar atributos para um único campo de uma instância do Form.

O método str () ( unicode no Python 2) desse objeto exibe o HTML para este campo.

Para recuperar um único BoundField, use a sintaxe de pesquisa de dicionário no seu formulário usando o nome do campo como chave:

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

Para recuperar todos os objetos BoundField, itere o formulário:

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

A saída específica do campo respeita a configuração auto_id do objeto de formulário:

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

Existe uma ferramenta muito fácil de instalar e ótima feita para o Django que eu uso para estilizar e pode ser usada para qualquer estrutura de front-end como Bootstrap, Materialize, Foundation, etc. É chamada de widget-tweaks Documentação: Widget Tweaks

  1. Você pode usá-lo com as visões genéricas do Django
  2. Ou com seus próprios formulários:

de formulários de importação de django

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

Em vez de usar o padrão:

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

Você pode editá-lo à sua maneira, usando o atributo render_field, que oferece uma maneira mais semelhante a html de denominá-lo como este exemplo:

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>

Esta biblioteca oferece a oportunidade de separar o front-end do seu back-end

Alam Téllez
fonte
1

No Django 1.10 (possivelmente antes também), você pode fazer o seguinte.

Modelo:

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

Formato:

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

Modelo:

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

Edit: Outra maneira (um pouco melhor) de fazer o que estou sugerindo é respondida aqui: Django form input field styling

Todas as opções acima são impressionantes, apenas pensei em incluir essa porque é diferente.

Se você deseja estilo, classes, etc. personalizados em seus formulários, pode fazer uma entrada html no seu modelo que corresponda ao seu campo de formulário. Para um CharField, por exemplo, (o widget padrão é TextInput), digamos que você queira uma entrada de texto com aparência de inicialização. Você faria algo assim:

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

E desde que você coloque o nome do campo de formulário correspondente ao nameatributo html (e o widget provavelmente também precise corresponder ao tipo de entrada), o Django executará todos os mesmos validadores nesse campo quando você executar validateouform.is_valid() e

O estilo de outras coisas, como marcadores, mensagens de erro e texto de ajuda, não requer muita solução, pois você pode fazer algo como form.field.error.as_textestilizá-las da maneira que desejar. Os campos reais são aqueles que exigem algumas brincadeiras.

Não sei se essa é a melhor maneira, ou a maneira que eu recomendaria, mas é uma maneira e pode ser certa para alguém.

Aqui está uma explicação útil dos formulários de estilo e inclui a maioria das respostas listadas no SO (como usar o attr nos widgets e ajustes de widget). https://simpleisbetterthancomplex.com/article/2017/08/19/how-to-render-django-form-manually.html

kimbo
fonte
0

Instâncias do widget de estilo

Se você quiser fazer uma instância de widget parecer diferente de outra, será necessário especificar atributos adicionais no momento em que o objeto do widget for instanciado e designado a um campo de formulário (e talvez adicione algumas regras aos seus arquivos CSS).

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

Para fazer isso, use o argumento Widget.attrs ao criar o 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'}))

Você também pode modificar um widget na definição de formulário:

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, se o campo não for declarado diretamente no formulário (como campos de formulário de modelo), você poderá usar o atributo 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')

O Django incluirá os atributos extras na saída renderizada:

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