from django.contrib import admin from mptt.admin import MPTTModelAdmin, DraggableMPTTAdmin from .models import Item, BOMNode, EntityType, RoutingStep, WorkCenter # Register your models here. # Создаем инлайн для отображения RoutingStep в Item class RoutingStepInline(admin.TabularInline): model = RoutingStep extra = 1 # Поле tech_params в стандартной админке — это просто текстовое поле. # Позже его можно будет "причесать" с помощью JS, чтобы оно выглядело красиво. fields = ('order', 'operation_type', 'work_center', 'drawing_file', 'tech_params') # Создаем инлайн для отображения BOMNode в Item class BOMNodeInline(admin.TabularInline): model = BOMNode # Указываем fk_key т fk_name = 'parent' # Указываем поля для отображения в инлайне fields = ('item', 'quantity') # Колличество пустых строк в инлайне extra = 3 # добавим автокомплит для удобства autocomplete_fields = ['item'] def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "parent": # Родителями в инлайне могут быть только Сборки/Изделия/Комплексы kwargs["queryset"] = BOMNode.objects.filter( item__entity_type__in=[EntityType.UNIT, EntityType.ASSEMBLY, EntityType.COMPLEX] ) return super().formfield_for_foreignkey(db_field, request, **kwargs) @admin.register(Item) class ItemAdmin(admin.ModelAdmin): list_display = ('get_full_name', 'entity_type', 'drawing') list_filter = ('entity_type', 'is_assembly') # Поиск по номеру и названию search_fields = ['designation', 'title'] @admin.display(description='Наименование') def get_full_name(self, obj): return f"{obj.designation or ''} {obj.title}" inlines = [RoutingStepInline] # def get_queryset(self, request): # qs = super().get_queryset(request) # return qs.filter(entity_type=EntityType.ASSEMBLY) @admin.register(BOMNode) class BOMNodeAdmin(DraggableMPTTAdmin): # Настройки отображения древовидной структуры mptt_level_indent = 40 # отступ в пикселях # Поля для отображения в админке fields = ('parent', 'item', 'quantity') # Чтобы в списке было видно не только название связи, но и данные из Item list_display = ('tree_actions', 'indented_title', 'get_designation', 'quantity') list_display_links = ('indented_title',) # Отображаем инлайн. inlines = [BOMNodeInline] # Метод для отображения децимального номера из связанной модели Item def get_designation(self, obj): return obj.item.designation get_designation.short_description = 'Децимальный номер' # Оптимизация запросов (чтобы админка не тормозила) def get_queryset(self, request): qs = super().get_queryset(request) return qs.select_related('item', 'parent') # def formfield_for_foreignkey(self, db_field, request, **kwargs): # if db_field.name == "parent": # # Фильтруем: родителями могут быть только узлы, # # где связанный Item является сборкой или изделием # kwargs["queryset"] = BOMNode.objects.filter( # item__entity_type__in=[EntityType.UNIT, EntityType.ASSEMBLY] # ) # return super().formfield_for_foreignkey(db_field, request, **kwargs) # переопределим метод get_inlines в BOMNodeAdmin. # Если узел привязан к «Детали» или «Стандартному изделию», таблица внизу просто не появится. def get_inlines(self, request, obj=None): # Если мы создаем новый узел (obj is None), инлайн можно показать if obj is None: return [] # Разрешенные типы (те, в которые МОЖНО вкладывать) allowed_types = [EntityType.UNIT, EntityType.ASSEMBLY, EntityType.COMPLEX] # Если тип изделия связанного узла в списке разрешенных — показываем инлайн if obj.item.entity_type in allowed_types: return [BOMNodeInline] # Для остальных (детали, стандартные) возвращаем пустой список return [] @admin.register(RoutingStep) class RoutingStepAdmin(admin.ModelAdmin): # Поля для отображения в админке list_display = ('item', 'operation_type', 'work_center', 'order') list_filter = ('operation_type', 'work_center') search_fields = ['item__title'] @admin.register(WorkCenter) class WorkCenterAdmin(admin.ModelAdmin): list_display = ('name', 'rate_per_hour') search_fields = ['name']