From 39a6bcdf3e896c2ccbf3d1345acf572736971e6e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 14 Apr 2017 11:46:18 +1000 Subject: [PATCH] Improved Part API --- InvenTree/part/models.py | 37 +++++++++++++++------------- InvenTree/part/serializers.py | 12 +++++++++- InvenTree/part/urls.py | 34 ++++++++++++++++++-------- InvenTree/part/views.py | 45 +++++++++++++++++++++++++++++++---- 4 files changed, 96 insertions(+), 32 deletions(-) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index c479037743..16f136287e 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -95,13 +95,8 @@ class PartParameterTemplate(models.Model): A PartParameterTemplate can be optionally associated with a PartCategory """ name = models.CharField(max_length=20) - description = models.CharField(max_length=100, blank=True) units = models.CharField(max_length=10, blank=True) - default_value = models.CharField(max_length=50, blank=True) - default_min = models.CharField(max_length=50, blank=True) - default_max = models.CharField(max_length=50, blank=True) - # Parameter format PARAM_NUMERIC = 10 PARAM_TEXT = 20 @@ -143,10 +138,31 @@ class CategoryParameterLink(models.Model): verbose_name_plural = "Category Parameters" +class PartParameterManager(models.Manager): + """ Manager for handling PartParameter objects + """ + + def create(self, *args, **kwargs): + """ Prevent creation of duplicate PartParameter + """ + + part_id = kwargs['part'] + template_id = kwargs['template'] + + try: + params = self.filter(part=part_id, template=template_id) + return params[0] + except: + pass + + return super(PartParameterManager, self).create(*args, **kwargs) + class PartParameter(models.Model): """ PartParameter is associated with a single part """ + objects = PartParameterManager() + part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='parameters') template = models.ForeignKey(PartParameterTemplate) @@ -155,17 +171,6 @@ class PartParameter(models.Model): min_value = models.CharField(max_length=50, blank=True) max_value = models.CharField(max_length=50, blank=True) - # Prevent multiple parameters of the same template - # from being added to the same part - def save(self, *args, **kwargs): - params = PartParameter.objects.filter(part=self.part, template=self.template) - if len(params) > 1: - return - if len(params) == 1 and params[0].id != self.id: - return - - super(PartParameter, self).save(*args, **kwargs) - def __str__(self): return "{name} : {val}{units}".format( name=self.template.name, diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 774815ac1a..06ad724b50 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from .models import Part, PartCategory, PartParameter +from .models import Part, PartCategory, PartParameter, PartParameterTemplate class PartParameterSerializer(serializers.ModelSerializer): @@ -58,3 +58,13 @@ class PartCategoryDetailSerializer(serializers.ModelSerializer): 'path', 'children', 'parts') + + +class PartTemplateSerializer(serializers.ModelSerializer): + + class Meta: + model = PartParameterTemplate + fields = ('pk', + 'name', + 'units', + 'format') diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index 01db39c149..4fe5a369a4 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -16,16 +16,24 @@ categorypatterns = [ url(r'^$', views.PartCategoryList.as_view()) ] -""" URL patterns associated with a particular part: -/part/ -> Detail view of a given part -/part//parameters -> List parameters associated with a part -""" -partdetailpatterns = [ - # Single part detail - url(r'^$', views.PartDetail.as_view()), +partparampatterns = [ + # Detail of a single part parameter + url(r'^(?P[0-9]+)/$', views.PartParamDetail.as_view()), + + # Parameters associated with a particular part + url(r'^\?[^/]*/$', views.PartParamList.as_view()), + + # All part parameters + url(r'^$', views.PartParamList.as_view()), +] + +parttemplatepatterns = [ + # Detail of a single part field template + url(r'^(?P[0-9]+)/$', views.PartTemplateDetail.as_view()), + + # List all part field templates + url(r'^$', views.PartTemplateList.as_view()) - # View part parameters - url(r'parameters/$', views.PartParameters.as_view()) ] """ Top-level URL patterns for the Part app: @@ -36,11 +44,17 @@ partdetailpatterns = [ """ urlpatterns = [ # Individual part - url(r'^(?P[0-9]+)/', include(partdetailpatterns)), + url(r'^(?P[0-9]+)/$', views.PartDetail.as_view()), # Part categories url(r'^category/', views.PartCategoryList.as_view()), + # Part parameters + url(r'^parameters/', include(partparampatterns)), + + # Part templates + url(r'^templates/', include(parttemplatepatterns)), + # List of all parts url(r'^$', views.PartList.as_view()) ] diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 55d5255789..289f38eb43 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -1,9 +1,10 @@ from rest_framework import generics, permissions -from .models import PartCategory, Part, PartParameter +from .models import PartCategory, Part, PartParameter, PartParameterTemplate from .serializers import PartSerializer from .serializers import PartCategoryDetailSerializer from .serializers import PartParameterSerializer +from .serializers import PartTemplateSerializer class PartDetail(generics.RetrieveUpdateDestroyAPIView): @@ -14,13 +15,33 @@ class PartDetail(generics.RetrieveUpdateDestroyAPIView): permission_classes = (permissions.IsAuthenticatedOrReadOnly,) -class PartParameters(generics.ListCreateAPIView): +class PartParamList(generics.ListCreateAPIView): """ Return all parameters associated with a particular part """ def get_queryset(self): - part_id = self.kwargs['pk'] - return PartParameter.objects.filter(part=part_id) + part_id = self.request.query_params.get('part', None) + if part_id: + return PartParameter.objects.filter(part=part_id) + else: + return PartParameter.objects.all() + + serializer_class = PartParameterSerializer + permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + + def create(self, request, *args, **kwargs): + # Ensure part link is set correctly + part_id = self.request.query_params.get('part', None) + if part_id: + request.data['part'] = part_id + return super(PartParamList, self).create(request, *args, **kwargs) + + +class PartParamDetail(generics.RetrieveUpdateDestroyAPIView): + """ Detail view of a single PartParameter + """ + + queryset = PartParameter.objects.all() serializer_class = PartParameterSerializer permission_classes = (permissions.IsAuthenticatedOrReadOnly,) @@ -32,7 +53,7 @@ class PartList(generics.ListCreateAPIView): permission_classes = (permissions.IsAuthenticatedOrReadOnly,) -class PartCategoryDetail(generics.RetrieveUpdateAPIView): +class PartCategoryDetail(generics.RetrieveUpdateDestroyAPIView): """ Return information on a single PartCategory """ queryset = PartCategory.objects.all() @@ -47,3 +68,17 @@ class PartCategoryList(generics.ListCreateAPIView): queryset = PartCategory.objects.filter(parent=None) serializer_class = PartCategoryDetailSerializer permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + + +class PartTemplateDetail(generics.RetrieveUpdateDestroyAPIView): + + queryset = PartParameterTemplate.objects.all() + serializer_class = PartTemplateSerializer + permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + + +class PartTemplateList(generics.ListCreateAPIView): + + queryset = PartParameterTemplate.objects.all() + serializer_class = PartTemplateSerializer + permission_classes = (permissions.IsAuthenticatedOrReadOnly,)