feat: item image now is required
This commit is contained in:
@@ -1,20 +1,35 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Item, ItemUsage, Category
|
||||
from .models import Item, ItemUsage, Category, ItemImage, UsageImage
|
||||
|
||||
|
||||
class ItemImageInline(admin.TabularInline):
|
||||
"""物品图片内联编辑"""
|
||||
model = ItemImage
|
||||
extra = 1
|
||||
fields = ['image', 'description', 'is_primary']
|
||||
|
||||
|
||||
class UsageImageInline(admin.TabularInline):
|
||||
"""使用记录图片内联编辑"""
|
||||
model = UsageImage
|
||||
extra = 1
|
||||
fields = ['image', 'image_type', 'description']
|
||||
|
||||
|
||||
@admin.register(Item)
|
||||
class ItemAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'serial_number', 'category', 'status', 'location', 'created_at']
|
||||
list_display = ['name', 'serial_number', 'category', 'status', 'location', 'owner', 'created_at']
|
||||
list_filter = ['status', 'category', 'created_at']
|
||||
search_fields = ['name', 'serial_number', 'description']
|
||||
search_fields = ['name', 'serial_number', 'description', 'owner']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
inlines = [ItemImageInline]
|
||||
fieldsets = (
|
||||
('基本信息', {
|
||||
'fields': ('name', 'description', 'serial_number', 'category')
|
||||
}),
|
||||
('状态和位置', {
|
||||
'fields': ('status', 'location')
|
||||
'fields': ('status', 'location', 'owner')
|
||||
}),
|
||||
('购买信息', {
|
||||
'fields': ('purchase_date', 'value')
|
||||
@@ -28,16 +43,17 @@ class ItemAdmin(admin.ModelAdmin):
|
||||
|
||||
@admin.register(ItemUsage)
|
||||
class ItemUsageAdmin(admin.ModelAdmin):
|
||||
list_display = ['item', 'user', 'start_time', 'end_time', 'is_returned', 'purpose']
|
||||
list_display = ['item', 'user', 'borrower_contact', 'start_time', 'end_time', 'is_returned', 'purpose']
|
||||
list_filter = ['is_returned', 'start_time', 'item__category']
|
||||
search_fields = ['item__name', 'user__username', 'purpose']
|
||||
search_fields = ['item__name', 'user', 'purpose', 'borrower_contact']
|
||||
readonly_fields = ['created_at']
|
||||
inlines = [UsageImageInline]
|
||||
fieldsets = (
|
||||
('使用信息', {
|
||||
'fields': ('item', 'user', 'purpose', 'notes')
|
||||
'fields': ('item', 'user', 'borrower_contact', 'purpose', 'notes')
|
||||
}),
|
||||
('时间信息', {
|
||||
'fields': ('start_time', 'end_time', 'is_returned')
|
||||
'fields': ('start_time', 'end_time', 'expected_return_time', 'is_returned')
|
||||
}),
|
||||
('状况记录', {
|
||||
'fields': ('condition_before', 'condition_after')
|
||||
@@ -49,6 +65,22 @@ class ItemUsageAdmin(admin.ModelAdmin):
|
||||
)
|
||||
|
||||
|
||||
@admin.register(ItemImage)
|
||||
class ItemImageAdmin(admin.ModelAdmin):
|
||||
list_display = ['item', 'description', 'is_primary', 'created_at']
|
||||
list_filter = ['is_primary', 'created_at']
|
||||
search_fields = ['item__name', 'description']
|
||||
readonly_fields = ['created_at']
|
||||
|
||||
|
||||
@admin.register(UsageImage)
|
||||
class UsageImageAdmin(admin.ModelAdmin):
|
||||
list_display = ['usage', 'image_type', 'description', 'created_at']
|
||||
list_filter = ['image_type', 'created_at']
|
||||
search_fields = ['usage__item__name', 'usage__user', 'description']
|
||||
readonly_fields = ['created_at']
|
||||
|
||||
|
||||
@admin.register(Category)
|
||||
class CategoryAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'description', 'created_at']
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from items.models import Category
|
||||
|
||||
# 添加物品类别:python manage.py create_default_categories
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '创建默认的物品类别'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
# 默认物品类别列表
|
||||
default_categories = [
|
||||
{
|
||||
'name': '电子设备',
|
||||
'description': '包括电脑、手机、平板等电子产品'
|
||||
},
|
||||
{
|
||||
'name': '办公用品',
|
||||
'description': '办公桌椅、文具、打印机等办公设备'
|
||||
},
|
||||
{
|
||||
'name': '实验器材',
|
||||
'description': '实验室设备、仪器、工具等'
|
||||
},
|
||||
{
|
||||
'name': '家具',
|
||||
'description': '桌子、椅子、柜子等家具用品'
|
||||
},
|
||||
{
|
||||
'name': '图书资料',
|
||||
'description': '书籍、文档、资料等'
|
||||
},
|
||||
{
|
||||
'name': '工具设备',
|
||||
'description': '维修工具、测量仪器等'
|
||||
},
|
||||
{
|
||||
'name': '音响设备',
|
||||
'description': '音响、麦克风、投影仪等音视频设备'
|
||||
},
|
||||
{
|
||||
'name': '运动器材',
|
||||
'description': '体育用品、健身器材等'
|
||||
},
|
||||
{
|
||||
'name': '清洁用品',
|
||||
'description': '清洁工具、清洁剂等'
|
||||
},
|
||||
{
|
||||
'name': '网络设备',
|
||||
'description': '路由器、交换机、网线等网络相关设备'
|
||||
},
|
||||
{
|
||||
'name': '安全设备',
|
||||
'description': '监控摄像头、门禁系统、报警器等安全设备'
|
||||
},
|
||||
{
|
||||
'name': '医疗用品',
|
||||
'description': '急救包、体温计、血压计等医疗相关用品'
|
||||
},
|
||||
{
|
||||
'name': '厨房用具',
|
||||
'description': '微波炉、咖啡机、餐具等厨房设备'
|
||||
},
|
||||
{
|
||||
'name': '照明设备',
|
||||
'description': '台灯、吊灯、应急灯等照明用品'
|
||||
},
|
||||
{
|
||||
'name': '存储设备',
|
||||
'description': '硬盘、U盘、移动硬盘等存储设备'
|
||||
},
|
||||
{
|
||||
'name': '车辆工具',
|
||||
'description': '汽车配件、维修工具、车载设备等'
|
||||
},
|
||||
{
|
||||
'name': '服装用品',
|
||||
'description': '工作服、防护服、鞋帽等服装类物品'
|
||||
},
|
||||
{
|
||||
'name': '教学用品',
|
||||
'description': '黑板、白板、教学模型等教学设备'
|
||||
},
|
||||
{
|
||||
'name': '通讯设备',
|
||||
'description': '对讲机、电话、传真机等通讯设备'
|
||||
},
|
||||
{
|
||||
'name': '空调制冷',
|
||||
'description': '空调、风扇、加湿器等温度调节设备'
|
||||
},
|
||||
{
|
||||
'name': '消防设备',
|
||||
'description': '灭火器、烟雾报警器、消防栓等消防用品'
|
||||
},
|
||||
{
|
||||
'name': '软件许可',
|
||||
'description': '软件授权、许可证、数字资产等'
|
||||
},
|
||||
{
|
||||
'name': '包装材料',
|
||||
'description': '纸箱、胶带、包装袋等包装用品'
|
||||
},
|
||||
{
|
||||
'name': '园艺用品',
|
||||
'description': '花盆、园艺工具、肥料等园艺相关用品'
|
||||
},
|
||||
{
|
||||
'name': '其他',
|
||||
'description': '其他未分类的物品'
|
||||
}
|
||||
]
|
||||
|
||||
created_count = 0
|
||||
updated_count = 0
|
||||
|
||||
for category_data in default_categories:
|
||||
category, created = Category.objects.get_or_create(
|
||||
name=category_data['name'],
|
||||
defaults={'description': category_data['description']}
|
||||
)
|
||||
|
||||
if created:
|
||||
created_count += 1
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(f'创建类别: {category.name}')
|
||||
)
|
||||
else:
|
||||
# 更新描述(如果不同)
|
||||
if category.description != category_data['description']:
|
||||
category.description = category_data['description']
|
||||
category.save()
|
||||
updated_count += 1
|
||||
self.stdout.write(
|
||||
self.style.WARNING(f'更新类别: {category.name}')
|
||||
)
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
f'\n任务完成!创建了 {created_count} 个新类别,更新了 {updated_count} 个类别。'
|
||||
)
|
||||
)
|
||||
@@ -1,4 +1,27 @@
|
||||
from django.db import models
|
||||
import os
|
||||
import uuid
|
||||
|
||||
|
||||
def get_item_image_path(instance, filename):
|
||||
"""生成物品图片的存储路径"""
|
||||
# 如果物品还没有ID,先生成一个临时文件夹名
|
||||
item_id = instance.item.id if instance.item.id else str(uuid.uuid4())
|
||||
return f'items/{item_id}/initial/{filename}'
|
||||
|
||||
|
||||
def get_usage_borrow_image_path(instance, filename):
|
||||
"""生成借用时图片的存储路径"""
|
||||
item_id = instance.usage.item.id if instance.usage.item.id else str(uuid.uuid4())
|
||||
usage_id = instance.usage.id if instance.usage.id else str(uuid.uuid4())
|
||||
return f'items/{item_id}/usage/{usage_id}/borrow/{filename}'
|
||||
|
||||
|
||||
def get_usage_return_image_path(instance, filename):
|
||||
"""生成归还时图片的存储路径"""
|
||||
item_id = instance.usage.item.id if instance.usage.item.id else str(uuid.uuid4())
|
||||
usage_id = instance.usage.id if instance.usage.id else str(uuid.uuid4())
|
||||
return f'items/{item_id}/usage/{usage_id}/return/{filename}'
|
||||
|
||||
|
||||
class Item(models.Model):
|
||||
@@ -34,6 +57,23 @@ class Item(models.Model):
|
||||
return f"{self.name} ({self.serial_number})"
|
||||
|
||||
|
||||
class ItemImage(models.Model):
|
||||
"""物品图片模型"""
|
||||
item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='images', verbose_name='物品')
|
||||
image = models.ImageField(upload_to=get_item_image_path, verbose_name='图片')
|
||||
description = models.CharField(max_length=200, blank=True, verbose_name='图片描述')
|
||||
is_primary = models.BooleanField(default=False, verbose_name='是否为主图')
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='上传时间')
|
||||
|
||||
class Meta:
|
||||
verbose_name = '物品图片'
|
||||
verbose_name_plural = '物品图片'
|
||||
ordering = ['-is_primary', '-created_at']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.item.name} - 图片"
|
||||
|
||||
|
||||
class ItemUsage(models.Model):
|
||||
"""物品使用记录模型"""
|
||||
item = models.ForeignKey(Item, on_delete=models.CASCADE, verbose_name='物品')
|
||||
@@ -58,6 +98,53 @@ class ItemUsage(models.Model):
|
||||
return f"{self.item.name} - {self.user} ({self.start_time.strftime('%Y-%m-%d %H:%M')})"
|
||||
|
||||
|
||||
class UsageImage(models.Model):
|
||||
"""使用记录图片模型"""
|
||||
IMAGE_TYPE_CHOICES = [
|
||||
('borrow', '借用时'),
|
||||
('return', '归还时'),
|
||||
]
|
||||
|
||||
usage = models.ForeignKey(ItemUsage, on_delete=models.CASCADE, related_name='images', verbose_name='使用记录')
|
||||
image = models.ImageField(upload_to='usage_images/', verbose_name='图片')
|
||||
image_type = models.CharField(max_length=10, choices=IMAGE_TYPE_CHOICES, verbose_name='图片类型')
|
||||
description = models.CharField(max_length=200, blank=True, verbose_name='图片描述')
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='上传时间')
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
# 保存后重新组织文件路径
|
||||
if self.image and self.usage_id and self.usage.item_id:
|
||||
import os
|
||||
import shutil
|
||||
from django.conf import settings
|
||||
|
||||
old_path = self.image.path
|
||||
if self.image_type == 'borrow':
|
||||
new_relative_path = f'items/{self.usage.item_id}/usage/{self.usage_id}/borrow/{os.path.basename(old_path)}'
|
||||
else:
|
||||
new_relative_path = f'items/{self.usage.item_id}/usage/{self.usage_id}/return/{os.path.basename(old_path)}'
|
||||
|
||||
new_path = os.path.join(settings.MEDIA_ROOT, new_relative_path)
|
||||
|
||||
# 创建目录
|
||||
os.makedirs(os.path.dirname(new_path), exist_ok=True)
|
||||
|
||||
# 移动文件
|
||||
if os.path.exists(old_path) and old_path != new_path:
|
||||
shutil.move(old_path, new_path)
|
||||
self.image.name = new_relative_path
|
||||
super().save(update_fields=['image'])
|
||||
|
||||
class Meta:
|
||||
verbose_name = '使用记录图片'
|
||||
verbose_name_plural = '使用记录图片'
|
||||
ordering = ['-created_at']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.usage.item.name} - {self.get_image_type_display()}"
|
||||
|
||||
|
||||
class Category(models.Model):
|
||||
"""物品类别模型"""
|
||||
name = models.CharField(max_length=50, unique=True, verbose_name='类别名称')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework import serializers
|
||||
|
||||
from .models import Item, ItemUsage, Category
|
||||
from .models import Item, ItemUsage, Category, ItemImage, UsageImage
|
||||
|
||||
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
@@ -16,15 +16,47 @@ class CategorySerializer(serializers.ModelSerializer):
|
||||
fields = ['id', 'name', 'description', 'created_at']
|
||||
|
||||
|
||||
class ItemImageSerializer(serializers.ModelSerializer):
|
||||
"""物品图片序列化器"""
|
||||
image_url = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = ItemImage
|
||||
fields = ['id', 'image', 'image_url', 'description', 'is_primary', 'created_at']
|
||||
|
||||
def get_image_url(self, obj):
|
||||
request = self.context.get('request')
|
||||
if obj.image and request:
|
||||
return request.build_absolute_uri(obj.image.url)
|
||||
return None
|
||||
|
||||
|
||||
class UsageImageSerializer(serializers.ModelSerializer):
|
||||
"""使用记录图片序列化器"""
|
||||
image_url = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = UsageImage
|
||||
fields = ['id', 'image', 'image_url', 'image_type', 'description', 'created_at']
|
||||
|
||||
def get_image_url(self, obj):
|
||||
request = self.context.get('request')
|
||||
if obj.image and request:
|
||||
return request.build_absolute_uri(obj.image.url)
|
||||
return None
|
||||
|
||||
|
||||
class ItemSerializer(serializers.ModelSerializer):
|
||||
current_user = serializers.SerializerMethodField()
|
||||
images = ItemImageSerializer(many=True, read_only=True)
|
||||
primary_image = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Item
|
||||
fields = [
|
||||
'id', 'name', 'description', 'serial_number', 'category', 'status',
|
||||
'location', 'owner', 'purchase_date', 'value', 'created_at', 'updated_at',
|
||||
'current_user'
|
||||
'current_user', 'images', 'primary_image'
|
||||
]
|
||||
|
||||
def get_current_user(self, obj):
|
||||
@@ -39,20 +71,43 @@ class ItemSerializer(serializers.ModelSerializer):
|
||||
}
|
||||
return None
|
||||
|
||||
def get_primary_image(self, obj):
|
||||
"""获取主图片"""
|
||||
primary_image = obj.images.filter(is_primary=True).first()
|
||||
if primary_image:
|
||||
request = self.context.get('request')
|
||||
if request:
|
||||
return request.build_absolute_uri(primary_image.image.url)
|
||||
return None
|
||||
|
||||
|
||||
class ItemUsageSerializer(serializers.ModelSerializer):
|
||||
item_name = serializers.CharField(source='item.name', read_only=True)
|
||||
item_serial = serializers.CharField(source='item.serial_number', read_only=True)
|
||||
images = UsageImageSerializer(many=True, read_only=True)
|
||||
borrow_images = serializers.SerializerMethodField()
|
||||
return_images = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = ItemUsage
|
||||
fields = [
|
||||
'id', 'item', 'item_name', 'item_serial', 'user', 'borrower_contact',
|
||||
'start_time', 'end_time', 'purpose', 'notes', 'is_returned',
|
||||
'condition_before', 'condition_after', 'expected_return_time', 'created_at'
|
||||
'condition_before', 'condition_after', 'expected_return_time', 'created_at',
|
||||
'images', 'borrow_images', 'return_images'
|
||||
]
|
||||
read_only_fields = ['created_at']
|
||||
|
||||
def get_borrow_images(self, obj):
|
||||
"""获取借用时图片"""
|
||||
borrow_images = obj.images.filter(image_type='borrow')
|
||||
return UsageImageSerializer(borrow_images, many=True, context=self.context).data
|
||||
|
||||
def get_return_images(self, obj):
|
||||
"""获取归还时图片"""
|
||||
return_images = obj.images.filter(image_type='return')
|
||||
return UsageImageSerializer(return_images, many=True, context=self.context).data
|
||||
|
||||
|
||||
class ItemDetailSerializer(ItemSerializer):
|
||||
"""物品详情序列化器,包含使用历史"""
|
||||
@@ -64,4 +119,4 @@ class ItemDetailSerializer(ItemSerializer):
|
||||
def get_usage_history(self, obj):
|
||||
"""获取物品的使用历史"""
|
||||
usages = ItemUsage.objects.filter(item=obj).order_by('-start_time')[:10]
|
||||
return ItemUsageSerializer(usages, many=True).data
|
||||
return ItemUsageSerializer(usages, many=True, context=self.context).data
|
||||
|
||||
@@ -8,6 +8,8 @@ router.register(r'items', views.ItemViewSet)
|
||||
router.register(r'usages', views.ItemUsageViewSet)
|
||||
router.register(r'item_categories', views.CategoryViewSet)
|
||||
router.register(r'users', views.UserViewSet)
|
||||
router.register(r'item-images', views.ItemImageViewSet)
|
||||
router.register(r'usage-images', views.UsageImageViewSet)
|
||||
|
||||
urlpatterns = [
|
||||
path('api/', include(router.urls)),
|
||||
|
||||
+145
-4
@@ -4,17 +4,19 @@ from rest_framework import viewsets, status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from rest_framework_simplejwt.authentication import JWTAuthentication
|
||||
from rest_framework.parsers import MultiPartParser, FormParser
|
||||
|
||||
from .models import Item, ItemUsage, Category
|
||||
from .models import Item, ItemUsage, Category, ItemImage, UsageImage
|
||||
from .serializers import (
|
||||
ItemSerializer, ItemDetailSerializer, ItemUsageSerializer,
|
||||
CategorySerializer, UserSerializer
|
||||
CategorySerializer, UserSerializer, ItemImageSerializer, UsageImageSerializer
|
||||
)
|
||||
|
||||
|
||||
class ItemViewSet(viewsets.ModelViewSet):
|
||||
"""物品管理API"""
|
||||
authentication_classes = [JWTAuthentication]
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
queryset = Item.objects.all()
|
||||
serializer_class = ItemSerializer
|
||||
|
||||
@@ -23,6 +25,80 @@ class ItemViewSet(viewsets.ModelViewSet):
|
||||
return ItemDetailSerializer
|
||||
return ItemSerializer
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""创建物品,强制要求上传至少一张图片"""
|
||||
# 检查是否上传了图片
|
||||
images = request.FILES.getlist('images')
|
||||
if not images:
|
||||
return Response(
|
||||
{'error': '创建物品时必须上传至少一张图片'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
# 创建物品
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
item = serializer.save()
|
||||
|
||||
# 保存图片
|
||||
for i, image in enumerate(images):
|
||||
ItemImage.objects.create(
|
||||
item=item,
|
||||
image=image,
|
||||
description=request.data.get(f'image_descriptions[{i}]', ''),
|
||||
is_primary=(i == 0) # 第一张图片设为主图
|
||||
)
|
||||
|
||||
# 返回包含图片的完整数据
|
||||
response_serializer = ItemDetailSerializer(item, context={'request': request})
|
||||
return Response(response_serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def upload_images(self, request, pk=None):
|
||||
"""上传物品图片"""
|
||||
item = self.get_object()
|
||||
images = request.FILES.getlist('images')
|
||||
|
||||
if not images:
|
||||
return Response(
|
||||
{'error': '请选择要上传的图片'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
uploaded_images = []
|
||||
for i, image in enumerate(images):
|
||||
item_image = ItemImage.objects.create(
|
||||
item=item,
|
||||
image=image,
|
||||
description=request.data.get(f'image_descriptions[{i}]', ''),
|
||||
is_primary=False
|
||||
)
|
||||
uploaded_images.append(item_image)
|
||||
|
||||
serializer = ItemImageSerializer(uploaded_images, many=True, context={'request': request})
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def set_primary_image(self, request, pk=None):
|
||||
"""设置主图片"""
|
||||
item = self.get_object()
|
||||
image_id = request.data.get('image_id')
|
||||
|
||||
try:
|
||||
# 取消当前主图
|
||||
item.images.filter(is_primary=True).update(is_primary=False)
|
||||
# 设置新主图
|
||||
new_primary = item.images.get(id=image_id)
|
||||
new_primary.is_primary = True
|
||||
new_primary.save()
|
||||
|
||||
return Response({'message': '主图设置成功'})
|
||||
except ItemImage.DoesNotExist:
|
||||
return Response(
|
||||
{'error': '图片不存在'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def borrow(self, request, pk=None):
|
||||
"""借用物品"""
|
||||
@@ -57,11 +133,22 @@ class ItemViewSet(viewsets.ModelViewSet):
|
||||
condition_before=condition_before
|
||||
)
|
||||
|
||||
# 保存借用时的图片
|
||||
borrow_images = request.FILES.getlist('borrow_images')
|
||||
for i, image in enumerate(borrow_images):
|
||||
UsageImage.objects.create(
|
||||
usage=usage,
|
||||
image=image,
|
||||
image_type='borrow',
|
||||
description=request.data.get(f'borrow_image_descriptions[{i}]', '')
|
||||
)
|
||||
|
||||
# 更新物品状态
|
||||
item.status = 'in_use'
|
||||
item.save()
|
||||
|
||||
return Response(ItemUsageSerializer(usage).data)
|
||||
response_serializer = ItemUsageSerializer(usage, context={'request': request})
|
||||
return Response(response_serializer.data)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def return_item(self, request, pk=None):
|
||||
@@ -86,11 +173,22 @@ class ItemViewSet(viewsets.ModelViewSet):
|
||||
current_usage.notes = request.data.get('return_notes', current_usage.notes)
|
||||
current_usage.save()
|
||||
|
||||
# 保存归还时的图片
|
||||
return_images = request.FILES.getlist('return_images')
|
||||
for i, image in enumerate(return_images):
|
||||
UsageImage.objects.create(
|
||||
usage=current_usage,
|
||||
image=image,
|
||||
image_type='return',
|
||||
description=request.data.get(f'return_image_descriptions[{i}]', '')
|
||||
)
|
||||
|
||||
# 更新物品状态
|
||||
item.status = 'available'
|
||||
item.save()
|
||||
|
||||
return Response(ItemUsageSerializer(current_usage).data)
|
||||
response_serializer = ItemUsageSerializer(current_usage, context={'request': request})
|
||||
return Response(response_serializer.data)
|
||||
|
||||
@action(detail=False)
|
||||
def available(self, request):
|
||||
@@ -110,9 +208,36 @@ class ItemViewSet(viewsets.ModelViewSet):
|
||||
class ItemUsageViewSet(viewsets.ModelViewSet):
|
||||
"""使用记录管理API"""
|
||||
authentication_classes = [JWTAuthentication]
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
queryset = ItemUsage.objects.all()
|
||||
serializer_class = ItemUsageSerializer
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def upload_images(self, request, pk=None):
|
||||
"""上传使用记录图片"""
|
||||
usage = self.get_object()
|
||||
images = request.FILES.getlist('images')
|
||||
image_type = request.data.get('image_type', 'borrow')
|
||||
|
||||
if not images:
|
||||
return Response(
|
||||
{'error': '请选择要上传的图片'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
uploaded_images = []
|
||||
for i, image in enumerate(images):
|
||||
usage_image = UsageImage.objects.create(
|
||||
usage=usage,
|
||||
image=image,
|
||||
image_type=image_type,
|
||||
description=request.data.get(f'image_descriptions[{i}]', '')
|
||||
)
|
||||
uploaded_images.append(usage_image)
|
||||
|
||||
serializer = UsageImageSerializer(uploaded_images, many=True, context={'request': request})
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(detail=False)
|
||||
def current(self, request):
|
||||
"""获取当前使用中的记录"""
|
||||
@@ -143,3 +268,19 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
authentication_classes = [JWTAuthentication]
|
||||
queryset = User.objects.all()
|
||||
serializer_class = UserSerializer
|
||||
|
||||
|
||||
class ItemImageViewSet(viewsets.ModelViewSet):
|
||||
"""物品图片管理API"""
|
||||
authentication_classes = [JWTAuthentication]
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
queryset = ItemImage.objects.all()
|
||||
serializer_class = ItemImageSerializer
|
||||
|
||||
|
||||
class UsageImageViewSet(viewsets.ModelViewSet):
|
||||
"""使用记录图片管理API"""
|
||||
authentication_classes = [JWTAuthentication]
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
queryset = UsageImage.objects.all()
|
||||
serializer_class = UsageImageSerializer
|
||||
|
||||
Reference in New Issue
Block a user