From 5ee339bb27e54f1135e21f7cd27b5e15f141aeab Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 25 Apr 2019 23:42:22 +1000 Subject: [PATCH 01/16] Add 'delete' button for supplier parts --- InvenTree/part/templates/part/supplier.html | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/InvenTree/part/templates/part/supplier.html b/InvenTree/part/templates/part/supplier.html index b5426971eb..2548297774 100644 --- a/InvenTree/part/templates/part/supplier.html +++ b/InvenTree/part/templates/part/supplier.html @@ -7,11 +7,16 @@

Part Suppliers

- + +
-
-
@@ -74,4 +79,6 @@ url: "{% url 'api-part-supplier-list' %}" }); + linkButtonsToSelection($("#supplier-table"), ['#supplier-part-options']) + {% endblock %} \ No newline at end of file From 907bccf7484f78ba530c6171f08c76b221ac5fbd Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 25 Apr 2019 23:56:03 +1000 Subject: [PATCH 02/16] Add SupplierPart detail API - RUD view --- InvenTree/part/api.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 5601850e1c..50e0150d46 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -167,6 +167,16 @@ class SupplierPartList(generics.ListAPIView): ] +class SupplierPartDetail(generics.RetrieveUpdateDestroyAPIView): + + queryset = SupplierPart.objects.all() + serializer_class = SupplierPartSerializer + permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + + read_only_fields = [ + ] + + class SupplierPriceBreakList(generics.ListCreateAPIView): queryset = SupplierPriceBreak.objects.all() @@ -189,13 +199,21 @@ cat_api_urls = [ url(r'^$', CategoryList.as_view(), name='api-part-category-list'), ] +supplier_part_api_urls = [ + + url(r'^(?P\d+)/?', SupplierPartDetail.as_view(), name='api-supplier-part-detail'), + + # Catch anything else + url(r'^.*$', SupplierPartList.as_view(), name='api-part-supplier-list'), +] + part_api_urls = [ url(r'^tree/?', PartCategoryTree.as_view(), name='api-part-tree'), url(r'^category/', include(cat_api_urls)), + url(r'^supplier/?', include(supplier_part_api_urls)), url(r'^price-break/?', SupplierPriceBreakList.as_view(), name='api-part-supplier-price'), - url(r'^supplier/?', SupplierPartList.as_view(), name='api-part-supplier-list'), url(r'^bom/?', BomList.as_view(), name='api-bom-list'), url(r'^(?P\d+)/', PartDetail.as_view(), name='api-part-detail'), From 9b4e86f36a548274fda3710f93e1135503159fa1 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 26 Apr 2019 00:06:35 +1000 Subject: [PATCH 03/16] Add ability to create a new supplierpart from the API --- InvenTree/part/api.py | 2 +- InvenTree/part/serializers.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 50e0150d46..18498207c2 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -146,7 +146,7 @@ class BomList(generics.ListAPIView): ] -class SupplierPartList(generics.ListAPIView): +class SupplierPartList(generics.ListCreateAPIView): queryset = SupplierPart.objects.all() serializer_class = SupplierPartSerializer diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 378dbe4732..5a501b0f7c 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -90,8 +90,9 @@ class SupplierPartSerializer(serializers.ModelSerializer): url = serializers.CharField(source='get_absolute_url', read_only=True) - part = PartBriefSerializer(many=False, read_only=True) - supplier = CompanyBriefSerializer(many=False, read_only=True) + part_name = serializers.CharField(source='part.name', read_only=True) + + supplier_name = serializers.CharField(source='supplier.name', read_only=True) class Meta: model = SupplierPart @@ -99,7 +100,9 @@ class SupplierPartSerializer(serializers.ModelSerializer): 'pk', 'url', 'part', + 'part_name', 'supplier', + 'supplier_name', 'SKU', 'manufacturer', 'MPN', From 6f7082a9191340ca96afc9ded54269a16ba5d575 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 26 Apr 2019 00:07:06 +1000 Subject: [PATCH 04/16] Peppy --- InvenTree/part/serializers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 5a501b0f7c..afafa43b17 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -3,8 +3,6 @@ from rest_framework import serializers from .models import Part, PartCategory, BomItem from .models import SupplierPart, SupplierPriceBreak -from company.serializers import CompanyBriefSerializer - class CategorySerializer(serializers.ModelSerializer): From 9ae5ea3876fae6ef0bc092d87c71d9ea86040cf7 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 26 Apr 2019 00:10:34 +1000 Subject: [PATCH 05/16] Add RUD endpoint for Company --- InvenTree/company/api.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/InvenTree/company/api.py b/InvenTree/company/api.py index 455a0e76e1..d90cfdb1ee 100644 --- a/InvenTree/company/api.py +++ b/InvenTree/company/api.py @@ -43,7 +43,19 @@ class CompanyList(generics.ListCreateAPIView): ordering = 'name' +class CompanyDetail(generics.RetrieveUpdateDestroyAPIView): + + queryset = Company.objects.all() + serializer_class = CompanySerializer + + permission_classes = [ + permissions.IsAuthenticatedOrReadOnly, + ] + + company_api_urls = [ + url(r'^(?P\d+)/?', CompanyDetail.as_view(), name='api-company-detail'), + url(r'^.*$', CompanyList.as_view(), name='api-company-list'), ] From 6c3041d99ca491c0dc7df28d46c6492702fde7c1 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 26 Apr 2019 00:16:18 +1000 Subject: [PATCH 06/16] Remove 'draft' function from StockItem RUD endpoint --- InvenTree/InvenTree/serializers.py | 12 ------------ InvenTree/stock/api.py | 3 +-- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/InvenTree/InvenTree/serializers.py b/InvenTree/InvenTree/serializers.py index 57eddd53bd..ef4dd080fa 100644 --- a/InvenTree/InvenTree/serializers.py +++ b/InvenTree/InvenTree/serializers.py @@ -22,15 +22,3 @@ class UserSerializerBrief(serializers.ModelSerializer): 'pk', 'username', ] - - -class DraftRUDView(generics.RetrieveAPIView, generics.UpdateAPIView, generics.DestroyAPIView): - - def perform_update(self, serializer): - - ctx_data = serializer._context['request'].data - - if ctx_data.get('_is_final', False) in [True, u'true', u'True', 1]: - super(generics.UpdateAPIView, self).perform_update(serializer) - else: - pass diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index 52edd2945e..215a9e311f 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -13,7 +13,6 @@ from .serializers import LocationSerializer from .serializers import StockTrackingSerializer from InvenTree.views import TreeSerializer -from InvenTree.serializers import DraftRUDView from rest_framework.serializers import ValidationError from rest_framework.views import APIView @@ -26,7 +25,7 @@ class StockCategoryTree(TreeSerializer): model = StockLocation -class StockDetail(DraftRUDView): +class StockDetail(generics.RetrieveUpdateDestroyAPIView): """ get: From 4e3b8bdd92a524d58a597dab95d74a39f0fe2bd4 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 26 Apr 2019 00:17:02 +1000 Subject: [PATCH 07/16] Remove draft feature from Part RUD endpoint --- InvenTree/part/api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 18498207c2..d3d8677428 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -17,7 +17,6 @@ from .serializers import SupplierPartSerializer, SupplierPriceBreakSerializer from .serializers import CategorySerializer from InvenTree.views import TreeSerializer -from InvenTree.serializers import DraftRUDView class PartCategoryTree(TreeSerializer): @@ -56,7 +55,7 @@ class CategoryList(generics.ListCreateAPIView): ] -class PartDetail(DraftRUDView): +class PartDetail(generics.RetrieveUpdateDestroyAPIView): queryset = Part.objects.all() serializer_class = PartSerializer From da540e1b262a2d3b1b9bb7a4df74d906e938e04c Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 26 Apr 2019 00:29:53 +1000 Subject: [PATCH 08/16] Improve BOM API endpoint - Origin at /api/bom - Add detail view - Add create power to list view --- InvenTree/InvenTree/urls.py | 11 ++++++----- InvenTree/part/api.py | 21 +++++++++++++++++++-- InvenTree/part/serializers.py | 10 ++++++---- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index b5a0f328da..f9a40af893 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -11,7 +11,7 @@ from stock.urls import stock_urls from build.urls import build_urls -from part.api import part_api_urls +from part.api import part_api_urls, bom_api_urls from company.api import company_api_urls from stock.api import stock_api_urls from build.api import build_api_urls @@ -29,10 +29,11 @@ from users.urls import user_urls admin.site.site_header = "InvenTree Admin" apipatterns = [ - url(r'^part/', include(part_api_urls)), - url(r'^company/', include(company_api_urls)), - url(r'^stock/', include(stock_api_urls)), - url(r'^build/', include(build_api_urls)), + url(r'^part/?', include(part_api_urls)), + url(r'^bom/?', include(bom_api_urls)), + url(r'^company/?', include(company_api_urls)), + url(r'^stock/?', include(stock_api_urls)), + url(r'^build/?', include(build_api_urls)), # User URLs url(r'^user/', include(user_urls)), diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index d3d8677428..2b4ceb69f5 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -124,7 +124,7 @@ class PartList(generics.ListCreateAPIView): ] -class BomList(generics.ListAPIView): +class BomList(generics.ListCreateAPIView): queryset = BomItem.objects.all() serializer_class = BomItemSerializer @@ -145,6 +145,16 @@ class BomList(generics.ListAPIView): ] +class BomDetail(generics.RetrieveUpdateDestroyAPIView): + + queryset = BomItem.objects.all() + serializer_class = BomItemSerializer + + permission_classes = [ + permissions.IsAuthenticatedOrReadOnly, + ] + + class SupplierPartList(generics.ListCreateAPIView): queryset = SupplierPart.objects.all() @@ -213,9 +223,16 @@ part_api_urls = [ url(r'^supplier/?', include(supplier_part_api_urls)), url(r'^price-break/?', SupplierPriceBreakList.as_view(), name='api-part-supplier-price'), - url(r'^bom/?', BomList.as_view(), name='api-bom-list'), url(r'^(?P\d+)/', PartDetail.as_view(), name='api-part-detail'), url(r'^.*$', PartList.as_view(), name='api-part-list'), ] + +bom_api_urls = [ + # BOM Item Detail + url('^(?P\d+)/?', BomDetail.as_view(), name='api-bom-detail'), + + # Catch-all + url(r'^.*$', BomList.as_view(), name='api-bom-list'), +] \ No newline at end of file diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index afafa43b17..102da8bd76 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -67,18 +67,20 @@ class PartSerializer(serializers.ModelSerializer): class BomItemSerializer(serializers.ModelSerializer): - url = serializers.CharField(source='get_absolute_url', read_only=True) + # url = serializers.CharField(source='get_absolute_url', read_only=True) - part = PartBriefSerializer(many=False, read_only=True) - sub_part = PartBriefSerializer(many=False, read_only=True) + part_detail = PartBriefSerializer(source='part', many=False, read_only=True) + sub_part_detail = PartBriefSerializer(source='sub_part', many=False, read_only=True) class Meta: model = BomItem fields = [ 'pk', - 'url', + # 'url', 'part', + 'part_detail', 'sub_part', + 'sub_part_detail', 'quantity', 'note', ] From accec07fe98146477f34914f86ba7b329ac95274 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 26 Apr 2019 22:36:44 +1000 Subject: [PATCH 09/16] BOM serializer now validates against model - Need to call instance.clean() - https://stackoverflow.com/questions/40697648/django-dry-model-form-serializer-validation --- InvenTree/InvenTree/urls.py | 10 +++++----- InvenTree/part/api.py | 2 +- InvenTree/part/models.py | 5 +++-- InvenTree/part/serializers.py | 5 +++++ 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index f9a40af893..c33689be61 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -29,11 +29,11 @@ from users.urls import user_urls admin.site.site_header = "InvenTree Admin" apipatterns = [ - url(r'^part/?', include(part_api_urls)), - url(r'^bom/?', include(bom_api_urls)), - url(r'^company/?', include(company_api_urls)), - url(r'^stock/?', include(stock_api_urls)), - url(r'^build/?', include(build_api_urls)), + url(r'^part/', include(part_api_urls)), + url(r'^bom/', include(bom_api_urls)), + url(r'^company/', include(company_api_urls)), + url(r'^stock/', include(stock_api_urls)), + url(r'^build/', include(build_api_urls)), # User URLs url(r'^user/', include(user_urls)), diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 2b4ceb69f5..839c00c7f2 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -220,7 +220,7 @@ part_api_urls = [ url(r'^tree/?', PartCategoryTree.as_view(), name='api-part-tree'), url(r'^category/', include(cat_api_urls)), - url(r'^supplier/?', include(supplier_part_api_urls)), + url(r'^supplier/', include(supplier_part_api_urls)), url(r'^price-break/?', SupplierPriceBreakList.as_view(), name='api-part-supplier-price'), diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 6c9dc4ec54..40cf511b78 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -359,11 +359,12 @@ class BomItem(models.Model): # A part cannot refer to itself in its BOM if self.part == self.sub_part: - raise ValidationError(_('A part cannot contain itself as a BOM item')) + raise ValidationError({'sub_part': _('Part cannot be added to its own Bill of Materials')}) + # Test for simple recursion for item in self.sub_part.bom_items.all(): if self.part == item.sub_part: - raise ValidationError(_("Part '{p1}' is used in BOM for '{p2}' (recursive)".format(p1=str(self.part), p2=str(self.sub_part)))) + raise ValidationError({'sub_part': _("Part '{p1}' is used in BOM for '{p2}' (recursive)".format(p1=str(self.part), p2=str(self.sub_part)))}) class Meta: verbose_name = "BOM Item" diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 102da8bd76..fbd9421806 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -69,6 +69,11 @@ class BomItemSerializer(serializers.ModelSerializer): # url = serializers.CharField(source='get_absolute_url', read_only=True) + def validate(self, data): + instance = BomItem(**data) + instance.clean() + return data + part_detail = PartBriefSerializer(source='part', many=False, read_only=True) sub_part_detail = PartBriefSerializer(source='sub_part', many=False, read_only=True) From 73282801c9d81d0ab86af7a7dd5049fc69692c57 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 26 Apr 2019 22:41:29 +1000 Subject: [PATCH 10/16] Fix for BOM display - Perhaps need to make this cleaner, later on - Is this too much data pushed through the API? --- InvenTree/static/script/inventree/bom.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/InvenTree/static/script/inventree/bom.js b/InvenTree/static/script/inventree/bom.js index 3d8db7d7e2..b749e9b4e0 100644 --- a/InvenTree/static/script/inventree/bom.js +++ b/InvenTree/static/script/inventree/bom.js @@ -87,7 +87,7 @@ function loadBomTable(table, options) { // Part column cols.push( { - field: 'sub_part', + field: 'sub_part_detail', title: 'Part', sortable: true, formatter: function(value, row, index, field) { @@ -99,7 +99,7 @@ function loadBomTable(table, options) { // Part description cols.push( { - field: 'sub_part.description', + field: 'sub_part_detail.description', title: 'Description', } ); @@ -137,14 +137,14 @@ function loadBomTable(table, options) { else { cols.push( { - field: 'sub_part.available_stock', + field: 'sub_part_detail.available_stock', title: 'Available', searchable: false, sortable: true, formatter: function(value, row, index, field) { var text = ""; - if (row.quantity < row.sub_part.available_stock) + if (row.quantity < row.sub_part_detail.available_stock) { text = "" + value + ""; } From 1c710e71a526e256365bf1427bfb90ea02b63a1e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 26 Apr 2019 23:00:48 +1000 Subject: [PATCH 11/16] Modal forms now display non-field errors - Critical for errors not relating to a particular field (e.g. unique_together) - Ref: https://stackoverflow.com/questions/7419535/how-to-render-django-form-errors-not-in-a-ul --- InvenTree/templates/modal_form.html | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/InvenTree/templates/modal_form.html b/InvenTree/templates/modal_form.html index 44fd4c70f8..1318e4f238 100644 --- a/InvenTree/templates/modal_form.html +++ b/InvenTree/templates/modal_form.html @@ -1,5 +1,14 @@ +{% if form.non_field_errors %} + +{% endif %} +
{% csrf_token %} {% load crispy_forms_tags %} + + {% crispy form %}
\ No newline at end of file From dee10e4922a5674571b672069283d57747cd862a Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 26 Apr 2019 23:04:08 +1000 Subject: [PATCH 12/16] Fix rendering of part supplier table --- InvenTree/part/templates/part/supplier.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/part/templates/part/supplier.html b/InvenTree/part/templates/part/supplier.html index 2548297774..fba1ec38db 100644 --- a/InvenTree/part/templates/part/supplier.html +++ b/InvenTree/part/templates/part/supplier.html @@ -51,10 +51,10 @@ }, { sortable: true, - field: 'supplier', + field: 'supplier_name', title: 'Supplier', formatter: function(value, row, index, field) { - return renderLink(value.name, value.url); + return renderLink(value, '/company/' + row.supplier + '/'); } }, { From 08aca6a5a8f8af8b060e6cd5bfa7f32abba6a4d7 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 26 Apr 2019 23:08:13 +1000 Subject: [PATCH 13/16] Fix rendering for company part list --- InvenTree/company/templates/company/detail_part.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/company/templates/company/detail_part.html b/InvenTree/company/templates/company/detail_part.html index c3d9f0a8b3..1db55c12d5 100644 --- a/InvenTree/company/templates/company/detail_part.html +++ b/InvenTree/company/templates/company/detail_part.html @@ -50,10 +50,10 @@ }, { sortable: true, - field: 'part', + field: 'part_name', title: 'Part', formatter: function(value, row, index, field) { - return renderLink(value.name, value.url + 'suppliers/'); + return renderLink(value, '/part/' + row.part + '/suppliers/'); } }, { From cec61655cfbd4cfe40bdf6b3ef057ca1aabec181 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 26 Apr 2019 23:12:06 +1000 Subject: [PATCH 14/16] Fix table rendering for 'used_in' view --- InvenTree/part/templates/part/used_in.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/InvenTree/part/templates/part/used_in.html b/InvenTree/part/templates/part/used_in.html index bff5eff8ed..bce5dfcffa 100644 --- a/InvenTree/part/templates/part/used_in.html +++ b/InvenTree/part/templates/part/used_in.html @@ -41,14 +41,14 @@ visible: false, }, { - field: 'part', + field: 'part_detail', title: 'Part', formatter: function(value, row, index, field) { - return renderLink(value.name, value.url); + return renderLink(value.name, value.url + 'bom/'); } }, { - field: 'part.description', + field: 'part_detail.description', title: 'Description', }, { From e57a8cdcd184e1a66632739d3bfcd70d0749b7bf Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 26 Apr 2019 23:32:22 +1000 Subject: [PATCH 15/16] Fix rendering of BOM table view - Edit and delete rows now works --- InvenTree/part/models.py | 2 +- InvenTree/static/script/inventree/bom.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 40cf511b78..fa04b48c9b 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -337,7 +337,7 @@ class BomItem(models.Model): """ def get_absolute_url(self): - return reverse('bom-detail', kwargs={'pk': self.id}) + return reverse('bom-item-detail', kwargs={'pk': self.id}) # A link to the parent part # Each part will get a reverse lookup field 'bom_items' diff --git a/InvenTree/static/script/inventree/bom.js b/InvenTree/static/script/inventree/bom.js index b749e9b4e0..cc533f5201 100644 --- a/InvenTree/static/script/inventree/bom.js +++ b/InvenTree/static/script/inventree/bom.js @@ -127,8 +127,8 @@ function loadBomTable(table, options) { if (options.editable) { cols.push({ formatter: function(value, row, index, field) { - var bEdit = ""; - var bDelt = ""; + var bEdit = ""; + var bDelt = ""; return "
" + bEdit + bDelt + "
"; } From 529beb3d58b4ba18aa72dcd0fb3ec8dcf813e483 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 26 Apr 2019 23:34:15 +1000 Subject: [PATCH 16/16] Abstract DRF serializer validation - Subclass InvenTree.InvenTreeModelSerializer - Ensures model data is checked too (after serializer checks are performed) --- InvenTree/InvenTree/serializers.py | 18 +++++++++++++++++- InvenTree/part/api.py | 2 +- InvenTree/part/serializers.py | 9 +++------ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/InvenTree/InvenTree/serializers.py b/InvenTree/InvenTree/serializers.py index ef4dd080fa..4ab1fd694d 100644 --- a/InvenTree/InvenTree/serializers.py +++ b/InvenTree/InvenTree/serializers.py @@ -2,7 +2,6 @@ from __future__ import unicode_literals from rest_framework import serializers -from rest_framework import generics from django.contrib.auth.models import User @@ -22,3 +21,20 @@ class UserSerializerBrief(serializers.ModelSerializer): 'pk', 'username', ] + + +class InvenTreeModelSerializer(serializers.ModelSerializer): + """ + Inherits the standard Django ModelSerializer class, + but also ensures that the underlying model class data are checked on validation. + """ + + def validate(self, data): + # Run any native validation checks first (may throw an ValidationError) + data = super(serializers.ModelSerializer, self).validate(data) + + # Now ensure the underlying model is correct + instance = self.Meta.model(**data) + instance.clean() + + return data diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 839c00c7f2..96606c643a 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -235,4 +235,4 @@ bom_api_urls = [ # Catch-all url(r'^.*$', BomList.as_view(), name='api-bom-list'), -] \ No newline at end of file +] diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index fbd9421806..68066645bd 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -3,6 +3,8 @@ from rest_framework import serializers from .models import Part, PartCategory, BomItem from .models import SupplierPart, SupplierPriceBreak +from InvenTree.serializers import InvenTreeModelSerializer + class CategorySerializer(serializers.ModelSerializer): @@ -65,15 +67,10 @@ class PartSerializer(serializers.ModelSerializer): ] -class BomItemSerializer(serializers.ModelSerializer): +class BomItemSerializer(InvenTreeModelSerializer): # url = serializers.CharField(source='get_absolute_url', read_only=True) - def validate(self, data): - instance = BomItem(**data) - instance.clean() - return data - part_detail = PartBriefSerializer(source='part', many=False, read_only=True) sub_part_detail = PartBriefSerializer(source='sub_part', many=False, read_only=True)