Skip to content

Commit e0c4635

Browse files
authored
Merge pull request #557 from PROCOLLAB-github/feture/new_fields_for_user_achievements
Переработана выдача данных о достижениях пользователя
2 parents 0545cbb + 1e9b6a3 commit e0c4635

3 files changed

Lines changed: 65 additions & 36 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Generated by Django 4.2.24 on 2025-10-23 10:41
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("users", "0056_userachievementfile_userachievement_year_and_more"),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name="usereducation",
15+
name="description",
16+
field=models.TextField(
17+
blank=True,
18+
max_length=1000,
19+
null=True,
20+
verbose_name="Направление обучения",
21+
),
22+
),
23+
migrations.AlterField(
24+
model_name="userworkexperience",
25+
name="description",
26+
field=models.TextField(
27+
blank=True,
28+
max_length=1000,
29+
null=True,
30+
verbose_name="Краткое описание деятельности",
31+
),
32+
),
33+
]

users/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ class Meta(TypedModelMeta):
291291

292292

293293
class UserAchievementFile(models.Model):
294-
ALLOWED_EXTENSIONS = {"pdf", "doc", "docx", "jpg", "jpeg", "png"}
294+
ALLOWED_EXTENSIONS = {"pdf", "doc", "docx", "jpg", "jpeg", "png", "webp"}
295295
MAX_UPLOAD_SIZE = 50 * 1024 * 1024
296296
achievement = models.ForeignKey(
297297
UserAchievement,

users/serializers.py

Lines changed: 31 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@
3838

3939
class 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

5252
class UserFileReadSerializer(serializers.ModelSerializer):
@@ -874,13 +874,19 @@ class Meta:
874874

875875

876876
class 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

Comments
 (0)