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