Files
Scoring-System/backend/competition/serializers.py
爽哒哒 21f892fdf6
All checks were successful
Deploy to Server / deploy (push) Successful in 2m4s
feat: 移除轮播图管理,系列活动改卡片布局,课程描述限10行,首页配置优化
2026-03-22 00:10:34 +08:00

123 lines
4.6 KiB
Python

from rest_framework import serializers
from django.conf import settings
from .models import Competition, CompetitionEnrollment, ScoreDimension, Project, ProjectFile, Score, Comment, HomePageConfig
from shop.serializers import WeChatUserSerializer
def _media_url(file_field):
"""返回 media 文件的相对路径,避免 build_absolute_uri 生成容器内部地址"""
if file_field and file_field.name:
return settings.MEDIA_URL + file_field.name
return None
class HomePageConfigSerializer(serializers.ModelSerializer):
display_banner = serializers.SerializerMethodField()
class Meta:
model = HomePageConfig
fields = ['id', 'banner_image', 'banner_image_url', 'display_banner',
'main_title', 'organizer', 'undertaker']
def get_display_banner(self, obj):
return _media_url(obj.banner_image) or obj.banner_image_url
class ScoreDimensionSerializer(serializers.ModelSerializer):
class Meta:
model = ScoreDimension
fields = ['id', 'name', 'description', 'weight', 'max_score', 'order']
class CompetitionSerializer(serializers.ModelSerializer):
score_dimensions = ScoreDimensionSerializer(many=True, read_only=True)
display_cover_image = serializers.SerializerMethodField()
status_display = serializers.CharField(source='get_status_display', read_only=True)
class Meta:
model = Competition
fields = ['id', 'title', 'description', 'rule_description', 'condition_description',
'cover_image', 'cover_image_url', 'display_cover_image',
'start_time', 'end_time', 'status', 'project_visibility', 'status_display', 'is_active',
'score_dimensions', 'created_at']
def get_display_cover_image(self, obj):
return _media_url(obj.cover_image) or obj.cover_image_url
class CompetitionEnrollmentSerializer(serializers.ModelSerializer):
user = WeChatUserSerializer(read_only=True)
class Meta:
model = CompetitionEnrollment
fields = ['id', 'competition', 'user', 'role', 'status', 'created_at']
read_only_fields = ['status']
class ProjectFileSerializer(serializers.ModelSerializer):
class Meta:
model = ProjectFile
fields = ['id', 'project', 'file_type', 'file', 'file_url', 'name', 'created_at']
def validate_file(self, value):
if not value:
return value
limit_mb = 50
if value.size > limit_mb * 1024 * 1024:
raise serializers.ValidationError(f"文件大小不能超过 {limit_mb}MB")
return value
class ProjectSerializer(serializers.ModelSerializer):
files = ProjectFileSerializer(many=True, read_only=True)
contestant_info = serializers.SerializerMethodField()
display_cover_image = serializers.SerializerMethodField()
class Meta:
model = Project
fields = ['id', 'competition', 'contestant', 'title', 'description', 'team_info',
'cover_image', 'cover_image_url', 'display_cover_image',
'status', 'final_score', 'files', 'contestant_info', 'created_at']
read_only_fields = ['final_score', 'contestant']
def get_contestant_info(self, obj):
return {
"nickname": obj.contestant.user.nickname,
"avatar_url": obj.contestant.user.avatar_url
}
def get_display_cover_image(self, obj):
return _media_url(obj.cover_image) or obj.cover_image_url
class ScoreSerializer(serializers.ModelSerializer):
judge_name = serializers.CharField(source='judge.user.nickname', read_only=True)
dimension_name = serializers.CharField(source='dimension.name', read_only=True)
class Meta:
model = Score
fields = ['id', 'project', 'judge', 'dimension', 'score', 'judge_name', 'dimension_name', 'created_at']
read_only_fields = ['judge']
class CommentSerializer(serializers.ModelSerializer):
judge_name = serializers.CharField(source='judge.user.nickname', read_only=True)
score = serializers.SerializerMethodField()
class Meta:
model = Comment
fields = ['id', 'project', 'judge', 'content', 'judge_name', 'created_at', 'score']
read_only_fields = ['judge']
def get_score(self, obj):
scores = Score.objects.filter(project=obj.project, judge=obj.judge)
if not scores.exists():
return None
total_score = 0
total_weight = 0
for score in scores:
total_score += score.score * score.dimension.weight
total_weight += score.dimension.weight
if total_weight > 0:
return round(total_score / total_weight, 1)
return None