Package: paper-admin
paper-adminCustom Django admin interface based on Bootstrap 4.RequirementsPython >= 3.7Django >= 3.2Table of ContentsInstallationPatchesBadgeAdmin menuReorderable drag-and-drop listsForm tabsForm includesHierarchyFilterStylizationFieldsetsTable rowsInline formsSettingsAdditional ReferencesInstallationInstall the latest release with pip:pipinstallpaper-adminAddpaper_adminto your INSTALLED_APPS settingbeforedjango.contrib.admin.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",# ...]PatchesSome third-party libraries override the standard Django templates and within thepaper-admininterface look disfigured. To fix these, you can use the patches included in this package.The following patches are available:paper_admin.patches.dalFixes the style of thedjango-autocomplete-lightwidgets.paper_admin.patches.django_moneyFixes the style of thedjango-moneywidget.paper_admin.patches.django_soloFixes the breadcrumbs indjango-solo.paper_admin.patches.mpttAdaptation ofdjango-mptt.
Adds the ability to sort tree nodes (when thesortableproperty is specified).paper_admin.patches.logentry_adminFixes the filters and hides unwanted buttons indjango-logentry-admin.paper_admin.patches.tree_queriesAdds the ability to sort tree nodes fordjango-tree-queries.It is necessaryto use the specialTreeNodeModelAdminclass instead of the standardModelAdmin:# admin.pyfromdjango.contribimportadminfrompaper_admin.patches.tree_queries.adminimportTreeNodeModelAdmin# <--from.modelsimportMyTreeNode@admin.register(MyTreeNode)classMyTreeNodeAdmin(TreeNodeModelAdmin):...sortable="position"To use a patch, you need to add it to theINSTALLED_APPSsetting.Note: as a rule, patches should be addedbeforethe libraries they fix.BadgeBadge is a text description of the environment to prevent confusion between the
development and production servers.The color and text of the badge can be changed in thesettings.pyfile:PAPER_ENVIRONMENT_NAME="development"PAPER_ENVIRONMENT_COLOR="#FFFF00"Admin menuThepaper-adminprovides 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.UsageTo 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:fromdjango.utils.translationimportgettext_lazyas_frompaper_admin.menuimportItem,DividerPAPER_MENU=[Item(label=_("Dashboard"),url="admin:index",icon="bi-speedometer2",),Divider(),Item(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",),])]ItemTheItemclass 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.Theappandmodelparameters cannot be set simultaneously. However, you must
specify at least one of the following parameters:app,model, orlabel.Example usage:fromdjango.utils.translationimportgettext_lazyas_frompaper_admin.menuimportItemPAPER_MENU=[# Menu item with a specified label and URLItem(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 itemItem(label=_("Logs"),icon="bi-clock-history",perms="admin.view_logentry",children=[Item(model="admin.LogEntry",),]),# Add CSS classes and a target attribute to a linkItem(label="Google",url="https://google.com/",icon="bi-google",classes="text-warning",target="_blank",)]When defining permissions for menu items using thepermsparameter, you can use
the special valuesPAPER_MENU_SUPERUSER_PERMISSION(default issuperuser)
andPAPER_MENU_STAFF_PERMISSION(default isstaff) to restrict access to
superusers and staff members respectively.Example:frompaper_admin.menuimportItemPAPER_MENU=[Item(app="payments",icon="bi-money",perms=["staff"],children=[Item(model="Payment",perms=["superuser"],),Item(model="Invoice",),]),]DividerTheDividerclass 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.fromdjango.utils.translationimportgettext_lazyas_frompaper_admin.menuimportItem,DividerPAPER_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="#",),]GroupTheGroupclass represents a group of menu items. It can be used to group related
items together under a common heading.fromdjango.utils.translationimportgettext_lazyas_frompaper_admin.menuimportItem,GroupPAPER_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="#",),]),]Reorderable drag-and-drop listsThepaper-adminpackage provides the ability to reorder items in lists using
drag-and-drop. To enable this feature, you need to set thesortableproperty of
the model'sModelAdminto the name of the field that stores the order.# models.pyfromdjango.dbimportmodelsclassCompany(models.Model):# ...position=models.IntegerField("position",default=0)# admin.pyfromdjango.contribimportadminclassCompanyAdmin(admin.ModelAdmin):# ...sortable="position"https://user-images.githubusercontent.com/6928240/125331456-0f1bb280-e359-11eb-8b17-b04be4b1e62c.mp4The sorting is performed using AJAX and is saved to the database automatically.Reorderable inline formsThepaper-adminpackage 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.fromdjango.contribimportadminclassStaffInline(admin.StackedInline):# ...sortable="position"classIndustryInline(admin.TabularInline):# ...sortable="position"https://user-images.githubusercontent.com/6928240/125331956-b6004e80-e359-11eb-8422-832dfe37bb6c.mp4The 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.mp4Form tabsThepaper-adminprovides 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 thetabsproperty of the model'sModelAdminto a list of tab definitions.fromdjango.contribimportadminfromdjango.utils.translationimportgettext_lazyas_classTablularInline(admin.TabularInline):tab='inlines'@admin.register(Page)classPageAdmin(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.mp4Tabs can also be dynamically generated by the `get_tabs()` method:fromdjango.contribimportadminfromdjango.utils.translationimportgettext_lazyas_from.modelsimportPage@admin.register(Page)classPageAdmin(admin.ModelAdmin):defget_tabs(self,request,obj=None):return[('general',_('General')),('content',_('Content')),('seo',_('SEO')),]Form includespaper-adminprovides a convenient way to include custom templates within admin forms
at various positions, such astop,middle, orbottom. These positions correspond
to different sections of the admin form page.Each custom form include definition can include the following parameters:Path to Template (Required): The path to the template you want to include within
the form.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.Tab Name (Optional): If you are using Form tabs, you can specify the name of
the tab where the include should be displayed.fromdjango.contribimportadminfromdjango.utils.translationimportgettext_lazyas_from..modelsimportPerson@admin.register(Person)classPersonAdmin(admin.ModelAdmin):# ...tabs=[("general",_("General")),("pets",_("Pets")),]form_includes=[("app/includes/disclaimer.html","top","general"),("app/includes/privacy_notice.html",),]<!-- app/includes/privacy_notice.html --><divclass="paper-card paper-card--info card my-3"><divclass="card-header"><h5class="card-title mb-0">Privacy notice</h5></div><divclass="card-body"><smallclass="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:In addition to the standard way of including custom templates in forms usingpaper-admin,
there's the capability to dynamically create includes using theget_form_includesmethod.
This enables you to flexibly determine which templates to include based on the current
request or other conditions.fromdjango.contribimportadminfrom..modelsimportPerson@admin.register(Person)classPersonAdmin(admin.ModelAdmin):defget_form_includes(self,request,obj=None):ifrequest.user.is_superuser:return[]return[("app/includes/disclaimer.html","top","general"),("app/includes/privacy_notice.html",),]HierarchyFilterHierarchyFilteris a special type of filter that can be used to filter objects by a
hierarchical model field. It is similar to thedate_hierarchyfilter, but it works
with any hierarchical model field.fromdjango.contribimportadminfromdjango.utils.translationimportgettext_lazyas_frompaper_admin.admin.filtersimportHierarchyFilterfrom.modelsimportGroup,MessageclassGroupFilter(HierarchyFilter):title=_("Group")parameter_name="group"deflookups(self,changelist):return((pk,name)forpk,nameinGroup.objects.values_list("pk","name"))defqueryset(self,request,queryset):value=self.value()ifnotvalue:returnquerysetreturnqueryset.filter(group__in=value)@admin.register(Message)classMessageAdmin(admin.ModelAdmin):# ...list_filters=[GroupFilter]Result:StylizationTable rowsYou can use theget_row_classesmethod of theModelAdminclass to add custom classes to the rows in the list view.fromdjango.contribimportadminfrom.modelsimportCategory@admin.register(Category)classCategoryAdmin(admin.ModelAdmin):defget_row_classes(self,request,obj):ifobj.status=="success":return["table-success"]elifobj.status=="failed":return["table-danger"]return[]FieldsetsDjangoprovidesa way to add a custom CSS classes to the fieldsets in the admin interface.To use this feature, specify theclassesparameter in theModelAdmin.fieldsetsattribute:fromdjango.contribimportadminfromdjango.utils.translationimportgettext_lazyas_from.modelsimportCategory@admin.register(Category)classCategoryAdmin(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":(# ...)}),)Inline formsYou can use theget_form_classesmethod of theModelAdminclass
to add custom classes to the inline forms:fromdjango.contribimportadminclassStackedInline(admin.StackedInline):defget_form_classes(self,request,obj):ifobj.status=="success":return["paper-card--success"]elifobj.status=="failed":return["paper-card--danger"]return[]classTablularInlines(admin.TabularInline):defget_form_classes(self,request,obj):ifobj.status=="success":return["table-success"]elifobj.status=="failed":return["table-danger"]return[]SettingsPAPER_FAVICONThe path to the favicon for the admin interface.Default:"paper_admin/dist/assets/default_favicon.png"PAPER_ENVIRONMENT_NAMEThe text of the environment badge.Default:""PAPER_ENVIRONMENT_COLORThe color of the environment badge.Default:""PAPER_MENUA list of menu items. SeeAdmin menufor details.Default:NonePAPER_MENU_DIVIDERA string representing the menu item divider.Default:"-"PAPER_MENU_STAFF_PERMISSIONThe special permission string that allows access to the menu item for staff users.Default:"staff"PAPER_MENU_SUPERUSER_PERMISSIONThe special permission string that allows access to the menu item for superusers.Default:"superuser"PAPER_MENU_COLLAPSE_SINGLE_CHILDSWhether to collapse a menu item if it has only one child.Default:TruePAPER_DEFAULT_TAB_NAMEThe name of the tab to activate in the form by default.Default:"general"PAPER_DEFAULT_TAB_TITLEThe title of the tab to activate in the form by default.Default:_("General")PAPER_LOCALE_PACKAGESThe list of packages to search for translation strings.Default:["paper_admin", "django.contrib.admin"]PAPER_NONE_PLACEHOLDERThe placeholder text for the "None" option in the filters.Default:␀Additional ReferencesModals