3838
3939class AchievementListSerializer (serializers .ModelSerializer ):
4040 year = serializers .IntegerField (required = False , allow_null = True )
41- files = serializers .SerializerMethodField ()
41+ file_link = serializers .SerializerMethodField ()
4242
4343 class Meta :
4444 model = UserAchievement
45- fields = ["id" , "title" , "status" , "year" , "files " ]
45+ fields = ["id" , "title" , "status" , "year" , "file_link " ]
4646
47- def get_files (self , obj ):
48- uafs = obj .files .all ()
49- return [ UserFileReadSerializer ( uf .file ). data for uf in uafs if uf .file ]
47+ def get_file_link (self , obj ):
48+ uaf = obj .files .first ()
49+ return uaf .file . link if ( uaf and uaf .file ) else None
5050
5151
5252class UserFileReadSerializer (serializers .ModelSerializer ):
@@ -874,13 +874,19 @@ class Meta:
874874
875875
876876class AchievementDetailSerializer (serializers .ModelSerializer ):
877- files = AchievementFileReadSerializer (many = True , read_only = True )
878- files_links = FilesWriteField (write_only = True , required = False )
877+ file_link = serializers .URLField (required = False , allow_null = True )
879878 user = serializers .PrimaryKeyRelatedField (read_only = True )
880879
881880 class Meta :
882881 model = UserAchievement
883- fields = ["id" , "title" , "status" , "year" , "user" , "files" , "files_links" ]
882+ fields = ["id" , "title" , "status" , "year" , "user" , "file_link" ]
883+ read_only_fields = ["id" , "user" ]
884+
885+ def to_representation (self , instance ):
886+ data = super ().to_representation (instance )
887+ rel = instance .files .first () # UserAchievementFile
888+ data ["file_link" ] = rel .file .link if (rel and rel .file ) else None
889+ return data
884890
885891 def validate_year (self , value ):
886892 import datetime
@@ -893,47 +899,37 @@ def validate_year(self, value):
893899 return value
894900
895901 @transaction .atomic
896- def _sync_files (self , achievement : UserAchievement , files : list [UserFile ]):
897- """
898- Синхронизируем связи через link (PK у UserFile).
899- clean() на UserAchievementFile проверит size/extension и владельца ещё раз.
900- """
901- current_links = set (
902- UserAchievementFile .objects .filter (achievement = achievement ).values_list (
903- "file_id" , flat = True
904- )
905- )
906- target_links = set (f .link for f in files )
902+ def _set_single_file (self , achievement : UserAchievement , link : str | None ):
903+ UserAchievementFile .objects .filter (achievement = achievement ).delete ()
907904
908- to_add = target_links - current_links
909- to_remove = current_links - target_links
905+ if not link :
906+ return
910907
911- if to_remove :
912- UserAchievementFile .objects .filter (
913- achievement = achievement , file_id__in = to_remove
914- ).delete ()
908+ uf , _ = UserFile .objects .get_or_create (link = link )
915909
916- for link in to_add :
917- rel = UserAchievementFile (achievement = achievement , file_id = link )
918- rel .clean ()
919- rel .save ()
910+ rel = UserAchievementFile (achievement = achievement , file = uf )
911+ rel .clean ()
912+ rel .save ()
920913
921914 @transaction .atomic
922915 def create (self , validated_data ):
923- files = validated_data .pop ("files_links " , [] )
916+ link = validated_data .pop ("file_link " , None )
924917 achievement = UserAchievement .objects .create (** validated_data )
925- if files :
926- self ._sync_files (achievement , files )
918+ self ._set_single_file (achievement , link )
927919 return achievement
928920
929921 @transaction .atomic
930922 def update (self , instance , validated_data ):
931- files = validated_data .pop ("files_links" , None )
923+ sentinel = object ()
924+ link = validated_data .pop ("file_link" , sentinel )
925+
932926 for attr , val in validated_data .items ():
933927 setattr (instance , attr , val )
934928 instance .save ()
935- if files is not None :
936- self ._sync_files (instance , files )
929+
930+ if link is not sentinel :
931+ self ._set_single_file (instance , link )
932+
937933 return instance
938934
939935
0 commit comments