Как придать стиль ярлыку Django crispy-form

Я разрабатываю небольшой проект, используя Django v5 и Bootstrap v5. На данном этапе я просто играю со страницами registration и login, но я хотел бы стилизовать форму, используя crispy-form и crispy FormHelper. Я могу изменить отображаемую метку, но (пока) мне не удается сделать метку жирной и/или подчеркнутой, чтобы показать, что это обязательное поле (а не использовать звездочку в crispy).

Вот мой файл forms.py:

from django import forms
from django.contrib.auth import get_user_model
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Field, Layout

class LoginForm(forms.Form):
    username = forms.CharField(widget=forms.TextInput(attrs={'autofocus': 'autofocus'}))
    password = forms.CharField(widget = forms.PasswordInput)

class UserRegistrationForm(forms.ModelForm):
    password = forms.CharField(
        label = 'Password',
        widget = forms.PasswordInput
    )
    password2 = forms.CharField(
        label = 'Repeat password',
        widget = forms.PasswordInput
    )

    class Meta:
        model = get_user_model()
        fields = ['username','email','first_name','last_name']
        widgets = {
            "username": forms.TextInput(attrs={'autofocus': 'autofocus'}),
        }

    def clean_password2(self):
        cd = self.cleaned_data
        if cd['password'] != cd['password2']:
            raise forms.ValidationError("Passwords don't match!")
        return cd['password2']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.fields['username'].label= "User Name"
        self.fields['username'].help_text= "This will be your Login ID and must be unique"
        #self.helper.layout = Layout(
            #Field('username', label='User Name - Doug', css_class="fs-2")
        #)

        self.helper.add_input(Submit('submit', 'Register'))

Примечание - закомментированный раздел изменяет стиль input, а не label.

и register.html шаблон

{% extends "registration/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<h2>Registration Form</h2>
<div class="content-section">
    <form method="POST">
        {% csrf_token %}
        <fieldset class="form-group">
                {%  crispy form %}
        </fieldset>
        <div class="pb-4">
            <small class="text-muted">Required fields are marked by *</small>
        </div>
    </form>
</div>
{% endblock %}

Как сделать username label (и другие метки) bold и underlined, в идеале используя класс Bootstrap fw-bold?

Единственный способ, который я пока нашел, - это изменить класс css requiredField, добавив элемент style в base.html непосредственно под ссылкой cdn на Bootstrap, как показано ниже :

<style>
  .requiredField {
    font-weight:bold;
    text-decoration:underline;
  }
</style>

Если кто-нибудь знает способ получше, я был бы признателен, если бы он поделился своими знаниями.

Это можно сделать, расширив класс layout.Field. Мы можем создать наш собственный подкласс, который принимает дополнительный аргумент label_class, а затем модифицировать метод render, добавив label_class к extra_context. По умолчанию, когда вы делаете что-то вроде этого: self.helper.label_class = 'fw-bold' это добавляет label_class в глобальный контекст, и, как вы правильно заметили, это значение будет использоваться для всех меток полей формы. Но с помощью extra_context мы можем динамически переопределять контекстные переменные для конкретной метки поля.

Здесь будет задан дополнительный контекст . Как только поле будет отображено, исходный контекст будет восстановлен, это реализовано здесь с помощью KeepContext контекстного менеджера. Вот пример того, как может выглядеть наш пользовательский Field:

from crispy_forms.layout import Field
from crispy_forms.utils import TEMPLATE_PACK


class CustomField(Field):  
    def __init__(  
            self,  
            *fields,  
            css_class=None,  
            wrapper_class=None,  
            template=None,  
            label_class='',  
            **kwargs) -> None:  
  
        super().__init__(  
            *fields,  
            css_class=css_class,  
            wrapper_class=wrapper_class,  
            template=template,  
            **kwargs,  
        )  
        self.label_class = label_class  
  
    def render(  
            self,  
            form,  
            context,  
            template_pack=TEMPLATE_PACK,  
            extra_context=None,  
            **kwargs):  
  
        extra_context = extra_context or {}  
        context_label_class = context.get('label_class', '')  
        # Merge FormHelper().label_class and CustomField().label_class
        label_class = self.label_class + ' ' + context_label_class  
        extra_context['label_class'] = label_class.strip()  
        return super().render(  
            form=form,  
            context=context,  
            template_pack=template_pack,  
            extra_context=extra_context,  
            **kwargs,  
        )

А теперь пример формы, в которой мы будем использовать CustomField для динамического переопределения label_class обязательных полей для добавления двух классов bootstrap: 'fw-bold', 'text-decoration-underline':

from functools import cached_property  

from crispy_forms.helper import FormHelper  
from crispy_forms.layout import Field, Layout, Submit
  
from django import forms  
from django.contrib.auth import get_user_model  


class UserRegistrationForm(forms.ModelForm):  
    password = forms.CharField(  
        label = 'Password',  
        widget = forms.PasswordInput  
    )  
    password2 = forms.CharField(  
        label = 'Repeat password',  
        widget = forms.PasswordInput  
    )  
  
    class Meta:  
        model = get_user_model()  
        fields = ['username', 'email', 'first_name', 'last_name']  
        widgets = {  
            "username": forms.TextInput(attrs={'autofocus': 'autofocus'}),  
        }  
  
    def clean_password2(self):  
        cd = self.cleaned_data  
        if cd['password'] != cd['password2']:  
            raise forms.ValidationError("Passwords don't match!")  
        return cd['password2']  
  
    def __init__(self, *args, **kwargs):  
        super().__init__(*args, **kwargs)  
        self.fields['username'].label= "User Name"  
        self.fields['username'].help_text= (  
            "This will be your Login ID and must be unique"  
        )  
        self.helper.add_input(Submit('submit', 'Register'))  
  
    @cached_property  
    def helper(self):  
        def make_field(f_name: str, is_required: bool) -> Field:  
            return (  
                Field(f_name) if not is_required  
                else CustomField(f_name, label_class=label_class)  
            )  
  
        helper = FormHelper()  
        label_class = ' '.join(['fw-bold', 'text-decoration-underline'])  
        helper.layout = Layout(  
            *(  
                make_field(field_name, field_obj.required)  
                for field_name, field_obj in self.fields.items()  
            )  
        )  
        return helper

Результат будет выглядеть следующим образом:

example1

<время работы/>

Вот еще один простой пример, где глобально задается label_class = 'fw-bold' и дополнительно добавляется уникальный цвет для каждой метки поля:

class ExampleForm(forms.Form):  
    foo = forms.CharField(max_length=100)  
    bar = forms.CharField(max_length=100)  
    baz = forms.CharField(max_length=100)  
  
    def __init__(self, *args, **kwargs):  
        super().__init__(*args, **kwargs)  
        self.helper = FormHelper()  
        self.helper.layout = Layout(  
            CustomField('foo', label_class='text-info'),  
            CustomField('bar', label_class='text-danger'),  
            CustomField('baz', label_class='text-success'),  
        )  
        self.helper.label_class = 'fw-bold'

Результат будет выглядеть следующим образом:

example2

Вернуться на верх