mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Replace "edit part category" form
This commit is contained in:
parent
c425f36a35
commit
621f47e46c
@ -10,11 +10,13 @@ from django.db import models
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from django.db.models.signals import pre_delete
|
from django.db.models.signals import pre_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from mptt.models import MPTTModel, TreeForeignKey
|
from mptt.models import MPTTModel, TreeForeignKey
|
||||||
|
from mptt.exceptions import InvalidMove
|
||||||
|
|
||||||
from .validators import validate_tree_name
|
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
|
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:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
@ -110,6 +110,19 @@ class InvenTreeModelSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
return initials
|
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):
|
def run_validation(self, data=empty):
|
||||||
"""
|
"""
|
||||||
Perform serializer validation.
|
Perform serializer validation.
|
||||||
|
@ -127,7 +127,10 @@ class CategoryList(generics.ListCreateAPIView):
|
|||||||
|
|
||||||
|
|
||||||
class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
|
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
|
serializer_class = part_serializers.CategorySerializer
|
||||||
queryset = PartCategory.objects.all()
|
queryset = PartCategory.objects.all()
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ class CategorySerializer(InvenTreeModelSerializer):
|
|||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
'default_location',
|
'default_location',
|
||||||
|
'default_keywords',
|
||||||
'pathstring',
|
'pathstring',
|
||||||
'url',
|
'url',
|
||||||
'parent',
|
'parent',
|
||||||
|
@ -268,13 +268,23 @@
|
|||||||
|
|
||||||
{% if category %}
|
{% if category %}
|
||||||
$("#cat-edit").click(function () {
|
$("#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
|
reload: true
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
{% if category.parent %}
|
{% if category.parent %}
|
||||||
|
@ -294,11 +294,6 @@ class CategoryTest(PartViewTestCase):
|
|||||||
# Form should still return OK
|
# Form should still return OK
|
||||||
self.assertEqual(response.status_code, 200)
|
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):
|
def test_set_category(self):
|
||||||
""" Test that the "SetCategory" view works """
|
""" Test that the "SetCategory" view works """
|
||||||
|
|
||||||
|
@ -104,7 +104,6 @@ category_urls = [
|
|||||||
|
|
||||||
# Category detail views
|
# Category detail views
|
||||||
url(r'(?P<pk>\d+)/', include([
|
url(r'(?P<pk>\d+)/', include([
|
||||||
url(r'^edit/', views.CategoryEdit.as_view(), name='category-edit'),
|
|
||||||
url(r'^delete/', views.CategoryDelete.as_view(), name='category-delete'),
|
url(r'^delete/', views.CategoryDelete.as_view(), name='category-delete'),
|
||||||
url(r'^parameters/', include(category_parameter_urls)),
|
url(r'^parameters/', include(category_parameter_urls)),
|
||||||
|
|
||||||
|
@ -388,8 +388,13 @@ function constructFormBody(fields, options) {
|
|||||||
function submitFormData(fields, options) {
|
function submitFormData(fields, options) {
|
||||||
|
|
||||||
// Form data to be uploaded to the server
|
// Form data to be uploaded to the server
|
||||||
|
// Only used if file / image upload is required
|
||||||
var form_data = new FormData();
|
var form_data = new FormData();
|
||||||
|
|
||||||
|
var data = {};
|
||||||
|
|
||||||
|
var has_files = false;
|
||||||
|
|
||||||
// Extract values for each field
|
// Extract values for each field
|
||||||
options.field_names.forEach(function(name) {
|
options.field_names.forEach(function(name) {
|
||||||
|
|
||||||
@ -411,20 +416,31 @@ function submitFormData(fields, options) {
|
|||||||
var file = field_files[0];
|
var file = field_files[0];
|
||||||
|
|
||||||
form_data.append(name, file);
|
form_data.append(name, file);
|
||||||
|
|
||||||
|
has_files = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Normal field (not a file or image)
|
// Normal field (not a file or image)
|
||||||
form_data.append(name, value);
|
form_data.append(name, value);
|
||||||
|
|
||||||
|
data[name] = value;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`WARNING: Could not find field matching '${name}'`);
|
console.log(`WARNING: Could not find field matching '${name}'`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var upload_func = inventreePut;
|
||||||
|
|
||||||
|
if (has_files) {
|
||||||
|
upload_func = inventreeFormDataUpload;
|
||||||
|
data = form_data;
|
||||||
|
}
|
||||||
|
|
||||||
// Submit data
|
// Submit data
|
||||||
inventreeFormDataUpload(
|
upload_func(
|
||||||
options.url,
|
options.url,
|
||||||
form_data,
|
data,
|
||||||
{
|
{
|
||||||
method: options.method,
|
method: options.method,
|
||||||
success: function(response, status) {
|
success: function(response, status) {
|
||||||
@ -708,6 +724,7 @@ function initializeRelatedField(name, field, options) {
|
|||||||
ajax: {
|
ajax: {
|
||||||
url: field.api_url,
|
url: field.api_url,
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
|
placeholder: '',
|
||||||
allowClear: !field.required,
|
allowClear: !field.required,
|
||||||
dropdownParent: $(options.modal),
|
dropdownParent: $(options.modal),
|
||||||
dropdownAutoWidth: false,
|
dropdownAutoWidth: false,
|
||||||
|
Loading…
Reference in New Issue
Block a user