paper-admin


Namepaper-admin JSON
Version 7.7.1 PyPI version JSON
download
home_pagehttps://github.com/dldevinc/paper-admin
SummaryCustom Django admin interface.
upload_time2024-05-25 12:49:44
maintainerMihail Mishakin
docs_urlNone
authorMihail Mishakin
requires_python>=3.7
licenseBSD license
keywords
VCS
bugtrack_url
requirements django-autocomplete-light django-logentry-admin django-money django-mptt django-mptt django-solo django-solo django-tree-queries django-tree-queries Pillow pytest pytest pytest-cov pytest-cov pytest-django pytest-django pytest-xdist pytest-xdist
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # paper-admin

Custom Django admin interface based on Bootstrap 4.

[![PyPI](https://img.shields.io/pypi/v/paper-admin.svg)](https://pypi.org/project/paper-admin/)
[![Build Status](https://github.com/dldevinc/paper-admin/actions/workflows/release.yml/badge.svg)](https://github.com/dldevinc/paper-admin)
[![Software license](https://img.shields.io/pypi/l/paper-admin.svg)](https://pypi.org/project/paper-admin/)

## Requirements

-   Python >= 3.7
-   Django >= 3.2

## Table of Contents

-   [Installation](#installation)
-   [Patches](#patches)
-   [Badge](#badge)
-   [Admin menu](#admin-menu)
-   [Reorderable drag-and-drop lists](#reorderable-drag-and-drop-lists)
-   [Form tabs](#form-tabs)
-   [Form includes](#form-includes)
-   [HierarchyFilter](#hierarchyFilter)
-   [Stylization](#stylization)
    -   [Fieldsets](#fieldsets)
    -   [Table rows](#table-rows)
    -   [Inline forms](#inline-forms)
-   [Additional admin widgets](#Additional-admin-widgets)
-   [Settings](#settings)
-   [Additional References](#additional-References)

## Installation

Install the latest release with pip:

```shell
pip install paper-admin
```

Add `paper_admin` to your INSTALLED_APPS setting **before** `django.contrib.admin`.

```python
INSTALLED_APPS = [
    "paper_admin",
    "paper_admin.patches.dal",              # optional
    "paper_admin.patches.django_money",     # optional
    "paper_admin.patches.django_solo",      # optional
    "paper_admin.patches.mptt",             # optional
    "paper_admin.patches.logentry_admin",   # optional
    "paper_admin.patches.tree_queries",     # optional
    # ...
    "django.contrib.admin",
    # ...
]
```

## Patches

Some third-party libraries override the standard Django templates and within the `paper-admin`
interface look disfigured. To fix these, you can use the patches included in this package.

The following patches are available:

-   `paper_admin.patches.dal`<br>
    Fixes the style of the [django-autocomplete-light](https://github.com/yourlabs/django-autocomplete-light) widgets.

-   `paper_admin.patches.django_money`<br>
    Fixes the style of the [django-money](https://github.com/django-money/django-money) widget.

-   `paper_admin.patches.django_solo`<br>
    Fixes the breadcrumbs in [django-solo](https://github.com/lazybird/django-solo).

-   `paper_admin.patches.mptt`<br>
    Adaptation of [django-mptt](https://github.com/django-mptt/django-mptt).
    Adds the ability to sort tree nodes (when the `sortable` property is specified).

-   `paper_admin.patches.logentry_admin`<br>
    Fixes the filters and hides unwanted buttons in [django-logentry-admin](https://github.com/yprez/django-logentry-admin).

-   `paper_admin.patches.tree_queries`<br>
    Adds the ability to sort tree nodes for [django-tree-queries](https://github.com/matthiask/django-tree-queries).
    **It is necessary** to use the special `TreeNodeModelAdmin` class instead of the standard `ModelAdmin`:

    ```python
    # admin.py
    from django.contrib import admin
    from paper_admin.patches.tree_queries.admin import TreeNodeModelAdmin  # <--
    from .models import MyTreeNode


    @admin.register(MyTreeNode)
    class MyTreeNodeAdmin(TreeNodeModelAdmin):
        ...
        sortable = "position"
    ```

To use a patch, you need to add it to the `INSTALLED_APPS` setting.<br>
**Note**: as a rule, patches should be added **before** the libraries they fix.

## Badge

Badge is a text description of the environment to prevent confusion between the
development and production servers.

![](https://user-images.githubusercontent.com/6928240/125350052-4a28e080-e36f-11eb-8772-4d797d64863a.png)

The color and text of the badge can be changed in the `settings.py` file:

```python
PAPER_ENVIRONMENT_NAME = "development"
PAPER_ENVIRONMENT_COLOR = "#FFFF00"
```

## Admin menu

The `paper-admin` provides a way to create menus in the Django admin interface.
It allows you to define a menu tree with items that represent apps, models or custom links.
You can also define groups of items and dividers to separate them.

### Usage

To use the menu system, you need to define a menu tree in your Django project's
settings file. The menu tree is defined as a list of items, where each item can be
either a string, a dictionary or an instance of a menu item class.

The following types of menu items are available:

-   `Item`: Represents a link to a specific URL.
-   `Divider`: Represents a divider that separates items in the menu.
-   `Group`: Represents a group of items in the menu.

Here's an example of a simple menu tree:

```python
from django.utils.translation import gettext_lazy as _
from paper_admin.menu import Item, Divider


PAPER_MENU = [
    Item(
        label=_("Dashboard"),
        url="admin:index",
        icon="bi-speedometer2",
    ),
    Divider(),
    Item(
        app="auth",
        label=_("Authentication and Authorization"),
        icon="bi-person-circle",
        children=[
            Item(
                model="User",
                icon="bi-lg bi-people",
            ),
            Item(
                model="Group",
                icon="bi-lg bi-people-fill",
            ),
        ]
    )
]
```

### `Item`

The `Item` class represents a link to a specific URL. You can use it to create links
to Django admin pages, external websites or any other URL.

Properties:

-   `app`: A string representing the name of the Django app that the menu item
    will point to. If this is provided, the URL of the menu item will point to the app
    index page.
-   `model`: A string representing the name of the Django model that the menu
    item will point to. If this is provided, the URL of the menu item will point
    to the model list page.
-   `label`: A string representing the text that will be displayed in the menu item.
-   `url`: A string representing the URL that the menu item will point to.
-   `icon`: The CSS classes to use for the icon of the menu item.
-   `perms`: A list of permission strings that are required for the user to see this
    item in the menu. If this property is not provided, the item will be visible
    to all users.
-   `classes`: A string of additional CSS classes to add to the menu item.
-   `target`: The target attribute to use for the link.
-   `children`: An optional list of Item or Group instances representing sub-items of the menu item.

The `app` and `model` parameters cannot be set simultaneously. However, you must
specify at least one of the following parameters: `app`, `model`, or `label`.

Example usage:

```python
from django.utils.translation import gettext_lazy as _
from paper_admin.menu import Item


PAPER_MENU = [
    # Menu item with a specified label and URL
    Item(
        label=_("Dashboard"),
        url="admin:index",
        icon="bi-speedometer2",
    ),

    # Menu for the 'auth' app. Child items will be automatically generated
    # from the app's models.
    Item(
        app="auth"
    ),

    # App 'app' with a specified list of models.
    # The app's name is implicitly added to the model names.
    Item(
        app="app",
        icon="bi-house-fill",
        children=[
            Item(
                model="Widgets",
            ),
            Item(
                model="Message",
            ),
            Item(
                model="Book",
            ),
        ]
    ),

    # Specify a model from a specific app as a child item
    Item(
        label=_("Logs"),
        icon="bi-clock-history",
        perms="admin.view_logentry",
        children=[
            Item(
                model="admin.LogEntry",
            ),
        ]
    ),

    # Add CSS classes and a target attribute to a link
    Item(
        label="Google",
        url="https://google.com/",
        icon="bi-google",
        classes="text-warning",
        target="_blank",
    )
]
```

![image](https://user-images.githubusercontent.com/6928240/232227638-cc952405-051e-40a2-96ac-e1df84079d40.png)

When defining permissions for menu items using the `perms` parameter, you can use 
the special value `superuser` (changeable with `PAPER_MENU_SUPERUSER_PERMISSION`) 
to restrict access to superusers, and `staff` (changeable with `PAPER_MENU_STAFF_PERMISSION`) 
to restrict access to staff members.

Example:

```python
from paper_admin.menu import Item


PAPER_MENU = [
    Item(
        app="payments",
        icon="bi-money",
        perms=["staff"],
        children=[
            Item(
                model="Payment",
                perms=["superuser"],
            ),
            Item(
                model="Invoice",
            ),
        ]
    ),
]
```

### Divider

The `Divider` class is used to add a horizontal line to separate items in the menu
tree. It doesn't have any properties or methods, it's simply used to visually group
items together.

```python
from django.utils.translation import gettext_lazy as _
from paper_admin.menu import Item, Divider


PAPER_MENU = [
    Item(
        label=_("Dashboard"),
        url="#",
    ),
    Divider(),
    Item(
        label=_("About Us"),
        url="#",
    ),
    Item(
        label=_("Blog"),
        url="#",
    ),
    Item(
        label=_("Contacts"),
        url="#",
    ),
    Divider(),
    Item(
        label=_("Users"),
        url="#",
    ),
    Item(
        label=_("Logs"),
        url="#",
    ),
]
```

![image](https://user-images.githubusercontent.com/6928240/232309685-d6315de8-a39a-4c30-9862-26d139ae8008.png)

### Group

The `Group` class represents a group of menu items. It can be used to group related
items together under a common heading.

```python
from django.utils.translation import gettext_lazy as _
from paper_admin.menu import Item, Group


PAPER_MENU = [
    Item(
        label=_("Dashboard"),
        url="#",
    ),
    Group(
        label=_("Content"),
        children=[
            Item(
                label=_("About Us"),
                url="#",
            ),
            Item(
                label=_("Blog"),
                url="#",
            ),
        ]
    ),
    Group(
        label=_("Admin Area"),
        perms=["superuser"],
        children=[
            Item(
                label=_("Backups"),
                url="#",
            ),
            Item(
                label=_("Logs"),
                url="#",
            ),
        ]
    ),
]
```

![image](https://user-images.githubusercontent.com/6928240/232309872-3502acbf-68ef-4e38-a7b3-9507597466e6.png)

## Reorderable drag-and-drop lists

The `paper-admin` package provides the ability to reorder items in lists using
drag-and-drop. To enable this feature, you need to set the `sortable` property of
the model's `ModelAdmin` to the name of the field that stores the order.

```python
# models.py
from django.db import models


class Company(models.Model):
    # ...
    position = models.IntegerField(
        "position",
        default=0
    )
```

```python
# admin.py
from django.contrib import admin


class CompanyAdmin(admin.ModelAdmin):
    # ...
    sortable = "position"
```

https://github.com/dldevinc/paper-admin/assets/6928240/8d71f942-d222-44c6-8d4f-6f2e117bfd8d

<br>
The sorting is performed using AJAX and is saved to the database automatically.

### Reorderable inline forms

The `paper-admin` package also provides the ability to reorder inline forms in
the same way. But in this case, the sorting is not saved to the database automatically.

```python
from django.contrib import admin


class StaffInline(admin.StackedInline):
    # ...
    sortable = "position"


class IndustryInline(admin.TabularInline):
    # ...
    sortable = "position"
```

https://user-images.githubusercontent.com/6928240/125331956-b6004e80-e359-11eb-8422-832dfe37bb6c.mp4

<br>

The sorting is compatible with [django-mptt](https://github.com/django-mptt/django-mptt)
(if the `paper_admin.patches.mptt` patch is added to `INSTALLED_APPS`).
You can only change the order of elements that have the same parent and are at the same
level of nesting:

https://user-images.githubusercontent.com/6928240/125340277-55760f00-e363-11eb-94d4-49a978cb7ae4.mp4

## Form tabs

The `paper-admin` provides a way to divide forms into sections. Sections are
represented as tabs, which makes it easier to navigate between them.

To use this feature, you need to set the `tabs` property of the model's
`ModelAdmin` to a list of tab definitions.

```python
from django.contrib import admin
from django.utils.translation import gettext_lazy as _


class TablularInline(admin.TabularInline):
    tab = 'inlines'


@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
    fieldsets = (
        (None, {
            'tab': 'related-fields',
            'fields': (
                # ...
            ),
        }),
        (None, {
            'tab': 'standard-fields',
            'fields': (
                # ...
            )
        }),
        (None, {
            'tab': 'file-fields',
            'fields': (
                # ...
            )
        }),
    )
    tabs = [
        ('related-fields', _('Related fields')),
        ('standard-fields', _('Standard Fields')),
        ('file-fields', _('File Fields')),
        ('inlines', _('Inlines')),
    ]
    inlines = (TablularInline, )
```

https://user-images.githubusercontent.com/6928240/226703428-c9413de1-42c1-4178-b75f-37412925f18f.mp4

<br>
Tabs can also be dynamically generated by the `get_tabs()` method:

```python
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from .models import Page


@admin.register(Page)
class PageAdmin(admin.ModelAdmin):
    def get_tabs(self, request, obj=None):
        return [
            ('general', _('General')),
            ('content', _('Content')),
            ('seo', _('SEO')),
        ]
```

## Form includes

`paper-admin` provides a convenient way to include custom templates within admin forms 
at various positions, such as `top`, `middle`, or `bottom`. These positions correspond 
to different sections of the admin form page.

Each custom form include definition can include the following parameters:

1. **Path to Template (Required)**: The path to the template you want to include within 
   the form.
2. **Position (Optional)**: The desired position for the include. It can be one of 
   the following:
   * "top": Above the fieldsets.
   * "middle": Between the fieldsets and inlines.
   * "bottom" (Default): After the inlines.
3. **Tab Name (Optional)**: If you are using Form tabs, you can specify the name of 
   the tab where the include should be displayed.

```python
from django.contrib import admin
from django.utils.translation import gettext_lazy as _

from ..models import Person


@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    # ...
    tabs = [
        ("general", _("General")),
        ("pets", _("Pets")),
    ]
    form_includes = [
        ("app/includes/disclaimer.html", "top", "general"),
        ("app/includes/privacy_notice.html",),
    ]
```

```html
<!-- app/includes/privacy_notice.html -->
<div class="paper-card paper-card--info card my-3">
  <div class="card-header">
    <h5 class="card-title mb-0">Privacy notice</h5>
  </div>
  <div class="card-body">
    <small class="text-secondary">
      We use your Personal Data to respond to your enquiries about our services,
      to send you other useful information and to provide our services to you.
    </small>
  </div>
</div>
```

Result:

![image](https://github.com/dldevinc/paper-admin/assets/6928240/55f172b8-9f6e-4943-9435-f6e74b026c64)

In addition to the standard way of including custom templates in forms using `paper-admin`,
there's the capability to dynamically create includes using the `get_form_includes` method. 
This enables you to flexibly determine which templates to include based on the current 
request or other conditions.

```python
from django.contrib import admin

from ..models import Person


@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
    def get_form_includes(self, request, obj=None):
        if request.user.is_superuser:
            return []

        return [
            ("app/includes/disclaimer.html", "top", "general"),
            ("app/includes/privacy_notice.html",),
        ]
```

## HierarchyFilter

`HierarchyFilter` is a special type of filter that can be used to filter objects by a
hierarchical model field. It is similar to the `date_hierarchy` filter, but it works
with any hierarchical model field.

```python
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from paper_admin.admin.filters import HierarchyFilter
from .models import Group, Message


class GroupFilter(HierarchyFilter):
    title = _("Group")
    parameter_name = "group"

    def lookups(self, changelist):
        return (
            (pk, name)
            for pk, name in Group.objects.values_list("pk", "name")
        )

    def queryset(self, request, queryset):
        value = self.value()
        if not value:
            return queryset

        return queryset.filter(group__in=value)


@admin.register(Message)
class MessageAdmin(admin.ModelAdmin):
    # ...
    list_filters = [GroupFilter]
```

Result:

![image](https://github.com/dldevinc/paper-admin/assets/6928240/fb1c3ad7-9c40-4c63-b69b-dae1566ebefb)

## Stylization

### Table rows

You can use the `get_row_classes` method of the `ModelAdmin`
class to add custom classes to the rows in the list view.

```python
from django.contrib import admin
from .models import Category


@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):

    def get_row_classes(self, request, obj):
        if obj.status == "success":
            return ["table-success"]
        elif obj.status == "failed":
            return ["table-danger"]
        return []
```

![image](https://user-images.githubusercontent.com/6928240/233795075-d0f85cc2-d34a-43a9-b393-20cd81f96d99.png)


### Fieldsets

Django [provides](https://docs.djangoproject.com/en/4.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets)
a way to add a custom CSS classes to the fieldsets in the admin interface.

To use this feature, specify the `classes` parameter in the `ModelAdmin.fieldsets`
attribute:

```python
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from .models import Category


@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    fieldsets = (
        (_("Info Section"), {
            "classes": ("paper-card--info", ),
            "description": _("Description for the fieldset"),
            "fields": (
                # ...
            ),
        }),
        (_("Success Section"), {
            "classes": ("paper-card--success",),
            "fields": (
                # ...
            )
        }),
        (_("Danger Section"), {
            "classes": ("paper-card--danger",),
            "fields": (
                # ...
            )
        }),
    )
```

![image](https://user-images.githubusercontent.com/6928240/233795448-17d2fa5c-739a-4751-8266-b4b4e879b1c8.png)

### Inline forms

You can use the `get_form_classes` method of the `ModelAdmin` class
to add custom classes to the inline forms:

```python
from django.contrib import admin


class StackedInline(admin.StackedInline):
    def get_form_classes(self, request, obj):
        if obj.status == "success":
            return ["paper-card--success"]
        elif obj.status == "failed":
            return ["paper-card--danger"]
        return []


class TablularInlines(admin.TabularInline):
    def get_form_classes(self, request, obj):
        if obj.status == "success":
            return ["table-success"]
        elif obj.status == "failed":
            return ["table-danger"]
        return []
```

![image](https://user-images.githubusercontent.com/6928240/233794982-1ca7248a-dc54-48c4-aea2-44d0d973d083.png)
![image](https://user-images.githubusercontent.com/6928240/233795183-c056b82e-8b01-4f7d-9c09-65c0becbdc33.png)

## Additional Admin Widgets

You can enhance the Django admin interface with various custom widgets provided 
by the `paper-admin` package.

### AdminSwitchInput

The `AdminSwitchInput` widget offers a toggle switch input field for boolean fields 
in the Django admin interface. This provides a more user-friendly alternative 
to the default checkbox input for handling boolean values.

```python
from django.contrib import admin
from django.db import models
from paper_admin.widgets import AdminSwitchInput
from .models import MyModel

class MyModelAdmin(admin.ModelAdmin):
    formfield_overrides = {
        models.BooleanField: {"widget": AdminSwitchInput},
    }

admin.site.register(MyModel, MyModelAdmin)
```

![image](https://github.com/dldevinc/paper-admin/assets/6928240/98a4c4f7-7e63-43a9-9c15-42093f078f4a)

### AdminCheckboxSelectMultiple

The `AdminCheckboxSelectMultiple` widget improves multiple choice selection 
by displaying options as checkboxes instead of a dropdown list.

![image](https://github.com/dldevinc/paper-admin/assets/6928240/754fc2ad-254d-4685-a3a2-b1f5149387fd)

### AdminCheckboxTree

The `AdminCheckboxTree` widget presents a scrollable list of checkboxes, providing 
a compact and efficient way to handle multiple selections.

```python
# custom_users/admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserChangeForm
from django.contrib.auth.models import User

from paper_admin.admin.widgets import AdminCheckboxTree


class CustomUserChangeForm(UserChangeForm):
    class Meta:
        widgets = {
            "user_permissions": AdminCheckboxTree,
        }


class CustomUserAdmin(UserAdmin):
    form = CustomUserChangeForm


admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
```

![image](https://github.com/dldevinc/paper-admin/assets/6928240/72766127-ccc6-4538-ac4f-5dae14a30e1f)

For permission fields, you can take it a step further and utilize [paper-admin-permission-field](https://github.com/dldevinc/paper-admin-permission-field).

## Settings

`PAPER_FAVICON`<br>
The path to the favicon for the admin interface.<br>
Default: `"paper_admin/dist/assets/default_favicon.png"`

`PAPER_ENVIRONMENT_NAME`<br>
The text of the environment badge.<br>
Default: `""`

`PAPER_ENVIRONMENT_COLOR`<br>
The color of the environment badge.<br>
Default: `""`

`PAPER_MENU`<br>
A list of menu items. See [Admin menu](#admin-menu) for details.<br>
Default: `None`

`PAPER_MENU_DIVIDER`<br>
A string representing the menu item divider.<br>
Default: `"-"`

`PAPER_MENU_STAFF_PERMISSION`<br>
The special permission string that allows access to the menu item for staff users.<br>
Default: `"staff"`

`PAPER_MENU_SUPERUSER_PERMISSION`<br>
The special permission string that allows access to the menu item for superusers.<br>
Default: `"superuser"`

`PAPER_MENU_COLLAPSE_SINGLE_CHILDS`<br>
Whether to collapse a menu item if it has only one child.<br>
Default: `True`

`PAPER_DEFAULT_TAB_NAME`<br>
The name of the tab to activate in the form by default.<br>
Default: `"general"`

`PAPER_DEFAULT_TAB_TITLE`<br>
The title of the tab to activate in the form by default.<br>
Default: `_("General")`

`PAPER_LOCALE_PACKAGES`<br>
The list of packages to search for translation strings.<br>
Default: `["paper_admin", "django.contrib.admin"]`

`PAPER_NONE_PLACEHOLDER`<br>
The placeholder text for the "None" option in the filters.<br>
Default: `␀`

## Additional References

-   [Modals](docs/modals.md)

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/dldevinc/paper-admin",
    "name": "paper-admin",
    "maintainer": "Mihail Mishakin",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "x896321475@gmail.com",
    "keywords": null,
    "author": "Mihail Mishakin",
    "author_email": "x896321475@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/92/b1/26f09043b15b29a6ae9459023d4625ca58f85bff1dcfef815f8307e97c4d/paper_admin-7.7.1.tar.gz",
    "platform": "OS Independent",
    "description": "# paper-admin\n\nCustom Django admin interface based on Bootstrap 4.\n\n[![PyPI](https://img.shields.io/pypi/v/paper-admin.svg)](https://pypi.org/project/paper-admin/)\n[![Build Status](https://github.com/dldevinc/paper-admin/actions/workflows/release.yml/badge.svg)](https://github.com/dldevinc/paper-admin)\n[![Software license](https://img.shields.io/pypi/l/paper-admin.svg)](https://pypi.org/project/paper-admin/)\n\n## Requirements\n\n-   Python >= 3.7\n-   Django >= 3.2\n\n## Table of Contents\n\n-   [Installation](#installation)\n-   [Patches](#patches)\n-   [Badge](#badge)\n-   [Admin menu](#admin-menu)\n-   [Reorderable drag-and-drop lists](#reorderable-drag-and-drop-lists)\n-   [Form tabs](#form-tabs)\n-   [Form includes](#form-includes)\n-   [HierarchyFilter](#hierarchyFilter)\n-   [Stylization](#stylization)\n    -   [Fieldsets](#fieldsets)\n    -   [Table rows](#table-rows)\n    -   [Inline forms](#inline-forms)\n-   [Additional admin widgets](#Additional-admin-widgets)\n-   [Settings](#settings)\n-   [Additional References](#additional-References)\n\n## Installation\n\nInstall the latest release with pip:\n\n```shell\npip install paper-admin\n```\n\nAdd `paper_admin` to your INSTALLED_APPS setting **before** `django.contrib.admin`.\n\n```python\nINSTALLED_APPS = [\n    \"paper_admin\",\n    \"paper_admin.patches.dal\",              # optional\n    \"paper_admin.patches.django_money\",     # optional\n    \"paper_admin.patches.django_solo\",      # optional\n    \"paper_admin.patches.mptt\",             # optional\n    \"paper_admin.patches.logentry_admin\",   # optional\n    \"paper_admin.patches.tree_queries\",     # optional\n    # ...\n    \"django.contrib.admin\",\n    # ...\n]\n```\n\n## Patches\n\nSome third-party libraries override the standard Django templates and within the `paper-admin`\ninterface look disfigured. To fix these, you can use the patches included in this package.\n\nThe following patches are available:\n\n-   `paper_admin.patches.dal`<br>\n    Fixes the style of the [django-autocomplete-light](https://github.com/yourlabs/django-autocomplete-light) widgets.\n\n-   `paper_admin.patches.django_money`<br>\n    Fixes the style of the [django-money](https://github.com/django-money/django-money) widget.\n\n-   `paper_admin.patches.django_solo`<br>\n    Fixes the breadcrumbs in [django-solo](https://github.com/lazybird/django-solo).\n\n-   `paper_admin.patches.mptt`<br>\n    Adaptation of [django-mptt](https://github.com/django-mptt/django-mptt).\n    Adds the ability to sort tree nodes (when the `sortable` property is specified).\n\n-   `paper_admin.patches.logentry_admin`<br>\n    Fixes the filters and hides unwanted buttons in [django-logentry-admin](https://github.com/yprez/django-logentry-admin).\n\n-   `paper_admin.patches.tree_queries`<br>\n    Adds the ability to sort tree nodes for [django-tree-queries](https://github.com/matthiask/django-tree-queries).\n    **It is necessary** to use the special `TreeNodeModelAdmin` class instead of the standard `ModelAdmin`:\n\n    ```python\n    # admin.py\n    from django.contrib import admin\n    from paper_admin.patches.tree_queries.admin import TreeNodeModelAdmin  # <--\n    from .models import MyTreeNode\n\n\n    @admin.register(MyTreeNode)\n    class MyTreeNodeAdmin(TreeNodeModelAdmin):\n        ...\n        sortable = \"position\"\n    ```\n\nTo use a patch, you need to add it to the `INSTALLED_APPS` setting.<br>\n**Note**: as a rule, patches should be added **before** the libraries they fix.\n\n## Badge\n\nBadge is a text description of the environment to prevent confusion between the\ndevelopment and production servers.\n\n![](https://user-images.githubusercontent.com/6928240/125350052-4a28e080-e36f-11eb-8772-4d797d64863a.png)\n\nThe color and text of the badge can be changed in the `settings.py` file:\n\n```python\nPAPER_ENVIRONMENT_NAME = \"development\"\nPAPER_ENVIRONMENT_COLOR = \"#FFFF00\"\n```\n\n## Admin menu\n\nThe `paper-admin` provides a way to create menus in the Django admin interface.\nIt allows you to define a menu tree with items that represent apps, models or custom links.\nYou can also define groups of items and dividers to separate them.\n\n### Usage\n\nTo use the menu system, you need to define a menu tree in your Django project's\nsettings file. The menu tree is defined as a list of items, where each item can be\neither a string, a dictionary or an instance of a menu item class.\n\nThe following types of menu items are available:\n\n-   `Item`: Represents a link to a specific URL.\n-   `Divider`: Represents a divider that separates items in the menu.\n-   `Group`: Represents a group of items in the menu.\n\nHere's an example of a simple menu tree:\n\n```python\nfrom django.utils.translation import gettext_lazy as _\nfrom paper_admin.menu import Item, Divider\n\n\nPAPER_MENU = [\n    Item(\n        label=_(\"Dashboard\"),\n        url=\"admin:index\",\n        icon=\"bi-speedometer2\",\n    ),\n    Divider(),\n    Item(\n        app=\"auth\",\n        label=_(\"Authentication and Authorization\"),\n        icon=\"bi-person-circle\",\n        children=[\n            Item(\n                model=\"User\",\n                icon=\"bi-lg bi-people\",\n            ),\n            Item(\n                model=\"Group\",\n                icon=\"bi-lg bi-people-fill\",\n            ),\n        ]\n    )\n]\n```\n\n### `Item`\n\nThe `Item` class represents a link to a specific URL. You can use it to create links\nto Django admin pages, external websites or any other URL.\n\nProperties:\n\n-   `app`: A string representing the name of the Django app that the menu item\n    will point to. If this is provided, the URL of the menu item will point to the app\n    index page.\n-   `model`: A string representing the name of the Django model that the menu\n    item will point to. If this is provided, the URL of the menu item will point\n    to the model list page.\n-   `label`: A string representing the text that will be displayed in the menu item.\n-   `url`: A string representing the URL that the menu item will point to.\n-   `icon`: The CSS classes to use for the icon of the menu item.\n-   `perms`: A list of permission strings that are required for the user to see this\n    item in the menu. If this property is not provided, the item will be visible\n    to all users.\n-   `classes`: A string of additional CSS classes to add to the menu item.\n-   `target`: The target attribute to use for the link.\n-   `children`: An optional list of Item or Group instances representing sub-items of the menu item.\n\nThe `app` and `model` parameters cannot be set simultaneously. However, you must\nspecify at least one of the following parameters: `app`, `model`, or `label`.\n\nExample usage:\n\n```python\nfrom django.utils.translation import gettext_lazy as _\nfrom paper_admin.menu import Item\n\n\nPAPER_MENU = [\n    # Menu item with a specified label and URL\n    Item(\n        label=_(\"Dashboard\"),\n        url=\"admin:index\",\n        icon=\"bi-speedometer2\",\n    ),\n\n    # Menu for the 'auth' app. Child items will be automatically generated\n    # from the app's models.\n    Item(\n        app=\"auth\"\n    ),\n\n    # App 'app' with a specified list of models.\n    # The app's name is implicitly added to the model names.\n    Item(\n        app=\"app\",\n        icon=\"bi-house-fill\",\n        children=[\n            Item(\n                model=\"Widgets\",\n            ),\n            Item(\n                model=\"Message\",\n            ),\n            Item(\n                model=\"Book\",\n            ),\n        ]\n    ),\n\n    # Specify a model from a specific app as a child item\n    Item(\n        label=_(\"Logs\"),\n        icon=\"bi-clock-history\",\n        perms=\"admin.view_logentry\",\n        children=[\n            Item(\n                model=\"admin.LogEntry\",\n            ),\n        ]\n    ),\n\n    # Add CSS classes and a target attribute to a link\n    Item(\n        label=\"Google\",\n        url=\"https://google.com/\",\n        icon=\"bi-google\",\n        classes=\"text-warning\",\n        target=\"_blank\",\n    )\n]\n```\n\n![image](https://user-images.githubusercontent.com/6928240/232227638-cc952405-051e-40a2-96ac-e1df84079d40.png)\n\nWhen defining permissions for menu items using the `perms` parameter, you can use \nthe special value `superuser` (changeable with `PAPER_MENU_SUPERUSER_PERMISSION`) \nto restrict access to superusers, and `staff` (changeable with `PAPER_MENU_STAFF_PERMISSION`) \nto restrict access to staff members.\n\nExample:\n\n```python\nfrom paper_admin.menu import Item\n\n\nPAPER_MENU = [\n    Item(\n        app=\"payments\",\n        icon=\"bi-money\",\n        perms=[\"staff\"],\n        children=[\n            Item(\n                model=\"Payment\",\n                perms=[\"superuser\"],\n            ),\n            Item(\n                model=\"Invoice\",\n            ),\n        ]\n    ),\n]\n```\n\n### Divider\n\nThe `Divider` class is used to add a horizontal line to separate items in the menu\ntree. It doesn't have any properties or methods, it's simply used to visually group\nitems together.\n\n```python\nfrom django.utils.translation import gettext_lazy as _\nfrom paper_admin.menu import Item, Divider\n\n\nPAPER_MENU = [\n    Item(\n        label=_(\"Dashboard\"),\n        url=\"#\",\n    ),\n    Divider(),\n    Item(\n        label=_(\"About Us\"),\n        url=\"#\",\n    ),\n    Item(\n        label=_(\"Blog\"),\n        url=\"#\",\n    ),\n    Item(\n        label=_(\"Contacts\"),\n        url=\"#\",\n    ),\n    Divider(),\n    Item(\n        label=_(\"Users\"),\n        url=\"#\",\n    ),\n    Item(\n        label=_(\"Logs\"),\n        url=\"#\",\n    ),\n]\n```\n\n![image](https://user-images.githubusercontent.com/6928240/232309685-d6315de8-a39a-4c30-9862-26d139ae8008.png)\n\n### Group\n\nThe `Group` class represents a group of menu items. It can be used to group related\nitems together under a common heading.\n\n```python\nfrom django.utils.translation import gettext_lazy as _\nfrom paper_admin.menu import Item, Group\n\n\nPAPER_MENU = [\n    Item(\n        label=_(\"Dashboard\"),\n        url=\"#\",\n    ),\n    Group(\n        label=_(\"Content\"),\n        children=[\n            Item(\n                label=_(\"About Us\"),\n                url=\"#\",\n            ),\n            Item(\n                label=_(\"Blog\"),\n                url=\"#\",\n            ),\n        ]\n    ),\n    Group(\n        label=_(\"Admin Area\"),\n        perms=[\"superuser\"],\n        children=[\n            Item(\n                label=_(\"Backups\"),\n                url=\"#\",\n            ),\n            Item(\n                label=_(\"Logs\"),\n                url=\"#\",\n            ),\n        ]\n    ),\n]\n```\n\n![image](https://user-images.githubusercontent.com/6928240/232309872-3502acbf-68ef-4e38-a7b3-9507597466e6.png)\n\n## Reorderable drag-and-drop lists\n\nThe `paper-admin` package provides the ability to reorder items in lists using\ndrag-and-drop. To enable this feature, you need to set the `sortable` property of\nthe model's `ModelAdmin` to the name of the field that stores the order.\n\n```python\n# models.py\nfrom django.db import models\n\n\nclass Company(models.Model):\n    # ...\n    position = models.IntegerField(\n        \"position\",\n        default=0\n    )\n```\n\n```python\n# admin.py\nfrom django.contrib import admin\n\n\nclass CompanyAdmin(admin.ModelAdmin):\n    # ...\n    sortable = \"position\"\n```\n\nhttps://github.com/dldevinc/paper-admin/assets/6928240/8d71f942-d222-44c6-8d4f-6f2e117bfd8d\n\n<br>\nThe sorting is performed using AJAX and is saved to the database automatically.\n\n### Reorderable inline forms\n\nThe `paper-admin` package also provides the ability to reorder inline forms in\nthe same way. But in this case, the sorting is not saved to the database automatically.\n\n```python\nfrom django.contrib import admin\n\n\nclass StaffInline(admin.StackedInline):\n    # ...\n    sortable = \"position\"\n\n\nclass IndustryInline(admin.TabularInline):\n    # ...\n    sortable = \"position\"\n```\n\nhttps://user-images.githubusercontent.com/6928240/125331956-b6004e80-e359-11eb-8422-832dfe37bb6c.mp4\n\n<br>\n\nThe sorting is compatible with [django-mptt](https://github.com/django-mptt/django-mptt)\n(if the `paper_admin.patches.mptt` patch is added to `INSTALLED_APPS`).\nYou can only change the order of elements that have the same parent and are at the same\nlevel of nesting:\n\nhttps://user-images.githubusercontent.com/6928240/125340277-55760f00-e363-11eb-94d4-49a978cb7ae4.mp4\n\n## Form tabs\n\nThe `paper-admin` provides a way to divide forms into sections. Sections are\nrepresented as tabs, which makes it easier to navigate between them.\n\nTo use this feature, you need to set the `tabs` property of the model's\n`ModelAdmin` to a list of tab definitions.\n\n```python\nfrom django.contrib import admin\nfrom django.utils.translation import gettext_lazy as _\n\n\nclass TablularInline(admin.TabularInline):\n    tab = 'inlines'\n\n\n@admin.register(Page)\nclass PageAdmin(admin.ModelAdmin):\n    fieldsets = (\n        (None, {\n            'tab': 'related-fields',\n            'fields': (\n                # ...\n            ),\n        }),\n        (None, {\n            'tab': 'standard-fields',\n            'fields': (\n                # ...\n            )\n        }),\n        (None, {\n            'tab': 'file-fields',\n            'fields': (\n                # ...\n            )\n        }),\n    )\n    tabs = [\n        ('related-fields', _('Related fields')),\n        ('standard-fields', _('Standard Fields')),\n        ('file-fields', _('File Fields')),\n        ('inlines', _('Inlines')),\n    ]\n    inlines = (TablularInline, )\n```\n\nhttps://user-images.githubusercontent.com/6928240/226703428-c9413de1-42c1-4178-b75f-37412925f18f.mp4\n\n<br>\nTabs can also be dynamically generated by the `get_tabs()` method:\n\n```python\nfrom django.contrib import admin\nfrom django.utils.translation import gettext_lazy as _\nfrom .models import Page\n\n\n@admin.register(Page)\nclass PageAdmin(admin.ModelAdmin):\n    def get_tabs(self, request, obj=None):\n        return [\n            ('general', _('General')),\n            ('content', _('Content')),\n            ('seo', _('SEO')),\n        ]\n```\n\n## Form includes\n\n`paper-admin` provides a convenient way to include custom templates within admin forms \nat various positions, such as `top`, `middle`, or `bottom`. These positions correspond \nto different sections of the admin form page.\n\nEach custom form include definition can include the following parameters:\n\n1. **Path to Template (Required)**: The path to the template you want to include within \n   the form.\n2. **Position (Optional)**: The desired position for the include. It can be one of \n   the following:\n   * \"top\": Above the fieldsets.\n   * \"middle\": Between the fieldsets and inlines.\n   * \"bottom\" (Default): After the inlines.\n3. **Tab Name (Optional)**: If you are using Form tabs, you can specify the name of \n   the tab where the include should be displayed.\n\n```python\nfrom django.contrib import admin\nfrom django.utils.translation import gettext_lazy as _\n\nfrom ..models import Person\n\n\n@admin.register(Person)\nclass PersonAdmin(admin.ModelAdmin):\n    # ...\n    tabs = [\n        (\"general\", _(\"General\")),\n        (\"pets\", _(\"Pets\")),\n    ]\n    form_includes = [\n        (\"app/includes/disclaimer.html\", \"top\", \"general\"),\n        (\"app/includes/privacy_notice.html\",),\n    ]\n```\n\n```html\n<!-- app/includes/privacy_notice.html -->\n<div class=\"paper-card paper-card--info card my-3\">\n  <div class=\"card-header\">\n    <h5 class=\"card-title mb-0\">Privacy notice</h5>\n  </div>\n  <div class=\"card-body\">\n    <small class=\"text-secondary\">\n      We use your Personal Data to respond to your enquiries about our services,\n      to send you other useful information and to provide our services to you.\n    </small>\n  </div>\n</div>\n```\n\nResult:\n\n![image](https://github.com/dldevinc/paper-admin/assets/6928240/55f172b8-9f6e-4943-9435-f6e74b026c64)\n\nIn addition to the standard way of including custom templates in forms using `paper-admin`,\nthere's the capability to dynamically create includes using the `get_form_includes` method. \nThis enables you to flexibly determine which templates to include based on the current \nrequest or other conditions.\n\n```python\nfrom django.contrib import admin\n\nfrom ..models import Person\n\n\n@admin.register(Person)\nclass PersonAdmin(admin.ModelAdmin):\n    def get_form_includes(self, request, obj=None):\n        if request.user.is_superuser:\n            return []\n\n        return [\n            (\"app/includes/disclaimer.html\", \"top\", \"general\"),\n            (\"app/includes/privacy_notice.html\",),\n        ]\n```\n\n## HierarchyFilter\n\n`HierarchyFilter` is a special type of filter that can be used to filter objects by a\nhierarchical model field. It is similar to the `date_hierarchy` filter, but it works\nwith any hierarchical model field.\n\n```python\nfrom django.contrib import admin\nfrom django.utils.translation import gettext_lazy as _\nfrom paper_admin.admin.filters import HierarchyFilter\nfrom .models import Group, Message\n\n\nclass GroupFilter(HierarchyFilter):\n    title = _(\"Group\")\n    parameter_name = \"group\"\n\n    def lookups(self, changelist):\n        return (\n            (pk, name)\n            for pk, name in Group.objects.values_list(\"pk\", \"name\")\n        )\n\n    def queryset(self, request, queryset):\n        value = self.value()\n        if not value:\n            return queryset\n\n        return queryset.filter(group__in=value)\n\n\n@admin.register(Message)\nclass MessageAdmin(admin.ModelAdmin):\n    # ...\n    list_filters = [GroupFilter]\n```\n\nResult:\n\n![image](https://github.com/dldevinc/paper-admin/assets/6928240/fb1c3ad7-9c40-4c63-b69b-dae1566ebefb)\n\n## Stylization\n\n### Table rows\n\nYou can use the `get_row_classes` method of the `ModelAdmin`\nclass to add custom classes to the rows in the list view.\n\n```python\nfrom django.contrib import admin\nfrom .models import Category\n\n\n@admin.register(Category)\nclass CategoryAdmin(admin.ModelAdmin):\n\n    def get_row_classes(self, request, obj):\n        if obj.status == \"success\":\n            return [\"table-success\"]\n        elif obj.status == \"failed\":\n            return [\"table-danger\"]\n        return []\n```\n\n![image](https://user-images.githubusercontent.com/6928240/233795075-d0f85cc2-d34a-43a9-b393-20cd81f96d99.png)\n\n\n### Fieldsets\n\nDjango [provides](https://docs.djangoproject.com/en/4.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets)\na way to add a custom CSS classes to the fieldsets in the admin interface.\n\nTo use this feature, specify the `classes` parameter in the `ModelAdmin.fieldsets`\nattribute:\n\n```python\nfrom django.contrib import admin\nfrom django.utils.translation import gettext_lazy as _\nfrom .models import Category\n\n\n@admin.register(Category)\nclass CategoryAdmin(admin.ModelAdmin):\n    fieldsets = (\n        (_(\"Info Section\"), {\n            \"classes\": (\"paper-card--info\", ),\n            \"description\": _(\"Description for the fieldset\"),\n            \"fields\": (\n                # ...\n            ),\n        }),\n        (_(\"Success Section\"), {\n            \"classes\": (\"paper-card--success\",),\n            \"fields\": (\n                # ...\n            )\n        }),\n        (_(\"Danger Section\"), {\n            \"classes\": (\"paper-card--danger\",),\n            \"fields\": (\n                # ...\n            )\n        }),\n    )\n```\n\n![image](https://user-images.githubusercontent.com/6928240/233795448-17d2fa5c-739a-4751-8266-b4b4e879b1c8.png)\n\n### Inline forms\n\nYou can use the `get_form_classes` method of the `ModelAdmin` class\nto add custom classes to the inline forms:\n\n```python\nfrom django.contrib import admin\n\n\nclass StackedInline(admin.StackedInline):\n    def get_form_classes(self, request, obj):\n        if obj.status == \"success\":\n            return [\"paper-card--success\"]\n        elif obj.status == \"failed\":\n            return [\"paper-card--danger\"]\n        return []\n\n\nclass TablularInlines(admin.TabularInline):\n    def get_form_classes(self, request, obj):\n        if obj.status == \"success\":\n            return [\"table-success\"]\n        elif obj.status == \"failed\":\n            return [\"table-danger\"]\n        return []\n```\n\n![image](https://user-images.githubusercontent.com/6928240/233794982-1ca7248a-dc54-48c4-aea2-44d0d973d083.png)\n![image](https://user-images.githubusercontent.com/6928240/233795183-c056b82e-8b01-4f7d-9c09-65c0becbdc33.png)\n\n## Additional Admin Widgets\n\nYou can enhance the Django admin interface with various custom widgets provided \nby the `paper-admin` package.\n\n### AdminSwitchInput\n\nThe `AdminSwitchInput` widget offers a toggle switch input field for boolean fields \nin the Django admin interface. This provides a more user-friendly alternative \nto the default checkbox input for handling boolean values.\n\n```python\nfrom django.contrib import admin\nfrom django.db import models\nfrom paper_admin.widgets import AdminSwitchInput\nfrom .models import MyModel\n\nclass MyModelAdmin(admin.ModelAdmin):\n    formfield_overrides = {\n        models.BooleanField: {\"widget\": AdminSwitchInput},\n    }\n\nadmin.site.register(MyModel, MyModelAdmin)\n```\n\n![image](https://github.com/dldevinc/paper-admin/assets/6928240/98a4c4f7-7e63-43a9-9c15-42093f078f4a)\n\n### AdminCheckboxSelectMultiple\n\nThe `AdminCheckboxSelectMultiple` widget improves multiple choice selection \nby displaying options as checkboxes instead of a dropdown list.\n\n![image](https://github.com/dldevinc/paper-admin/assets/6928240/754fc2ad-254d-4685-a3a2-b1f5149387fd)\n\n### AdminCheckboxTree\n\nThe `AdminCheckboxTree` widget presents a scrollable list of checkboxes, providing \na compact and efficient way to handle multiple selections.\n\n```python\n# custom_users/admin.py\n\nfrom django.contrib import admin\nfrom django.contrib.auth.admin import UserAdmin\nfrom django.contrib.auth.forms import UserChangeForm\nfrom django.contrib.auth.models import User\n\nfrom paper_admin.admin.widgets import AdminCheckboxTree\n\n\nclass CustomUserChangeForm(UserChangeForm):\n    class Meta:\n        widgets = {\n            \"user_permissions\": AdminCheckboxTree,\n        }\n\n\nclass CustomUserAdmin(UserAdmin):\n    form = CustomUserChangeForm\n\n\nadmin.site.unregister(User)\nadmin.site.register(User, CustomUserAdmin)\n```\n\n![image](https://github.com/dldevinc/paper-admin/assets/6928240/72766127-ccc6-4538-ac4f-5dae14a30e1f)\n\nFor permission fields, you can take it a step further and utilize [paper-admin-permission-field](https://github.com/dldevinc/paper-admin-permission-field).\n\n## Settings\n\n`PAPER_FAVICON`<br>\nThe path to the favicon for the admin interface.<br>\nDefault: `\"paper_admin/dist/assets/default_favicon.png\"`\n\n`PAPER_ENVIRONMENT_NAME`<br>\nThe text of the environment badge.<br>\nDefault: `\"\"`\n\n`PAPER_ENVIRONMENT_COLOR`<br>\nThe color of the environment badge.<br>\nDefault: `\"\"`\n\n`PAPER_MENU`<br>\nA list of menu items. See [Admin menu](#admin-menu) for details.<br>\nDefault: `None`\n\n`PAPER_MENU_DIVIDER`<br>\nA string representing the menu item divider.<br>\nDefault: `\"-\"`\n\n`PAPER_MENU_STAFF_PERMISSION`<br>\nThe special permission string that allows access to the menu item for staff users.<br>\nDefault: `\"staff\"`\n\n`PAPER_MENU_SUPERUSER_PERMISSION`<br>\nThe special permission string that allows access to the menu item for superusers.<br>\nDefault: `\"superuser\"`\n\n`PAPER_MENU_COLLAPSE_SINGLE_CHILDS`<br>\nWhether to collapse a menu item if it has only one child.<br>\nDefault: `True`\n\n`PAPER_DEFAULT_TAB_NAME`<br>\nThe name of the tab to activate in the form by default.<br>\nDefault: `\"general\"`\n\n`PAPER_DEFAULT_TAB_TITLE`<br>\nThe title of the tab to activate in the form by default.<br>\nDefault: `_(\"General\")`\n\n`PAPER_LOCALE_PACKAGES`<br>\nThe list of packages to search for translation strings.<br>\nDefault: `[\"paper_admin\", \"django.contrib.admin\"]`\n\n`PAPER_NONE_PLACEHOLDER`<br>\nThe placeholder text for the \"None\" option in the filters.<br>\nDefault: `\u2400`\n\n## Additional References\n\n-   [Modals](docs/modals.md)\n",
    "bugtrack_url": null,
    "license": "BSD license",
    "summary": "Custom Django admin interface.",
    "version": "7.7.1",
    "project_urls": {
        "Homepage": "https://github.com/dldevinc/paper-admin"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "57516a44fc466584374fe9151e365fc8d8d8676922c6004216cc228a4e88c028",
                "md5": "636b274c1b035936f50843f5ba7f6a42",
                "sha256": "951b14a4f7c092fa18f783463a9f24caa346dc3cb4ecc274d52b09e20ae6fc80"
            },
            "downloads": -1,
            "filename": "paper_admin-7.7.1-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "636b274c1b035936f50843f5ba7f6a42",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": ">=3.7",
            "size": 3547541,
            "upload_time": "2024-05-25T12:49:41",
            "upload_time_iso_8601": "2024-05-25T12:49:41.115226Z",
            "url": "https://files.pythonhosted.org/packages/57/51/6a44fc466584374fe9151e365fc8d8d8676922c6004216cc228a4e88c028/paper_admin-7.7.1-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "92b126f09043b15b29a6ae9459023d4625ca58f85bff1dcfef815f8307e97c4d",
                "md5": "fc3018a254559453f3907192bb8fcf18",
                "sha256": "3e31567476838a7f5e76184c9734aad48ea76a47443a09540f8839f8e24a50ee"
            },
            "downloads": -1,
            "filename": "paper_admin-7.7.1.tar.gz",
            "has_sig": false,
            "md5_digest": "fc3018a254559453f3907192bb8fcf18",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 3485308,
            "upload_time": "2024-05-25T12:49:44",
            "upload_time_iso_8601": "2024-05-25T12:49:44.198327Z",
            "url": "https://files.pythonhosted.org/packages/92/b1/26f09043b15b29a6ae9459023d4625ca58f85bff1dcfef815f8307e97c4d/paper_admin-7.7.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-05-25 12:49:44",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "dldevinc",
    "github_project": "paper-admin",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "django-autocomplete-light",
            "specs": [
                [
                    "==",
                    "3.11.0"
                ]
            ]
        },
        {
            "name": "django-logentry-admin",
            "specs": [
                [
                    "==",
                    "1.1.0"
                ]
            ]
        },
        {
            "name": "django-money",
            "specs": [
                [
                    "==",
                    "3.5.2"
                ]
            ]
        },
        {
            "name": "django-mptt",
            "specs": [
                [
                    "==",
                    "0.14.0"
                ]
            ]
        },
        {
            "name": "django-mptt",
            "specs": [
                [
                    "==",
                    "0.16.0"
                ]
            ]
        },
        {
            "name": "django-solo",
            "specs": [
                [
                    "==",
                    "2.1.0"
                ]
            ]
        },
        {
            "name": "django-solo",
            "specs": [
                [
                    "==",
                    "2.2.0"
                ]
            ]
        },
        {
            "name": "django-tree-queries",
            "specs": [
                [
                    "==",
                    "0.15.0"
                ]
            ]
        },
        {
            "name": "django-tree-queries",
            "specs": [
                [
                    "==",
                    "0.19.0"
                ]
            ]
        },
        {
            "name": "Pillow",
            "specs": []
        },
        {
            "name": "pytest",
            "specs": [
                [
                    "==",
                    "7.4.4"
                ]
            ]
        },
        {
            "name": "pytest",
            "specs": [
                [
                    "==",
                    "8.2.1"
                ]
            ]
        },
        {
            "name": "pytest-cov",
            "specs": [
                [
                    "==",
                    "4.1.0"
                ]
            ]
        },
        {
            "name": "pytest-cov",
            "specs": [
                [
                    "==",
                    "5.0.0"
                ]
            ]
        },
        {
            "name": "pytest-django",
            "specs": [
                [
                    "==",
                    "4.5.2"
                ]
            ]
        },
        {
            "name": "pytest-django",
            "specs": [
                [
                    "==",
                    "4.8.0"
                ]
            ]
        },
        {
            "name": "pytest-xdist",
            "specs": [
                [
                    "==",
                    "3.5.0"
                ]
            ]
        },
        {
            "name": "pytest-xdist",
            "specs": [
                [
                    "==",
                    "3.6.1"
                ]
            ]
        }
    ],
    "tox": true,
    "lcname": "paper-admin"
}
        
Elapsed time: 0.27652s