Use django-crispy-forms to improve form UI
Heh, another fluff piece on beautifying Django forms. I've already encapsulated Tailwind styles myself and still find it insufficient, so I insist on tinkering with ancient relics like django-crispy-forms—Bootstrap is already outdated, hello! Though crispy-tailwind is passable, at least a bit more elegant than writing templates manually. Anyway, it'll all get rewritten in React eventually, so why bother wasting time?
目录11 个章节
Foreword
Alright, let's continue with this series of articles derived from the middle-end project.
Today, I'd like to introduce a library that can beautify the UI.
Django development emphasizes speed—"all martial arts in the world, only speed is unbreakable."
The forms feature is naturally essential; it comes with built-in form UI generation and validation, making it very convenient for building demos.
I previously encapsulated a set of forms behaviors and styles in the DjangoStarter framework, located in src/django_starter/contrib/forms.
That set is already quite good and uses TailwindCSS for styling, making it look nice.
However, while developing the middle-end project, I discovered the django-crispy-forms library, which offers more flexibility for beautifying form UI.
Implementation in DjangoStarter
Let's first examine the implementation in the DjangoStarter framework.
forms
├─ widgets
│ ├─ __init__.py
│ ├─ multiple_file.py
│ └─ flowbite_date_picker.py
├─ templates
│ └─ django_starter
│ └─ forms
│ ├─ widgets
│ └─ form_template.html
├─ __init__.py
├─ widget_classes.py
├─ mixins.py
└─ base.py
template
src/django_starter/contrib/forms/templates/django_starter/forms/form_template.html
{% for field in form %}
<div>
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
<div class="mt-2 text-sm text-gray-500 dark:text-gray-200">
{{ field.help_text }}
</div>
<div class="mt-2 text-sm text-red-600 dark:text-red-500">
{{ field.errors }}
</div>
</div>
{% endfor %}
Widgets
src/django_starter/contrib/forms/widgets/flowbite_date_picker.py
from django import forms
class FlowbiteDatePickerWidget(forms.DateInput):
template_name = 'django_starter/forms/widgets/flowbite_date_picker.html'
def __init__(self, attrs=None, custom_class=''):
final_attrs = {'class': custom_class}
if attrs:
final_attrs.update(attrs)
super(FlowbiteDatePickerWidget, self).__init__(attrs=final_attrs)
src/django_starter/contrib/forms/widgets/multiple_file.py
from django import forms
class MultipleFileInput(forms.ClearableFileInput):
allow_multiple_selected = True
class MultipleFileField(forms.FileField):
def __init__(self, *args, **kwargs):
kwargs.setdefault("widget", MultipleFileInput())
super().__init__(*args, **kwargs)
def clean(self, data, initial=None):
single_file_clean = super().clean
if isinstance(data, (list, tuple)):
result = [single_file_clean(d, initial) for d in data]
else:
result = [single_file_clean(data, initial)]
return result
Forms Code
src/django_starter/contrib/forms/mixins.py
from django import forms
from .widget_classes import *
class BaseFormMixin:
"""A generic mixin class for providing form styling"""
widget_classes = {
forms.TextInput: TEXT_INPUT_CLASS,
forms.Textarea: TEXT_AREA_CLASS,
forms.EmailInput: TEXT_INPUT_CLASS,
forms.PasswordInput: PASSWORD_INPUT_CLASS,
forms.Select: SELECT_CLASS,
forms.DateInput: DATE_INPUT_CLASS,
forms.NumberInput: NUMBER_INPUT_CLASS,
}
def apply_widget_classes(self):
"""Applies styling to form fields based on the widget type"""
for field_name, field in self.fields.items():
widget_class = self.widget_classes.get(type(field.widget))
if widget_class:
field.widget.attrs.update({'class': widget_class})
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.apply_widget_classes()
The code for widget_classes.py is not included here.
It uses Tailwind CSS styling.
How does crispy-forms work?
After installation, you can use it with just a simple configuration.
Usage looks something like this:
{% load crispy_forms_tags %}
<div class="sm:col-span-6">
{{ form.name|as_crispy_field }}
</div>
<div class="sm:col-span-3">
{{ form.dvr_brand|as_crispy_field }}
</div>
<div class="sm:col-span-3">
{{ form.dvr_model|as_crispy_field }}
</div>
<div class="sm:col-span-3">
{{ form.dvr_ip|as_crispy_field }}
</div>
<div class="sm:col-span-3">
{{ form.status|as_crispy_field }}
</div>
Installation
Install dependencies
pdm install django-crispy-forms
Then add to
INSTALLED_APPS = (
...
'crispy_forms',
)
Template packs
Then you need to install the Template packs; otherwise, it's just an empty shell.
The officially supported ones are only the Bootstrap series, which is a bit outdated.
Fortunately, the community also provides many options. Here I'm focusing only on the Tailwind CSS library, named crispy-tailwind.
pdm add crispy-tailwind
There are many other UI libraries. For those who are interested, you can find them on the official website: https://django-crispy-forms.readthedocs.io/en/latest/install.html#template-packs
This one also needs to be added.
INSTALLED_APPS = (
...
'crispy_forms',
'crispy_tailwind',
)
Configuration
Modify the default Template Packs in the configuration file
src/config/settings/components/crispy_forms.py
CRISPY_ALLOWED_TEMPLATE_PACKS = "tailwind"
CRISPY_TEMPLATE_PACK = "tailwind"
Usage
The simplest way to use this is by only modifying the template file.
{% load crispy_forms_tags %}
<form method="post" class="my-class">
{{ my_formset|crispy }}
</form>
Currently, the mid-end project only uses this method.
Combined with django-select2, it can implement a dropdown search box.
However, the styling is difficult to modify; I spent some time trying but couldn't get it right, so I decided to leave it for now. After all, this is just a quickly implemented DEMO.
If there's a need later, we can rewrite the page using React.
Summary
I found that Django-related technology is still relatively niche.
Generally, writing this kind of article doesn't attract many readers.
I've been working with Django for several years. I've studied the framework's source code, worked with scaffolding tools, and it's become very comfortable to use.
Although there are many Python web frameworks now, all projects eventually end up looking like Django 🤣
That's about it. For Python projects, I will continue to stick with Django because it is truly convenient and easy to use.
Of course, I'm not opposed to trying new things. For example, recently there's Litestar which claims to dethrone FastAPI—it seems quite interesting, and I might give it a try when I have time.
_EOF_
Use django-crispy-forms to improve form UI
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
DjangoStarter v3.3.0 版本更新
DjangoStarter v3.3.0?哦,不过是优化了Docker和换了个Granian服务器。导航栏多了管理后台,还扔了unfold。过渡版本就这?
2025-12-03 · DjangoStarter · 约 1 分钟
Django集成腾讯COS对象存储
腾讯COS集成Django真够折腾的,官方SDK和文档烂到不行。自己封装Storage虽然简单,但腾讯那帮人写代码的心思让人看不懂。Django这套东西倒是好用,`FileField`、`ImageField`自动转OSS,不过`django-filer`那中文locale问题得手动改。
2025-07-17 · DjangoStarter · 约 1 分钟
关于正在开发中的DjangoStarter v3版本
最近做的这个项目大量使用了 python 及其相关的生态,因此自然而然选择了我的 DjangoStarter 作为后端框架
2024-07-15 · DjangoStarter · 约 1 分钟
DeepSeek+Claude强强联手,使用AI驱动DjangoStarter 3.1框架升级
上个月底培训了一周就没时间更新博客 上周有一些空闲时间,就继续体验最近很火的AI辅助编程
2025-03-16 · DjangoStarter · 约 1 分钟
讨论
共 0 条加载评论中…