use mixin instead of repeating patterns (#12977)

This commit is contained in:
Matthias Mair authored and GitHub committed 2026-10-08 20:26:25 +12:00
1 parent df579c094b
commit 5a2d223cd0
6 files changed
+46 -115

No files matched your search

+5 -28
View File
@@ -1,7 +1,6 @@
"""Admin classes."""
from django.contrib import admin
from django.http.request import HttpRequest
from django.utils import timezone
from allauth.usersessions.admin import UserSessionAdmin
@@ -11,14 +10,12 @@ from django_q.models import Schedule
from djmoney.contrib.exchange.admin import RateAdmin
from djmoney.contrib.exchange.models import Rate
from common.admin_shared import NoAddAdminMixin, NoEditAdminMixin, ReadOnlyAdminMixin
class CustomRateAdmin(RateAdmin):
class CustomRateAdmin(NoAddAdminMixin, RateAdmin):
"""Admin interface for the Rate class."""
def has_add_permission(self, request: HttpRequest) -> bool:
"""Disable the 'add' permission for Rate objects."""
return False
admin.site.unregister(Rate)
admin.site.register(Rate, CustomRateAdmin)
@@ -34,39 +31,19 @@ def run_schedule_now(modeladmin, request, queryset):
run_schedule_now.short_description = 'Run selected tasks now'
class ReadOnlyScheduleAdmin(ScheduleAdmin):
class ReadOnlyScheduleAdmin(ReadOnlyAdminMixin, ScheduleAdmin):
"""Read-only admin interface for django-q Schedule objects."""
actions = [run_schedule_now]
def has_add_permission(self, request: HttpRequest) -> bool:
"""Prevent adding new Schedule objects."""
return False
def has_change_permission(self, request: HttpRequest, obj=None) -> bool:
"""Prevent changing existing Schedule objects."""
return False
def has_delete_permission(self, request: HttpRequest, obj=None) -> bool:
"""Prevent deleting Schedule objects."""
return False
admin.site.unregister(Schedule)
admin.site.register(Schedule, ReadOnlyScheduleAdmin)
class InvenTreeUserSessionAdmin(UserSessionAdmin):
class InvenTreeUserSessionAdmin(NoEditAdminMixin, UserSessionAdmin):
"""Admin interface for UserSession - view and delete only, no add or edit."""
def has_add_permission(self, request: HttpRequest) -> bool:
"""Prevent creating sessions via admin."""
return False
def has_change_permission(self, request: HttpRequest, obj=None) -> bool:
"""Prevent editing sessions via admin."""
return False
admin.site.unregister(UserSession)
admin.site.register(UserSession, InvenTreeUserSessionAdmin)
+8 -59
View File
@@ -4,6 +4,7 @@ from django.contrib import admin
import common.models
import common.validators
from common.admin_shared import NoEditAdminMixin, ReadOnlyAdminMixin
@admin.register(common.models.ParameterTemplate)
@@ -89,7 +90,7 @@ class AttachmentAdmin(admin.ModelAdmin):
@admin.register(common.models.DataOutput)
class DataOutputAdmin(admin.ModelAdmin):
class DataOutputAdmin(NoEditAdminMixin, admin.ModelAdmin):
"""Admin interface for DataOutput objects - view and delete only."""
list_display = ('user', 'created', 'output_type', 'output')
@@ -106,17 +107,9 @@ class DataOutputAdmin(admin.ModelAdmin):
autocomplete_fields = ('user',)
def has_add_permission(self, request):
"""Prevent addition of new DataOutput objects via the admin interface."""
return False
def has_change_permission(self, request, obj=None):
"""Prevent modification of DataOutput objects via the admin interface."""
return False
@admin.register(common.models.BarcodeScanResult)
class BarcodeScanResultAdmin(admin.ModelAdmin):
class BarcodeScanResultAdmin(NoEditAdminMixin, admin.ModelAdmin):
"""Admin interface for BarcodeScanResult objects - read-only audit log."""
list_display = ('data', 'timestamp', 'user', 'endpoint', 'result')
@@ -127,14 +120,6 @@ class BarcodeScanResultAdmin(admin.ModelAdmin):
autocomplete_fields = ('user',)
def has_add_permission(self, request):
"""Prevent addition of new BarcodeScanResult objects via the admin interface."""
return False
def has_change_permission(self, request, obj=None):
"""Prevent modification of BarcodeScanResult objects via the admin interface."""
return False
@admin.register(common.models.ProjectCode)
class ProjectCodeAdmin(admin.ModelAdmin):
@@ -196,24 +181,16 @@ class WebhookAdmin(admin.ModelAdmin):
@admin.register(common.models.NotificationEntry)
class NotificationEntryAdmin(admin.ModelAdmin):
class NotificationEntryAdmin(NoEditAdminMixin, admin.ModelAdmin):
"""Admin settings for NotificationEntry - view and delete only."""
list_display = ('key', 'uid', 'updated')
search_fields = ('key', 'uid')
def has_add_permission(self, request):
"""Prevent addition of new NotificationEntry objects via the admin interface."""
return False
def has_change_permission(self, request, obj=None):
"""Prevent modification of NotificationEntry objects via the admin interface."""
return False
@admin.register(common.models.NotificationMessage)
class NotificationMessageAdmin(admin.ModelAdmin):
class NotificationMessageAdmin(NoEditAdminMixin, admin.ModelAdmin):
"""Admin settings for NotificationMessage - view and delete only."""
list_display = (
@@ -232,46 +209,18 @@ class NotificationMessageAdmin(admin.ModelAdmin):
autocomplete_fields = ('user',)
def has_add_permission(self, request):
"""Prevent addition of new NotificationMessage objects via the admin interface."""
return False
def has_change_permission(self, request, obj=None):
"""Prevent modification of NotificationMessage objects via the admin interface."""
return False
@admin.register(common.models.NewsFeedEntry)
class NewsFeedEntryAdmin(admin.ModelAdmin):
class NewsFeedEntryAdmin(NoEditAdminMixin, admin.ModelAdmin):
"""Admin settings for NewsFeedEntry - view and delete only."""
list_display = ('title', 'author', 'published', 'summary')
search_fields = ('title', 'author', 'summary')
def has_add_permission(self, request):
"""Prevent addition of new NewsFeedEntry objects via the admin interface."""
return False
def has_change_permission(self, request, obj=None):
"""Prevent modification of NewsFeedEntry objects via the admin interface."""
return False
class ReadOnlyAdmin(admin.ModelAdmin):
"""Base admin class that prevents all modifications."""
def has_add_permission(self, request):
"""Prevent addition of new objects via the admin interface."""
return False
def has_change_permission(self, request, obj=None):
"""Prevent modification of objects via the admin interface."""
return False
def has_delete_permission(self, request, obj=None):
"""Prevent deletion of objects via the admin interface."""
return False
class ReadOnlyAdmin(ReadOnlyAdminMixin, admin.ModelAdmin):
"""Admin class that makes the model read-only."""
admin.site.register(common.models.WebhookMessage, ReadOnlyAdmin)
@@ -0,0 +1,25 @@
"""Helpers for controlling admin interface permissions."""
class NoAddAdminMixin:
"""Mixin to prevent addition of new objects via the admin interface."""
def has_add_permission(self, request, obj=None):
"""Prevent addition of new objects via the admin interface."""
return False
class NoEditAdminMixin(NoAddAdminMixin):
"""Base admin class that prevents editing of objects."""
def has_change_permission(self, request, obj=None):
"""Prevent modification of objects via the admin interface."""
return False
class ReadOnlyAdminMixin(NoEditAdminMixin):
"""Base admin class that prevents all modifications."""
def has_delete_permission(self, request, obj=None):
"""Prevent deletion of objects via the admin interface."""
return False
+2 -5
View File
@@ -2,20 +2,17 @@
from django.contrib import admin
from common.admin_shared import NoAddAdminMixin
from machine import models
class MachineSettingInline(admin.TabularInline):
class MachineSettingInline(NoAddAdminMixin, admin.TabularInline):
"""Inline admin class for MachineSetting."""
model = models.MachineSetting
readonly_fields = ['key', 'config_type']
def has_add_permission(self, request, obj):
"""The machine settings should not be meddled with manually."""
return False
@admin.register(models.MachineConfig)
class MachineConfigAdmin(admin.ModelAdmin):
+3 -10
View File
@@ -2,6 +2,7 @@
from django.contrib import admin
from common.admin_shared import NoAddAdminMixin
from plugin import models
from plugin.registry import registry as pl_registry
@@ -34,29 +35,21 @@ def plugin_deactivate(modeladmin, request, queryset):
plugin_update(queryset, False)
class PluginSettingInline(admin.TabularInline):
class PluginSettingInline(NoAddAdminMixin, admin.TabularInline):
"""Inline admin class for PluginSetting."""
model = models.PluginSetting
readonly_fields = ['key']
def has_add_permission(self, request, obj):
"""The plugin settings should not be meddled with manually."""
return False
class PluginUserSettingInline(admin.TabularInline):
class PluginUserSettingInline(NoAddAdminMixin, admin.TabularInline):
"""Inline admin class for PluginUserSetting."""
model = models.PluginUserSetting
readonly_fields = ['key']
def has_add_permission(self, request, obj):
"""The plugin user settings should not be meddled with manually."""
return False
class PluginConfigAdmin(admin.ModelAdmin):
"""Custom admin with restricted id fields."""
+3 -13
View File
@@ -3,6 +3,8 @@
from django.contrib import admin
from django.db.models import Count
from common.admin_shared import ReadOnlyAdminMixin
from .models import (
StockItem,
StockItemTestResult,
@@ -77,7 +79,7 @@ class StockItemAdmin(admin.ModelAdmin):
@admin.register(StockItemTracking)
class StockTrackingAdmin(admin.ModelAdmin):
class StockTrackingAdmin(ReadOnlyAdminMixin, admin.ModelAdmin):
"""Admin class for StockTracking - read-only to preserve audit trail integrity."""
list_display = ('item', 'date', 'label')
@@ -86,18 +88,6 @@ class StockTrackingAdmin(admin.ModelAdmin):
autocomplete_fields = ['item']
def has_add_permission(self, request):
"""Prevent addition of new tracking entries via the admin interface."""
return False
def has_change_permission(self, request, obj=None):
"""Prevent modification of tracking entries via the admin interface."""
return False
def has_delete_permission(self, request, obj=None):
"""Prevent deletion of tracking entries via the admin interface."""
return False
@admin.register(StockItemTestResult)
class StockItemTestResultAdmin(admin.ModelAdmin):