Django Tailwind CSS, Alpine.js Tutorial Series:
- Introduction
- How to Setup Tailwind CSS with Django (Part 1)
- How to Setup Tailwind CSS with Django (Part 2)
- Optimize Tailwind CSS in Django
- Render Django Form with Tailwind CSS Style
- Integrate Alpine.js with Django (Part 1) (coming soon)
- Integrate Alpine.js with Django (Part 2) (coming soon)
- Build Task List with Tailwind CSS, Alpine.js and Django (coming soon)
- Django Form Validation in Tailwind Modal (Alpine.js) (coming soon)
- Django Form Validation in Tailwind Modal (Alpine.js + HTMX) (coming soon)
- How to deploy Django Tailwind CSS project with Docker (coming soon)
The source code is on Github/django-tailwind-alpine-htmx
Recommended Posts:
UPDATE: crispy-tailwind
is not recommended anymore, please check django-formify, which seamlessly integrates Tailwind CSS styles into your Django forms for a modern look.
Objectives
By the end of this chapter, you should be able to:
- Render Django form with
django-crispy-forms
andcrispy-tailwind
- Use
tailwindcss/forms
plugin to reset form style.
Tasks App
Let's first create django app tasks
(env)$ python manage.py startapp tasks
.
├── db.sqlite3
├── django_tailwind_app
├── env
├── frontend
├── manage.py
├── requirements.txt
└── tasks # new
Add tasks
to the INSTALLED_APPS
in django_tailwind_app/settings.py
INSTALLED_APPS = [
...
'tasks', # new
]
Model
Update tasks/models.py
from django.db import models
from django.utils import timezone
class Task(models.Model):
title = models.CharField(max_length=250)
due_date = models.DateField(default=timezone.now)
Migrate the db
(env)$ python manage.py makemigrations
(env)$ python manage.py migrate
Form
Create tasks/forms.py
from django import forms
from .models import Task
class TaskForm(forms.ModelForm):
class Meta:
model = Task
fields = ("title", "due_date")
View
Update tasks/views.py
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render
from .forms import TaskForm
def create_task(request):
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
form.save()
return HttpResponse('ok')
else:
form = TaskForm()
return render(request, 'tasks/task_create.html', {'form': form})
Template
Create django_tailwind_app/templates/base.html
{% load webpack_loader %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% stylesheet_pack 'app' %}
</head>
<body>
{% block content %}
{% endblock content %}
{% javascript_pack 'app' %}
</body>
</html>
Create django_tailwind_app/templates/tasks/task_create.html
{% extends "base.html" %}
{% block content %}
<div class="w-full max-w-7xl mx-auto px-4">
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn-blue" value="Submit">Submit</button>
</form>
</div>
{% endblock %}
URL
Create tasks/urls.py
from django.urls import path
from .views import (
create_task
)
urlpatterns = [
path("create/", create_task, name="task-create"),
]
Create django_tailwind_app/urls.py
from django.contrib import admin
from django.urls import include, path
from django.views.generic import TemplateView
urlpatterns = [
path('', TemplateView.as_view(template_name="index.html")),
path("tasks/", include("tasks.urls")),
path('admin/', admin.site.urls),
]
Manual Test
(env)$ python manage.py runserver
If we check http://127.0.0.1:8000/tasks/create/
As you can see, even we import Tailwind to our Django project, the default form style still look ugly.
Next, let's start improving the form style.
tailwindcss-forms
tailwindcss/forms is a plugin that provides a basic reset for form styles that makes form elements easy to override with utilities.
$ cd frontend
$ npm install @tailwindcss/forms
Update frontend/tailwind.config.js
module.exports = {
//
plugins: [
require('@tailwindcss/forms'), // new
],
}
crispy-tailwind
crispy-tailwind is a Tailwind template pack for django-crispy-forms
Update requirements.txt
django-crispy-forms==1.13.0 # new
crispy-tailwind==0.5.0 # new
$ pip install -r requirements.txt
Update django_tailwind_app/settings.py
INSTALLED_APPS = [
"crispy_forms", # new
"crispy_tailwind", # new
]
CRISPY_ALLOWED_TEMPLATE_PACKS = "tailwind"
CRISPY_TEMPLATE_PACK = "tailwind"
Update django_tailwind_app/templates/tasks/task_create.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="w-full max-w-7xl mx-auto px-4">
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn-blue" value="Submit">Submit</button>
</form>
</div>
{% endblock %}
Notes:
- We load
crispy_forms_tags
at the top {{ form|crispy }}
will render the form usingTailwind
template pack. (set byCRISPY_TEMPLATE_PACK
)
JIT
If we check the elements in the devtools, we notice some tailwind css such as mb-2
is not working.
Why does that happen?
Because we did not tell Tailwind CSS which css classes are used by crispy-tailwind
If you use other 3-party packages to manipulate tailwind css classnames, you should also do it.
Update frontend/tailwind.config.js
const Path = require("path");
const pwd = process.env.PWD;
const pySitePackages = process.env.pySitePackages;
// We can add current project paths here
const projectPaths = [
Path.join(pwd, "../django_tailwind_app/templates/**/*.html"),
// add js file paths if you need
];
// We can add 3-party python packages here
let pyPackagesPaths = []
if (pySitePackages){
pyPackagesPaths = [
Path.join(pySitePackages, "./crispy_tailwind/**/*.html"),
Path.join(pySitePackages, "./crispy_tailwind/**/*.py"),
Path.join(pySitePackages, "./crispy_tailwind/**/*.js"),
];
}
const contentPaths = [...projectPaths, ...pyPackagesPaths];
console.log(`tailwindcss will scan ${contentPaths}`);
module.exports = {
content: contentPaths,
theme: {
extend: {},
},
plugins: [],
}
If we set pySitePackages
when running npm run start
, it should work as
(env)$ python3 -c "import sysconfig; print(sysconfig.get_path('purelib'))"
/Users/michaelyin/django_tailwind_project/env/lib/python3.9/site-packages
# set it to pySitePackages ENV variable
(env)$ export pySitePackages=$(python3 -c "import sysconfig; print(sysconfig.get_path('purelib'))")
# check
(env)$ env | grep pySitePackages
(env)$ cd frontend
(env)$ npm run start
As you can see, now the margin under the input
element is working!
Alternative Solution
If you don't want to use crispy-tailwind
, you can also use django-widget-tweaks
to add Tailwind CSS classes to the form elements.
You can check this blog post
Django Tailwind CSS, Alpine.js Tutorial Series:
- Introduction
- How to Setup Tailwind CSS with Django (Part 1)
- How to Setup Tailwind CSS with Django (Part 2)
- Optimize Tailwind CSS in Django
- Render Django Form with Tailwind CSS Style
- Integrate Alpine.js with Django (Part 1) (coming soon)
- Integrate Alpine.js with Django (Part 2) (coming soon)
- Build Task List with Tailwind CSS, Alpine.js and Django (coming soon)
- Django Form Validation in Tailwind Modal (Alpine.js) (coming soon)
- Django Form Validation in Tailwind Modal (Alpine.js + HTMX) (coming soon)
- How to deploy Django Tailwind CSS project with Docker (coming soon)
The source code is on Github/django-tailwind-alpine-htmx
Recommended Posts: