Klasse toevoegen aan formulierveld Django ModelForm

Ik probeer een Bootstrap-formulier te schrijven met Django ModelForm. Ik heb de Django-documentatie Django-documentatie over formulieren, dus ik heb deze code:

<div class="form-group">
{{ form.subject.errors }}
<label for="{{ form.subject.id_for_label }}">Email subject:</label>
{{ form.subject }}</div>

De {{form.subject}}wordt weergegeven door Django, bijvoorbeeld in het CharField-veldmodel, als input-tag,

<input type="text"....> etc.

Ik moet de klasse “form-control”aan elke invoer toevoegen om het uiterlijk van Bootstrap-invoer te krijgen (zonder pakketten van derden). Ik vond deze oplossing Django voegt klasse toe aan formulier <input ..> veld. Is er een manier om standaard aan elk veld een klasse toe te voegen zonder deze in elk attribuut van de klasse van de Form-klasse op te geven?

class ExampleForm(forms.Form):
   name = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
   email = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
   address = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
   country = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))

en ga zo maar door ..


Antwoord 1, autoriteit 100%

Als u geen app van derden kunt gebruiken en u wilt een klasse (bijv. “form-control”) op een DROGE manier aan elk veld in een formulier toevoegen, dan kunt u dit doen in het formulier class __init__()methode als volgt:

class ExampleForm(forms.Form):
    # Your declared form fields here
    ...
    def __init__(self, *args, **kwargs):
        super(ExampleForm, self).__init__(*args, **kwargs)
        for visible in self.visible_fields():
            visible.field.widget.attrs['class'] = 'form-control'

Misschien moet je ook controleren op bestaande klassen in attrs, als je om de een of andere reden klassen toevoegt, zowel declaratief enbinnen __init__(). De bovenstaande code houdt geen rekening met dat geval.

Vermeldenswaardig:

Je hebt aangegeven dat je geen pakketten van derden wilt gebruiken. Ik zal echter even de tijd nemen om te vermelden dat een van de eenvoudigste manieren om automatisch formulieren te laten renderen in de stijl van Bootstrap, is om django-crispy-forms, zoals dit:

# settings.py
CRISPY_TEMPLATE_PACK = 'bootstrap3'
# forms.py
from crispy_forms.helper import FormHelper
class ExampleForm(forms.Form):
    # Your declared form fields here
    ...
    helper = FormHelper()
# In your template, this renders the form Bootstrap-style:
{% load crispy_forms_tags %}
{% crispy form %}

Antwoord 2, autoriteit 32%

u kunt CSS-klassen toevoegen in form.py

subject = forms.CharField(label='subject', max_length=100 , widget=forms.TextInput(attrs={'class': "form-control"}))

Antwoord 3, autoriteit 14%

Omdat het me meer uren kostte dan ik zou willen (django newbie), om dit uit te zoeken, zal ik mijn uitkomst hier ook plaatsen.

Het instellen van een widget op elk veld om steeds maar één klasse toe te voegen is in strijd met de programmeerregel van herhaling en leidt tot veel onnodige rijen. Dit gebeurt vooral bij het werken met bootstrap-formulieren.

Hier is mijn (werkende) voorbeeld om niet alleen bootstrap-klassen toe te voegen:

forms.py

class CompanyForm(forms.Form):
    name = forms.CharField(label='Jméno')
    shortcut = forms.CharField(label='Zkratka')
    webpage = forms.URLField(label='Webové stránky')
    logo = forms.FileField(label='Logo')

templatetags/custom_tags.py

from django import template
from django.urls import reverse
register = template.Library()
@register.filter('input_type')
def input_type(ob):
    '''
    Extract form field type
    :param ob: form field
    :return: string of form field widget type
    '''
    return ob.field.widget.__class__.__name__
@register.filter(name='add_classes')
def add_classes(value, arg):
    '''
    Add provided classes to form field
    :param value: form field
    :param arg: string of classes seperated by ' '
    :return: edited field
    '''
    css_classes = value.field.widget.attrs.get('class', '')
    # check if class is set or empty and split its content to list (or init list)
    if css_classes:
        css_classes = css_classes.split(' ')
    else:
        css_classes = []
    # prepare new classes to list
    args = arg.split(' ')
    for a in args:
        if a not in css_classes:
            css_classes.append(a)
    # join back to single string
    return value.as_widget(attrs={'class': ' '.join(css_classes)})

reusable_form_fields.html (sjabloon)

{% load custom_tags %}
{% csrf_token %}
{% for field in form %}
    <div class="form-group row">
        {% if field|input_type == 'TextInput' %}
            <div for="{{ field.label }}" class="col-sm-2 col-form-label">
                {{ field.label_tag }}
            </div>
            <div class="col-sm-10">
                {{ field|add_classes:'form-control'}}
                {% if field.help_text %}
                    <small class="form-text text-muted">{{ field.help_text }}</small>
                {% endif %}
            </div>
        {% else %}
            ...
        {% endif %}
    </div>
{% endfor %}

Antwoord 4, autoriteit 6%

