Расположение элементов Django-формы с помощью Bootstrap 5 с помощью встроенной сетки (Grid System) и флексбоксами (Flexbox)

15 октября 2026

Вариант 1. Элементы в одну строку (через Grid)
Чтобы выстроить поля и кнопку в один ряд, оберните их в родительский контейнер с классом row и используйте класс col для каждого элемента.
 

<form class="row g-3 align-items-center" method="post" action="{% url 'finances:add-income' %}">
    {% csrf_token %}
    <div class="col-auto">
        <div class="form-floating">
            {{ form.amount|add_class:"form-control" }}
            <label for="{{ form.amount.id_for_label }}">Сумма</label>
        </div>
    </div>
    <div class="col-auto">
        <div class="form-floating">
            {{ form.date_bank|add_class:"form-control" }}
            <label for="{{ form.date_bank.id_for_label }}">Дата</label>
        </div>
    </div>
    <div class="col-auto">
        <button class="btn btn-primary h-100" type="submit">Отправить</button>
    </div>
</form>

 

  • row g-3 создает строку с небольшими отступами между колонками.
  • col-auto заставляет колонку сжиматься под размер ее содержимого.
  • Примечание: Для form-floating внутри полей обязательно нужны теги <label>, иначе Bootstrap-эффект "плавающего" текста не будет работать корректно.
Вариант 2. Один над другим с ограниченной шириной
Чтобы расположить элементы вертикально и ограничить их ширину (например, не более 10 символов), лучше всего использовать Inline-стили (max-width: 10ch) или стандартную сетку. Символьная единица измерения ch в CSS идеально подходит для ограничения ширины по количеству знаков.
 

<form class="d-flex flex-column gap-3" method="post" action="{% url 'finances:add-income' %}">
    {% csrf_token %}
    
    <!-- Контейнер с ограничением ширины в 10 символов -->
    <div style="max-width: 10ch;">
        <div class="form-floating">
            {{ form.amount|add_class:"form-control" }}
            <label for="{{ form.amount.id_for_label }}">Сумма</label>
        </div>
    </div>

    <div style="max-width: 10ch;">
        <div class="form-floating">
            {{ form.date_bank|add_class:"form-control" }}
            <label for="{{ form.date_bank.id_for_label }}">Дата</label>
        </div>
    </div>

    <div style="max-width: 10ch;">
        <input class="btn btn-primary w-100" type="submit" value="Отправить">
    </div>
</form>

 

  • d-flex flex-column gap-3 выстраивает элементы вертикально с отступами друг от друга.
  • max-width: 10ch; задает максимальную ширину ровно в 10 символов 0.
  • w-100 у кнопки растягивает её на всю доступную ширину её 10-символьного контейнера, чтобы она была одинаковой ширины с полями.

 

Способ 3. Фиксированная высота (Самый надежный для form-floating)
Так как кнопка и поле даты выбиваются из высоты плавающего инпута, принудительно задайте им класс h-100 (высота 100%), а сами колонки col-auto сделайте одинаковыми по высоте с помощью align-items-stretch в строке:

<form class="row g-3 align-items-stretch" method="post" action="{% url 'finances:add-income' %}">
    {% csrf_token %}
    <div class="col-auto">
        <div class="form-floating">
            {{ form.amount|add_class:"form-control" }}
            <label for="{{ form.amount.id_for_label }}">Сумма</label>
        </div>
    </div>
    <div class="col-auto">
        <div class="form-floating h-100">
            {{ form.date_bank|add_class:"form-control h-100" }}
            <label for="{{ form.date_bank.id_for_label }}">Дата</label>
        </div>
    </div>
    <div class="col-auto d-flex align-items-stretch">
        <button class="btn btn-primary px-4" type="submit">Отправить</button>
    </div>
</form>

 

 

Способ 4. Отказ от form-floating в пользу классического стиля
Плавающие лейблы (form-floating) часто конфликтуют с нативными календарями (type="date"). Если сделать форму классической, используя стандартные компактные инпуты Bootstrap, все элементы автоматически станут одной базовой высоты:
 

