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
This commit is contained in:
Oliver 2023-03-31 10:42:54 +11:00 committed by GitHub
parent 327ecf2156
commit 08d0084e07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 66 additions and 283 deletions

View File

@ -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)

View File

@ -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'<int:pk>/', 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'),
])),

View File

@ -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<pk>\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<pk>\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<pk>\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'),
])),

View File

@ -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'<int:pk>/', 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'<int:pk>/', 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'<int:pk>/', 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'),
])),

View File

@ -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('<int:pk>/', 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('<int:pk>/', 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'<int:pk>/', 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('<int:pk>/', 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('<int:pk>/', 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'),

View File

@ -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'<int:pk>/', 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<pk>\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'<int:pk>/', 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'),
])),

View File

@ -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'<int:pk>/', 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'<int:pk>/', 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'<int:pk>/', 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'<int:pk>/', 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'<int:pk>/', 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'<int:pk>/', 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'),
])),

View File

@ -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'<int:pk>/', 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'<int:pk>/', 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'),