Knapperige vormen zijn de beste keuze. Tips voor Bootstrap 4. Toevoegen aan het antwoord van @Christian Abbott, voor formulieren, zegt bootstrap, gebruik form-group en form-control .
Dit is hoe het voor mij werkte.

Mijn formulieren.py

class BlogPostForm(forms.ModelForm):
    class Meta:
        model = models.Post
        fields = ['title', 'text', 'tags', 'author', 'slug']
    helper = FormHelper()
    helper.form_class = 'form-group'
    helper.layout = Layout(
        Field('title', css_class='form-control mt-2 mb-3'),
        Field('text', rows="3", css_class='form-control mb-3'),
        Field('author', css_class='form-control mb-3'),
        Field('tags', css_class='form-control mb-3'),
        Field('slug', css_class='form-control'),
    )

Mijn post_create.html

{% extends 'blog/new_blog_base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<form method='POST' enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.media }}
    {% crispy form %}
<hr>
<input type="submit" name="Save" value="Save" class='btn btn-primary'> <a href="{% url 'home' %}" class='btn btn-danger'>Cancel</a>
</form>
</div>
{% endblock %}

Opmerking: als u CK Editor RichTextField() gebruikt voor uw modelveld, wordt dat veld niet beïnvloed. Als iemand het weet, update dit dan.


Antwoord 5

Dit is een antwoord dat het @Christian Abbott juiste antwoordaanvult.

Als je veel formulieren gebruikt, is het misschien een optie om init niet elke keer te overschrijven door je eigen formulierklasse te maken:

class MyBaseForm(forms.Form):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for visible in self.visible_fields():
            visible.field.widget.attrs['class'] = 'form-control'

Dan kun je van deze klasse erven en het gaat automatisch de stijlen voor je maken.

class ExampleForm(MyBaseForm):
    # Your declared form fields here
    ...

Hetzelfde kan worden gedaan met ModelForm door eenvoudig een MyBaseModelForm te maken die overerft van ModelForm.


Antwoord 6

U kunt ook expliciet het veld vermelden waarop u de klasse wilt toepassen

class ProfileForm(ModelForm):
   class Meta:
       model = Profile 
        fields = ['avatar','company']  
        def __init__(self, *args, **kwargs):
           super().__init__(*args, **kwargs)
           self.fields['avatar'].widget.attrs.update({'class': 'form-control'})
           self.fields['company'].widget.attrs.update({'class':'form-control'})

Antwoord 7

Eén manier is om een ​​basisvormklasse te maken en het attribuut van het veld handmatig bij te werken binnen de methode __init__.

Een andere is door reeds bestaande bibliotheken te gebruiken, zoals deze:
https://github.com/dyve/django-bootstrap3

Er zijn genoeg van deze bibliotheken rond github. Kijk om je heen.


Antwoord 8

Ik vond het gemakkelijker om het element via css te identificeren en de stijl daar toe te voegen. Met django-formulieren krijgt u een unieke id voor elk formulierveld (voorvoegsels voor gebruikersformulieren als u het formulier meerdere keren in uw sjabloon weergeeft).

# views.py
def my_view_function(request):
    form_a = MyForm(prefix="a")
    form_b = MyForm(prefix="b")
    context = {
        "form_a": form_a,
        "form_b": form_b
    }
    return render(request, "template/file.html", context)

stijl

// file.css
form input#by_id {
  width: 100%;
}

Antwoord 9

Ok, er is een tijd verstreken, maar ik had dezelfde problemen. Ik kwam tot deze oplossing:

class FormCssAttrsMixin():
    cssAttrs = {}
    def inject_css_attrs(self):
        # iterate through fields
        for field in self.fields:
            widget = self.fields[field].widget
            widgetClassName = widget.__class__.__name__
            # found widget which should be manipulated?
            if widgetClassName in self.cssAttrs.keys():
                # inject attributes
                attrs = self.cssAttrs[widgetClassName]
                for attr in attrs:
                    if attr in widget.attrs:  # attribute already existing
                        widget.attrs.update[attr] = widget[attr] + " " +    attrs[attr]  # append
                    else:  # create attribute since its not existing yet
                        widget.attrs[attr] = attrs[attr]
class MyForm(FormCssAttrsMixin, forms.Form):
    # add class attribute to all django textinputs widgets
    cssAttrs = {"TextInput": {"class": "form-control"}}
    name = forms.CharField()
    email = forms.CharField()
    address = forms.CharField()
    country = forms.CharField()
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.inject_css_attrs()

Met deze Mixin-klasse kun je de attributen van formulierwidgets op een generieke manier manipuleren. Voeg eenvoudig een woordenboek toe als klassevariabele die de gewenste attributen en waarden per widget bevat.
Op deze manier kunt u uw css-klassen toevoegen op dezelfde locatie waar u uw velden definieert. Enige nadeel is dat je ergens de “inject_css_attrs”-methode moet aanroepen, maar ik denk dat dat oké is.


Antwoord 10

Dit is erg praktisch:

class CreateSomethingForm(forms.ModelForm):
    class Meta:
        model = Something
        exclude = []
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'

Op deze manier hoef je niet te gaan per veld.

Other episodes