fix: just forgot something...

This commit is contained in:
2025-09-27 20:57:26 +08:00
parent 700267d7d1
commit 3a3c5970a2
8 changed files with 181 additions and 0 deletions
View File
+28
View File
@@ -0,0 +1,28 @@
from django.contrib import admin
from .models import Memo, MemoImage
class MemoImageInline(admin.TabularInline):
model = MemoImage
extra = 0
@admin.register(Memo)
class MemoAdmin(admin.ModelAdmin):
list_display = ['title', 'created_by', 'created_at', 'updated_at', 'is_active']
list_filter = ['is_active', 'created_at', 'updated_at']
search_fields = ['title', 'content']
readonly_fields = ['created_at', 'updated_at']
inlines = [MemoImageInline]
def save_model(self, request, obj, form, change):
if not change:
obj.created_by = request.user
super().save_model(request, obj, form, change)
@admin.register(MemoImage)
class MemoImageAdmin(admin.ModelAdmin):
list_display = ['memo', 'alt_text', 'uploaded_at']
list_filter = ['uploaded_at']
search_fields = ['memo__title', 'alt_text']
+6
View File
@@ -0,0 +1,6 @@
from django.apps import AppConfig
class MemoConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "memo"
+40
View File
@@ -0,0 +1,40 @@
from django.db import models
from django.contrib.auth.models import User
class Memo(models.Model):
title = models.CharField(max_length=200, verbose_name="标题")
content = models.TextField(verbose_name="内容", blank=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="创建者")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
is_active = models.BooleanField(default=True, verbose_name="是否启用")
class Meta:
verbose_name = "备忘录"
verbose_name_plural = "备忘录"
ordering = ['-updated_at']
def __str__(self):
return self.title
@property
def content_preview(self):
"""返回内容的缩略预览"""
if len(self.content) > 100:
return self.content[:100] + "..."
return self.content
class MemoImage(models.Model):
memo = models.ForeignKey(Memo, on_delete=models.CASCADE, related_name='images', verbose_name="备忘录")
image = models.ImageField(upload_to='memo_images/', verbose_name="图片")
uploaded_at = models.DateTimeField(auto_now_add=True, verbose_name="上传时间")
alt_text = models.CharField(max_length=200, blank=True, verbose_name="图片描述")
class Meta:
verbose_name = "备忘录图片"
verbose_name_plural = "备忘录图片"
def __str__(self):
return f"{self.memo.title} - 图片"
+33
View File
@@ -0,0 +1,33 @@
from rest_framework import serializers
from .models import Memo, MemoImage
class MemoImageSerializer(serializers.ModelSerializer):
class Meta:
model = MemoImage
fields = ['id', 'image', 'uploaded_at', 'alt_text']
class MemoSerializer(serializers.ModelSerializer):
images = MemoImageSerializer(many=True, read_only=True)
content_preview = serializers.ReadOnlyField()
created_by_name = serializers.CharField(source='created_by.username', read_only=True)
class Meta:
model = Memo
fields = ['id', 'title', 'content', 'content_preview', 'created_by', 'created_by_name',
'created_at', 'updated_at', 'is_active', 'images']
read_only_fields = ['created_by', 'created_at', 'updated_at']
def create(self, validated_data):
validated_data['created_by'] = self.context['request'].user
return super().create(validated_data)
class MemoListSerializer(serializers.ModelSerializer):
content_preview = serializers.ReadOnlyField()
created_by_name = serializers.CharField(source='created_by.username', read_only=True)
class Meta:
model = Memo
fields = ['id', 'title', 'content_preview', 'created_by_name', 'updated_at']
+3
View File
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.
+10
View File
@@ -0,0 +1,10 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import MemoViewSet
router = DefaultRouter()
router.register(r'memos', MemoViewSet)
urlpatterns = [
path('api/', include(router.urls)),
]
+61
View File
@@ -0,0 +1,61 @@
from rest_framework import viewsets, filters, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser, FormParser
from django_filters.rest_framework import DjangoFilterBackend
from django.db.models import Q
from .models import Memo, MemoImage
from .serializers import MemoSerializer, MemoListSerializer, MemoImageSerializer
class MemoViewSet(viewsets.ModelViewSet):
queryset = Memo.objects.filter(is_active=True)
serializer_class = MemoSerializer
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
search_fields = ['title', 'content']
ordering_fields = ['created_at', 'updated_at']
ordering = ['-updated_at']
def get_serializer_class(self):
if self.action == 'list':
return MemoListSerializer
return MemoSerializer
def get_queryset(self):
queryset = super().get_queryset()
search = self.request.query_params.get('search', None)
if search:
queryset = queryset.filter(
Q(title__icontains=search) | Q(content__icontains=search)
)
return queryset
@action(detail=True, methods=['post'], parser_classes=[MultiPartParser, FormParser])
def upload_image(self, request, pk=None):
memo = self.get_object()
image = request.FILES.get('image')
alt_text = request.data.get('alt_text', '')
if not image:
return Response({'error': '请选择图片文件'}, status=status.HTTP_400_BAD_REQUEST)
memo_image = MemoImage.objects.create(
memo=memo,
image=image,
alt_text=alt_text
)
serializer = MemoImageSerializer(memo_image)
return Response(serializer.data, status=status.HTTP_201_CREATED)
@action(detail=True, methods=['delete'])
def delete_image(self, request, pk=None):
memo = self.get_object()
image_id = request.data.get('image_id')
try:
memo_image = MemoImage.objects.get(id=image_id, memo=memo)
memo_image.delete()
return Response({'message': '图片删除成功'}, status=status.HTTP_200_OK)
except MemoImage.DoesNotExist:
return Response({'error': '图片不存在'}, status=status.HTTP_404_NOT_FOUND)