mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #1962 from SchrodingersGat/attachment-edit
Attachment edit
This commit is contained in:
commit
fa163b8866
@ -5,8 +5,10 @@ Generic models which provide extra functionality over base Django model types.
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import logging
|
||||
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@ -21,6 +23,9 @@ from mptt.exceptions import InvalidMove
|
||||
from .validators import validate_tree_name
|
||||
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
|
||||
|
||||
def rename_attachment(instance, filename):
|
||||
"""
|
||||
Function for renaming an attachment file.
|
||||
@ -77,6 +82,72 @@ class InvenTreeAttachment(models.Model):
|
||||
def basename(self):
|
||||
return os.path.basename(self.attachment.name)
|
||||
|
||||
@basename.setter
|
||||
def basename(self, fn):
|
||||
"""
|
||||
Function to rename the attachment file.
|
||||
|
||||
- Filename cannot be empty
|
||||
- Filename cannot contain illegal characters
|
||||
- Filename must specify an extension
|
||||
- Filename cannot match an existing file
|
||||
"""
|
||||
|
||||
fn = fn.strip()
|
||||
|
||||
if len(fn) == 0:
|
||||
raise ValidationError(_('Filename must not be empty'))
|
||||
|
||||
attachment_dir = os.path.join(
|
||||
settings.MEDIA_ROOT,
|
||||
self.getSubdir()
|
||||
)
|
||||
|
||||
old_file = os.path.join(
|
||||
settings.MEDIA_ROOT,
|
||||
self.attachment.name
|
||||
)
|
||||
|
||||
new_file = os.path.join(
|
||||
settings.MEDIA_ROOT,
|
||||
self.getSubdir(),
|
||||
fn
|
||||
)
|
||||
|
||||
new_file = os.path.abspath(new_file)
|
||||
|
||||
# Check that there are no directory tricks going on...
|
||||
if not os.path.dirname(new_file) == attachment_dir:
|
||||
logger.error(f"Attempted to rename attachment outside valid directory: '{new_file}'")
|
||||
raise ValidationError(_("Invalid attachment directory"))
|
||||
|
||||
# Ignore further checks if the filename is not actually being renamed
|
||||
if new_file == old_file:
|
||||
return
|
||||
|
||||
forbidden = ["'", '"', "#", "@", "!", "&", "^", "<", ">", ":", ";", "/", "\\", "|", "?", "*", "%", "~", "`"]
|
||||
|
||||
for c in forbidden:
|
||||
if c in fn:
|
||||
raise ValidationError(_(f"Filename contains illegal character '{c}'"))
|
||||
|
||||
if len(fn.split('.')) < 2:
|
||||
raise ValidationError(_("Filename missing extension"))
|
||||
|
||||
if not os.path.exists(old_file):
|
||||
logger.error(f"Trying to rename attachment '{old_file}' which does not exist")
|
||||
return
|
||||
|
||||
if os.path.exists(new_file):
|
||||
raise ValidationError(_("Attachment with this filename already exists"))
|
||||
|
||||
try:
|
||||
os.rename(old_file, new_file)
|
||||
self.attachment.name = os.path.join(self.getSubdir(), fn)
|
||||
self.save()
|
||||
except:
|
||||
raise ValidationError(_("Error renaming file"))
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
@ -167,6 +167,18 @@ class InvenTreeModelSerializer(serializers.ModelSerializer):
|
||||
|
||||
return self.instance
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
Catch any django ValidationError, and re-throw as a DRF ValidationError
|
||||
"""
|
||||
|
||||
try:
|
||||
instance = super().update(instance, validated_data)
|
||||
except (ValidationError, DjangoValidationError) as exc:
|
||||
raise ValidationError(detail=serializers.as_serializer_error(exc))
|
||||
|
||||
return instance
|
||||
|
||||
def run_validation(self, data=empty):
|
||||
"""
|
||||
Perform serializer validation.
|
||||
@ -188,7 +200,10 @@ class InvenTreeModelSerializer(serializers.ModelSerializer):
|
||||
|
||||
# Update instance fields
|
||||
for attr, value in data.items():
|
||||
setattr(instance, attr, value)
|
||||
try:
|
||||
setattr(instance, attr, value)
|
||||
except (ValidationError, DjangoValidationError) as exc:
|
||||
raise ValidationError(detail=serializers.as_serializer_error(exc))
|
||||
|
||||
# Run a 'full_clean' on the model.
|
||||
# Note that by default, DRF does *not* perform full model validation!
|
||||
@ -208,6 +223,22 @@ class InvenTreeModelSerializer(serializers.ModelSerializer):
|
||||
return data
|
||||
|
||||
|
||||
class InvenTreeAttachmentSerializer(InvenTreeModelSerializer):
|
||||
"""
|
||||
Special case of an InvenTreeModelSerializer, which handles an "attachment" model.
|
||||
|
||||
The only real addition here is that we support "renaming" of the attachment file.
|
||||
"""
|
||||
|
||||
# The 'filename' field must be present in the serializer
|
||||
filename = serializers.CharField(
|
||||
label=_('Filename'),
|
||||
required=False,
|
||||
source='basename',
|
||||
allow_blank=False,
|
||||
)
|
||||
|
||||
|
||||
class InvenTreeAttachmentSerializerField(serializers.FileField):
|
||||
"""
|
||||
Override the DRF native FileField serializer,
|
||||
|
@ -10,7 +10,8 @@ from django.db.models import BooleanField
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from InvenTree.serializers import InvenTreeModelSerializer, InvenTreeAttachmentSerializerField, UserSerializerBrief
|
||||
from InvenTree.serializers import InvenTreeModelSerializer, InvenTreeAttachmentSerializer
|
||||
from InvenTree.serializers import InvenTreeAttachmentSerializerField, UserSerializerBrief
|
||||
|
||||
from stock.serializers import StockItemSerializerBrief
|
||||
from stock.serializers import LocationSerializer
|
||||
@ -158,7 +159,7 @@ class BuildItemSerializer(InvenTreeModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class BuildAttachmentSerializer(InvenTreeModelSerializer):
|
||||
class BuildAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||
"""
|
||||
Serializer for a BuildAttachment
|
||||
"""
|
||||
@ -172,6 +173,7 @@ class BuildAttachmentSerializer(InvenTreeModelSerializer):
|
||||
'pk',
|
||||
'build',
|
||||
'attachment',
|
||||
'filename',
|
||||
'comment',
|
||||
'upload_date',
|
||||
]
|
||||
|
@ -369,6 +369,7 @@ loadAttachmentTable(
|
||||
|
||||
constructForm(url, {
|
||||
fields: {
|
||||
filename: {},
|
||||
comment: {},
|
||||
},
|
||||
onSuccess: reloadAttachmentTable,
|
||||
|
@ -14,6 +14,7 @@ from rest_framework import serializers
|
||||
from sql_util.utils import SubqueryCount
|
||||
|
||||
from InvenTree.serializers import InvenTreeModelSerializer
|
||||
from InvenTree.serializers import InvenTreeAttachmentSerializer
|
||||
from InvenTree.serializers import InvenTreeMoneySerializer
|
||||
from InvenTree.serializers import InvenTreeAttachmentSerializerField
|
||||
|
||||
@ -160,7 +161,7 @@ class POLineItemSerializer(InvenTreeModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class POAttachmentSerializer(InvenTreeModelSerializer):
|
||||
class POAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||
"""
|
||||
Serializers for the PurchaseOrderAttachment model
|
||||
"""
|
||||
@ -174,6 +175,7 @@ class POAttachmentSerializer(InvenTreeModelSerializer):
|
||||
'pk',
|
||||
'order',
|
||||
'attachment',
|
||||
'filename',
|
||||
'comment',
|
||||
'upload_date',
|
||||
]
|
||||
@ -381,7 +383,7 @@ class SOLineItemSerializer(InvenTreeModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class SOAttachmentSerializer(InvenTreeModelSerializer):
|
||||
class SOAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||
"""
|
||||
Serializers for the SalesOrderAttachment model
|
||||
"""
|
||||
@ -395,6 +397,7 @@ class SOAttachmentSerializer(InvenTreeModelSerializer):
|
||||
'pk',
|
||||
'order',
|
||||
'attachment',
|
||||
'filename',
|
||||
'comment',
|
||||
'upload_date',
|
||||
]
|
||||
|
@ -122,6 +122,7 @@
|
||||
|
||||
constructForm(url, {
|
||||
fields: {
|
||||
filename: {},
|
||||
comment: {},
|
||||
},
|
||||
onSuccess: reloadAttachmentTable,
|
||||
|
@ -112,6 +112,7 @@
|
||||
|
||||
constructForm(url, {
|
||||
fields: {
|
||||
filename: {},
|
||||
comment: {},
|
||||
},
|
||||
onSuccess: reloadAttachmentTable,
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""
|
||||
JSON serializers for Part app
|
||||
"""
|
||||
|
||||
import imghdr
|
||||
from decimal import Decimal
|
||||
|
||||
@ -16,7 +17,9 @@ from djmoney.contrib.django_rest_framework import MoneyField
|
||||
from InvenTree.serializers import (InvenTreeAttachmentSerializerField,
|
||||
InvenTreeImageSerializerField,
|
||||
InvenTreeModelSerializer,
|
||||
InvenTreeAttachmentSerializer,
|
||||
InvenTreeMoneySerializer)
|
||||
|
||||
from InvenTree.status_codes import BuildStatus, PurchaseOrderStatus
|
||||
from stock.models import StockItem
|
||||
|
||||
@ -51,7 +54,7 @@ class CategorySerializer(InvenTreeModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class PartAttachmentSerializer(InvenTreeModelSerializer):
|
||||
class PartAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||
"""
|
||||
Serializer for the PartAttachment class
|
||||
"""
|
||||
@ -65,6 +68,7 @@ class PartAttachmentSerializer(InvenTreeModelSerializer):
|
||||
'pk',
|
||||
'part',
|
||||
'attachment',
|
||||
'filename',
|
||||
'comment',
|
||||
'upload_date',
|
||||
]
|
||||
|
@ -868,6 +868,7 @@
|
||||
|
||||
constructForm(url, {
|
||||
fields: {
|
||||
filename: {},
|
||||
comment: {},
|
||||
},
|
||||
title: '{% trans "Edit Attachment" %}',
|
||||
|
@ -25,7 +25,7 @@ import common.models
|
||||
from company.serializers import SupplierPartSerializer
|
||||
from part.serializers import PartBriefSerializer
|
||||
from InvenTree.serializers import UserSerializerBrief, InvenTreeModelSerializer
|
||||
from InvenTree.serializers import InvenTreeAttachmentSerializerField
|
||||
from InvenTree.serializers import InvenTreeAttachmentSerializer, InvenTreeAttachmentSerializerField
|
||||
|
||||
|
||||
class LocationBriefSerializer(InvenTreeModelSerializer):
|
||||
@ -253,7 +253,7 @@ class LocationSerializer(InvenTreeModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class StockItemAttachmentSerializer(InvenTreeModelSerializer):
|
||||
class StockItemAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||
""" Serializer for StockItemAttachment model """
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -277,6 +277,7 @@ class StockItemAttachmentSerializer(InvenTreeModelSerializer):
|
||||
'pk',
|
||||
'stock_item',
|
||||
'attachment',
|
||||
'filename',
|
||||
'comment',
|
||||
'upload_date',
|
||||
'user',
|
||||
|
@ -215,6 +215,7 @@
|
||||
|
||||
constructForm(url, {
|
||||
fields: {
|
||||
filename: {},
|
||||
comment: {},
|
||||
},
|
||||
title: '{% trans "Edit Attachment" %}',
|
||||
|
Loading…
Reference in New Issue
Block a user