django-unfold 0.12.0__py3-none-any.whl → 0.13.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_unfold-0.12.0.dist-info → django_unfold-0.13.0.dist-info}/METADATA +209 -102
- {django_unfold-0.12.0.dist-info → django_unfold-0.13.0.dist-info}/RECORD +32 -13
- unfold/admin.py +26 -6
- unfold/contrib/guardian/__init__.py +0 -0
- unfold/contrib/guardian/apps.py +6 -0
- unfold/contrib/guardian/templates/admin/guardian/model/change_form.html +13 -0
- unfold/contrib/guardian/templates/admin/guardian/model/field.html +11 -0
- unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage.html +35 -0
- unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_group.html +55 -0
- unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.html +56 -0
- unfold/contrib/guardian/templates/unfold/guardian/group_form.html +72 -0
- unfold/contrib/guardian/templates/unfold/guardian/user_form.html +72 -0
- unfold/contrib/simple_history/__init__.py +0 -0
- unfold/contrib/simple_history/apps.py +6 -0
- unfold/contrib/simple_history/templates/simple_history/_object_history_list.html +80 -0
- unfold/contrib/simple_history/templates/simple_history/object_history.html +21 -0
- unfold/contrib/simple_history/templates/simple_history/object_history_form.html +55 -0
- unfold/contrib/simple_history/templates/simple_history/submit_line.html +25 -0
- unfold/settings.py +1 -0
- unfold/sites.py +4 -0
- unfold/static/unfold/css/styles.css +1 -1
- unfold/styles.css +134 -66
- unfold/templates/admin/edit_inline/tabular.html +1 -1
- unfold/templates/admin/includes/fieldset.html +3 -7
- unfold/templates/admin/widgets/radio.html +20 -0
- unfold/templates/admin/widgets/radio_option.html +12 -0
- unfold/templates/unfold/helpers/field_readonly.html +9 -0
- unfold/templates/unfold/helpers/submit.html +3 -0
- unfold/templatetags/unfold.py +14 -0
- unfold/widgets.py +19 -0
- /django_unfold-0.12.0.dist-info/LICENSE → /django_unfold-0.13.0.dist-info/LICENSE.md +0 -0
- {django_unfold-0.12.0.dist-info → django_unfold-0.13.0.dist-info}/WHEEL +0 -0
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            Metadata-Version: 2.1
         | 
| 2 2 | 
             
            Name: django-unfold
         | 
| 3 | 
            -
            Version: 0. | 
| 3 | 
            +
            Version: 0.13.0
         | 
| 4 4 | 
             
            Summary: Clean & minimal Django admin theme based on Tailwind CSS
         | 
| 5 5 | 
             
            Home-page: https://unfoldadmin.com
         | 
| 6 6 | 
             
            License: MIT
         | 
| @@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.8 | |
| 17 17 | 
             
            Classifier: Programming Language :: Python :: 3.9
         | 
| 18 18 | 
             
            Classifier: Programming Language :: Python :: 3.10
         | 
| 19 19 | 
             
            Classifier: Programming Language :: Python :: 3.11
         | 
| 20 | 
            +
            Classifier: Programming Language :: Python :: 3.12
         | 
| 20 21 | 
             
            Requires-Dist: django (>=3.2)
         | 
| 21 22 | 
             
            Requires-Dist: importlib-metadata (==6.7.0)
         | 
| 22 23 | 
             
            Project-URL: Repository, https://github.com/unfoldadmin/django-unfold
         | 
| @@ -24,7 +25,7 @@ Description-Content-Type: text/markdown | |
| 24 25 |  | 
| 25 26 | 
             
            
         | 
| 26 27 |  | 
| 27 | 
            -
            ## Unfold Django Admin Theme
         | 
| 28 | 
            +
            ## Unfold Django Admin Theme <!-- omit from toc -->
         | 