<form class="row g-2 align-items-center" method="post" action="{% url 'finances:add-income' %}">
    {% csrf_token %}
    <div class="col-auto">
        <!-- Вместо плавающего лейбла используем placeholder или обычный label сверху -->
        {{ form.amount|add_class:"form-control" }}
    </div>
    <div class="col-auto">
        {{ form.date_bank|add_class:"form-control" }}
    </div>
    <div class="col-auto">
        <button class="btn btn-primary" type="submit">Отправить</button>
    </div>
</form>

 

 

Виджет AdminDateWidget тянет за собой скрытые встроенные стили и JavaScript из панели администратора Django. Из-за этого он принудительно добавляет ссылки рядом с инпутом («Today» и иконку календаря), которые ломают выравнивание строки (flex-line) в Bootstrap.

from django import forms

class ReceiptForm(forms.Form):
    amount = forms.DecimalField(
        label="Сумма", 
        initial=110050
    )
    
    date_bank = forms.DateField(
        label="Дата",
        widget=forms.DateInput(
            attrs={
                'type': 'date',  # Включает встроенный календарь браузера
                'class': 'form-control',
            }
        )
    )

 

 
Шаг 2. Верстаем форму в шаблоне HTML
Так как мы убрали form-floating, для идеального выравнивания по центру строки используйте класс align-items-center на контейнере row.

<form class="row g-2 align-items-center" method="post" action="{% url 'finances:add-income' %}">
    {% csrf_token %}
    
    <!-- Колонка для суммы -->
    <div class="col-auto">
        {{ form.amount|add_class:"form-control" }}
    </div>
    
    <!-- Колонка для даты -->
    <div class="col-auto">
        {{ form.date_bank }}
    </div>
    
    <!-- Колонка для кнопки -->
    <div class="col-auto">
        <button class="btn btn-primary" type="submit">Отправить</button>
    </div>
</form>

 
Что изменится:
  1. Исчезнут текстовая ссылка "Today" и иконка, которые смещали инпут даты вниз.
  2. Поля станут одинаковой высоты и встанут строго по одной горизонтальной оси.
  3. При клике на поле даты будет открываться аккуратный системный календарь.

Убрать стрелки переключения (spin buttons) у числового поля amount проще всего с помощью короткого CSS-правила. По умолчанию браузеры добавляют эти стрелки для всех инпутов с типом number.

<style>
    /* Для Chrome, Safari, Edge и Opera */
    input::-webkit-outer-spin-button,
    input::-webkit-inner-spin-button {
        -webkit-appearance: none;
        margin: 0;
    }

    /* Для Firefox */
    input[type=number] {
        -moz-appearance: textfield;
    }
</style>

 
Как это работает:
  • Этот код находит все числовые поля на странице и полностью скрывает внутренние элементы управления браузера.
  • Поле сохранит все свойства DecimalField (ввод только цифр и точек/запятых), но визуально станет выглядеть как чистое текстовое поле.

Чтобы кнопка POST-формы визуально ничем не отличалась от обычной GET-ссылки, ей нужно задать класс btn-link и убрать стандартные внутренние отступы с помощью p-0.

<li>
    <form method="post" action="{% url 'finances:debug-clear' %}" class="d-inline">
        {% csrf_token %}
        <button type="submit" class="btn btn-link nav-link p-0 border-0 align-baseline">Инициализировать</button>
    </form>
</li>
<li>
    <a href="{% url 'finances:debug-state' %}" class="nav-link">&lt;Все таблицы&gt;</a>
</li>

Что здесь изменилось:
  • btn-link — превращает кнопку в текстовую ссылку.
  • nav-link — добавляет те же стили (цвет, шрифт, поведение), что и у вашей соседней GET-ссылки.
  • p-0 border-0 — полностью убирает рамки и внутренние отступы кнопки, которые могли бы её сдвинуть.
  • d-inline на форме и align-baseline на кнопке — гарантируют, что скрытый тег <form> не создаст перенос строки и кнопка встанет ровно по базовой линии текста.