### 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 <title> 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 %} <div class="flex flex-col"> {% component "unfold/components/card.html" %} {% component "unfold/components/title.html" %} Card Title {% endcomponent %} {% endcomponent %} </div> ``` -------------------------------- ### 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" %} <div class="flex flex-col gap-5"> {% 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 %} </div> {% 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" %} <div class="flex flex-col gap-4"> {% component "unfold/components/navigation.html" with items=navigation %} {% endcomponent %} {% component "unfold/components/navigation.html" with class="ml-auto" items=filters %} {% endcomponent %} </div> <div class="grid grid-cols-3"> {% 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 %} </div> {% 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") ```