From 08d0084e07a273361a22843f6e4f76623d5b70d3 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 31 Mar 2023 10:42:54 +1100 Subject: [PATCH] Metadata API refactor (#4545) * Add generic metadata API endpoint (cherry picked from commit 7bbd53fc7647e2bb18d36c8c351e3fc080037ab1) * Refactor metadata endpoints for build models (cherry picked from commit 722b44e1259f1c5b046c7bc4328995b8238fc342) * Update metadata views for company models * labels * orders * part * report * stock --- InvenTree/InvenTree/api.py | 25 +++++++++++ InvenTree/build/api.py | 30 ++----------- InvenTree/company/api.py | 43 +++--------------- InvenTree/label/api.py | 41 +++--------------- InvenTree/order/api.py | 89 ++++---------------------------------- InvenTree/part/api.py | 51 +++------------------- InvenTree/report/api.py | 41 ++++-------------- InvenTree/stock/api.py | 29 ++----------- 8 files changed, 66 insertions(+), 283 deletions(-) diff --git a/InvenTree/InvenTree/api.py b/InvenTree/InvenTree/api.py index ba2b725a67..5f52c8431e 100644 --- a/InvenTree/InvenTree/api.py +++ b/InvenTree/InvenTree/api.py @@ -16,7 +16,9 @@ import users.models from InvenTree.mixins import ListCreateAPI from InvenTree.permissions import RolePermission from part.templatetags.inventree_extras import plugins_info +from plugin.serializers import MetadataSerializer +from .mixins import RetrieveUpdateAPI from .status import is_worker_running from .version import (inventreeApiVersion, inventreeInstanceName, inventreeVersion) @@ -354,3 +356,26 @@ class StatusView(APIView): } return Response(data) + + +class MetadataView(RetrieveUpdateAPI): + """Generic API endpoint for reading and editing metadata for a model""" + + MODEL_REF = 'model' + + def get_model_type(self): + """Return the model type associated with this API instance""" + model = self.kwargs.get(self.MODEL_REF, None) + + if model is None: + raise ValidationError(f"MetadataView called without '{self.MODEL_REF}' parameter") + + return model + + def get_queryset(self): + """Return the queryset for this endpoint""" + return self.get_model_type().objects.all() + + def get_serializer(self, *args, **kwargs): + """Return MetadataSerializer instance""" + return MetadataSerializer(self.get_model_type(), *args, **kwargs) diff --git a/InvenTree/build/api.py b/InvenTree/build/api.py index 46f735507d..67b3ce559e 100644 --- a/InvenTree/build/api.py +++ b/InvenTree/build/api.py @@ -10,13 +10,11 @@ from rest_framework.exceptions import ValidationError from django_filters.rest_framework import DjangoFilterBackend from django_filters import rest_framework as rest_filters -from InvenTree.api import AttachmentMixin, APIDownloadMixin, ListCreateDestroyAPIView, StatusView +from InvenTree.api import AttachmentMixin, APIDownloadMixin, ListCreateDestroyAPIView, MetadataView, StatusView from InvenTree.helpers import str2bool, isNull, DownloadFile from InvenTree.filters import InvenTreeOrderingFilter from InvenTree.status_codes import BuildStatus -from InvenTree.mixins import CreateAPI, RetrieveUpdateAPI, RetrieveUpdateDestroyAPI, ListCreateAPI - -from plugin.serializers import MetadataSerializer +from InvenTree.mixins import CreateAPI, RetrieveUpdateDestroyAPI, ListCreateAPI import build.admin import build.serializers @@ -292,16 +290,6 @@ class BuildOrderContextMixin: return ctx -class BuildOrderMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating BuildOrder metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return MetadataSerializer instance""" - return MetadataSerializer(Build, *args, **kwargs) - - queryset = Build.objects.all() - - class BuildOutputCreate(BuildOrderContextMixin, CreateAPI): """API endpoint for creating new build output(s).""" @@ -473,16 +461,6 @@ class BuildItemList(ListCreateAPI): ] -class BuildItemMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating BuildItem metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return MetadataSerializer instance""" - return MetadataSerializer(BuildItem, *args, **kwargs) - - queryset = BuildItem.objects.all() - - class BuildAttachmentList(AttachmentMixin, ListCreateDestroyAPIView): """API endpoint for listing (and creating) BuildOrderAttachment objects.""" @@ -516,7 +494,7 @@ build_api_urls = [ # Build Items re_path(r'^item/', include([ path(r'/', include([ - re_path(r'^metadata/', BuildItemMetadata.as_view(), name='api-build-item-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': BuildItem}, name='api-build-item-metadata'), re_path(r'^.*$', BuildItemDetail.as_view(), name='api-build-item-detail'), ])), re_path(r'^.*$', BuildItemList.as_view(), name='api-build-item-list'), @@ -532,7 +510,7 @@ build_api_urls = [ re_path(r'^finish/', BuildFinish.as_view(), name='api-build-finish'), re_path(r'^cancel/', BuildCancel.as_view(), name='api-build-cancel'), re_path(r'^unallocate/', BuildUnallocate.as_view(), name='api-build-unallocate'), - re_path(r'^metadata/', BuildOrderMetadata.as_view(), name='api-build-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': Build}, name='api-build-metadata'), re_path(r'^.*$', BuildDetail.as_view(), name='api-build-detail'), ])), diff --git a/InvenTree/company/api.py b/InvenTree/company/api.py index 89dccdb427..62224fb4ca 100644 --- a/InvenTree/company/api.py +++ b/InvenTree/company/api.py @@ -8,12 +8,11 @@ from django_filters.rest_framework import DjangoFilterBackend from rest_framework import filters import part.models -from InvenTree.api import AttachmentMixin, ListCreateDestroyAPIView +from InvenTree.api import (AttachmentMixin, ListCreateDestroyAPIView, + MetadataView) from InvenTree.filters import InvenTreeOrderingFilter from InvenTree.helpers import str2bool -from InvenTree.mixins import (ListCreateAPI, RetrieveUpdateAPI, - RetrieveUpdateDestroyAPI) -from plugin.serializers import MetadataSerializer +from InvenTree.mixins import ListCreateAPI, RetrieveUpdateDestroyAPI from .models import (Company, CompanyAttachment, Contact, ManufacturerPart, ManufacturerPartAttachment, ManufacturerPartParameter, @@ -87,16 +86,6 @@ class CompanyDetail(RetrieveUpdateDestroyAPI): return queryset -class CompanyMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating Company metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return MetadataSerializer instance for a Company""" - return MetadataSerializer(Company, *args, **kwargs) - - queryset = Company.objects.all() - - class CompanyAttachmentList(AttachmentMixin, ListCreateDestroyAPIView): """API endpoint for the CompanyAttachment model""" @@ -231,16 +220,6 @@ class ManufacturerPartDetail(RetrieveUpdateDestroyAPI): serializer_class = ManufacturerPartSerializer -class ManufacturerPartMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating ManufacturerPart metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return MetadataSerializer instance for a Company""" - return MetadataSerializer(ManufacturerPart, *args, **kwargs) - - queryset = ManufacturerPart.objects.all() - - class ManufacturerPartAttachmentList(AttachmentMixin, ListCreateDestroyAPIView): """API endpoint for listing (and creating) a ManufacturerPartAttachment (file upload).""" @@ -470,16 +449,6 @@ class SupplierPartDetail(RetrieveUpdateDestroyAPI): ] -class SupplierPartMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating SupplierPart metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return MetadataSerializer instance for a Company""" - return MetadataSerializer(SupplierPart, *args, **kwargs) - - queryset = SupplierPart.objects.all() - - class SupplierPriceBreakFilter(rest_filters.FilterSet): """Custom API filters for the SupplierPriceBreak list endpoint""" @@ -567,7 +536,7 @@ manufacturer_part_api_urls = [ ])), re_path(r'^(?P\d+)/?', include([ - re_path('^metadata/', ManufacturerPartMetadata.as_view(), name='api-manufacturer-part-metadata'), + re_path('^metadata/', MetadataView.as_view(), {'model': ManufacturerPart}, name='api-manufacturer-part-metadata'), re_path('^.*$', ManufacturerPartDetail.as_view(), name='api-manufacturer-part-detail'), ])), @@ -579,7 +548,7 @@ manufacturer_part_api_urls = [ supplier_part_api_urls = [ re_path(r'^(?P\d+)/?', include([ - re_path('^metadata/', SupplierPartMetadata.as_view(), name='api-supplier-part-metadata'), + re_path('^metadata/', MetadataView.as_view(), {'model': SupplierPart}, name='api-supplier-part-metadata'), re_path('^.*$', SupplierPartDetail.as_view(), name='api-supplier-part-detail'), ])), @@ -601,7 +570,7 @@ company_api_urls = [ ])), re_path(r'^(?P\d+)/?', include([ - re_path(r'^metadata/', CompanyMetadata.as_view(), name='api-company-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': Company}, name='api-company-metadata'), re_path(r'^.*$', CompanyDetail.as_view(), name='api-company-detail'), ])), diff --git a/InvenTree/label/api.py b/InvenTree/label/api.py index 3a298f3bc5..df56924109 100644 --- a/InvenTree/label/api.py +++ b/InvenTree/label/api.py @@ -13,13 +13,12 @@ from rest_framework.exceptions import NotFound import common.models import InvenTree.helpers -from InvenTree.mixins import (ListAPI, RetrieveAPI, RetrieveUpdateAPI, - RetrieveUpdateDestroyAPI) +from InvenTree.api import MetadataView +from InvenTree.mixins import ListAPI, RetrieveAPI, RetrieveUpdateDestroyAPI from InvenTree.tasks import offload_task from part.models import Part from plugin.base.label import label as plugin_label from plugin.registry import registry -from plugin.serializers import MetadataSerializer from stock.models import StockItem, StockLocation from .models import PartLabel, StockItemLabel, StockLocationLabel @@ -307,16 +306,6 @@ class StockItemLabelDetail(StockItemLabelMixin, RetrieveUpdateDestroyAPI): pass -class StockItemLabelMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating StockItemLabel metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return MetadataSerializer instance for a Company""" - return MetadataSerializer(StockItemLabel, *args, **kwargs) - - queryset = StockItemLabel.objects.all() - - class StockItemLabelPrint(StockItemLabelMixin, LabelPrintMixin, RetrieveAPI): """API endpoint for printing a StockItemLabel object.""" pass @@ -349,16 +338,6 @@ class StockLocationLabelDetail(StockLocationLabelMixin, RetrieveUpdateDestroyAPI pass -class StockLocationLabelMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating StockLocationLabel metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return MetadataSerializer instance for a Company""" - return MetadataSerializer(StockLocationLabel, *args, **kwargs) - - queryset = StockLocationLabel.objects.all() - - class StockLocationLabelPrint(StockLocationLabelMixin, LabelPrintMixin, RetrieveAPI): """API endpoint for printing a StockLocationLabel object.""" pass @@ -378,16 +357,6 @@ class PartLabelList(PartLabelMixin, LabelListView): pass -class PartLabelMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating PartLabel metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return MetadataSerializer instance for a Company""" - return MetadataSerializer(PartLabel, *args, **kwargs) - - queryset = PartLabel.objects.all() - - class PartLabelDetail(PartLabelMixin, RetrieveUpdateDestroyAPI): """API endpoint for a single PartLabel object.""" pass @@ -405,7 +374,7 @@ label_api_urls = [ # Detail views path(r'/', include([ re_path(r'print/?', StockItemLabelPrint.as_view(), name='api-stockitem-label-print'), - re_path(r'metadata/', StockItemLabelMetadata.as_view(), name='api-stockitem-label-metadata'), + re_path(r'metadata/', MetadataView.as_view(), {'model': StockItemLabel}, name='api-stockitem-label-metadata'), re_path(r'^.*$', StockItemLabelDetail.as_view(), name='api-stockitem-label-detail'), ])), @@ -418,7 +387,7 @@ label_api_urls = [ # Detail views path(r'/', include([ re_path(r'print/?', StockLocationLabelPrint.as_view(), name='api-stocklocation-label-print'), - re_path(r'metadata/', StockLocationLabelMetadata.as_view(), name='api-stocklocation-label-metadata'), + re_path(r'metadata/', MetadataView.as_view(), {'model': StockLocationLabel}, name='api-stocklocation-label-metadata'), re_path(r'^.*$', StockLocationLabelDetail.as_view(), name='api-stocklocation-label-detail'), ])), @@ -431,7 +400,7 @@ label_api_urls = [ # Detail views path(r'/', include([ re_path(r'^print/', PartLabelPrint.as_view(), name='api-part-label-print'), - re_path(r'^metadata/', PartLabelMetadata.as_view(), name='api-part-label-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': PartLabel}, name='api-part-label-metadata'), re_path(r'^.*$', PartLabelDetail.as_view(), name='api-part-label-detail'), ])), diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index 6a5522be0d..975dfb5f66 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -20,11 +20,11 @@ from common.models import InvenTreeSetting from common.settings import settings from company.models import SupplierPart from InvenTree.api import (APIDownloadMixin, AttachmentMixin, - ListCreateDestroyAPIView, StatusView) + ListCreateDestroyAPIView, MetadataView, StatusView) from InvenTree.filters import InvenTreeOrderingFilter from InvenTree.helpers import DownloadFile, str2bool from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI, - RetrieveUpdateAPI, RetrieveUpdateDestroyAPI) + RetrieveUpdateDestroyAPI) from InvenTree.status_codes import (PurchaseOrderStatus, ReturnOrderLineStatus, ReturnOrderStatus, SalesOrderStatus) from order.admin import (PurchaseOrderExtraLineResource, @@ -32,7 +32,6 @@ from order.admin import (PurchaseOrderExtraLineResource, ReturnOrderResource, SalesOrderExtraLineResource, SalesOrderLineItemResource, SalesOrderResource) from part.models import Part -from plugin.serializers import MetadataSerializer from users.models import Owner @@ -385,16 +384,6 @@ class PurchaseOrderIssue(PurchaseOrderContextMixin, CreateAPI): serializer_class = serializers.PurchaseOrderIssueSerializer -class PurchaseOrderMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating PurchaseOrder metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return MetadataSerializer instance for a PurchaseOrder""" - return MetadataSerializer(models.PurchaseOrder, *args, **kwargs) - - queryset = models.PurchaseOrder.objects.all() - - class PurchaseOrderReceive(PurchaseOrderContextMixin, CreateAPI): """API endpoint to receive stock items against a PurchaseOrder. @@ -557,16 +546,6 @@ class PurchaseOrderLineItemDetail(PurchaseOrderLineItemMixin, RetrieveUpdateDest pass -class PurchaseOrderLineItemMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating PurchaseOrderLineItem metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return MetadataSerializer instance for a Company""" - return MetadataSerializer(models.PurchaseOrderLineItem, *args, **kwargs) - - queryset = models.PurchaseOrderLineItem.objects.all() - - class PurchaseOrderExtraLineList(GeneralExtraLineList, ListCreateAPI): """API endpoint for accessing a list of PurchaseOrderExtraLine objects.""" @@ -590,16 +569,6 @@ class PurchaseOrderExtraLineDetail(RetrieveUpdateDestroyAPI): serializer_class = serializers.PurchaseOrderExtraLineSerializer -class PurchaseOrderExtraLineItemMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating PurchaseOrderExtraLineItem metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return MetadataSerializer instance""" - return MetadataSerializer(models.PurchaseOrderExtraLine, *args, **kwargs) - - queryset = models.PurchaseOrderExtraLine.objects.all() - - class SalesOrderAttachmentList(AttachmentMixin, ListCreateDestroyAPIView): """API endpoint for listing (and creating) a SalesOrderAttachment (file upload)""" @@ -873,16 +842,6 @@ class SalesOrderLineItemDetail(SalesOrderLineItemMixin, RetrieveUpdateDestroyAPI pass -class SalesOrderLineItemMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating SalesOrderLineItem metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return MetadataSerializer instance""" - return MetadataSerializer(models.SalesOrderLineItem, *args, **kwargs) - - queryset = models.SalesOrderLineItem.objects.all() - - class SalesOrderExtraLineList(GeneralExtraLineList, ListCreateAPI): """API endpoint for accessing a list of SalesOrderExtraLine objects.""" @@ -906,16 +865,6 @@ class SalesOrderExtraLineDetail(RetrieveUpdateDestroyAPI): serializer_class = serializers.SalesOrderExtraLineSerializer -class SalesOrderExtraLineItemMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating SalesOrderExtraLineItem metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return MetadataSerializer instance""" - return MetadataSerializer(models.SalesOrderExtraLine, *args, **kwargs) - - queryset = models.SalesOrderExtraLine.objects.all() - - class SalesOrderContextMixin: """Mixin to add sales order object as serializer context variable.""" @@ -951,16 +900,6 @@ class SalesOrderComplete(SalesOrderContextMixin, CreateAPI): serializer_class = serializers.SalesOrderCompleteSerializer -class SalesOrderMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating SalesOrder metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return a metadata serializer for the SalesOrder model""" - return MetadataSerializer(models.SalesOrder, *args, **kwargs) - - queryset = models.SalesOrder.objects.all() - - class SalesOrderAllocateSerials(SalesOrderContextMixin, CreateAPI): """API endpoint to allocation stock items against a SalesOrder, by specifying serial numbers.""" @@ -1122,16 +1061,6 @@ class SalesOrderShipmentComplete(CreateAPI): return ctx -class SalesOrderShipmentMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating SalesOrderShipment metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return MetadataSerializer instance""" - return MetadataSerializer(models.SalesOrderShipment, *args, **kwargs) - - queryset = models.SalesOrderShipment.objects.all() - - class PurchaseOrderAttachmentList(AttachmentMixin, ListCreateDestroyAPIView): """API endpoint for listing (and creating) a PurchaseOrderAttachment (file upload)""" @@ -1604,7 +1533,7 @@ order_api_urls = [ re_path(r'^cancel/', PurchaseOrderCancel.as_view(), name='api-po-cancel'), re_path(r'^complete/', PurchaseOrderComplete.as_view(), name='api-po-complete'), re_path(r'^issue/', PurchaseOrderIssue.as_view(), name='api-po-issue'), - re_path(r'^metadata/', PurchaseOrderMetadata.as_view(), name='api-po-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': models.PurchaseOrder}, name='api-po-metadata'), re_path(r'^receive/', PurchaseOrderReceive.as_view(), name='api-po-receive'), # PurchaseOrder detail API endpoint @@ -1621,7 +1550,7 @@ order_api_urls = [ # API endpoints for purchase order line items re_path(r'^po-line/', include([ path('/', include([ - re_path(r'^metadata/', PurchaseOrderLineItemMetadata.as_view(), name='api-po-line-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': models.PurchaseOrderLineItem}, name='api-po-line-metadata'), re_path(r'^.*$', PurchaseOrderLineItemDetail.as_view(), name='api-po-line-detail'), ])), re_path(r'^.*$', PurchaseOrderLineItemList.as_view(), name='api-po-line-list'), @@ -1630,7 +1559,7 @@ order_api_urls = [ # API endpoints for purchase order extra line re_path(r'^po-extra-line/', include([ path('/', include([ - re_path(r'^metadata/', PurchaseOrderExtraLineItemMetadata.as_view(), name='api-po-extra-line-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': models.PurchaseOrderExtraLine}, name='api-po-extra-line-metadata'), re_path(r'^.*$', PurchaseOrderExtraLineDetail.as_view(), name='api-po-extra-line-detail'), ])), path('', PurchaseOrderExtraLineList.as_view(), name='api-po-extra-line-list'), @@ -1646,7 +1575,7 @@ order_api_urls = [ re_path(r'^shipment/', include([ path(r'/', include([ path('ship/', SalesOrderShipmentComplete.as_view(), name='api-so-shipment-ship'), - re_path(r'^metadata/', SalesOrderShipmentMetadata.as_view(), name='api-so-shipment-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': models.SalesOrderShipment}, name='api-so-shipment-metadata'), re_path(r'^.*$', SalesOrderShipmentDetail.as_view(), name='api-so-shipment-detail'), ])), re_path(r'^.*$', SalesOrderShipmentList.as_view(), name='api-so-shipment-list'), @@ -1659,7 +1588,7 @@ order_api_urls = [ re_path(r'^cancel/', SalesOrderCancel.as_view(), name='api-so-cancel'), re_path(r'^issue/', SalesOrderIssue.as_view(), name='api-so-issue'), re_path(r'^complete/', SalesOrderComplete.as_view(), name='api-so-complete'), - re_path(r'^metadata/', SalesOrderMetadata.as_view(), name='api-so-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': models.SalesOrder}, name='api-so-metadata'), # SalesOrder detail endpoint re_path(r'^.*$', SalesOrderDetail.as_view(), name='api-so-detail'), @@ -1675,7 +1604,7 @@ order_api_urls = [ # API endpoints for sales order line items re_path(r'^so-line/', include([ path('/', include([ - re_path(r'^metadata/', SalesOrderLineItemMetadata.as_view(), name='api-so-line-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': models.SalesOrderLineItem}, name='api-so-line-metadata'), re_path(r'^.*$', SalesOrderLineItemDetail.as_view(), name='api-so-line-detail'), ])), path('', SalesOrderLineItemList.as_view(), name='api-so-line-list'), @@ -1684,7 +1613,7 @@ order_api_urls = [ # API endpoints for sales order extra line re_path(r'^so-extra-line/', include([ path('/', include([ - re_path(r'^metadata/', SalesOrderExtraLineItemMetadata.as_view(), name='api-so-extra-line-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': models.SalesOrderExtraLine}, name='api-so-extra-line-metadata'), re_path(r'^.*$', SalesOrderExtraLineDetail.as_view(), name='api-so-extra-line-detail'), ])), path('', SalesOrderExtraLineList.as_view(), name='api-so-extra-line-list'), diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 6ea2a3b673..2683c26673 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -16,7 +16,7 @@ from rest_framework.response import Response import order.models from build.models import Build, BuildItem from InvenTree.api import (APIDownloadMixin, AttachmentMixin, - ListCreateDestroyAPIView) + ListCreateDestroyAPIView, MetadataView) from InvenTree.filters import InvenTreeOrderingFilter from InvenTree.helpers import (DownloadFile, increment_serial_number, isNull, str2bool, str2int) @@ -28,7 +28,6 @@ from InvenTree.permissions import RolePermission from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus, SalesOrderStatus) from part.admin import PartCategoryResource, PartResource -from plugin.serializers import MetadataSerializer from . import serializers as part_serializers from . import views @@ -230,16 +229,6 @@ class CategoryTree(ListAPI): ordering = ['level', 'name'] -class CategoryMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating PartCategory metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return a MetadataSerializer pointing to the referenced PartCategory instance""" - return MetadataSerializer(PartCategory, *args, **kwargs) - - queryset = PartCategory.objects.all() - - class CategoryParameterList(ListCreateAPI): """API endpoint for accessing a list of PartCategoryParameterTemplate objects. @@ -698,16 +687,6 @@ class PartRequirements(RetrieveAPI): return Response(data) -class PartMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating Part metadata.""" - - def get_serializer(self, *args, **kwargs): - """Returns a MetadataSerializer instance pointing to the referenced Part""" - return MetadataSerializer(Part, *args, **kwargs) - - queryset = Part.objects.all() - - class PartPricingDetail(RetrieveUpdateAPI): """API endpoint for viewing part pricing data""" @@ -1415,16 +1394,6 @@ class PartParameterTemplateDetail(RetrieveUpdateDestroyAPI): serializer_class = part_serializers.PartParameterTemplateSerializer -class PartParameterTemplateMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating PartParameterTemplate metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return a MetadataSerializer pointing to the referenced PartParameterTemplate instance""" - return MetadataSerializer(PartParameterTemplate, *args, **kwargs) - - queryset = PartParameterTemplate.objects.all() - - class PartParameterList(ListCreateAPI): """API endpoint for accessing a list of PartParameter objects. @@ -1886,16 +1855,6 @@ class BomItemSubstituteDetail(RetrieveUpdateDestroyAPI): serializer_class = part_serializers.BomItemSubstituteSerializer -class BomItemMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating PartBOM metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return a MetadataSerializer pointing to the referenced PartCategory instance""" - return MetadataSerializer(BomItem, *args, **kwargs) - - queryset = BomItem.objects.all() - - part_api_urls = [ # Base URL for PartCategory API endpoints @@ -1910,7 +1869,7 @@ part_api_urls = [ # Category detail endpoints path(r'/', include([ - re_path(r'^metadata/', CategoryMetadata.as_view(), name='api-part-category-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': PartCategory}, name='api-part-category-metadata'), # PartCategory detail endpoint re_path(r'^.*$', CategoryDetail.as_view(), name='api-part-category-detail'), @@ -1953,7 +1912,7 @@ part_api_urls = [ re_path(r'^parameter/', include([ path('template/', include([ re_path(r'^(?P\d+)/', include([ - re_path(r'^metadata/?', PartParameterTemplateMetadata.as_view(), name='api-part-parameter-template-metadata'), + re_path(r'^metadata/?', MetadataView.as_view(), {'model': PartParameter}, name='api-part-parameter-template-metadata'), re_path(r'^.*$', PartParameterTemplateDetail.as_view(), name='api-part-parameter-template-detail'), ])), re_path(r'^.*$', PartParameterTemplateList.as_view(), name='api-part-parameter-template-list'), @@ -2000,7 +1959,7 @@ part_api_urls = [ re_path(r'^bom-validate/', PartValidateBOM.as_view(), name='api-part-bom-validate'), # Part metadata - re_path(r'^metadata/', PartMetadata.as_view(), name='api-part-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': Part}, name='api-part-metadata'), # Part pricing re_path(r'^pricing/', PartPricingDetail.as_view(), name='api-part-pricing'), @@ -2032,7 +1991,7 @@ bom_api_urls = [ # BOM Item Detail path(r'/', include([ re_path(r'^validate/?', BomItemValidate.as_view(), name='api-bom-item-validate'), - re_path(r'^metadata/?', BomItemMetadata.as_view(), name='api-bom-item-metadata'), + re_path(r'^metadata/?', MetadataView.as_view(), {'model': BomItem}, name='api-bom-item-metadata'), re_path(r'^.*$', BomDetail.as_view(), name='api-bom-item-detail'), ])), diff --git a/InvenTree/report/api.py b/InvenTree/report/api.py index 45490a5754..e01309217c 100644 --- a/InvenTree/report/api.py +++ b/InvenTree/report/api.py @@ -18,9 +18,8 @@ import common.models import InvenTree.helpers import order.models import part.models -from InvenTree.mixins import (ListAPI, RetrieveAPI, RetrieveUpdateAPI, - RetrieveUpdateDestroyAPI) -from plugin.serializers import MetadataSerializer +from InvenTree.api import MetadataView +from InvenTree.mixins import ListAPI, RetrieveAPI, RetrieveUpdateDestroyAPI from stock.models import StockItem, StockItemAttachment from .models import (BillOfMaterialsReport, BuildReport, PurchaseOrderReport, @@ -449,31 +448,6 @@ class ReturnOrderReportPrint(ReturnOrderReportMixin, ReportPrintMixin, RetrieveA pass -class ReportMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating Report metadata.""" - MODEL_REF = 'reportmodel' - - def _get_model(self, *args, **kwargs): - """Return model depending on which report type is requested in get_view constructor.""" - reportmodel = self.kwargs.get(self.MODEL_REF, PurchaseOrderReport) - - if reportmodel not in [PurchaseOrderReport, SalesOrderReport, BuildReport, BillOfMaterialsReport, TestReport]: - raise ValidationError("Invalid report model") - return reportmodel - - # Return corresponding Serializer - def get_serializer(self, *args, **kwargs): - """Return correct MetadataSerializer instance depending on which model is requested""" - # Get type of report, make sure its one of the allowed values - UseModel = self._get_model(*args, **kwargs) - return MetadataSerializer(UseModel, *args, **kwargs) - - def get_queryset(self, *args, **kwargs): - """Return correct queryset depending on which model is requested""" - UseModel = self._get_model(*args, **kwargs) - return UseModel.objects.all() - - report_api_urls = [ # Purchase order reports @@ -481,7 +455,7 @@ report_api_urls = [ # Detail views path(r'/', include([ re_path(r'print/', PurchaseOrderReportPrint.as_view(), name='api-po-report-print'), - re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: PurchaseOrderReport}, name='api-po-report-metadata'), + re_path(r'metadata/', MetadataView.as_view(), {'model': PurchaseOrderReport}, name='api-po-report-metadata'), path('', PurchaseOrderReportDetail.as_view(), name='api-po-report-detail'), ])), @@ -494,7 +468,7 @@ report_api_urls = [ # Detail views path(r'/', include([ re_path(r'print/', SalesOrderReportPrint.as_view(), name='api-so-report-print'), - re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: SalesOrderReport}, name='api-so-report-metadata'), + re_path(r'metadata/', MetadataView.as_view(), {'model': SalesOrderReport}, name='api-so-report-metadata'), path('', SalesOrderReportDetail.as_view(), name='api-so-report-detail'), ])), @@ -505,6 +479,7 @@ report_api_urls = [ re_path(r'ro/', include([ path(r'/', include([ path(r'print/', ReturnOrderReportPrint.as_view(), name='api-return-order-report-print'), + re_path(r'metadata/', MetadataView.as_view(), {'model': ReturnOrderReport}, name='api-so-report-metadata'), path('', ReturnOrderReportDetail.as_view(), name='api-return-order-report-detail'), ])), path('', ReturnOrderReportList.as_view(), name='api-return-order-report-list'), @@ -515,7 +490,7 @@ report_api_urls = [ # Detail views path(r'/', include([ re_path(r'print/?', BuildReportPrint.as_view(), name='api-build-report-print'), - re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: BuildReport}, name='api-build-report-metadata'), + re_path(r'metadata/', MetadataView.as_view(), {'model': BuildReport}, name='api-build-report-metadata'), re_path(r'^.$', BuildReportDetail.as_view(), name='api-build-report-detail'), ])), @@ -529,7 +504,7 @@ report_api_urls = [ # Detail views path(r'/', include([ re_path(r'print/?', BOMReportPrint.as_view(), name='api-bom-report-print'), - re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: BillOfMaterialsReport}, name='api-bom-report-metadata'), + re_path(r'metadata/', MetadataView.as_view(), {'model': BillOfMaterialsReport}, name='api-bom-report-metadata'), re_path(r'^.*$', BOMReportDetail.as_view(), name='api-bom-report-detail'), ])), @@ -542,7 +517,7 @@ report_api_urls = [ # Detail views path(r'/', include([ re_path(r'print/?', StockItemTestReportPrint.as_view(), name='api-stockitem-testreport-print'), - re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: TestReport}, name='api-stockitem-testreport-metadata'), + re_path(r'metadata/', MetadataView.as_view(), {'report': TestReport}, name='api-stockitem-testreport-metadata'), re_path(r'^.*$', StockItemTestReportDetail.as_view(), name='api-stockitem-testreport-detail'), ])), diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index c0e58aa8ce..014b8765e2 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -23,13 +23,13 @@ from build.models import Build from company.models import Company, SupplierPart from company.serializers import CompanySerializer, SupplierPartSerializer from InvenTree.api import (APIDownloadMixin, AttachmentMixin, - ListCreateDestroyAPIView, StatusView) + ListCreateDestroyAPIView, MetadataView, StatusView) from InvenTree.filters import InvenTreeOrderingFilter from InvenTree.helpers import (DownloadFile, extract_serial_numbers, isNull, str2bool, str2int) from InvenTree.mixins import (CreateAPI, CustomRetrieveUpdateDestroyAPI, ListAPI, ListCreateAPI, RetrieveAPI, - RetrieveUpdateAPI, RetrieveUpdateDestroyAPI) + RetrieveUpdateDestroyAPI) from InvenTree.status_codes import StockHistoryCode, StockStatus from order.models import (PurchaseOrder, ReturnOrder, SalesOrder, SalesOrderAllocation) @@ -37,7 +37,6 @@ from order.serializers import (PurchaseOrderSerializer, ReturnOrderSerializer, SalesOrderSerializer) from part.models import BomItem, Part, PartCategory from part.serializers import PartBriefSerializer -from plugin.serializers import MetadataSerializer from stock.admin import LocationResource, StockItemResource from stock.models import (StockItem, StockItemAttachment, StockItemTestResult, StockItemTracking, StockLocation) @@ -83,16 +82,6 @@ class StockDetail(RetrieveUpdateDestroyAPI): return self.serializer_class(*args, **kwargs) -class StockMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating StockItem metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return serializer.""" - return MetadataSerializer(StockItem, *args, **kwargs) - - queryset = StockItem.objects.all() - - class StockItemContextMixin: """Mixin class for adding StockItem object to serializer context.""" @@ -1344,16 +1333,6 @@ class StockTrackingList(ListAPI): ] -class LocationMetadata(RetrieveUpdateAPI): - """API endpoint for viewing / updating StockLocation metadata.""" - - def get_serializer(self, *args, **kwargs): - """Return serializer.""" - return MetadataSerializer(StockLocation, *args, **kwargs) - - queryset = StockLocation.objects.all() - - class LocationDetail(CustomRetrieveUpdateDestroyAPI): """API endpoint for detail view of StockLocation object. @@ -1391,7 +1370,7 @@ stock_api_urls = [ # Stock location detail endpoints path(r'/', include([ - re_path(r'^metadata/', LocationMetadata.as_view(), name='api-location-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': StockLocation}, name='api-location-metadata'), re_path(r'^.*$', LocationDetail.as_view(), name='api-location-detail'), ])), @@ -1433,7 +1412,7 @@ stock_api_urls = [ path(r'/', include([ re_path(r'^convert/', StockItemConvert.as_view(), name='api-stock-item-convert'), re_path(r'^install/', StockItemInstall.as_view(), name='api-stock-item-install'), - re_path(r'^metadata/', StockMetadata.as_view(), name='api-stock-item-metadata'), + re_path(r'^metadata/', MetadataView.as_view(), {'model': StockItem}, name='api-stock-item-metadata'), re_path(r'^return/', StockItemReturn.as_view(), name='api-stock-item-return'), re_path(r'^serialize/', StockItemSerialize.as_view(), name='api-stock-item-serialize'), re_path(r'^uninstall/', StockItemUninstall.as_view(), name='api-stock-item-uninstall'),