### Basic Tailwind CSS Stylesheet Setup
Source: https://unfoldadmin.com/docs/styles-scripts/customizing-tailwind
Set up the `styles.css` file to import Tailwind's base, component, and utility styles. Includes an example of applying a custom background color class.
```css
/* styles.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Your custom styles */
.some-class {
@apply bg-primary-600;
}
```
--------------------------------
### Install and Configure Pre-commit Hooks
Source: https://unfoldadmin.com/docs/development
Install pre-commit, set up commit hooks, and run all checks to ensure code quality before committing. This includes commit-msg hooks.
```bash
pip install pre-commit
pre-commit install
pre-commit install --hook-type commit-msg
pre-commit run --all-files # Check if everything is okay
```
--------------------------------
### Install Tailwind CSS and CLI
Source: https://unfoldadmin.com/docs/styles-scripts/customizing-tailwind
Install Tailwind CSS and its CLI tool using npm. This is required for compiling your styles.
```bash
npm i tailwindcss @tailwindcss/cli
```
--------------------------------
### Install Django Unfold using pip, uv, or poetry
Source: https://unfoldadmin.com/docs/installation/quickstart
Choose the appropriate command for your package manager to install Django Unfold.
```bash
pip install django-unfold
```
```bash
uv add django-unfold
```
```bash
poetry add django-unfold
```
--------------------------------
### Implement Autocomplete Fields in Django
Source: https://unfoldadmin.com/docs/fields/autocomplete
Example demonstrating how to set up custom autocomplete views and fields for ModelChoiceField and ModelMultipleChoiceField.
```python
from django import forms
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from unfold.admin import ModelAdmin
from unfold.views import BaseAutocompleteView
from unfold.fields import (
UnfoldAdminAutocompleteModelChoiceField,
UnfoldAdminMultipleAutocompleteModelChoiceField,
)
from .models import MyModel
# Custom ListView returning JSON with available select options
class MyAutocompleteView(BaseAutocompleteView):
model = MyModel
def dispatch(self, request, *args, **kwargs):
# Permissions checks here
return super().dispatch(request, *args, **kwargs)
def get_queryset(self):
# Search query is available in the request.GET object under the key "term"
term = self.request.GET.get("term")
# Additional filters and permissions checks here
qs = super().get_queryset()
# No search provided, return all results
if term == "":
return qs
# Search query provided, filter results
return qs.filter(my_field__icontains=term)
@admin.register(MyModel)
class MyModelAdmin(ModelAdmin):
# Register custom ListView above
custom_urls = (
(
"autocomplete-url-path",
"custom_autocomplete_path_name",
MyAutocompleteView.as_view()
),
)
class MyForm(forms.Form):
one_object = UnfoldAdminAutocompleteModelChoiceField(
label=_("Object - Single value"),
# Important for validation. Make sure it offers same results as the custom view
queryset=MyModel.objects.all(),
# Map autocomplete results to the custom view
url_path="admin:custom_autocomplete_path_name",
)
multiple_objects = UnfoldAdminMultipleAutocompleteModelChoiceField(
label=_("Objects - Multiple values"),
queryset=MyModel.objects.all(),
url_path="admin:custon_autocomplete_path_name",
)
```
--------------------------------
### Install Django Unfold using poetry
Source: https://unfoldadmin.com/docs/installation
Use poetry, a dependency management tool, to add django-unfold to your project.
```bash
poetry add django-unfold
```
--------------------------------
### Install Django Crispy Forms
Source: https://unfoldadmin.com/docs/configuration/crispy-forms
Install django-crispy-forms using pip, uv, or poetry. Ensure you add it to your project's dependencies.
```bash
pip install django-crispy-forms
```
```bash
uv add django-crispy-forms
```
```bash
poetry add django-crispy-forms
```
--------------------------------
### Install Django Unfold using uv
Source: https://unfoldadmin.com/docs/installation
Use uv, a fast Python package installer, to add django-unfold to your project dependencies.
```bash
uv add django-unfold
```
--------------------------------
### Install unfold.contrib.forms
Source: https://unfoldadmin.com/docs/widgets/wysiwyg
Add 'unfold.contrib.forms' to INSTALLED_APPS to use the WysiwygWidget. This is a mandatory dependency.
```python
# settings.py
INSTALLED_APPS = [
"unfold",
"unfold.contrib.forms",
]
```
--------------------------------
### Custom Tracker Data Preparation in Component Class
Source: https://unfoldadmin.com/docs/components/tracker
Example of a component class for preparing data for the tracker component. The data is passed via the `data` parameter to the `get_context_data` method.
```python
# admin.py
from unfold.components import BaseComponent, register_component
@register_component
class MyTrackerComponent(BaseComponent):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
"data": DATA
"size": "md", # Optional: size of tracker: "md", "sm"
})
return context
```
--------------------------------
### Dataset Implementation Example
Source: https://unfoldadmin.com/docs/configuration/datasets
This snippet demonstrates how to define a custom Dataset and integrate it into a parent model's admin configuration. It includes search fields, display fields, links, pagination, and custom actions. The `get_queryset` method is crucial for filtering data based on the parent object and user permissions.
```python
from unfold.admin import ModelAdmin
from unfold.datasets import BaseDataset
class SomeDatasetAdmin(ModelAdmin):
search_fields = ["name"]
list_display = ["name", "city", "country", "custom_field"]
list_display_links = ["name", "city"]
list_per_page = 20 # Default: 10
actions = [
"custom_action",
]
# list_filter = [] # Warning: this is not supported
def custom_action(self, request, queryset):
# You can do something with selected queryset
pass
def get_queryset(self, request):
# `extra_context` contains current changeform object
obj_id = self.extra_context.get("object")
# If we are on create object page display no results
if not obj_id:
return super().get_queryset(request).none()
# If there is a permission requirement, make sure that
# everything is properly handled here
return super().get_queryset(request).filter(
related_field__pk=obj_id
)
class SomeDataset(BaseDataset):
model = SomeModel
model_admin = SomeDatasetAdmin
tab = True # Displays this dataset as tab
class UserAdmin(ModelAdmin):
change_form_datasets = [
SomeDataset,
]
```
--------------------------------
### Custom Dropdown Filter Implementation
Source: https://unfoldadmin.com/docs/filters/dropdown
Example of creating a custom dropdown filter by extending the base DropdownFilter class. This allows for custom options and query logic.
```python
from django.contrib import admin
from django.contrib.auth.models import User
from unfold.admin import ModelAdmin
from unfold.contrib.filters.admin import (
ChoicesDropdownFilter,
MultipleChoicesDropdownFilter,
RelatedDropdownFilter,
MultipleRelatedDropdownFilter,
DropdownFilter,
MultipleDropdownFilter
)
class CustomDropdownFilter(DropdownFilter):
title = _("Custom dropdown filter")
parameter_name = "query_param_in_uri"
def lookups(self, request, model_admin):
return [
["option_1", _("Option 1")],
["option_2", _("Option 2")],
]
def queryset(self, request, queryset):
if self.value() not in EMPTY_VALUES:
# Here write custom query
return queryset.filter(your_field=self.value())
return queryset
@admin.register(User)
class MyAdmin(ModelAdmin):
list_filter_submit = True # Submit button at the bottom of the filter
list_filter = [
CustomDropdownFilter,
("modelfield_with_choices", ChoicesDropdownFilter),
("modelfield_with_choices_multiple", MultipleChoicesDropdownFilter),
("modelfield_with_foreign_key", RelatedDropdownFilter)
("modelfield_with_foreign_key_multiple", MultipleRelatedDropdownFilter)
]
```
--------------------------------
### Install Django Unfold using pip
Source: https://unfoldadmin.com/docs/installation
Use pip to install the django-unfold package. This is a common method for Python package management.
```bash
pip install django-unfold
```
--------------------------------
### Registering Date and Datetime Filters in Django Admin
Source: https://unfoldadmin.com/docs/filters/datetime
This example shows how to register `RangeDateFilter` and `RangeDateTimeFilter` for your Django models in `admin.py`. Ensure `unfold.contrib.filters` is in your `INSTALLED_APPS`.
```python
from django.contrib import admin
from django.contrib.auth.models import User
from unfold.admin import ModelAdmin
from unfold.contrib.filters.admin import RangeDateFilter, RangeDateTimeFilter
@admin.register(User)
class YourModelAdmin(ModelAdmin):
list_filter_submit = True # Submit button at the bottom of the filter
list_filter = (
("field_E", RangeDateFilter), # Date filter
("field_F", RangeDateTimeFilter), # Datetime filter
)
```
--------------------------------
### Cohort Data Structure Example
Source: https://unfoldadmin.com/docs/components/cohort
This is an example of the expected data structure for the cohort component. It includes headers, rows with headers, and individual cell values, each potentially having a subtitle.
```python
DATA = {
"headers": [
# Col 1 header
{
"title": "Title",
"subtitle": "something", # Optional
},
],
"rows": [
# First row
{
# Row heading
"header": {
"title": "Title",
"subtitle": "something", # Optional
},
"cols": [
# Col 1 cell value
{
"value": "1",
"subtitle": "something", # Optional
}
]
},
# Second row
{
# Row heading
"header": {
"title": "Title",
"subtitle": "something", # Optional
},
"cols": [
# Col 1 cell value
{
"value": "1",
}
]
},
]
}
```
--------------------------------
### Configure Poetry for Local Development
Source: https://unfoldadmin.com/docs/development
Link the local django-unfold repository to your project's pyproject.toml for development. Ensure django-unfold is installed and Poetry is used for dependency management.
```toml
django-unfold = { path = "../django-unfold", develop = true}
```
--------------------------------
### Compile Tailwind CSS
Source: https://unfoldadmin.com/docs/development
Install Node.js dependencies and compile Tailwind CSS. Use 'tailwind:watch' for continuous compilation during development and 'tailwind:build' for a one-time build.
```bash
# Install dependencies
npm install
# run after each change in code
npm run tailwind:watch
# run once
npm run tailwind:build
```
--------------------------------
### Tracker Component Data Structure Example
Source: https://unfoldadmin.com/docs/components/tracker
Defines the structure for data used by the tracker component, including options for custom colors, tooltips, links, and CSS classes.
```python
DATA = [
{
# Optional: configure custom color for the item
"color": "bg-primary-400 dark:bg-primary-700",
# Optional: will show a tooltip on hover
"tooltip": "Custom value 1",
# Optional: will make the item a link
"href": "https://example.com",
# Optional: add custom CSS classes to the item
"class": "custom-class",
},
{
"color": "bg-primary-400 dark:bg-primary-700",
"tooltip": "Custom value 2",
}
]
```
--------------------------------
### Implementing a Changeform Action
Source: https://unfoldadmin.com/docs/actions/changeform
Define a changeform action using the `@action` decorator in your ModelAdmin class. This example shows how to block a user and redirect back to the user's change form page. Ensure the `actions_detail` attribute includes the action name.
```python
from django.contrib.admin import register
from django.contrib.auth.models import User
from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.http import HttpRequest
from unfold.admin import ModelAdmin
from unfold.decorators import action
@register(User)
class UserAdmin(ModelAdmin):
actions_detail = ["changeform_action"]
@action(
description=_("Changeform action"),
url_path="changeform-action",
attrs={"target": "_blank"},
permissions=["changeform_action"]
)
def changeform_action(self, request: HttpRequest, object_id: int):
user = User.objects.get(pk=object_id)
user.block()
return redirect(
reverse_lazy("admin:users_user_change", args=(object_id,))
)
def has_changeform_action_permission(self, request: HttpRequest, object_id: Union[str, int]):
pass
```
--------------------------------
### Implementing Dropdown Actions in Django Unfold
Source: https://unfoldadmin.com/docs/actions/dropdown-actions
This example demonstrates how to define a dropdown action in your Django admin.py file using the `actions_list` attribute. The dropdown includes a title, an optional icon, and a list of actions.
```python
from django.contrib.auth.models import User
from unfold.admin import ModelAdmin
@register(User)
class UserAdmin(ModelAdmin):
actions_list = ["action1", "action2", {
"title": "Dropdown action",
"icon": "person", # Optional, will display icon in the dropdown title
"items": [
"action3", "action4",
]
}]
```
--------------------------------
### ModelAdmin Customization Example
Source: https://unfoldadmin.com/docs/configuration/modeladmin
Demonstrates how to inherit from unfold.admin.ModelAdmin and configure various options to customize the Django admin interface. This includes settings for display links, field compression, form warnings, preprocessing readonly fields, filter display, changelist layout, and action configurations.
```python
from django import models
from django.contrib import admin
from django.contrib.postgres.fields import ArrayField
from django.db import models
from unfold.admin import ModelAdmin
from unfold.contrib.forms.widgets import ArrayWidget, WysiwygWidget
@admin.register(MyModel)
class CustomAdminClass(ModelAdmin):
# Display add link in changelist / changeform
show_add_link = True # Default: True
# Display fields in changeform in compressed mode
compressed_fields = True # Default: True
# Warn before leaving unsaved changes in changeform
warn_unsaved_form = True # Default: False
# Preprocess content of readonly fields before render
readonly_preprocess_fields = {
"model_field_name": "html.unescape",
"other_field_name": lambda content: content.strip(),
}
# Display submit button in filters
list_filter_submit = False
# Custom filter options
list_filter_options = {
"your_filter_field_path": {
"label": "Custom label", # Optional: custom label for the filter
"horizontal": True, # Optional: display filter in horizontal layout
}
}
# Display changelist in fullwidth
list_fullwidth = False
# Set to False, to enable filter as "sidebar"
list_filter_sheet = True
# Position horizontal scrollbar in changelist at the top
list_horizontal_scrollbar_top = False
# Disable select all action in changelist
list_disable_select_all = False
# Custom actions
actions_list = [] # Displayed above the results list
actions_row = [] # Displayed in a table row in results list
actions_detail = [] # Displayed at the top of for in object detail
actions_submit_line = [] # Displayed near save in object detail
# Changeform templates (located inside the form)
change_form_before_template = "some/template.html"
change_form_after_template = "some/template.html"
# Located outside of the form
change_form_outer_before_template = "some/template.html"
change_form_outer_after_template = "some/template.html"
# Display cancel button in submit line in changeform
change_form_show_cancel_button = True # show/hide cancel button in changeform, default: False
formfield_overrides = {
models.TextField: {
"widget": WysiwygWidget,
},
ArrayField: {
"widget": ArrayWidget,
}
}
```
--------------------------------
### Basic UNIFOLD Configuration
Source: https://unfoldadmin.com/docs/configuration
Set basic site information like title, header, and subheader. Customize the dropdown menu in the sidebar with icons, titles, and links. Define the site URL and specify paths to custom views.
```python
from django.templatetags.static import static
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
UNFOLD = {
"SITE_TITLE": "Custom suffix in
tag",
"SITE_HEADER": "Appears in sidebar at the top",
"SITE_SUBHEADER": "Appears under SITE_HEADER",
"SITE_DROPDOWN": [
{
"icon": "diamond",
"title": _("My site"),
"link": "https://example.com",
},
# ...
],
"SITE_URL": "/",
"SITE_VIEWS": [
("some-path-to-view", "name_of_view_1", "path.to.view_itself_1"),
("other-path-to-view", "another_name_of_view_2", "path.to.view_itself_2"),
],
# "SITE_ICON": lambda request: static("icon.svg"), # both modes, optimise for 32px height
"SITE_ICON": {
"light": lambda request: static("icon-light.svg"), # light mode
"dark": lambda request: static("icon-dark.svg"), # dark mode
},
# "SITE_LOGO": lambda request: static("logo.svg"), # both modes, optimise for 32px height
"SITE_LOGO": {
"light": lambda request: static("logo-light.svg"), # light mode
"dark": lambda request: static("logo-dark.svg"), # dark mode
},
"SITE_SYMBOL": "speed", # symbol from icon set
"SITE_FAVICONS": [
{
"rel": "icon",
"sizes": "32x32",
"type": "image/svg+xml",
"href": lambda request: static("favicon.svg"),
},
],
"SHOW_HISTORY": True, # show/hide "History" button, default: True
"SHOW_VIEW_ON_SITE": True, # show/hide "View on site" button, default: True
"SHOW_BACK_BUTTON": False, # show/hide "Back" button on changeform in header, default: False
"SHOW_UI_WARNINGS": False, # show/hide warnings in UI, default: False
"ENVIRONMENT": "sample_app.environment_callback", # environment name in header
"ENVIRONMENT_TITLE_PREFIX": "sample_app.environment_title_prefix_callback", # environment name prefix in title tag
"DASHBOARD_CALLBACK": "sample_app.dashboard_callback",
"THEME": "dark", # Force theme: "dark" or "light". Will disable theme switcher
"LOGIN": {
"image": lambda request: static("sample/login-bg.jpg"),
"redirect_after": lambda request: reverse_lazy("admin:APP_MODEL_changelist"),
# Inherits from `unfold.forms.AuthenticationForm`
"form": "app.forms.CustomLoginForm",
},
"STYLES": [
lambda request: static("css/style.css"),
],
"SCRIPTS": [
lambda request: static("js/script.js"),
],
"BORDER_RADIUS": "6px",
"COLORS": {
"base": {
"50": "oklch(98.5% .002 247.839)",
"100": "oklch(96.7% .003 264.542)",
"200": "oklch(92.8% .006 264.531)",
"300": "oklch(87.2% .01 258.338)",
"400": "oklch(70.7% .022 261.325)",
"500": "oklch(55.1% .027 264.364)",
"600": "oklch(44.6% .03 256.802)",
"700": "oklch(37.3% .034 259.733)",
"800": "oklch(27.8% .033 256.848)",
"900": "oklch(21% .034 264.665)",
"950": "oklch(13% .028 261.692)",
},
"primary": {
"50": "oklch(97.7% .014 308.299)",
"100": "oklch(94.6% .033 307.174)",
"200": "oklch(90.2% .063 306.703)",
"300": "oklch(82.7% .119 306.383)",
"400": "oklch(71.4% .203 305.504)",
"500": "oklch(62.7% .265 303.9)",
"600": "oklch(55.8% .288 302.321)",
"700": "oklch(49.6% .265 301.924)",
"800": "oklch(43.8% .218 303.724)",
"900": "oklch(38.1% .176 304.987)",
"950": "oklch(29.1% .149 302.717)",
},
"font": {
"subtle-light": "var(--color-base-500)", # text-base-500
"subtle-dark": "var(--color-base-400)", # text-base-400
"default-light": "var(--color-base-600)", # text-base-600
"default-dark": "var(--color-base-300)", # text-base-300
"important-light": "var(--color-base-900)", # text-base-900
"important-dark": "var(--color-base-100)", # text-base-100
},
},
"EXTENSIONS": {
"modeltranslation": {
"flags": {
"en": "🇬🇧",
"fr": "🇫🇷",
"nl": "🇧🇪",
},
},
},
}
```
--------------------------------
### Configure Basic Site Settings
Source: https://unfoldadmin.com/docs/configuration/settings
Set the site title, header, subheader, and external links for the admin interface.
```python
UNFOLD = {
"SITE_TITLE": "Custom suffix in tag",
"SITE_HEADER": "Appears in sidebar at the top",
"SITE_SUBHEADER": "Appears under SITE_HEADER",
"SITE_DROPDOWN": [
{
"icon": "diamond",
"title": _("My site"),
"link": "https://example.com",
},
# ...
],
"SITE_URL": "/",
# ...
}
```
--------------------------------
### Configure Environment and Dashboard
Source: https://unfoldadmin.com/docs/configuration/settings
Specify callbacks for environment names and dashboard rendering.
```python
"ENVIRONMENT": "sample_app.environment_callback", # environment name in header
"ENVIRONMENT_TITLE_PREFIX": "sample_app.environment_title_prefix_callback", # environment name prefix in title tag
"DASHBOARD_CALLBACK": "sample_app.dashboard_callback",
```
--------------------------------
### Configure INSTALLED_APPS for Django Simple History
Source: https://unfoldadmin.com/docs/integrations/django-simple-history
Add `unfold.contrib.simple_history` to your `settings.py` after `unfold` but before `simple_history` to ensure proper styling overrides.
```python
# settings.py
INSTALLED_APPS = [
"unfold",
# ...
"unfold.contrib.simple_history",
# ...
"simple_history",
]
```
--------------------------------
### Configure Site Icons and Logos
Source: https://unfoldadmin.com/docs/configuration/settings
Set light and dark mode variations for the site icon and logo using lambda functions to reference static files.
```python
"SITE_ICON": {
"light": lambda request: static("icon-light.svg"), # light mode
"dark": lambda request: static("icon-dark.svg"), # dark mode
},
"SITE_LOGO": {
"light": lambda request: static("logo-light.svg"), # light mode
"dark": lambda request: static("logo-dark.svg"), # dark mode
},
```
--------------------------------
### Optimize Autocomplete Querysets for Performance
Source: https://unfoldadmin.com/docs/fields/autocomplete
Override the queryset in the autocomplete view to optimize performance by loading only necessary data for GET and POST requests.
```python
class MyAutocompleteView(BaseAutocompleteView):
one_object = UnfoldAdminAutocompleteModelChoiceField(
label=_("Object - Single value"),
queryset=MyModel.objects.all(),
url_path="admin:custom_autocomplete_path_name",
)
def __init__(self, request, *args, **kwargs):
# The request argument needs to be manually passed. If you are using
# `FormView` just use `get_form_kwargs` to pass the request argument.
if request.method == "GET":
if "initial" in kwargs:
# Initial values are present
self.queryset = MyModel.objects.filter(pk__in=kwargs["initial"])
else:
self.queryset = MyModel.objects.none()
elif request.method == "POST":
self.queryset = MyModel.objects.filter(pk__in=request.POST.getlist("objects"))
super().__init__(request, *args, **kwargs)
```
--------------------------------
### Configure Styles and Scripts in settings.py
Source: https://unfoldadmin.com/docs/styles-scripts
Use the STYLES and SCRIPTS keys within the UNIFOLD dictionary to specify custom CSS and JavaScript files. These can be strings or lambda functions returning static file paths. Ensure `collectstatic` is run for production.
```python
from django.templatetags.static import static
UNFOLD = {
"STYLES": [
lambda request: static("css/styles.css"),
],
"SCRIPTS": [
lambda request: static("js/scripts.js"),
],
}
```
--------------------------------
### Configure INSTALLED_APPS in settings.py
Source: https://unfoldadmin.com/docs/installation/quickstart
Add 'unfold' and its optional contrib modules to your INSTALLED_APPS setting before 'django.contrib.admin'.
```python
# settings.py
INSTALLED_APPS = [
"unfold", # before django.contrib.admin
"unfold.contrib.filters", # optional, if special filters are needed
"unfold.contrib.forms", # optional, if special form elements are needed
"unfold.contrib.inlines", # optional, if special inlines are needed
"unfold.contrib.import_export", # optional, if django-import-export package is used
"unfold.contrib.guardian", # optional, if django-guardian package is used
"unfold.contrib.simple_history", # optional, if django-simple-history package is used
"unfold.contrib.location_field", # optional, if django-location-field package is used
"unfold.contrib.constance", # optional, if django-constance package is used
"unfold.contrib.hijack", # optional, if django-hijack package is used
"django.contrib.admin", # required
]
```
--------------------------------
### Custom Admin Dashboard Template
Source: https://unfoldadmin.com/docs/configuration/dashboard
Extend the 'admin/base.html' template to create a custom admin dashboard. This example shows how to override the title, branding, and content blocks.
```django
{% extends 'admin/base.html' %}
{% load i18n %}
{% block title %}
{% if subtitle %}
{{ subtitle }} |
{% endif %}
{{ title }} | {{ site_title|default:_('Django site admin') }}
{% endblock %}
{% block branding %}
{% include "unfold/helpers/site_branding.html" %}
{% endblock %}
{% block content %}
Start creating your own Tailwind components here
{% endblock %}
```
--------------------------------
### Configure Site Dropdown Menu
Source: https://unfoldadmin.com/docs/configuration/site-dropdown
Define the SITE_DROPDOWN list in your settings.py to populate the dropdown menu. Each item can include an icon, title, link, and optional HTML attributes.
```python
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
UNFOLD = {
"SITE_DROPDOWN": [
{
"icon": "diamond",
"title": _("My site"),
"link": "https://example.com",
"attrs": {
"target": "_blank",
},
},
{
"icon": "diamond",
"title": _("My site"),
"link": reverse_lazy("admin:index"),
},
]
}
```
--------------------------------
### Configure Styles and Scripts in settings.py
Source: https://unfoldadmin.com/docs/styles-scripts/loading-files
Use the STYLES and SCRIPTS keys within the UNFOlD dictionary to load custom CSS and JavaScript files. These keys accept a list of strings or lambda functions pointing to your static assets.
```python
# settings.py
from django.templatetags.static import static
UNFOLD = {
"STYLES": [
lambda request: static("css/styles.css"),
],
"SCRIPTS": [
lambda request: static("js/scripts.js"),
],
}
```
--------------------------------
### Implement Nested Inlines with TabularInline
Source: https://unfoldadmin.com/docs/inlines/nested
Define nested inlines by assigning a list of inline classes to the 'inlines' property. This example shows a three-level nesting structure, though only two levels are supported.
```python
from unfold.admin import ModelAdmin, TabularInline, StackedInline
class ProjectAdmin(ModelAdmin):
inlines = [TaskInline]
class TaskInline(TabularInline):
inlines = [SubTaskInline]
class SubTaskInline(TabularInline):
inlines = [AnotherInline] # This is not going to work
```
--------------------------------
### Basic Link Component Usage
Source: https://unfoldadmin.com/docs/components/link
Demonstrates how to use the Link component with a URL, an icon, and marking it as external. Ensure the 'unfold' template tags are loaded.
```django
{% load unfold %}
{% component "unfold/components/link.html" with href="https://example.com" icon="start" external=1 %}
{% trans "Link text" %}
{% endcomponent %}
```
--------------------------------
### Configure Autocomplete Filters in Django Admin
Source: https://unfoldadmin.com/docs/filters/autocomplete
Example of how to integrate AutocompleteSelectFilter and AutocompleteSelectMultipleFilter into your Django admin. Ensure 'unfold.contrib.filters' is in INSTALLED_APPS. The fields must be ForeignKey or ManyToManyField, and the referenced model needs 'search_fields'.
```python
from django.contrib import admin
from django.contrib.auth.models import User
from unfold.admin import ModelAdmin
from unfold.contrib.filters.admin import (
AutocompleteSelectFilter,
AutocompleteSelectMultipleFilter
)
@admin.register(User)
class YourModelAdmin(ModelAdmin):
list_filter = (
# Autocomplete filter
["other_model_field", AutocompleteSelectFilter],
# Autocomplete multiple filter
["other_multiple_model_field", AutocompleteSelectMultipleFilter],
)
class OtherModelAdmin(ModelAdmin):
search_fields = ["name"]
```
--------------------------------
### Basic Component Nesting
Source: https://unfoldadmin.com/docs/components/introduction
Demonstrates basic component nesting using the `component` tag. Ensure the `unfold` template tags are loaded.
```django
{% load unfold %}
{% component "unfold/components/card.html" %}
{% component "unfold/components/title.html" %}
Card Title
{% endcomponent %}
{% endcomponent %}
```
--------------------------------
### Configure Pagination and Optimize Queries with prefetch_related
Source: https://unfoldadmin.com/docs/configuration/sections
Reduce changelist queries by setting `list_per_page` to a smaller value. For optimal performance, override `get_queryset` to use `prefetch_related` for all necessary related fields, minimizing SQL queries.
```python
from unfold.admin import ModelAdmin
from .models import SomeModel
@admin.register(SomeModel)
class SomeAdmin(ModelAdmin):
list_per_page = 20 # Quick solution
list_sections = [CustomTableSection]
# Custom queryset prefetching related records
def get_queryset(self, request):
return (
super()
.get_queryset(request)
.prefetch_related(
"related_field_set",
"related_field__another_related_field",
"related_field__another_related_field__even_more_related_field",
)
)
```
--------------------------------
### Link Component within a Card
Source: https://unfoldadmin.com/docs/components/link
Shows how to embed Link components inside a Card component, illustrating a common pattern for displaying lists of links. This example also uses a Button component for an action.
```django
{% load unfold %}
{% trans "Card title to display" as features_title %}
{% capture as features_action silent %}
{% component "unfold/components/button.html" with href="#" variant="default" size="sm" %}
{% trans "View more" %}
{% endcomponent %}
{% endcapture %}
{% component "unfold/components/card.html" with title=features_title action=features_action title_class="!py-3" %}
{% component "unfold/components/link.html" with href="https://example.com" icon="start" external=1 %}
{% trans "Example link 1" %}
{% endcomponent %}
{% component "unfold/components/link.html" with href="https://example.com" icon="start" external=1 %}
{% trans "Example link 2" %}
{% endcomponent %}
{% endcomponent %}
```
--------------------------------
### Force Theme and Customize Login
Source: https://unfoldadmin.com/docs/configuration/settings
Force a specific theme ('dark' or 'light') and customize the login page with an image, redirect URL, and custom form.
```python
"THEME": "dark", # Force theme: "dark" or "light". Will disable theme switcher
"LOGIN": {
"image": lambda request: static("sample/login-bg.jpg"),
"redirect_after": lambda request: reverse_lazy("admin:APP_MODEL_changelist"),
# Inherits from `unfold.forms.AuthenticationForm`
"form": "app.forms.CustomLoginForm",
},
```
--------------------------------
### Configure ModelAdmin with Unfold Import/Export Forms
Source: https://unfoldadmin.com/docs/integrations/django-import-export
Use Unfold's custom forms for import and export to ensure styled form elements within your ModelAdmin. This setup is for django-import-export versions prior to 4.x.
```python
# admin.py
from unfold.admin import ModelAdmin
from import_export.admin import ImportExportModelAdmin
from unfold.contrib.import_export.forms import ExportForm, ImportForm, SelectableFieldsExportForm
class ExampleAdmin(ModelAdmin, ImportExportModelAdmin):
import_form_class = ImportForm
export_form_class = ExportForm
# export_form_class = SelectableFieldsExportForm
```
--------------------------------
### Override Location Widget in Django Admin
Source: https://unfoldadmin.com/docs/integrations/django-location-field
Customize the location field widget in your Django admin by overriding the form in your `ModelAdmin`. This example shows how to use `UnfoldAdminLocationWidget` and optionally set base fields or zoom level.
```python
# admin.py
from django.contrib import admin
from unfold.admin import ModelAdmin
# Custom form where we override the location widget
class ExampleModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Change the widget for location field
self.fields["location"].widget = UnfoldAdminLocationWidget(
# base_fields = ["city"],
# zoom = 7
)
@admin.register(ExampleModelWithLocationField)
class ExampleModelAdmin(ModelAdmin):
form = ExampleModelForm # Override default changeform
```
--------------------------------
### Registering Model and Readonly Fields in Admin
Source: https://unfoldadmin.com/docs/fields/json
Register your model with Unfold Admin and include the JSON field in the `readonly_fields` list. This is necessary for Unfold to display the JSON field with proper formatting and syntax highlighting (if Pygments is installed).
```python
from django.contrib import admin
from unfold.admin import ModelAdmin
@admin.register(CustomModel)
class CustomAdminClass(ModelAdmin):
readonly_fields = ["data"]
```
--------------------------------
### Add unfold.contrib.hijack to INSTALLED_APPS
Source: https://unfoldadmin.com/docs/integrations/django-hijack
Add `unfold.contrib.hijack` to your `INSTALLED_APPS` in `settings.py` to enable the integration. This loads new templates that enhance the styling of django-hijack features.
```python
# settings.py
INSTALLED_APPS = [
"unfold.contrib.hijack", # Add this to load new templates
]
```
--------------------------------
### Extend Base Template for Custom Page
Source: https://unfoldadmin.com/docs/pages
Extend 'unfold/layouts/base.html' to include Unfold components like header and sidebar. Place custom content within the 'content' block. This example also shows how to use the 'tab_list' helper.
```django
{% extends "admin/base.html" %}
{% load admin_urls i18n unfold %}
{% block content %}
{% tab_list "drivers" %}
{% trans "Custom page" %}
{% endblock %}
```
--------------------------------
### Sidebar Configuration
Source: https://unfoldadmin.com/docs/configuration/settings
Configure the sidebar's visibility for search and application lists, and define navigation items with titles, icons, links, badges, and permissions.
```python
"SIDEBAR": {
"show_search": False, # Search in applications and models names
"show_all_applications": False, # Dropdown with all applications and models
"navigation": [
{
"title": _("Navigation"),
"separator": True, # Top border
"collapsible": True, # Collapsible group of links
"items": [
{
"title": _("Dashboard"),
"icon": "dashboard", # Supported icon set: https://fonts.google.com/icons
"link": reverse_lazy("admin:index"),
"badge": "sample_app.badge_callback",
"badge_variant": "info", # info, success, warning, primary, danger
"badge_style": "solid", # background fill style
"permission": lambda request: request.user.is_superuser,
},
{
"title": _("Users"),
"icon": "people",
"link": reverse_lazy("admin:auth_user_changelist"),
},
],
},
],
}
```
--------------------------------
### Define and Use Nonrelated Inlines
Source: https://unfoldadmin.com/docs/inlines/nonrelated
Example of defining a `NonrelatedTabularInline` and registering it with a model admin. Ensure `unfold.contrib.inlines` is in `INSTALLED_APPS`. The `get_form_queryset` method must be implemented to fetch the related objects, and `save_new_instance` can be used for custom saving logic.
```python
from django.contrib.auth.models import User
from unfold.admin import ModelAdmin
from unfold.contrib.inlines.admin import NonrelatedTabularInline
from .models import OtherModel
class OtherNonrelatedInline(NonrelatedTabularInline): # NonrelatedStackedInline is available as well
model = OtherModel
fields = ["field1", "field2"] # Ignore property to display all fields
def get_form_queryset(self, obj):
"""
Gets all nonrelated objects needed for inlines. Method must be implemented.
"""
return self.model.objects.all()
def save_new_instance(self, parent, instance):
"""
Extra save method which can for example update inline instances based on current
main model object. Method must be implemented.
"""
pass
@admin.register(User)
class UserAdmin(ModelAdmin):
inlines = [OtherNonrelatedInline]
```
--------------------------------
### Configure URLs for Custom Admin Site
Source: https://unfoldadmin.com/docs/configuration/custom-sites
Include the URLs of your custom admin site in your project's `urls.py`. Ensure the path matches your desired admin URL.
```python
from django.urls import path
from .sites import custom_admin_site
urlpatterns = [
# other URL patterns
path("admin/", custom_admin_site.urls),
]
```
--------------------------------
### Implement Numeric Filters in Django Admin
Source: https://unfoldadmin.com/docs/filters/numeric
Configure various numeric filters for your Django model in admin.py. Ensure 'unfold.contrib.filters' is in INSTALLED_APPS. This example shows single field, range, slider, custom slider, and list filters.
```python
from django.contrib import admin
from django.contrib.auth.models import User
from unfold.admin import ModelAdmin
from unfold.contrib.filters.admin import (
RangeNumericListFilter,
RangeNumericFilter,
SingleNumericFilter,
SliderNumericFilter,
)
class CustomSliderNumericFilter(SliderNumericFilter):
MAX_DECIMALS = 2
STEP = 10
class CustomRangeNumericListFilter(RangeNumericListFilter):
parameter_name = "items_count"
title = "items"
@admin.register(User)
class YourModelAdmin(ModelAdmin):
list_filter_submit = True # Submit button at the bottom of the filter
list_filter = (
("field_A", SingleNumericFilter), # Numeric single field search, __gte lookup
("field_B", RangeNumericFilter), # Numeric range search, __gte and __lte lookup
("field_C", SliderNumericFilter), # Numeric range filter but with slider
("field_D", CustomSliderNumericFilter), # Numeric filter with custom attributes
CustomRangeNumericListFilter, # Numeric range search not restricted to a model field
)
def get_queryset(self, request):
return super().get_queryset().annotate(items_count=Count("item", distinct=True))
```
--------------------------------
### Minimal URL Configuration for Django Unfold
Source: https://unfoldadmin.com/docs/installation
Set up your project's urls.py to include the Django admin interface, which Unfold will then style.
```python
# urls.py
from django.contrib import admin
from django.urls import path
urlpatterns = [
path("admin/", admin.site.urls),
# Other URL paths
]
```
--------------------------------
### Include Custom Styles and Scripts
Source: https://unfoldadmin.com/docs/configuration/settings
Add custom CSS and JavaScript files to the admin interface using lambda functions to reference static files.
```python
"STYLES": [
lambda request: static("css/style.css"),
],
"SCRIPTS": [
lambda request: static("js/script.js"),
],
```
--------------------------------
### Unfold @display Decorator Examples
Source: https://unfoldadmin.com/docs/decorators
Demonstrates various uses of the Unfold @display decorator for customizing field display in Django admin, including status indicators with default and custom colors, and two-line headings with optional badges.
```python
from django.db.models import TextChoices
from django.utils.translation import gettext_lazy as _
from unfold.admin import ModelAdmin
from unfold.decorators import display
class UserStatus(TextChoices):
ACTIVE = "ACTIVE", _("Active")
PENDING = "PENDING", _("Pending")
INACTIVE = "INACTIVE", _("Inactive")
CANCELLED = "CANCELLED", _("Cancelled")
class UserAdmin(ModelAdmin):
list_display = [
"display_as_two_line_heading",
"show_status",
"show_status_with_custom_label",
]
@display(
description=_("Status"),
ordering="status",
label=True
)
def show_status_default_color(self, obj):
return obj.status
@display(
description=_("Status"),
ordering="status",
label={
UserStatus.ACTIVE: "success", # green
UserStatus.PENDING: "info", # blue
UserStatus.INACTIVE: "warning", # orange
UserStatus.CANCELLED: "danger", # red
},
)
def show_status_customized_color(self, obj):
return obj.status
@display(description=_("Status with label"), ordering="status", label=True)
def show_status_with_custom_label(self, obj):
return obj.status, obj.get_status_display()
@display(header=True)
def display_as_two_line_heading(self, obj):
"""
Third argument is short text which will appear as prefix in circle
"""
return [
"First main heading",
"Smaller additional description", # Use None in case you don't need it
"AB", # Short text which will appear in front of
# Image instead of initials. Initials are ignored if image is available
{
"path": "some/path/picture.jpg",
"squared": True, # Picture is displayed in square format, if empty circle
"borderless": True, # Picture will be displayed without border
"width": 64, # Removes default width. Use together with height
"height": 48, # Removes default height. Use together with width
}
]
```
--------------------------------
### Create List-Based Dropdown with Unfold Admin
Source: https://unfoldadmin.com/docs/decorators
Implement a dropdown menu with clickable list items. Configure title, item links, and visual styles like striping and sizing. The dropdown auto-positions and closes on outside clicks.
```python
class UserAdmin(ModelAdmin):
list_display = [
"display_dropdown",
]
@display(description=_("Status"), dropdown=True)
def display_dropdown(self, obj):
return {
# Clickable title displayed in the column
"title": "Custom dropdown title",
# Striped design for the items
"striped": True, # Optional
# Dropdown height. Will display scrollbar for longer content
"height": 200, # Optional
# Dropdown width
"width": 240, # Optional
"items": [
{
"title": "First title",
"link": "#" # Optional
},
{
"title": "Second title",
"link": "#" # Optional
},
]
}
```
--------------------------------
### Advanced Component Nesting with Parameters
Source: https://unfoldadmin.com/docs/components/introduction
Illustrates advanced component usage, including passing variables like `items` and `class` to components, and iterating over data to render multiple components. Requires loading `i18n` and `unfold` tags.
```django
{% load i18n unfold %}
{% block content %}
{% component "unfold/components/container.html" %}
{% component "unfold/components/navigation.html" with items=navigation %}
{% endcomponent %}
{% component "unfold/components/navigation.html" with class="ml-auto" items=filters %}
{% endcomponent %}
{% for card in cards %}
{% trans "Last 7 days" as label %}
{% component "unfold/components/card.html" with class="lg:w-1/3" %}
{% component "unfold/components/text.html" %}
{{ card.title }}
{% endcomponent %}
{% component "unfold/components/title.html" %}
{{ card.metric }}
{% endcomponent %}
{% endcomponent %}
{% endfor %}
{% endcomponent %}
{% endblock %}
```
--------------------------------
### Set Site Symbol and Favicons
Source: https://unfoldadmin.com/docs/configuration/settings
Define a site symbol from an icon set and configure favicons with different sizes and types.
```python
"SITE_SYMBOL": "speed", # symbol from icon set
"SITE_FAVICONS": [
{
"rel": "icon",
"sizes": "32x32",
"type": "image/svg+xml",
"href": lambda request: static("favicon.svg"),
},
],
```
--------------------------------
### Configure Changelist Tabs in settings.py
Source: https://unfoldadmin.com/docs/tabs/changelist
Define tab navigation for changelist views by configuring the UNFOLD dictionary's TABS key. Specify which models display tabs and list the individual tab items with their titles, links, and permissions.
```python
# settings.py
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
UNFOLD = {
"TABS": [
{
# Which models are going to display tab navigation
"models": [
"app_label.model_name_in_lowercase",
],
# List of tab items
"items": [
{
"title": _("Your custom title"),
"link": reverse_lazy("admin:app_label_model_name_changelist"),
"permission": "sample_app.permission_callback",
},
{
"title": _("Another custom title"),
"link": reverse_lazy("admin:app_label_another_model_name_changelist"),
"permission": "sample_app.permission_callback",
},
],
},
],
}
# Permission callback for tab item
def permission_callback(request):
return request.user.has_perm("sample_app.change_model")
```