From 621f47e46c1e7ba14f44e0134881fbbd35e2a671 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 30 Jun 2021 01:04:39 +1000 Subject: [PATCH] Replace "edit part category" form --- InvenTree/InvenTree/models.py | 11 +++++++++++ InvenTree/InvenTree/serializers.py | 13 +++++++++++++ InvenTree/part/api.py | 5 ++++- InvenTree/part/serializers.py | 1 + InvenTree/part/templates/part/category.html | 18 ++++++++++++++---- InvenTree/part/test_views.py | 5 ----- InvenTree/part/urls.py | 1 - InvenTree/templates/js/forms.js | 21 +++++++++++++++++++-- 8 files changed, 62 insertions(+), 13 deletions(-) diff --git a/InvenTree/InvenTree/models.py b/InvenTree/InvenTree/models.py index 5822f8a19f..2831a23151 100644 --- a/InvenTree/InvenTree/models.py +++ b/InvenTree/InvenTree/models.py @@ -10,11 +10,13 @@ from django.db import models from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.utils.translation import gettext_lazy as _ +from django.core.exceptions import ValidationError from django.db.models.signals import pre_delete from django.dispatch import receiver from mptt.models import MPTTModel, TreeForeignKey +from mptt.exceptions import InvalidMove from .validators import validate_tree_name @@ -91,6 +93,15 @@ class InvenTreeTree(MPTTModel): parent: The item immediately above this one. An item with a null parent is a top-level item """ + def save(self, *args, **kwargs): + + try: + super().save(*args, **kwargs) + except InvalidMove: + raise ValidationError({ + 'parent': _("Invalid choice"), + }) + class Meta: abstract = True diff --git a/InvenTree/InvenTree/serializers.py b/InvenTree/InvenTree/serializers.py index 772daa06ab..19c70fa29a 100644 --- a/InvenTree/InvenTree/serializers.py +++ b/InvenTree/InvenTree/serializers.py @@ -110,6 +110,19 @@ class InvenTreeModelSerializer(serializers.ModelSerializer): return initials + def save(self, **kwargs): + """ + Catch any django ValidationError thrown at the moment save() is called, + and re-throw as a DRF ValidationError + """ + + try: + super().save(**kwargs) + except (ValidationError, DjangoValidationError) as exc: + raise ValidationError(detail=serializers.as_serializer_error(exc)) + + return self.instance + def run_validation(self, data=empty): """ Perform serializer validation. diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 8ef6902600..2212743c2b 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -127,7 +127,10 @@ class CategoryList(generics.ListCreateAPIView): class CategoryDetail(generics.RetrieveUpdateDestroyAPIView): - """ API endpoint for detail view of a single PartCategory object """ + """ + API endpoint for detail view of a single PartCategory object + """ + serializer_class = part_serializers.CategorySerializer queryset = PartCategory.objects.all() diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index ff178c5941..fb5480f668 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -39,6 +39,7 @@ class CategorySerializer(InvenTreeModelSerializer): 'name', 'description', 'default_location', + 'default_keywords', 'pathstring', 'url', 'parent', diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html index ef250d4c89..125e089721 100644 --- a/InvenTree/part/templates/part/category.html +++ b/InvenTree/part/templates/part/category.html @@ -268,13 +268,23 @@ {% if category %} $("#cat-edit").click(function () { - launchModalForm( - "{% url 'category-edit' category.id %}", + + constructForm( + '{% url "api-part-category-detail" category.pk %}', { + fields: { + name: {}, + description: {}, + parent: {}, + default_location: {}, + default_keywords: { + icon: 'fa-key', + } + }, + title: '{% trans "Edit Part Category" %}', reload: true - }, + } ); - return false; }); {% if category.parent %} diff --git a/InvenTree/part/test_views.py b/InvenTree/part/test_views.py index c32753cbbb..231eed7896 100644 --- a/InvenTree/part/test_views.py +++ b/InvenTree/part/test_views.py @@ -294,11 +294,6 @@ class CategoryTest(PartViewTestCase): # Form should still return OK self.assertEqual(response.status_code, 200) - def test_edit(self): - """ Retrieve the part category editing form """ - response = self.client.get(reverse('category-edit', args=(1,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertEqual(response.status_code, 200) - def test_set_category(self): """ Test that the "SetCategory" view works """ diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index fb4a1ab9eb..80351b8dba 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -104,7 +104,6 @@ category_urls = [ # Category detail views url(r'(?P\d+)/', include([ - url(r'^edit/', views.CategoryEdit.as_view(), name='category-edit'), url(r'^delete/', views.CategoryDelete.as_view(), name='category-delete'), url(r'^parameters/', include(category_parameter_urls)), diff --git a/InvenTree/templates/js/forms.js b/InvenTree/templates/js/forms.js index 535391d2e1..c472a83ffd 100644 --- a/InvenTree/templates/js/forms.js +++ b/InvenTree/templates/js/forms.js @@ -388,8 +388,13 @@ function constructFormBody(fields, options) { function submitFormData(fields, options) { // Form data to be uploaded to the server + // Only used if file / image upload is required var form_data = new FormData(); + var data = {}; + + var has_files = false; + // Extract values for each field options.field_names.forEach(function(name) { @@ -411,20 +416,31 @@ function submitFormData(fields, options) { var file = field_files[0]; form_data.append(name, file); + + has_files = true; } } else { // Normal field (not a file or image) form_data.append(name, value); + + data[name] = value; } } else { console.log(`WARNING: Could not find field matching '${name}'`); } }); + var upload_func = inventreePut; + + if (has_files) { + upload_func = inventreeFormDataUpload; + data = form_data; + } + // Submit data - inventreeFormDataUpload( + upload_func( options.url, - form_data, + data, { method: options.method, success: function(response, status) { @@ -708,6 +724,7 @@ function initializeRelatedField(name, field, options) { ajax: { url: field.api_url, dataType: 'json', + placeholder: '', allowClear: !field.required, dropdownParent: $(options.modal), dropdownAutoWidth: false,