Package: django-admin-toolkit
This package offers a wide choice of admin mixins to best customize your Django admin site,
with a multitude of useful features that will simplify your development.DocumentationThe full documentation is athttps://django-admin-toolkit.readthedocs.io.QuickstartInstall Django Admin Toolkit:pip install django-admin-toolkitAdd it to yourINSTALLED_APPS:INSTALLED_APPS=(...'admin_toolkit',...)AfterSaveAdminMixinThrough this mixin it is possible to insert your own logics at the end of saving the model via Django admin site.fromadmin_toolkit.admin_mixinsimportAfterSaveAdminMixin@admin.register(app_models.AfterSaveExampleModel)classAfterSaveExampleModelAdmin(AfterSaveAdminMixin,admin.ModelAdmin):list_display=("__str__","test_text")fields=("test_text",)AllReadonlyAdminMixinThis admin mixin will turn all your admin fields into read only fields.fromadmin_toolkit.admin_mixinsimportAllReadonlyAdminMixin@admin.register(app_models.AllReadonlyExampleModel)classAllReadonlyExampleModelAdmin(AllReadonlyAdminMixin,admin.ModelAdmin):list_display=("id","test_text",)fields=("test_text",)In the image you can see that there is no permission to add other objects because in the mixinhas_add_permission()returns False.And in the detail page it is possible to see that all the fields present are readonly.AllReadonlyAdminInlineMixinThis admin mixin will turn all your inline admin fields into read only fields.fromadmin_toolkit.admin_mixinsimportAllReadonlyAdminInlineMixinclassAllReadonlyExampleModelAdminInline(AllReadonlyAdminInlineMixin,admin.TabularInline):model=app_models.AllReadonlyExampleModelfields=("test_text",)In the image you can see that there is no permission to add other objects in inline because in the mixinhas_add_permission()returns False.
And it is possible to see that all the fields present are readonly.BaseAdminMixinThis mixin provides a wide variety of display methods for all (or almost all)
of the readonly fields that you will need to display in your Django admin site.Create your model and add theadmin_changelist_url()method and theget_ct()method to it.classBaseExampleModel(models.Model):objects=app_managers.BaseExampleModelManager.from_queryset(app_queryset.BaseExampleModelQuerySet)()test_boolean=models.BooleanField("Test bool",null=True)test_datetime=models.DateTimeField("Test datetime")test_fk=models.ForeignKey("example.BaseExampleFkModel",verbose_name="Test fk",on_delete=models.CASCADE,related_name="example_for_base_admin_mixins")test_image=models.ImageField("Test image",upload_to="example/images/")test_m2m=models.ManyToManyField("example.BaseExampleM2MModel",related_name="example_for_base_admin_mixins",)example_generic_relation_model_for_base_admin_mixin=GenericRelation("example.BaseExampleGenericRelationModel",related_query_name='example_model_for_base_admin_mixin')def__str__(self):returnstr(self.id)@classmethoddefadmin_changelist_url(cls):returnreverse("admin:example_baseexamplemodel_changelist")defget_ct(self):returnContentType.objects.get(app_label=self._meta.app_label,model=self._meta.model_name)Then registerBaseExampleModeland start to add all yours displays.fromadmin_toolkit.admin_mixinsimportBaseAdminMixin@admin.register(app_models.BaseExampleModel)classBaseExampleModelAdmin(BaseAdminMixin,admin.ModelAdmin):list_display=("__str__","display_test_boolean","display_test_datetime","display_test_date","display_test_time","display_test_fk","display_test_image","display_test_m2m",)fields=("test_boolean","test_datetime","test_fk","test_image","test_m2m")readonly_fields=("display_test_boolean","display_test_datetime","display_test_date","display_test_time","display_test_fk","display_test_image","display_test_m2m","display_generic_relation",)How to display a boolean field:@mark_safedefdisplay_test_boolean(self,obj):ifobjandobj.test_boolean:returnself._display_boolean(obj.test_boolean)return""How to display a datetime (work only with datetime):@mark_safedefdisplay_test_datetime(self,obj):ifobjandobj.test_datetime:returnself._display_datetime(obj.test_datetime)return""How to display a date (work only with datetime):@mark_safedefdisplay_test_date(self,obj):ifobjandobj.test_datetime:returnself._display_date(obj.test_datetime)return""How to display a time (work only with datetime):@mark_safedefdisplay_test_time(self,obj):ifobjandobj.test_datetime:returnself._display_time(obj.test_datetime)return""How to display a ForeignKey:@mark_safedefdisplay_test_fk(self,obj):ifobjandobj.test_fk:returnself._display_fk_object(obj.test_fk)return""How to display an image:@mark_safedefdisplay_test_image(self,obj):ifobjandobj.test_image:returnself._display_image(obj.test_image)return""How to display a ManyToMany:@mark_safedefdisplay_test_m2m(self,obj):ifobjandobj.test_m2m:returnself._display_m2m_objects(obj,m2m_field_name="test_m2m",label="Example M2Ms")return""How to display a GenericRelation:@mark_safedefdisplay_generic_relation(self,obj):ifobjandobj.id:returnself._display_generic_related_objects(obj,"example_generic_relation_model_for_base_admin_mixin","Example Generic Relations")return""This is the final changelist result:ConfigurableWidgetsAdminMixinUse ConfigurableWidgetsMixinAdmin if you want to customize quickly
default widget/label/help_text or every related admin form configurations
without doing modifications of the auto created ModelForm.In this example I am going to modify the help_text and the widgets of the three fields.# ==================# project/models.py# ==================classConfigurableWidgetsExampleModel(models.Model):objects=ConfigurableWidgetsExampleModelManager.from_queryset(ConfigurableWidgetsExampleModelQuerySet)()test_text=models.CharField("Test Text",max_length=500,default="",blank=True)test_fk=models.ForeignKey(ConfigurableWidgetsExampleFKModel,verbose_name="Test FK",on_delete=models.SET_NULL,null=True)test_m2m=models.ManyToManyField(ConfigurableWidgetsExampleM2MModel,verbose_name="Test M2M")classMeta:verbose_name="Configurable widgets example model"verbose_name_plural="Configurable widgets example models"# ==================# project/admin.py# ==================fromadmin_toolkit.admin_mixinsimportConfigurableWidgetsAdminMixin@admin.register(app_models.ConfigurableWidgetsExampleModel)classConfigurableWidgetsExampleModelAdmin(ConfigurableWidgetsAdminMixin,admin.ModelAdmin):list_display=("id","test_text",)fieldsets=((None,{"fields":(("test_text",),("test_fk","test_m2m",),)}),)filter_horizontal=("test_m2m",)dbfield_overrides={"test_text":{"help_text":"Test Text Example help text","widget":forms.Textarea},}fkfield_overrides={"test_fk":{"help_text":"Test FK Example help text","widget":forms.RadioSelect},}m2mfield_overrides={"test_m2m":{"help_text":"Test M2M Example help text","widget":forms.CheckboxSelectMultiple}}This is the final result:DetailInInlineAdminMixinThis admin mixin allows you to have in each line of the inline a button that redirects to the change form of the element created.fromadmin_toolkit.admin_mixinsimportDetailInInlineAdminMixinclassDetailInInlineExampleModelAdminInline(DetailInInlineAdminMixin,admin.TabularInline):fields=("test_text",)model=app_models.DetailInInlineExampleModelThis is the final result:EmptyValueAdminMixinThis admin mixin allows you to define a label for a foreign key field for the empty value.@admin.register(app_models.EmptyValueExampleModel)classEmptyValueExampleModelAdmin(EmptyValueAdminMixin,admin.ModelAdmin):list_display=("id","test_text","test_fk",)fields=("test_text","test_fk")empty_values={"test_fk":_("NO TEST FK")}This is the final result:ExtraButtonAdminMixinThis admin mixin allows you to add extra buttons links next to the add button in a simple and immediate way.@admin.register(app_models.ExtraButtonExampleModel)classExtraButtonExampleModelAdmin(ExtraButtonAdminMixin,admin.ModelAdmin):list_display=("id","test_text")fields=("test_text",)extra_button=[{"label":"Example Extra Button","url":"http://example.com","class":""}]This is the final result:FloatingAdminMixinThis mixin makes the list filters collapsible vertically
in such a way as to have more space for any columns that otherwise would not be seen.@admin.register(app_models.FloatingExampleModel)classFloatingExampleModelAdmin(FloatingAdminMixin,admin.ModelAdmin):list_display=("id","test_text")list_filter=("test_text",)fields=("test_text",)This is the final result:When the list filter is collapsed.When the list filter is decollapsed.ImprovedRawIdFieldsAdminMixinThis admin mixin allows you to view the link of the changelist/changeform
of the selected objects inside the raw_id_field on ForeignKey and ManyToMany.@admin.register(app_models.ImprovedRawIdFieldsExampleModel)classImprovedRawIdFieldsExampleModelAdmin(ImprovedRawIdFieldsAdminMixin,admin.ModelAdmin):improved_raw_id_fields=["test_fk","test_m2m"]list_display=("id","test_name",)fieldsets=((None,{"fields":(("test_name",),("test_fk","test_m2m"),)}),)This is the result:When adding an object.When changing an object.ADMIN FILTERSThis package also comes with a set of list filters
that you can use to make it easier to write filters with special queries.CustomRelatedSelectFilterUsing this filter on Many to Many or Foreign Key it is possible
to have a list of elements associated with the field inserted in the list_filter
which becomes a select if the list exceeds 4 elements ordered according to a specific
field of the foreign key or many to many.classCustomRelatedSelectFilterForTestFK(admin_filters.CustomRelatedSelectFilter):defget_related_order(self):return["test_char"]classCustomRelatedSelectFilterForTestM2M(admin_filters.CustomRelatedSelectFilter):defget_related_order(self):return["test_char"]@admin.register(app_models.AdminFilterExampleModel)classAdminFilterExampleModelAdmin(admin.ModelAdmin):list_display=("id","test_char","get_test_choice_display","test_fk")list_filter=(("test_fk",CustomRelatedSelectFilterForTestFK),("test_m2m",CustomRelatedSelectFilterForTestM2M),)fieldsets=((None,{"fields":(("test_char","test_choice","test_fk","test_m2m"),)}),)This are the results:For the field test_fk:For the field test_m2m:RelatedSelectFilterUsing this filter on Many to Many or Foreign Key it is possible
to have a list of elements associated with the field inserted in the list_filter
which becomes a select if the list exceeds 4 elements.@admin.register(app_models.AdminFilterExampleModel)classAdminFilterExampleModelAdmin(admin.ModelAdmin):list_display=("id","test_char","get_test_choice_display","test_fk")list_filter=(("test_fk",RelatedSelectFilter),)fieldsets=((None,{"fields":(("test_char","test_choice","test_fk","test_m2m"),)}),)SelectFilterThis filter can be used on fields that contain choices to be able to display them in a select instead of seeing a bulleted list.@admin.register(app_models.AdminFilterExampleModel)classAdminFilterExampleModelAdmin(admin.ModelAdmin):list_display=("id","test_char","get_test_choice_display","test_fk")list_filter=(("test_choice",SelectFilter),)fieldsets=((None,{"fields":(("test_char","test_choice","test_fk","test_m2m"),)}),)This is the result:SimpleBooleanListFilterThis filter can be used to give a given query a boolean filter like this example:# tests/example/admin_filters.pyclassSimpleBooleanTestInTestCharFilter(admin_filters.SimpleBooleanListFilter):title="Test word is in Test char?"parameter_name="test_char"defget_true_queryset_values(self,queryset):returnqueryset.filter(test_char__icontains="test")defget_false_queryset_values(self,queryset):returnqueryset.exclude(test_char__icontains="test")# tests/example/admin.pyfromtests.exampleimportadmin_filtersasapp_admin_filters@admin.register(app_models.AdminFilterExampleModel)classAdminFilterExampleModelAdmin(admin.ModelAdmin):list_display=("id","test_char","get_test_choice_display","test_fk")list_filter=(app_admin_filters.SimpleBooleanTestInTestCharFilter,)fieldsets=((None,{"fields":(("test_char","test_choice","test_fk","test_m2m"),)}),)This is the result:Running TestsDoes the code actually work?source <YOURVIRTUALENV>/bin/activate
(myenv) $ pip install tox
(myenv) $ toxDevelopment commandspip install -r requirements_dev.txt
invoke -lCreditsTools used in rendering this package:Cookiecuttercookiecutter-djangopackageHistory0.1.0 (2021-11-10)First release on PyPI.0.1.1 (2021-11-10)Fixed HISTORY.rst problems with long_description_content_type0.1.3 (2021-11-10)Fixed HISTORY.rst problems changing + to _ for versions.0.1.4 (2021-12-6)Fixed bug on ConfigurableWidgetsAdminMixin.