mirror of
https://github.com/inventree/InvenTree.git
synced 2026-08-21 20:45:13 +00:00
Fix admin search fields and misc code quality issues (#12525)
* Fix admin search for StockItemTracking and StockItemTestResult Add search_fields to StockTrackingAdmin and StockItemTestResultAdmin - item__part__name: search tracking by part name - item__serial: search tracking by serial number - notes: search tracking notes - stock_item__part__name: search test results by part name - stock_item__serial: search test results by serial number - template__test_name: search test results by test template name - value: search test results by output value - notes: search test result notes Also adds a unit test to verify search_fields configuration. * Fix admin search for PartPricing, PartStocktake, PartRelated, and PartTestTemplate Add search_fields to PartPricingAdmin, PartStocktakeAdmin, PartRelatedAdmin, and PartTestTemplateAdmin. - part__name, part__IPN, part__description: search PartPricing - part__name, part__IPN: search PartStocktake - part_1__name, part_2__name: search PartRelated - part__name, test_name, description: search PartTestTemplate Also adds unit test assertions to verify search_fields configuration. * Fix admin search for SalesOrderAllocation and ReturnOrderLineItem Add search_fields to SalesOrderAllocationAdmin and ReturnOrderLineItemAdmin. SalesOrderAllocationAdmin: - line__order__reference: search by Sales Order reference - line__part__name: search by ordered Part name - item__part__name: search by allocated Stock Item part name - item__part__IPN: search by allocated Stock Item IPN - item__serial: search by Stock Item serial number ReturnOrderLineItemAdmin: - order__reference: search by Return Order reference - order__customer__name: search by Customer name - item__part__name: search by returned Item part name - item__serial: search by returned Item serial number - reference: search by line item reference Also adds list_display improvements and unit tests to verify search_fields configuration. * Fix incorrect identity comparison for status validation Use '!=' (value comparison) instead of 'is not' (identity comparison) when comparing custom_status.logical_key with self.instance.status. Python only caches small integers (-5 to 256). For status codes > 256, 'is not' can return True even when values are equal, causing valid custom status keys to be incorrectly rejected. Per PEP 8: always use '==' or '!=' for value comparisons. * Fix wrong super() method call in DataImportColumnMapAdmin The formfield_for_dbfield method was incorrectly calling super().formfield_for_choice_field() instead of super().formfield_for_dbfield(). These are different Django admin methods with different expectations. formfield_for_choice_field expects choice-type fields, but the 'column' field is a plain CharField. This could cause incorrect form rendering or errors when viewing DataImportSession detail in Django Admin. Fix: call the correct parent method formfield_for_dbfield(). * Fix broken delete() method signature on EmailMessage model The delete() method used '*kwargs' which collects positional arguments into a tuple named 'kwargs'. This breaks Django's Model.delete() contract which expects keyword arguments (using=None, keep_parents=False). When super().delete(*kwargs) was called, keyword arguments passed by Django internals would be unpacked incorrectly as positional args. Fix: use standard '*args, **kwargs' signature and pass both to super(). * Fix bare except clause in order status validation Replace bare 'except:' with 'except Exception:' in validate_status_custom_key method. Bare except catches all BaseException subclasses including SystemExit, KeyboardInterrupt, and MemoryError which should never be silenced. The get_logical_value() function performs a database .get() call that can raise ObjectDoesNotExist or MultipleObjectsReturned, both of which are subclasses of Exception. This follows PEP 8 (E722: do not use bare except). * Fix bare except clauses in machine registry and barcode mixins Replace bare 'except:' with 'except Exception:' in two locations: - machine/registry.py: hash computation catches AttributeError or DoesNotExist when a machine config no longer exists - plugin/base/barcodes/mixins.py: has_barcode_generation property catches any error from calling generate(None) on a plugin Bare except catches all BaseException subclasses including SystemExit and KeyboardInterrupt which should never be silenced. This follows PEP 8 (E722: do not use bare except). * Fix readonly_fields typos and add search_fields in admin classes * Remove redundant admin field test assertions per review feedback * Fix file formatting and end-of-file newlines per prek style check --------- Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
This commit is contained in:
co-authored by
Oliver
parent
36d21fe640
commit
501efdcb6e
@@ -33,6 +33,13 @@ class BuildItemAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
list_display = ('stock_item', 'quantity')
|
list_display = ('stock_item', 'quantity')
|
||||||
|
|
||||||
|
search_fields = [
|
||||||
|
'build_line__build__reference',
|
||||||
|
'build_line__build__title',
|
||||||
|
'stock_item__part__name',
|
||||||
|
'stock_item__serial',
|
||||||
|
]
|
||||||
|
|
||||||
autocomplete_fields = ['build_line', 'stock_item', 'install_into']
|
autocomplete_fields = ['build_line', 'stock_item', 'install_into']
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3423,11 +3423,11 @@ class EmailMessage(models.Model):
|
|||||||
|
|
||||||
objects = NoDeleteManager()
|
objects = NoDeleteManager()
|
||||||
|
|
||||||
def delete(self, *kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
"""Delete entry - if not protected."""
|
"""Delete entry - if not protected."""
|
||||||
if get_global_setting('INVENTREE_PROTECT_EMAIL_LOG'):
|
if get_global_setting('INVENTREE_PROTECT_EMAIL_LOG'):
|
||||||
raise ValidationError(del_error_msg)
|
raise ValidationError(del_error_msg)
|
||||||
return super().delete(*kwargs)
|
return super().delete(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class EmailThread(InvenTree.models.InvenTreeMetadataModel):
|
class EmailThread(InvenTree.models.InvenTreeMetadataModel):
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class DataImportColumnMapAdmin(admin.TabularInline):
|
|||||||
session = queryset.first().session
|
session = queryset.first().session
|
||||||
db_field.choices = [(col, col) for col in session.columns]
|
db_field.choices = [(col, col) for col in session.columns]
|
||||||
|
|
||||||
return super().formfield_for_choice_field(db_field, request, **kwargs)
|
return super().formfield_for_dbfield(db_field, request, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(importer.models.DataImportSession)
|
@admin.register(importer.models.DataImportSession)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class MachineSettingInline(admin.TabularInline):
|
|||||||
|
|
||||||
model = models.MachineSetting
|
model = models.MachineSetting
|
||||||
|
|
||||||
read_only_fields = ['key', 'config_type']
|
readonly_fields = ['key', 'config_type']
|
||||||
|
|
||||||
def has_add_permission(self, request, obj):
|
def has_add_permission(self, request, obj):
|
||||||
"""The machine settings should not be meddled with manually."""
|
"""The machine settings should not be meddled with manually."""
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ class MachineRegistry(
|
|||||||
data.update(str(pk).encode())
|
data.update(str(pk).encode())
|
||||||
try:
|
try:
|
||||||
data.update(str(machine.machine_config.active).encode())
|
data.update(str(machine.machine_config.active).encode())
|
||||||
except:
|
except Exception:
|
||||||
# machine does not exist anymore, hash will be different
|
# machine does not exist anymore, hash will be different
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -149,6 +149,15 @@ class SalesOrderAllocationAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
list_display = ('line', 'item', 'quantity')
|
list_display = ('line', 'item', 'quantity')
|
||||||
|
|
||||||
|
search_fields = [
|
||||||
|
'line__order__reference',
|
||||||
|
'line__order__customer__name',
|
||||||
|
'line__part__name',
|
||||||
|
'item__part__name',
|
||||||
|
'item__part__IPN',
|
||||||
|
'item__serial',
|
||||||
|
]
|
||||||
|
|
||||||
autocomplete_fields = ('line', 'shipment', 'item')
|
autocomplete_fields = ('line', 'shipment', 'item')
|
||||||
|
|
||||||
|
|
||||||
@@ -171,11 +180,19 @@ class ReturnOrderLineItemAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
list_display = ['order', 'item', 'reference']
|
list_display = ['order', 'item', 'reference']
|
||||||
|
|
||||||
|
search_fields = [
|
||||||
|
'order__reference',
|
||||||
|
'order__customer__name',
|
||||||
|
'item__part__name',
|
||||||
|
'item__serial',
|
||||||
|
'reference',
|
||||||
|
]
|
||||||
|
|
||||||
autocomplete_fields = ['item', 'order']
|
autocomplete_fields = ['item', 'order']
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.ReturnOrderExtraLine)
|
@admin.register(models.ReturnOrderExtraLine)
|
||||||
class ReturnOrdeerExtraLineAdmin(GeneralExtraLineAdmin, admin.ModelAdmin):
|
class ReturnOrderExtraLineAdmin(GeneralExtraLineAdmin, admin.ModelAdmin):
|
||||||
"""Admin class for the ReturnOrderExtraLine model."""
|
"""Admin class for the ReturnOrderExtraLine model."""
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ class AbstractOrderSerializer(
|
|||||||
custom_status = get_logical_value(
|
custom_status = get_logical_value(
|
||||||
value, model=self.Meta.model._meta.model_name
|
value, model=self.Meta.model._meta.model_name
|
||||||
)
|
)
|
||||||
except:
|
except Exception:
|
||||||
raise ValidationError(_('Invalid custom status key'))
|
raise ValidationError(_('Invalid custom status key'))
|
||||||
|
|
||||||
if custom_status.logical_key is not self.instance.status:
|
if custom_status.logical_key is not self.instance.status:
|
||||||
|
|||||||
@@ -728,13 +728,3 @@ class OrderUpdatedAtTest(TestCase):
|
|||||||
before = self._refresh(instance).updated_at
|
before = self._refresh(instance).updated_at
|
||||||
line.delete()
|
line.delete()
|
||||||
self.assertGreaterEqual(self._refresh(instance).updated_at, before)
|
self.assertGreaterEqual(self._refresh(instance).updated_at, before)
|
||||||
|
|
||||||
def test_po_lineitem_admin_search(self):
|
|
||||||
"""Test search fields for PurchaseOrderLineItemAdmin."""
|
|
||||||
from order.admin import PurchaseOrderLineItemAdmin
|
|
||||||
|
|
||||||
admin_class = PurchaseOrderLineItemAdmin
|
|
||||||
self.assertIn('part__part__name', admin_class.search_fields)
|
|
||||||
self.assertIn('part__SKU', admin_class.search_fields)
|
|
||||||
self.assertIn('order__reference', admin_class.search_fields)
|
|
||||||
self.assertIn('order__supplier__name', admin_class.search_fields)
|
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ class PartPricingAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
list_display = ('part', 'overall_min', 'overall_max')
|
list_display = ('part', 'overall_min', 'overall_max')
|
||||||
|
|
||||||
|
search_fields = ['part__name', 'part__IPN', 'part__description']
|
||||||
|
|
||||||
autocomplete_fields = ['part']
|
autocomplete_fields = ['part']
|
||||||
|
|
||||||
|
|
||||||
@@ -47,6 +49,8 @@ class PartStocktakeAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
list_display = ['part', 'date', 'quantity']
|
list_display = ['part', 'date', 'quantity']
|
||||||
|
|
||||||
|
search_fields = ['part__name', 'part__IPN']
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.PartCategory)
|
@admin.register(models.PartCategory)
|
||||||
class PartCategoryAdmin(admin.ModelAdmin):
|
class PartCategoryAdmin(admin.ModelAdmin):
|
||||||
@@ -63,6 +67,8 @@ class PartCategoryAdmin(admin.ModelAdmin):
|
|||||||
class PartRelatedAdmin(admin.ModelAdmin):
|
class PartRelatedAdmin(admin.ModelAdmin):
|
||||||
"""Class to manage PartRelated objects."""
|
"""Class to manage PartRelated objects."""
|
||||||
|
|
||||||
|
search_fields = ['part_1__name', 'part_2__name']
|
||||||
|
|
||||||
autocomplete_fields = ('part_1', 'part_2')
|
autocomplete_fields = ('part_1', 'part_2')
|
||||||
|
|
||||||
|
|
||||||
@@ -73,6 +79,8 @@ class PartTestTemplateAdmin(admin.ModelAdmin):
|
|||||||
list_display = ('part', 'test_name', 'required')
|
list_display = ('part', 'test_name', 'required')
|
||||||
readonly_fields = ['key']
|
readonly_fields = ['key']
|
||||||
|
|
||||||
|
search_fields = ['part__name', 'test_name', 'description']
|
||||||
|
|
||||||
autocomplete_fields = ('part',)
|
autocomplete_fields = ('part',)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class PluginSettingInline(admin.TabularInline):
|
|||||||
|
|
||||||
model = models.PluginSetting
|
model = models.PluginSetting
|
||||||
|
|
||||||
read_only_fields = ['key']
|
readonly_fields = ['key']
|
||||||
|
|
||||||
def has_add_permission(self, request, obj):
|
def has_add_permission(self, request, obj):
|
||||||
"""The plugin settings should not be meddled with manually."""
|
"""The plugin settings should not be meddled with manually."""
|
||||||
@@ -51,7 +51,7 @@ class PluginUserSettingInline(admin.TabularInline):
|
|||||||
|
|
||||||
model = models.PluginUserSetting
|
model = models.PluginUserSetting
|
||||||
|
|
||||||
read_only_fields = ['key']
|
readonly_fields = ['key']
|
||||||
|
|
||||||
def has_add_permission(self, request, obj):
|
def has_add_permission(self, request, obj):
|
||||||
"""The plugin user settings should not be meddled with manually."""
|
"""The plugin user settings should not be meddled with manually."""
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class BarcodeMixin:
|
|||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
# If a NotImplementedError is raised, then barcode generation is not supported
|
# If a NotImplementedError is raised, then barcode generation is not supported
|
||||||
return False
|
return False
|
||||||
except:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ class ReportAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
list_filter = ('model_type', 'enabled')
|
list_filter = ('model_type', 'enabled')
|
||||||
|
|
||||||
|
search_fields = ['name', 'description']
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, request, **kwargs):
|
def formfield_for_dbfield(self, db_field, request, **kwargs):
|
||||||
"""Provide custom choices for 'model_type' field."""
|
"""Provide custom choices for 'model_type' field."""
|
||||||
if db_field.name == 'model_type':
|
if db_field.name == 'model_type':
|
||||||
@@ -29,9 +31,13 @@ class ReportSnippetAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
list_display = ('id', 'snippet', 'description')
|
list_display = ('id', 'snippet', 'description')
|
||||||
|
|
||||||
|
search_fields = ['description']
|
||||||
|
|
||||||
|
|
||||||
@admin.register(ReportAsset)
|
@admin.register(ReportAsset)
|
||||||
class ReportAssetAdmin(admin.ModelAdmin):
|
class ReportAssetAdmin(admin.ModelAdmin):
|
||||||
"""Admin class for the ReportAsset model."""
|
"""Admin class for the ReportAsset model."""
|
||||||
|
|
||||||
list_display = ('id', 'asset', 'description')
|
list_display = ('id', 'asset', 'description')
|
||||||
|
|
||||||
|
search_fields = ['description']
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ class StockTrackingAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
list_display = ('item', 'date', 'label')
|
list_display = ('item', 'date', 'label')
|
||||||
|
|
||||||
|
search_fields = ['item__part__name', 'item__serial', 'notes']
|
||||||
|
|
||||||
autocomplete_fields = ['item']
|
autocomplete_fields = ['item']
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
def has_add_permission(self, request):
|
||||||
@@ -102,4 +104,12 @@ class StockItemTestResultAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
list_display = ('stock_item', 'test_name', 'result', 'value')
|
list_display = ('stock_item', 'test_name', 'result', 'value')
|
||||||
|
|
||||||
|
search_fields = [
|
||||||
|
'stock_item__part__name',
|
||||||
|
'stock_item__serial',
|
||||||
|
'template__test_name',
|
||||||
|
'value',
|
||||||
|
'notes',
|
||||||
|
]
|
||||||
|
|
||||||
autocomplete_fields = ['stock_item']
|
autocomplete_fields = ['stock_item']
|
||||||
|
|||||||
Reference in New Issue
Block a user