| 28 29 |  | 
| 29 30 | 
             
            [](https://github.com/unfoldadmin/django-unfold/actions?query=workflow%3Arelease)
         | 
| 30 31 | 
             
            [](https://pypi.org/project/django-unfold/)
         | 
| @@ -33,11 +34,11 @@ Description-Content-Type: text/markdown | |
| 33 34 |  | 
| 34 35 | 
             
            Unfold is theme for Django admin incorporating most common practises for building full-fledged admin areas. It is designed to work at the top of default administration provided by Django.
         | 
| 35 36 |  | 
| 36 | 
            -
            - demo site is available at [unfoldadmin.com](https://unfoldadmin.com)
         | 
| 37 | 
            -
            - repository with demo implementation at [github.com/unfoldadmin/formula](https://github.com/unfoldadmin/formula)
         | 
| 38 | 
            -
            - Django & Next.js boilerplate implementing Unfold at [github.com/unfoldadmin/turbo](https://github.com/unfoldadmin/turbo)
         | 
| 37 | 
            +
            - **Unfold:** demo site is available at [unfoldadmin.com](https://unfoldadmin.com)
         | 
| 38 | 
            +
            - **Formula:** repository with demo implementation at [github.com/unfoldadmin/formula](https://github.com/unfoldadmin/formula)
         | 
| 39 | 
            +
            - **Turbo:** Django & Next.js boilerplate implementing Unfold at [github.com/unfoldadmin/turbo](https://github.com/unfoldadmin/turbo)
         | 
| 39 40 |  | 
| 40 | 
            -
            ## Features
         | 
| 41 | 
            +
            ## Features <!-- omit from toc -->
         | 
| 41 42 |  | 
| 42 43 | 
             
            - **Visual**: provides new user interface based on Tailwind CSS framework
         | 
| 43 44 | 
             
            - **Sidebar:** simplifies definition of custom sidebar navigation with icons
         | 
| @@ -46,38 +47,41 @@ Unfold is theme for Django admin incorporating most common practises for buildin | |
| 46 47 | 
             
            - **Dependencies:** completely based only on `django.contrib.admin`
         | 
| 47 48 | 
             
            - **Actions:** multiple ways how to define actions within different parts of admin
         | 
| 48 49 | 
             
            - **WYSIWYG:** built-in support for WYSIWYG (Trix)
         | 
| 49 | 
            -
            - ** | 
| 50 | 
            -
            - **Datetime filters:** widgets for filtering datetime values
         | 
| 50 | 
            +
            - **Custom filters:** widgets for filtering number & datetime values
         | 
| 51 51 | 
             
            - **Dashboard:** helpers to bootstrap custom dashboard
         | 
| 52 52 | 
             
            - **Tabs:** define custom tab navigations for models
         | 
| 53 53 | 
             
            - **Colors:** possibility to override default color scheme
         | 
| 54 | 
            -
            - ** | 
| 54 | 
            +
            - **Third party packages:** default support for multiple popular applications
         | 
| 55 55 |  | 
| 56 | 
            -
            ## Table of  | 
| 56 | 
            +
            ## Table of contents <!-- omit from toc -->
         | 
| 57 57 |  | 
| 58 58 | 
             
            - [Installation](#installation)
         | 
| 59 59 | 
             
            - [Configuration](#configuration)
         | 
| 60 60 | 
             
              - [Available settings.py options](#available-settingspy-options)
         | 
| 61 61 | 
             
              - [Available unfold.admin.ModelAdmin options](#available-unfoldadminmodeladmin-options)
         | 
| 62 | 
            -
            - [Decorators](#decorators)
         | 
| 63 | 
            -
              - [@display](#display)
         | 
| 64 62 | 
             
            - [Actions](#actions)
         | 
| 65 63 | 
             
              - [Actions overview](#actions-overview)
         | 
| 66 64 | 
             
              - [Custom unfold @action decorator](#custom-unfold-action-decorator)
         | 
| 67 65 | 
             
              - [Action handler functions](#action-handler-functions)
         | 
| 68 | 
            -
                - [For submit row action](#for-submit-row-action)
         | 
| 69 | 
            -
                - [For global, row and detail action](#for-global-row-and-detail-action)
         | 
| 70 66 | 
             
              - [Action examples](#action-examples)
         | 
| 71 67 | 
             
            - [Filters](#filters)
         | 
| 68 | 
            +
              - [Numeric filters](#numeric-filters)
         | 
| 69 | 
            +
              - [Date/time filters](#datetime-filters)
         | 
| 70 | 
            +
            - [Display decorator](#display-decorator)
         | 
| 72 71 | 
             
            - [Third party packages](#third-party-packages)
         | 
| 72 | 
            +
              - [django-celery-beat](#django-celery-beat)
         | 
| 73 | 
            +
              - [django-guardian](#django-guardian)
         | 
| 73 74 | 
             
              - [django-import-export](#django-import-export)
         | 
| 75 | 
            +
              - [django-modeltranslation](#django-modeltranslation)
         | 
| 76 | 
            +
              - [django-money](#django-money)
         | 
| 77 | 
            +
              - [django-simple-history](#django-simple-history)
         | 
| 74 78 | 
             
            - [User Admin Form](#user-admin-form)
         | 
| 75 | 
            -
            - [Adding  | 
| 76 | 
            -
            - [Project  | 
| 77 | 
            -
            - [Custom  | 
| 78 | 
            -
            - [Unfold  | 
| 79 | 
            +
            - [Adding custom styles and scripts](#adding-custom-styles-and-scripts)
         | 
| 80 | 
            +
            - [Project level Tailwind stylesheet](#project-level-tailwind-stylesheet)
         | 
| 81 | 
            +
            - [Custom admin dashboard](#custom-admin-dashboard)
         | 
| 82 | 
            +
            - [Unfold development](#unfold-development)
         | 
| 79 83 | 
             
              - [Pre-commit](#pre-commit)
         | 
| 80 | 
            -
              - [Poetry  | 
| 84 | 
            +
              - [Poetry configuration](#poetry-configuration)
         | 
| 81 85 | 
             
              - [Compiling Tailwind](#compiling-tailwind)
         | 
| 82 86 | 
             
            - [Credits](#credits)
         | 
| 83 87 |  | 
| @@ -93,6 +97,8 @@ INSTALLED_APPS = [ | |
| 93 97 | 
             
                "unfold.contrib.filters",  # optional, if special filters are needed
         | 
| 94 98 | 
             
                "unfold.contrib.forms",  # optional, if special form elements are needed
         | 
| 95 99 | 
             
                "unfold.contrib.import_export",  # optional, if django-import-export package is used
         | 
| 100 | 
            +
                "unfold.contrib.guardian",  # optional, if django-guardian package is used
         | 
| 101 | 
            +
                "unfold.contrib.simple_history",  # optional, if django-simple-history package is used
         | 
| 96 102 | 
             
                "django.contrib.admin",  # required
         | 
| 97 103 | 
             
            ]
         | 
| 98 104 | 
             
            ```
         | 
| @@ -192,6 +198,7 @@ UNFOLD = { | |
| 192 198 | 
             
                        "700": "126 34 206",
         | 
| 193 199 | 
             
                        "800": "107 33 168",
         | 
| 194 200 | 
             
                        "900": "88 28 135",
         | 
| 201 | 
            +
                        "950": "59 7 100",
         | 
| 195 202 | 
             
                    },
         | 
| 196 203 | 
             
                },
         | 
| 197 204 | 
             
                "EXTENSIONS": {
         | 
| @@ -301,73 +308,6 @@ class CustomAdminClass(ModelAdmin): | |
| 301 308 | 
             
                }
         | 
| 302 309 | 
             
            ```
         | 
| 303 310 |  | 
| 304 | 
            -
            ## Decorators
         | 
| 305 | 
            -
             | 
| 306 | 
            -
            ### @display
         | 
| 307 | 
            -
             | 
| 308 | 
            -
            Unfold introduces it's own `unfold.decorators.display` decorator. By default it has exactly same behavior as native `django.contrib.admin.decorators.display` but it adds same customizations which helps to extends default logic.
         | 
| 309 | 
            -
             | 
| 310 | 
            -
            `@display(label=True)`, `@display(label={"value1": "success"})` displays a result as a label. This option fits for different types of statuses. Label can be either boolean indicating we want to use label with default color or dict where the dict is responsible for displaying labels in different colors. At the moment these color combinations are supported: success(green), info(blue), danger(red) and warning(orange).
         | 
| 311 | 
            -
             | 
| 312 | 
            -
            `@display(header=True)` displays in results list two information in one table cell. Good example is when we want to display customer information, first line is going to be customer's name and right below the name display corresponding email address. Method with such a decorator is supposed to return a list with two elements `return "Full name", "E-mail address"`.
         | 
| 313 | 
            -
             | 
| 314 | 
            -
            ```python
         | 
| 315 | 
            -
            # admin.py
         | 
| 316 | 
            -
             | 
| 317 | 
            -
            from django.db.models import TextChoices
         | 
| 318 | 
            -
            from django.utils.translation import gettext_lazy as _
         | 
| 319 | 
            -
             | 
| 320 | 
            -
            from unfold.admin import ModelAdmin
         | 
| 321 | 
            -
            from unfold.decorators import display
         | 
| 322 | 
            -
             | 
| 323 | 
            -
             | 
| 324 | 
            -
            class UserStatus(TextChoices):
         | 
| 325 | 
            -
                ACTIVE = "ACTIVE", _("Active")
         | 
| 326 | 
            -
                PENDING = "PENDING", _("Pending")
         | 
| 327 | 
            -
                INACTIVE = "INACTIVE", _("Inactive")
         | 
| 328 | 
            -
                CANCELLED = "CANCELLED", _("Cancelled")
         | 
| 329 | 
            -
             | 
| 330 | 
            -
             | 
| 331 | 
            -
            class UserAdmin(ModelAdmin):
         | 
| 332 | 
            -
                list_display = [
         | 
| 333 | 
            -
                    "display_as_two_line_heading",
         | 
| 334 | 
            -
                    "show_status",
         | 
| 335 | 
            -
                    "show_status_with_custom_label",
         | 
| 336 | 
            -
                ]
         | 
| 337 | 
            -
             | 
| 338 | 
            -
                @display(
         | 
| 339 | 
            -
                    description=_("Status"),
         | 
| 340 | 
            -
                    ordering="status",
         | 
| 341 | 
            -
                    label=True
         | 
| 342 | 
            -
                )
         | 
| 343 | 
            -
                def show_status_default_color(self, obj):
         | 
| 344 | 
            -
                    return obj.status
         | 
| 345 | 
            -
             | 
| 346 | 
            -
                @display(
         | 
| 347 | 
            -
                    description=_("Status"),
         | 
| 348 | 
            -
                    ordering="status",
         | 
| 349 | 
            -
                    label={
         | 
| 350 | 
            -
                        UserStatus.ACTIVE: "success",  # green
         | 
| 351 | 
            -
                        UserStatus.PENDING: "info",  # blue
         | 
| 352 | 
            -
                        UserStatus.INACTIVE: "warning",  # orange
         | 
| 353 | 
            -
                        UserStatus.CANCELLED: "danger",  # red
         | 
| 354 | 
            -
                    },
         | 
| 355 | 
            -
                )
         | 
| 356 | 
            -
                def show_status_customized_color(self, obj)
         | 
| 357 | 
            -
                    return obj.status
         | 
| 358 | 
            -
             | 
| 359 | 
            -
                @display(description=_("Status with label"), ordering="status", label=True)
         | 
| 360 | 
            -
                def show_status_with_custom_label(self, obj):
         | 
| 361 | 
            -
                    return obj.status, obj.get_status_display()
         | 
| 362 | 
            -
             | 
| 363 | 
            -
                @display(header=True)
         | 
| 364 | 
            -
                def display_as_two_line_heading(self, obj):
         | 
| 365 | 
            -
                    """
         | 
| 366 | 
            -
                    Third argument is short text which will appear as prefix in circle
         | 
| 367 | 
            -
                    """
         | 
| 368 | 
            -
                    return "First main heading", "Smaller additional description", "AB"
         | 
| 369 | 
            -
            ```
         | 
| 370 | 
            -
             | 
| 371 311 | 
             
            ## Actions
         | 
| 372 312 |  | 
| 373 313 | 
             
            It is highly recommended to read the base [Django actions documentation](https://docs.djangoproject.com/en/4.2/ref/contrib/admin/actions/) before reading this section, since Unfold actions are derived from Django actions.
         | 
| @@ -398,13 +338,13 @@ Unfold also uses custom `@action` decorator, supporting 2 more parameters in com | |
| 398 338 | 
             
            This section provides explanation of how the action handler functions should be constructed for Unfold actions.
         | 
| 399 339 | 
             
            For default actions, follow official Django admin documentation.
         | 
| 400 340 |  | 
| 401 | 
            -
            #### For submit row action
         | 
| 341 | 
            +
            #### For submit row action <!-- omit from toc -->
         | 
| 402 342 |  | 
| 403 343 | 
             
            Submit row actions work a bit differently when compared to other custom Unfold actions.
         | 
| 404 344 | 
             
            These actions first invoke form save (same as if you hit `Save` button) and then lets you
         | 
| 405 345 | 
             
            perform additional logic on already saved instance.
         | 
| 406 346 |  | 
| 407 | 
            -
            #### For global, row and detail action
         | 
| 347 | 
            +
            #### For global, row and detail action <!-- omit from toc -->
         | 
| 408 348 |  | 
| 409 349 | 
             
            All these actions are based on custom URLs generated for each of them. Handler function for these views is
         | 
| 410 350 | 
             
            basically function based view.
         | 
| @@ -502,6 +442,10 @@ class UserAdmin(ModelAdmin): | |
| 502 442 |  | 
| 503 443 | 
             
            By default, Django admin handles all filters as regular HTML links pointing at the same URL with different query parameters. This approach is for basic filtering more than enough. In the case of more advanced filtering by incorporating input fields, it is not going to work.
         | 
| 504 444 |  | 
| 445 | 
            +
            **Note:** when implementing a filter which contains input fields, there is a no way that user can submit the values, because default filters does not contain submit button. To implement submit button, `unfold.admin.ModelAdmin` contains boolean `list_filter_submit` flag which enables submit button in filter form.
         | 
| 446 | 
            +
             | 
| 447 | 
            +
            ### Numeric filters
         | 
| 448 | 
            +
             | 
| 505 449 | 
             
            Currently, Unfold implements numeric filters inside `unfold.contrib.filters` application. In order to use these filters, it is required to add this application into `INSTALLED_APPS` in `settings.py` right after `unfold` application.
         | 
| 506 450 |  | 
| 507 451 | 
             
            ```python
         | 
| @@ -516,8 +460,6 @@ from unfold.contrib.filters.admin import ( | |
| 516 460 | 
             
                RangeNumericFilter,
         | 
| 517 461 | 
             
                SingleNumericFilter,
         | 
| 518 462 | 
             
                SliderNumericFilter,
         | 
| 519 | 
            -
                RangeDateFilter,
         | 
| 520 | 
            -
                RangeDateTimeFilter,
         | 
| 521 463 | 
             
            )
         | 
| 522 464 |  | 
| 523 465 |  | 
| @@ -539,8 +481,6 @@ class YourModelAdmin(ModelAdmin): | |
| 539 481 | 
             
                    ("field_B", RangeNumericFilter),  # Numeric range search, __gte and __lte lookup
         | 
| 540 482 | 
             
                    ("field_C", SliderNumericFilter),  # Numeric range filter but with slider
         | 
| 541 483 | 
             
                    ("field_D", CustomSliderNumericFilter),  # Numeric filter with custom attributes
         | 
| 542 | 
            -
                    ("field_E", RangeDateFilter),  # Date filter
         | 
| 543 | 
            -
                    ("field_F", RangeDateTimeFilter),  # Datetime filter
         | 
| 544 484 | 
             
                    CustomRangeNumericListFilter,  # Numeric range search not restricted to a model field
         | 
| 545 485 | 
             
                )
         | 
| 546 486 |  | 
| @@ -548,13 +488,150 @@ class YourModelAdmin(ModelAdmin): | |
| 548 488 | 
             
                    return super().get_queryset().annotate(items_count=Count("item", distinct=True))
         | 
| 549 489 | 
             
            ```
         | 
| 550 490 |  | 
| 491 | 
            +
            ### Date/time filters
         | 
| 492 | 
            +
             | 
| 493 | 
            +
            ```python
         | 
| 494 | 
            +
            # admin.py
         | 
| 495 | 
            +
             | 
| 496 | 
            +
            from django.contrib import admin
         | 
| 497 | 
            +
            from django.contrib.auth.models import User
         | 
| 498 | 
            +
             | 
| 499 | 
            +
            from unfold.admin import ModelAdmin
         | 
| 500 | 
            +
            from unfold.contrib.filters.admin import (
         | 
| 501 | 
            +
                RangeDateFilter,
         | 
| 502 | 
            +
                RangeDateTimeFilter,
         | 
| 503 | 
            +
            )
         | 
| 504 | 
            +
             | 
| 505 | 
            +
             | 
| 506 | 
            +
            @admin.register(User)
         | 
| 507 | 
            +
            class YourModelAdmin(ModelAdmin):
         | 
| 508 | 
            +
                list_filter_submit = True  # Submit button at the bottom of the filter
         | 
| 509 | 
            +
                list_filter = (
         | 
| 510 | 
            +
                    ("field_E", RangeDateFilter),  # Date filter
         | 
| 511 | 
            +
                    ("field_F", RangeDateTimeFilter),  # Datetime filter
         | 
| 512 | 
            +
                )
         | 
| 513 | 
            +
            ```
         | 
| 514 | 
            +
             | 
| 515 | 
            +
            ## Display decorator
         | 
| 516 | 
            +
             | 
| 517 | 
            +
            Unfold introduces it's own `unfold.decorators.display` decorator. By default it has exactly same behavior as native `django.contrib.admin.decorators.display` but it adds same customizations which helps to extends default logic.
         | 
| 518 | 
            +
             | 
| 519 | 
            +
            `@display(label=True)`, `@display(label={"value1": "success"})` displays a result as a label. This option fits for different types of statuses. Label can be either boolean indicating we want to use label with default color or dict where the dict is responsible for displaying labels in different colors. At the moment these color combinations are supported: success(green), info(blue), danger(red) and warning(orange).
         | 
| 520 | 
            +
             | 
| 521 | 
            +
            `@display(header=True)` displays in results list two information in one table cell. Good example is when we want to display customer information, first line is going to be customer's name and right below the name display corresponding email address. Method with such a decorator is supposed to return a list with two elements `return "Full name", "E-mail address"`. There is a third optional argument, which is type of the string and its value is displayed in a circle before first two values on the front end. Its optimal usage is for displaying initials.
         | 
| 522 | 
            +
             | 
| 523 | 
            +
            ```python
         | 
| 524 | 
            +
            # admin.py
         | 
| 525 | 
            +
             | 
| 526 | 
            +
            from django.db.models import TextChoices
         | 
| 527 | 
            +
            from django.utils.translation import gettext_lazy as _
         | 
| 528 | 
            +
             | 
| 529 | 
            +
            from unfold.admin import ModelAdmin
         | 
| 530 | 
            +
            from unfold.decorators import display
         | 
| 531 | 
            +
             | 
| 532 | 
            +
             | 
| 533 | 
            +
            class UserStatus(TextChoices):
         | 
| 534 | 
            +
                ACTIVE = "ACTIVE", _("Active")
         | 
| 535 | 
            +
                PENDING = "PENDING", _("Pending")
         | 
| 536 | 
            +
                INACTIVE = "INACTIVE", _("Inactive")
         | 
| 537 | 
            +
                CANCELLED = "CANCELLED", _("Cancelled")
         | 
| 538 | 
            +
             | 
| 539 | 
            +
             | 
| 540 | 
            +
            class UserAdmin(ModelAdmin):
         | 
| 541 | 
            +
                list_display = [
         | 
| 542 | 
            +
                    "display_as_two_line_heading",
         | 
| 543 | 
            +
                    "show_status",
         | 
| 544 | 
            +
                    "show_status_with_custom_label",
         | 
| 545 | 
            +
                ]
         | 
| 546 | 
            +
             | 
| 547 | 
            +
                @display(
         | 
| 548 | 
            +
                    description=_("Status"),
         | 
| 549 | 
            +
                    ordering="status",
         | 
| 550 | 
            +
                    label=True
         | 
| 551 | 
            +
                )
         | 
| 552 | 
            +
                def show_status_default_color(self, obj):
         | 
| 553 | 
            +
                    return obj.status
         | 
| 554 | 
            +
             | 
| 555 | 
            +
                @display(
         | 
| 556 | 
            +
                    description=_("Status"),
         | 
| 557 | 
            +
                    ordering="status",
         | 
| 558 | 
            +
                    label={
         | 
| 559 | 
            +
                        UserStatus.ACTIVE: "success",  # green
         | 
| 560 | 
            +
                        UserStatus.PENDING: "info",  # blue
         | 
| 561 | 
            +
                        UserStatus.INACTIVE: "warning",  # orange
         | 
| 562 | 
            +
                        UserStatus.CANCELLED: "danger",  # red
         | 
| 563 | 
            +
                    },
         | 
| 564 | 
            +
                )
         | 
| 565 | 
            +
                def show_status_customized_color(self, obj)
         | 
| 566 | 
            +
                    return obj.status
         | 
| 567 | 
            +
             | 
| 568 | 
            +
                @display(description=_("Status with label"), ordering="status", label=True)
         | 
| 569 | 
            +
                def show_status_with_custom_label(self, obj):
         | 
| 570 | 
            +
                    return obj.status, obj.get_status_display()
         | 
| 571 | 
            +
             | 
| 572 | 
            +
                @display(header=True)
         | 
| 573 | 
            +
                def display_as_two_line_heading(self, obj):
         | 
| 574 | 
            +
                    """
         | 
| 575 | 
            +
                    Third argument is short text which will appear as prefix in circle
         | 
| 576 | 
            +
                    """
         | 
| 577 | 
            +
                    return "First main heading", "Smaller additional description", "AB"
         | 
| 578 | 
            +
            ```
         | 
| 579 | 
            +
             | 
| 551 580 | 
             
            ## Third party packages
         | 
| 552 581 |  | 
| 553 | 
            -
            ### django- | 
| 582 | 
            +
            ### django-celery-beat
         | 
| 583 | 
            +
             | 
| 584 | 
            +
            In general, django-celery-beat does not have any components that require special styling. The default changelist templates are not inheriting from Unfold's `ModelAdmin` but they are using default `ModelAdmin` coming from `django.contrib.admin` which is causing some design discrepancies in the changelist.
         | 
| 585 | 
            +
             | 
| 586 | 
            +
            In the source code below you can find a short code snippet to unregister all `django-celery-beat` admin classes and register them with the proper parent `ModelAdmin` class.
         | 
| 587 | 
            +
             | 
| 588 | 
            +
            ```python
         | 
| 589 | 
            +
            # admin.py
         | 
| 590 | 
            +
            from django.contrib import admin
         | 
| 591 | 
            +
            from unfold.admin import ModelAdmin
         | 
| 592 | 
            +
             | 
| 593 | 
            +
            from django_celery_beat.models import (
         | 
| 594 | 
            +
                ClockedSchedule,
         | 
| 595 | 
            +
                CrontabSchedule,
         | 
| 596 | 
            +
                IntervalSchedule,
         | 
| 597 | 
            +
                PeriodicTask,
         | 
| 598 | 
            +
                SolarSchedule,
         | 
| 599 | 
            +
            )
         | 
| 600 | 
            +
             | 
| 601 | 
            +
             | 
| 602 | 
            +
            admin.site.unregister(PeriodicTask)
         | 
| 603 | 
            +
            admin.site.unregister(IntervalSchedule)
         | 
| 604 | 
            +
            admin.site.unregister(CrontabSchedule)
         | 
| 605 | 
            +
            admin.site.unregister(SolarSchedule)
         | 
| 606 | 
            +
            admin.site.unregister(ClockedSchedule)
         | 
| 607 | 
            +
             | 
| 608 | 
            +
            @admin.register(PeriodicTask)
         | 
| 609 | 
            +
            class PeriodicTaskAdmin(ModelAdmin):
         | 
| 610 | 
            +
                pass
         | 
| 611 | 
            +
             | 
| 612 | 
            +
             | 
| 613 | 
            +
            @admin.register(IntervalSchedule)
         | 
| 614 | 
            +
            class IntervalScheduleAdmin(ModelAdmin):
         | 
| 615 | 
            +
                pass
         | 
| 616 | 
            +
             | 
| 617 | 
            +
             | 
| 618 | 
            +
            @admin.register(CrontabSchedule)
         | 
| 619 | 
            +
            class CrontabScheduleAdmin(ModelAdmin):
         | 
| 620 | 
            +
                pass
         | 
| 621 | 
            +
             | 
| 622 | 
            +
             | 
| 623 | 
            +
            @admin.register(SolarSchedule)
         | 
| 624 | 
            +
            class SolarScheduleAdmin(ModelAdmin):
         | 
| 625 | 
            +
                pass
         | 
| 626 | 
            +
            ```
         | 
| 627 | 
            +
             | 
| 628 | 
            +
            ### django-guardian
         | 
| 554 629 |  | 
| 555 | 
            -
             | 
| 630 | 
            +
            Adding support for django-guardian is quote straightforward in Unfold, just add `unfold.contrib.guardian` to `INSTALLED_APPS` at the beggining of the file. This action will override all templates coming from the django-guardian. Please note that **Object permissions** link is available in top right dropdown navigation.
         | 
| 556 631 |  | 
| 557 | 
            -
             | 
| 632 | 
            +
            ### django-import-export
         | 
| 633 | 
            +
             | 
| 634 | 
            +
            1. Add `unfold.contrib.import_export` to `INSTALLED_APPS` at the beggining of the file. This action will override all templates coming from the application.
         | 
| 558 635 | 
             
            2. Change `import_form_class` and `export_form_class` in ModelAdmin which is inheriting from `ImportExportModelAdmin`. This chunk of code is responsible for adding proper styling to form elements.
         | 
| 559 636 |  | 
| 560 637 | 
             
            ```python
         | 
| @@ -568,6 +645,36 @@ class ExampleAdmin(ModelAdmin, ImportExportModelAdmin): | |
| 568 645 | 
             
                export_form_class = ExportForm
         | 
| 569 646 | 
             
            ```
         | 
| 570 647 |  | 
| 648 | 
            +
            ### django-modeltranslation
         | 
| 649 | 
            +
             | 
| 650 | 
            +
            By default Unfold does not contain any specific implementation for django-modeltranslation and the application is partially supported. Basic behavior is supported except of tab navigation provided by django-modeltranslation. At the moment there are no plans in supporting this behavior.
         | 
| 651 | 
            +
             | 
| 652 | 
            +
            For django-modeltranslation fields for spefic languages, it is possible to define custom flags which will appear as a suffix in field's label. It is recommended to use emojis as suffix.
         | 
| 653 | 
            +
             | 
| 654 | 
            +
            ```python
         | 
| 655 | 
            +
            # settings.py
         | 
| 656 | 
            +
             | 
| 657 | 
            +
            UNFOLD = {
         | 
| 658 | 
            +
                "EXTENSIONS": {
         | 
| 659 | 
            +
                    "modeltranslation": {
         | 
| 660 | 
            +
                        "flags": {
         | 
| 661 | 
            +
                            "en": "🇬🇧",
         | 
| 662 | 
            +
                            "fr": "🇫🇷",
         | 
| 663 | 
            +
                            "nl": "🇧🇪",
         | 
| 664 | 
            +
                        },
         | 
| 665 | 
            +
                    },
         | 
| 666 | 
            +
                },
         | 
| 667 | 
            +
            }
         | 
| 668 | 
            +
            ```
         | 
| 669 | 
            +
             | 
| 670 | 
            +
            ### django-money
         | 
| 671 | 
            +
             | 
| 672 | 
            +
            This application is supported in Unfold by default. It is not needed to add any other applications into `INSTALLED_APPS`. Unfold is recognizing special form widget coming from django-money and applying specific styling.
         | 
| 673 | 
            +
             | 
| 674 | 
            +
            ### django-simple-history
         | 
| 675 | 
            +
             | 
| 676 | 
            +
            To make this application work, add `unfold.contrib.simple_history` into `settings.py` in `INSTALLED_APPS` variable before right after `unfold`. This app should ensure that templates coming from django-simple-history are overriden by Unfold.
         | 
| 677 | 
            +
             | 
| 571 678 | 
             
            ## User Admin Form
         | 
| 572 679 |  | 
| 573 680 | 
             
            User's admin in Django is specific as it contains several forms which are requiring custom styling. All of these forms has been inherited and accordingly adjusted. In user admin class it is needed to use these inherited form classes to enable custom styling matching rest of the website.
         | 
| @@ -590,7 +697,7 @@ class UserAdmin(BaseUserAdmin, ModelAdmin): | |
| 590 697 | 
             
                change_password_form = AdminPasswordChangeForm
         | 
| 591 698 | 
             
            ```
         | 
| 592 699 |  | 
| 593 | 
            -
            ## Adding  | 
| 700 | 
            +
            ## Adding custom styles and scripts
         | 
| 594 701 |  | 
| 595 702 | 
             
            To add new custom styles, for example for custom dashboard, it is possible to load them via **STYLES** key in **UNFOLD** dict. This key accepts a list of strings or lambda functions which will be loaded on all pages. JavaScript files can be loaded by using similar apprach, but **SCRIPTS** is used.
         | 
| 596 703 |  | 
| @@ -609,7 +716,7 @@ UNFOLD = { | |
| 609 716 | 
             
            }
         | 
| 610 717 | 
             
            ```
         | 
| 611 718 |  | 
| 612 | 
            -
            ## Project  | 
| 719 | 
            +
            ## Project level Tailwind stylesheet
         | 
| 613 720 |  | 
| 614 721 | 
             
            When creating custom dashboard or adding custom components, it is needed to add own styles. Adding custom styles is described above. Most of the time, it is supposed that new elements are going to match with the rest of the administration panel. First of all, create tailwind.config.js in your application. Below is located minimal configuration for this file.
         | 
| 615 722 |  | 
| @@ -642,7 +749,7 @@ Once the configuration file is set, it is possible to compile new styles which c | |
| 642 749 | 
             
            npx tailwindcss -o your_project/static/css/styles.css --watch --minify
         | 
| 643 750 | 
             
            ```
         | 
| 644 751 |  | 
| 645 | 
            -
            ## Custom  | 
| 752 | 
            +
            ## Custom admin dashboard
         | 
| 646 753 |  | 
| 647 754 | 
             
            The most common thing which needs to be adjusted for each project in admin is the dashboard. By default Unfold does not provide any dashboard components. The default dashboard experience with list of all applications and models is kept with proper styling matching rest of the components but thats it. Anyway, Unfold was created that creation of custom dashboard will be streamlined.
         | 
| 648 755 |  | 
| @@ -668,7 +775,7 @@ Create `templates/admin/index.html` in your project and paste the base template | |
| 668 775 |  | 
| 669 776 | 
             
            Note: In case that it is needed to pass custom variables into dashboard tamplate, check **DASHOARD_CALLBACK** in **UNFOLD** dict.
         | 
| 670 777 |  | 
| 671 | 
            -
            ## Unfold  | 
| 778 | 
            +
            ## Unfold development
         | 
| 672 779 |  | 
| 673 780 | 
             
            ### Pre-commit
         | 
| 674 781 |  | 
| @@ -680,7 +787,7 @@ pre-commit install | |
| 680 787 | 
             
            pre-commit install --hook-type commit-msg
         | 
| 681 788 | 
             
            ```
         | 
| 682 789 |  | 
| 683 | 
            -
            ### Poetry  | 
| 790 | 
            +
            ### Poetry configuration
         | 
| 684 791 |  | 
| 685 792 | 
             
            To add a new feature or fix the easiest approach is to use django-unfold in combination with Poetry. The process looks like:
         | 
| 686 793 |  | 
| @@ -703,9 +810,9 @@ npm run tailwind:build # run once | |
| 703 810 |  | 
| 704 811 | 
             
            Some components like datepickers, calendars or selectors in admin was not possible to style by overriding html templates so their default styles are overriden in **styles.css**.
         | 
| 705 812 |  | 
| 706 | 
            -
             | 
| 813 | 
            +
            **Note:** most of the custom styles located in style.css are created via `@apply some-tailwind-class;` as is not possible to manually add CSS class to element which are for example created via jQuery.
         | 
| 707 814 |  | 
| 708 | 
            -
             | 
| 815 | 
            +
            ## Credits
         | 
| 709 816 |  | 
| 710 817 | 
             
            - [TailwindCSS](https://tailwindcss.com/) - CSS framework
         | 
| 711 818 | 
             
            - [HTMX](https://htmx.org/) - AJAX communication with backend
         | 
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            unfold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 2 | 
            -
            unfold/admin.py,sha256= | 
| 2 | 
            +
            unfold/admin.py,sha256=vRFfTtKTaGCu-lOqPMc62EK1aVirjapIP2BKRrZUvXw,23085
         | 
| 3 3 | 
             
            unfold/apps.py,sha256=LXJVMj1WIoQXjRmiz2bFtsVc9gKhdby7UQczdtjieY0,307
         | 
| 4 4 | 
             
            unfold/checks.py,sha256=hCOJKAaumb-Rzj3IYyMpLi5oBTThciYfjGLMPVB3jak,1835
         | 
| 5 5 | 
             
            unfold/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| @@ -25,6 +25,15 @@ unfold/contrib/forms/static/unfold/forms/js/trix.js,sha256=0tRM-QGKIGPA83Z5zCyCo | |
| 25 25 | 
             
            unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html,sha256=yS8Zy-UrzvZ5RUYwdprQzREffnYq0NlIbXBfZM2UB04,9700
         | 
| 26 26 | 
             
            unfold/contrib/forms/templates/unfold/forms/wysiwyg.html,sha256=4ZefV6XrjJlUczcuSw8BhvMJUFSZPSXo1IkgkBivh5g,351
         | 
| 27 27 | 
             
            unfold/contrib/forms/widgets.py,sha256=_81_fsvK-yEsFIqLU59BTIIs2KAJk61pLs7J9sNi1G0,962
         | 
| 28 | 
            +
            unfold/contrib/guardian/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 29 | 
            +
            unfold/contrib/guardian/apps.py,sha256=m8z-GJ6NmPv8wqQ40qY_L-FhooshSytzkMLf_fktm2U,135
         | 
| 30 | 
            +
            unfold/contrib/guardian/templates/admin/guardian/model/change_form.html,sha256=hbobVP04sufV8cECaFjC04DSwCK3K2y_BL_8WDBhYD0,483
         | 
| 31 | 
            +
            unfold/contrib/guardian/templates/admin/guardian/model/field.html,sha256=V9ZgmYiIQAFy3GC464y0iBOHm3SDvEEymbuhT3S0qKU,296
         | 
| 32 | 
            +
            unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage.html,sha256=p7GmZOyDqj1urDAh9pyZhatnSpe-G6gXtQ_DpRK-J8k,1426
         | 
| 33 | 
            +
            unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_group.html,sha256=TOXgGhNqujwl4XUYMXOMfPmTPszNkY4Sb3-7oRAy158,2318
         | 
| 34 | 
            +
            unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.html,sha256=drAeEnr6lh6SgDPC-bkaGtLkd_DxOtcHBSbgb_zaV00,2316
         | 
| 35 | 
            +
            unfold/contrib/guardian/templates/unfold/guardian/group_form.html,sha256=P8WMC5EejUHV5AxEiIQ2LOGzefLHk5J5UHiNq9wnBgY,4145
         | 
| 36 | 
            +
            unfold/contrib/guardian/templates/unfold/guardian/user_form.html,sha256=ci7FRrhTEKbFKKxsJ-07_dWXBYz4mqXPoqu5HfqYLaM,4132
         | 
| 28 37 | 
             
            unfold/contrib/import_export/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 29 38 | 
             
            unfold/contrib/import_export/apps.py,sha256=TfdmxCH5BxFo0kk-kV6f6xZbUxL3_FXUVlBYp74Eb9o,142
         | 
| 30 39 | 
             
            unfold/contrib/import_export/forms.py,sha256=dLqLv7YP0i8_CrxupEfh3VncJ8CJHf7O1eQoWFOcydU,672
         | 
| @@ -39,20 +48,26 @@ unfold/contrib/import_export/templates/admin/import_export/import_errors.html,sh | |
| 39 48 | 
             
            unfold/contrib/import_export/templates/admin/import_export/import_form.html,sha256=wyV-j0EDIxLzFaJaS-OU1YuTYn6536bg5ohILsDcXoc,1312
         | 
| 40 49 | 
             
            unfold/contrib/import_export/templates/admin/import_export/import_preview.html,sha256=pNuLDW6zc5yOF1jurL2EgR0j05RL9ZVJLZiV4R21GJc,2413
         | 
| 41 50 | 
             
            unfold/contrib/import_export/templates/admin/import_export/import_validation.html,sha256=1wQOiXN_Ga9VO6GGyl__KEiuJlCh4gTqzZdzIbmKxG0,4880
         | 
| 51 | 
            +
            unfold/contrib/simple_history/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 52 | 
            +
            unfold/contrib/simple_history/apps.py,sha256=eF_KVYb60CAnGgWk2Z1YKYGfgA3TJBMr229qI7e2pgU,153
         | 
| 53 | 
            +
            unfold/contrib/simple_history/templates/simple_history/_object_history_list.html,sha256=aXOQ1zwsRBlFmzODsZApvMtb8t1IPXim6i4plXUR5XE,5112
         | 
| 54 | 
            +
            unfold/contrib/simple_history/templates/simple_history/object_history.html,sha256=AZ6uQRr7wKxV_rys5hGTVGYtVS-Fp5eHIqiXYW8FB1c,847
         | 
| 55 | 
            +
            unfold/contrib/simple_history/templates/simple_history/object_history_form.html,sha256=ii_1lM4wC-GOJhyKkmue4ymxek0gsvRSUK4XecRDezM,2289
         | 
| 56 | 
            +
            unfold/contrib/simple_history/templates/simple_history/submit_line.html,sha256=Qx3aZcVf93AEUB58QP2GLDfWB1TUQ0GUb_SaJ9C7s6E,1637
         | 
| 42 57 | 
             
            unfold/dataclasses.py,sha256=JJdGYzQ8MpeOe2FQPJqrMn_UJcxUz1VJgHCuCtkZCA8,199
         | 
| 43 58 | 
             
            unfold/decorators.py,sha256=BVDlxhZxB4ND3f5-5oiENRTv_W_Q_Eu-gZlsrYKOxiU,3272
         | 
| 44 59 | 
             
            unfold/exceptions.py,sha256=gcCj1ox61E137bk_0Cqy4YC3SttdPgB-fiJUqpmyHSE,43
         | 
| 45 60 | 
             
            unfold/forms.py,sha256=pLdtUttPmPxanLXKRqAgTLxLSifKJy43JfGcEyL4m2Y,3303
         | 
| 46 | 
            -
            unfold/settings.py,sha256= | 
| 47 | 
            -
            unfold/sites.py,sha256= | 
| 61 | 
            +
            unfold/settings.py,sha256=NdKo2mxxh7hoD6YFkAdUlmG2Pk4xcKG9jdtqj3U73NI,1149
         | 
| 62 | 
            +
            unfold/sites.py,sha256=VyrgHO_iwGmH4jGr7iTVEeohNhXWvdRAo7uVGtoqqNM,10349
         | 
| 48 63 | 
             
            unfold/static/unfold/css/simplebar.css,sha256=yedjIaMP55VAxaX-dEENPKgT4S4TBlsut5zk6WlEPRE,3890
         | 
| 49 | 
            -
            unfold/static/unfold/css/styles.css,sha256= | 
| 64 | 
            +
            unfold/static/unfold/css/styles.css,sha256=4W8XZiqf8LwTut92GWi8KnvrcrsLkrcqodVjTeCfQQ0,75926
         | 
| 50 65 | 
             
            unfold/static/unfold/js/alpine.js,sha256=XQltNouEUe1VZDk-nixNjSZDFH9lyKSBiK7BjpTB-0s,41071
         | 
| 51 66 | 
             
            unfold/static/unfold/js/alpine.persist.js,sha256=84PZYnPi25AFm7wIWRe1gzA74c5Rv2VYASYEfaqKLHI,626
         | 
| 52 67 | 
             
            unfold/static/unfold/js/app.js,sha256=utNVPAkw4ApD27c_wGNcjv_HPIa7hI_gKiK-Z92YVpA,2519
         | 
| 53 68 | 
             
            unfold/static/unfold/js/htmx.js,sha256=XOLqvnZiyEx46EW9vaJTBUaaWg8CGVVfXJkVsUmJbpI,42820
         | 
| 54 69 | 
             
            unfold/static/unfold/js/simplebar.js,sha256=eUk3RAE1plWQQSNaxZzi66kbAj3YdIkgg-Bj1iRO0Y4,65855
         | 
| 55 | 
            -
            unfold/styles.css,sha256= | 
| 70 | 
            +
            unfold/styles.css,sha256=tXCBwnSJ3ZAZk390tD6KZdR_ebsLwp_t1XnatQcL4kI,18273
         | 
| 56 71 | 
             
            unfold/templates/admin/actions.html,sha256=W8mGF_9VjqMsO1RSRdMo5DJvVmhSwEj5xNjamDWbfis,2519
         | 
| 57 72 | 
             
            unfold/templates/admin/app_index.html,sha256=lVjMIFsspHQ09LGHKfdfg7TlqlL39AX5LbwoeoZjFhk,1335
         | 
| 58 73 | 
             
            unfold/templates/admin/app_list.html,sha256=lNdqEIYIj3WuuF8ECN7UpRp6E3Qt-1HXt2lrwXrmdv4,3594
         | 
| @@ -69,9 +84,9 @@ unfold/templates/admin/date_hierarchy.html,sha256=BfUPbsLpHZVa40BHBahz1H9RSVuz36 | |
| 69 84 | 
             
            unfold/templates/admin/delete_confirmation.html,sha256=hpa2E14oZEXBBs6W1qdNQuF650TIO2Rhr52Q6UfwVeQ,5166
         | 
| 70 85 | 
             
            unfold/templates/admin/delete_selected_confirmation.html,sha256=Foka2yvwAMEZre-Kh1KNadRzrCotdKM2U4e6AJQYZu8,4941
         | 
| 71 86 | 
             
            unfold/templates/admin/edit_inline/stacked.html,sha256=YRgW-bMVIolWeOawpOnWHofdkwLUQIK12AtlEzIczSk,4372
         | 
| 72 | 
            -
            unfold/templates/admin/edit_inline/tabular.html,sha256= | 
| 87 | 
            +
            unfold/templates/admin/edit_inline/tabular.html,sha256=RAioUpBLzoKeZo4oPZ8FiRLu4y3yiLqcC_Hxki4DHKE,12218
         | 
| 73 88 | 
             
            unfold/templates/admin/filter.html,sha256=UnDlHT2_26DXRKYw3wVgq-i8_FMVov66M-YiJabnyEg,1479
         | 
| 74 | 
            -
            unfold/templates/admin/includes/fieldset.html,sha256= | 
| 89 | 
            +
            unfold/templates/admin/includes/fieldset.html,sha256=VNt412BhMzPCAxIv1PZfjKEJ6ApPOD4B9M-i5LrhE-4,2915
         | 
| 75 90 | 
             
            unfold/templates/admin/includes/object_delete_summary.html,sha256=Nv69SCzyJHFX14iJFfodxKM0IIpQegKZH0fvKB15QJI,468
         | 
| 76 91 | 
             
            unfold/templates/admin/index.html,sha256=9-mW2yW1ms6DSAKnpMGK--vjfRSeIc-s9xcGU4lkGpM,674
         | 
| 77 92 | 
             
            unfold/templates/admin/login.html,sha256=zZ1mppzvv9pNxW3O5XgU7rGsa8Oq6Kab5bofqv_FN9M,3494
         | 
| @@ -81,6 +96,8 @@ unfold/templates/admin/pagination.html,sha256=KWTPV7_hVSZ1374a-pqHXhnOueNQKu1UnS | |
| 81 96 | 
             
            unfold/templates/admin/search_form.html,sha256=BfAJPBzHo6DC6BO53ypZJpnQZBsPjRFp6GeFGOY5LXI,1190
         | 
| 82 97 | 
             
            unfold/templates/admin/submit_line.html,sha256=_J52WNMzXUC-ONZGLiTZiNWL_Ow5ZIy4prD1_4_-eRM,4186
         | 
| 83 98 | 
             
            unfold/templates/admin/widgets/clearable_file_input.html,sha256=01z48YtzG_uBIYENMcEJxQaD1xJqaNFXs3nBM0Q28nI,1851
         | 
| 99 | 
            +
            unfold/templates/admin/widgets/radio.html,sha256=3WcmclQNg7R_pRjEHL1dHkGjAzWlWNYnhHkAirC4nuA,646
         | 
| 100 | 
            +
            unfold/templates/admin/widgets/radio_option.html,sha256=IZgPx-aWKJuxrSalJ3K50RFd1vwSpb9Qk0yZwfV78_A,368
         | 
| 84 101 | 
             
            unfold/templates/admin/widgets/related_widget_wrapper.html,sha256=U6RaeR86xbi1AWUrMm1SbjlXGwpC3PZdNLTbk3alxXI,3799
         | 
| 85 102 | 
             
            unfold/templates/admin/widgets/split_datetime.html,sha256=eXLFZyCv84LCTFWAUhNO3xAIzWvGBvI1ZpYbB38_HOI,862
         | 
| 86 103 | 
             
            unfold/templates/auth/widgets/read_only_password_hash.html,sha256=Li9efo-3cFC5zj9im0SPfc62R4ZNVPQhs24H1U7xmD8,785
         | 
| @@ -98,6 +115,7 @@ unfold/templates/unfold/helpers/breadcrumb_item.html,sha256=k_1j57UV0WtzFFlMKaew | |
| 98 115 | 
             
            unfold/templates/unfold/helpers/display_header.html,sha256=E-yG9ydyb6rRIR5TT4FxekD3qokilfoOwaEaB7np8WI,433
         | 
| 99 116 | 
             
            unfold/templates/unfold/helpers/display_label.html,sha256=_hAHylpRgUGj2IiwJv3Jhs7vXbgY5fhcCxT-CIcBc3c,2072
         | 
| 100 117 | 
             
            unfold/templates/unfold/helpers/field.html,sha256=aQnkejALvuQkrIUUB9VuRDdM033fIxjWtT3K8olBb0U,335
         | 
| 118 | 
            +
            unfold/templates/unfold/helpers/field_readonly.html,sha256=v7-2oSSDgOsuYpP70y8DqdBqbRybubAfSDzstveoBuw,382
         | 
| 101 119 | 
             
            unfold/templates/unfold/helpers/form_errors.html,sha256=EwerIJptSCWXvtAJ1IZKfEn98qlShBIGavsTThbklAs,266
         | 
| 102 120 | 
             
            unfold/templates/unfold/helpers/help_text.html,sha256=9WjUjgUOYIOJAnkBaU12dN6CIt80_-MqQxsI-YyqFn8,151
         | 
| 103 121 | 
             
            unfold/templates/unfold/helpers/history.html,sha256=EFNNFG7mB064br8uWAvm8xB9GhNAiT01cVe9aq6KTmc,1995
         | 
| @@ -112,6 +130,7 @@ unfold/templates/unfold/helpers/pagination_ellipsis.html,sha256=Ar9Ntf2I_79mIVW5 | |
| 112 130 | 
             
            unfold/templates/unfold/helpers/search.html,sha256=T3JLlzEeHTEpX6qfjNQ0cQPW2rtVIOyE9quEyVHVXsA,1382
         | 
| 113 131 | 
             
            unfold/templates/unfold/helpers/search_results.html,sha256=nv2HDqGvP9aAmQ35Fdq0oiZcDFTXgkLic1AVMLumyU8,725
         | 
| 114 132 | 
             
            unfold/templates/unfold/helpers/site_icon.html,sha256=O7syHpahHnsiWfmNQOMkYrOIgkJjYViBeeQw3s6PAms,442
         | 
| 133 | 
            +
            unfold/templates/unfold/helpers/submit.html,sha256=oSzq85LRLhdOlbFtFZFhYm6ucT95u6LunTeSTDClszQ,206
         | 
| 115 134 | 
             
            unfold/templates/unfold/helpers/tab_list.html,sha256=9RsSXB2K8VACmojn-FxslwWqGhVqLQuNjDGhHHBopRs,2038
         | 
| 116 135 | 
             
            unfold/templates/unfold/helpers/theme_switch.html,sha256=skkl6fYUnYLM7fAPivHxWjnOnuQSKa-7aDxh7I8dGIg,2266
         | 
| 117 136 | 
             
            unfold/templates/unfold/helpers/userlinks.html,sha256=tl4jijMso7xEBBCuH_hFJaBvfeexVk5yIKIgTXGs7EY,403
         | 
| @@ -126,13 +145,13 @@ unfold/templates/unfold/widgets/split_money.html,sha256=AFLUBmzGbY-RXgsfz7gaDxVR | |
| 126 145 | 
             
            unfold/templates/unfold/widgets/textarea.html,sha256=4xIGWb20DuwiwumndByrAyh7xF-ReXKLulSpDImX_cs,459
         | 
| 127 146 | 
             
            unfold/templates/unfold/widgets/time.html,sha256=Dq9s-Kyfkw7p4TfchR-XFVOX6pOS7GkXPOWuP9CLRVw,106
         | 
| 128 147 | 
             
            unfold/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 129 | 
            -
            unfold/templatetags/unfold.py,sha256= | 
| 148 | 
            +
            unfold/templatetags/unfold.py,sha256=ii7yWAZb8g8kcdGD45MScdIHu22GWmoYqul4v2ktmqM,3943
         | 
| 130 149 | 
             
            unfold/templatetags/unfold_list.py,sha256=pkmVBqsh47T6b86vOjO-I8MgZlE1KeY-ZhIH5Qfksuo,10117
         | 
| 131 150 | 
             
            unfold/typing.py,sha256=MDuh0ZrS3UQdpmuunTV4_6Z0iwo1f3b2Ixz5ZbG0t_o,577
         | 
| 132 151 | 
             
            unfold/utils.py,sha256=XU08gcFEP4hY4AOrgiTxXCWJNK9a0_RK9Mwy1SS17Vc,3878
         | 
| 133 152 | 
             
            unfold/views.py,sha256=Ml3XlEoHLcbEWof59Dw8ihKBMcmp-gBAibThtBFj55A,708
         | 
| 134 | 
            -
            unfold/widgets.py,sha256= | 
| 135 | 
            -
            django_unfold-0. | 
| 136 | 
            -
            django_unfold-0. | 
| 137 | 
            -
            django_unfold-0. | 
| 138 | 
            -
            django_unfold-0. | 
| 153 | 
            +
            unfold/widgets.py,sha256=fpvOye1j-SRyZVrI19XF2GCNXb8ZJRxXZDxgLqxgoeQ,10186
         | 
| 154 | 
            +
            django_unfold-0.13.0.dist-info/LICENSE.md,sha256=D2dAcLArwGySIf62-9y-8Q4pcJOiWj8lLpBxYPzDCBc,1069
         | 
| 155 | 
            +
            django_unfold-0.13.0.dist-info/METADATA,sha256=VpXIv8DfR5ZPVH-_yvNVB5mMpY1PuoCTyzn9VsXS5AI,32704
         | 
| 156 | 
            +
            django_unfold-0.13.0.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
         | 
| 157 | 
            +
            django_unfold-0.13.0.dist-info/RECORD,,
         | 
    
        unfold/admin.py
    CHANGED
    
    | @@ -13,6 +13,7 @@ from django.db import models | |
| 13 13 | 
             
            from django.db.models import (
         | 
| 14 14 | 
             
                BLANK_CHOICE_DASH,
         | 
| 15 15 | 
             
                ForeignObjectRel,
         | 
| 16 | 
            +
                JSONField,
         | 
| 16 17 | 
             
                ManyToManyRel,
         | 
| 17 18 | 
             
                Model,
         | 
| 18 19 | 
             
                OneToOneField,
         | 
| @@ -60,6 +61,7 @@ from .widgets import ( | |
| 60 61 | 
             
                UnfoldAdminIntegerRangeWidget,
         | 
| 61 62 | 
             
                UnfoldAdminMoneyWidget,
         | 
| 62 63 | 
             
                UnfoldAdminNullBooleanSelectWidget,
         | 
| 64 | 
            +
                UnfoldAdminRadioSelectWidget,
         | 
| 63 65 | 
             
                UnfoldAdminSingleDateWidget,
         | 
| 64 66 | 
             
                UnfoldAdminSingleTimeWidget,
         | 
| 65 67 | 
             
                UnfoldAdminSplitDateTimeWidget,
         | 
| @@ -185,11 +187,23 @@ class UnfoldAdminReadonlyField(helpers.AdminReadonlyField): | |
| 185 187 | 
             
                        self.form.label_suffix,
         | 
| 186 188 | 
             
                    )
         | 
| 187 189 |  | 
| 188 | 
            -
                def  | 
| 189 | 
            -
                     | 
| 190 | 
            +
                def is_json(self) -> bool:
         | 
| 191 | 
            +
                    field, obj, model_admin = (
         | 
| 192 | 
            +
                        self.field["field"],
         | 
| 193 | 
            +
                        self.form.instance,
         | 
| 194 | 
            +
                        self.model_admin,
         | 
| 195 | 
            +
                    )
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                    try:
         | 
| 198 | 
            +
                        f, attr, value = lookup_field(field, obj, model_admin)
         | 
| 199 | 
            +
                    except (AttributeError, ValueError, ObjectDoesNotExist):
         | 
| 200 | 
            +
                        return False
         | 
| 190 201 |  | 
| 191 | 
            -
                     | 
| 202 | 
            +
                    return isinstance(f, JSONField)
         | 
| 192 203 |  | 
| 204 | 
            +
                def contents(self) -> str:
         | 
| 205 | 
            +
                    contents = self._get_contents()
         | 
| 206 | 
            +
                    contents = self._preprocess_field(contents)
         | 
| 193 207 | 
             
                    return contents
         | 
| 194 208 |  | 
| 195 209 | 
             
                def _get_contents(self) -> str:
         | 
| @@ -239,7 +253,6 @@ class UnfoldAdminReadonlyField(helpers.AdminReadonlyField): | |
| 239 253 | 
             
                        and self.field["field"] in self.model_admin.readonly_preprocess_fields
         | 
| 240 254 | 
             
                    ):
         | 
| 241 255 | 
             
                        func = self.model_admin.readonly_preprocess_fields[self.field["field"]]
         | 
| 242 | 
            -
             | 
| 243 256 | 
             
                        if isinstance(func, str):
         | 
| 244 257 | 
             
                            contents = import_string(func)(contents)
         | 
| 245 258 | 
             
                        elif callable(func):
         | 
| @@ -265,9 +278,16 @@ class ModelAdminMixin: | |
| 265 278 | 
             
                def formfield_for_choice_field(
         | 
| 266 279 | 
             
                    self, db_field: Field, request: HttpRequest, **kwargs
         | 
| 267 280 | 
             
                ) -> TypedChoiceField:
         | 
| 268 | 
            -
                    # Overrides widget for CharFields which have choices attribute
         | 
| 269 281 | 
             
                    if "widget" not in kwargs:
         | 
| 270 | 
            -
                         | 
| 282 | 
            +
                        if db_field.name in self.radio_fields:
         | 
| 283 | 
            +
                            kwargs["widget"] = UnfoldAdminRadioSelectWidget(
         | 
| 284 | 
            +
                                radio_style=self.radio_fields[db_field.name]
         | 
| 285 | 
            +
                            )
         | 
| 286 | 
            +
                        else:
         | 
| 287 | 
            +
                            kwargs["widget"] = forms.Select(
         | 
| 288 | 
            +
                                attrs={"class": " ".join(SELECT_CLASSES)}
         | 
| 289 | 
            +
                            )
         | 
| 290 | 
            +
             | 
| 271 291 | 
             
                        kwargs["choices"] = db_field.get_choices(
         | 
| 272 292 | 
             
                            include_blank=db_field.blank, blank_choice=[("", _("Select value"))]
         | 
| 273 293 | 
             
                        )
         | 
| 
            File without changes
         |