From 7d655cb4baa3b9c5d8ace783e88c9f319ac6ad62 Mon Sep 17 00:00:00 2001 From: Yakser Date: Fri, 18 Nov 2022 13:06:45 +0300 Subject: [PATCH 1/7] Make key_skills field an array --- users/models.py | 13 ++++++++++--- users/serializers.py | 10 ++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/users/models.py b/users/models.py index 4a966cb3..942302e6 100644 --- a/users/models.py +++ b/users/models.py @@ -82,10 +82,14 @@ class CustomUser(AbstractUser): objects = CustomUserManager() - def get_member_key_skills(self) -> str: + def get_member_key_skills(self) -> list[str]: if self.user_type == CustomUser.MEMBER: - return str(self.member.key_skills) - return "" + return [ + skill.strip() + for skill in self.member.key_skills.split(",") + if skill.strip() + ] + return [] def __str__(self): return f"User<{self.id}> - {self.first_name} {self.last_name}" @@ -170,6 +174,9 @@ class Member(models.Model): Industry, blank=True, related_name="members" ) + def get_key_skills(self) -> list[str]: + return [skill.strip() for skill in self.key_skills.split(",") if skill.strip()] + def __str__(self): return f"Member<{self.id}> - {self.user.first_name} {self.user.last_name}" diff --git a/users/serializers.py b/users/serializers.py index 0a01a0b4..c48b70a1 100644 --- a/users/serializers.py +++ b/users/serializers.py @@ -15,7 +15,17 @@ class Meta: ref_name = "Users" +class KeySkillsField(serializers.Field): + def to_representation(self, value): + return [skill.strip() for skill in value.split(",") if skill.strip()] + + def to_internal_value(self, data): + return ",".join(data) + + class MemberSerializer(serializers.ModelSerializer): + key_skills = KeySkillsField() + class Meta: model = Member fields = [ From 4b90d6d654138cf1ca58160a2221e0a9e609cdb6 Mon Sep 17 00:00:00 2001 From: Yakser Date: Fri, 18 Nov 2022 13:35:04 +0300 Subject: [PATCH 2/7] Fixed user achievements creation --- users/serializers.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/users/serializers.py b/users/serializers.py index c48b70a1..4f7625cd 100644 --- a/users/serializers.py +++ b/users/serializers.py @@ -7,11 +7,8 @@ class AchievementListSerializer(serializers.ModelSerializer): class Meta: model = UserAchievement - fields = [ - "id", - "title", - "status", - ] + fields = ["id", "title", "status", "user"] + extra_kwargs = {"user": {"write_only": True}} ref_name = "Users" From 1236fef7988ad8be980f9abf9b064fa99b4a3c9a Mon Sep 17 00:00:00 2001 From: Mikhail Khromov Date: Fri, 18 Nov 2022 14:21:25 +0300 Subject: [PATCH 3/7] fixed achievement creation --- projects/views.py | 1 - .../0019_alter_userachievement_user.py | 34 +++++++++++++++++++ users/models.py | 3 +- users/permissions.py | 7 ---- users/serializers.py | 4 +-- users/views.py | 20 +++++++++++ 6 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 users/migrations/0019_alter_userachievement_user.py diff --git a/projects/views.py b/projects/views.py index 4bc6956c..716c49dd 100644 --- a/projects/views.py +++ b/projects/views.py @@ -31,7 +31,6 @@ def create(self, request, *args, **kwargs): serializer.is_valid(raise_exception=True) # Doesn't work if not explicitly set like this serializer.validated_data["leader"] = request.user.id - serializer.validated_data["industry"] = request.data["industry"] self.perform_create(serializer) headers = self.get_success_headers(serializer.data) diff --git a/users/migrations/0019_alter_userachievement_user.py b/users/migrations/0019_alter_userachievement_user.py new file mode 100644 index 00000000..a7d247d9 --- /dev/null +++ b/users/migrations/0019_alter_userachievement_user.py @@ -0,0 +1,34 @@ +# Generated by Django 4.1.3 on 2022-11-18 11:18 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +def set_userachievement_user_to_not_null(apps, schema_editor): + UserAchievement = apps.get_model('users', 'UserAchievement') + User = apps.get_model('users', 'User') + legend = User.objects.all()[0] + for u in UserAchievement.objects.filter(user=None): + u.user = legend + u.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ("users", "0018_alter_customuser_about_me"), + ] + + operations = [ + migrations.RunPython(set_userachievement_user_to_not_null), + migrations.AlterField( + model_name="userachievement", + name="user", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="achievements", + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/users/models.py b/users/models.py index 942302e6..b713cb10 100644 --- a/users/models.py +++ b/users/models.py @@ -110,8 +110,7 @@ class UserAchievement(models.Model): user = models.ForeignKey( CustomUser, - on_delete=models.SET_NULL, - null=True, + on_delete=models.CASCADE, related_name="achievements", ) diff --git a/users/permissions.py b/users/permissions.py index 546c7075..df7eb8f9 100644 --- a/users/permissions.py +++ b/users/permissions.py @@ -6,13 +6,6 @@ class IsAchievementOwnerOrReadOnly(BasePermission): Allows access to update only to himself. """ - def has_permission(self, request, view) -> bool: - if request.method in SAFE_METHODS or ( - request.user and request.user.id == request.data.get("user") - ): - return True - return False - def has_object_permission(self, request, view, obj): if request.method in SAFE_METHODS or (obj.user == request.user): return True diff --git a/users/serializers.py b/users/serializers.py index 4f7625cd..2a3c0856 100644 --- a/users/serializers.py +++ b/users/serializers.py @@ -7,8 +7,8 @@ class AchievementListSerializer(serializers.ModelSerializer): class Meta: model = UserAchievement - fields = ["id", "title", "status", "user"] - extra_kwargs = {"user": {"write_only": True}} + fields = ["id", "title", "status"] + # extra_kwargs = {"user": {"write_only": True}} ref_name = "Users" diff --git a/users/views.py b/users/views.py index 0ed4426a..6c30b9c3 100644 --- a/users/views.py +++ b/users/views.py @@ -264,6 +264,26 @@ class AchievementList(ListCreateAPIView): serializer_class = AchievementListSerializer permission_classes = [IsAchievementOwnerOrReadOnly] + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.validated_data["user"] = request.user + # warning for someone who tries to set user variable (the user will always be yourself anyway) + if ( + request.data.get("user") is not None + and request.data.get("user") != request.user.id + ): + return Response( + { + "error": "you can't edit USER field for this view since " + "you can't create achievements for other people" + }, + status=status.HTTP_403_FORBIDDEN, + ) + self.perform_create(serializer) + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + class AchievementDetail(RetrieveUpdateDestroyAPIView): queryset = UserAchievement.objects.get_achievements_for_detail_view() From 7cc99a0a59f22bb92c215fdbe7bbceb056c39ced Mon Sep 17 00:00:00 2001 From: Mikhail Khromov Date: Fri, 18 Nov 2022 14:23:07 +0300 Subject: [PATCH 4/7] fixed tests failing --- users/migrations/0019_alter_userachievement_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/migrations/0019_alter_userachievement_user.py b/users/migrations/0019_alter_userachievement_user.py index a7d247d9..ad68dced 100644 --- a/users/migrations/0019_alter_userachievement_user.py +++ b/users/migrations/0019_alter_userachievement_user.py @@ -7,7 +7,7 @@ def set_userachievement_user_to_not_null(apps, schema_editor): UserAchievement = apps.get_model('users', 'UserAchievement') - User = apps.get_model('users', 'User') + User = apps.get_model('users', 'CustomUser') legend = User.objects.all()[0] for u in UserAchievement.objects.filter(user=None): u.user = legend From db0099672412e4aed8726d21f4f9a2016c2a01f8 Mon Sep 17 00:00:00 2001 From: Mikhail Khromov Date: Fri, 18 Nov 2022 14:25:16 +0300 Subject: [PATCH 5/7] fixed tests dying #2 --- users/migrations/0019_alter_userachievement_user.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/users/migrations/0019_alter_userachievement_user.py b/users/migrations/0019_alter_userachievement_user.py index ad68dced..fee4aff7 100644 --- a/users/migrations/0019_alter_userachievement_user.py +++ b/users/migrations/0019_alter_userachievement_user.py @@ -7,11 +7,7 @@ def set_userachievement_user_to_not_null(apps, schema_editor): UserAchievement = apps.get_model('users', 'UserAchievement') - User = apps.get_model('users', 'CustomUser') - legend = User.objects.all()[0] - for u in UserAchievement.objects.filter(user=None): - u.user = legend - u.save() + UserAchievement.objects.filter(user=None).delete() class Migration(migrations.Migration): From 93782f04b707642b018883b1362736919078f9db Mon Sep 17 00:00:00 2001 From: Mikhail Khromov Date: Fri, 18 Nov 2022 14:49:03 +0300 Subject: [PATCH 6/7] fixed projects not creating when not specifying leader/industry --- ...0008_alter_project_description_and_more.py | 67 +++++++++++++++++++ projects/models.py | 19 +++--- projects/serializers.py | 24 +++---- projects/views.py | 2 +- 4 files changed, 85 insertions(+), 27 deletions(-) create mode 100644 projects/migrations/0008_alter_project_description_and_more.py diff --git a/projects/migrations/0008_alter_project_description_and_more.py b/projects/migrations/0008_alter_project_description_and_more.py new file mode 100644 index 00000000..2ba3030c --- /dev/null +++ b/projects/migrations/0008_alter_project_description_and_more.py @@ -0,0 +1,67 @@ +# Generated by Django 4.1.3 on 2022-11-18 11:45 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +def set_userachievement_user_to_not_null(apps, schema_editor): + Project = apps.get_model('projects', 'Project') + Project.objects.filter(leader=None).delete() + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("industries", "0001_initial"), + ("projects", "0007_remove_collaborator_unique_collaorator_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="project", + name="description", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="project", + name="image_address", + field=models.URLField(blank=True, null=True), + ), + migrations.AlterField( + model_name="project", + name="industry", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="projects", + to="industries.industry", + ), + ), + migrations.RunPython(set_userachievement_user_to_not_null), + migrations.AlterField( + model_name="project", + name="leader", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="leaders_projects", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="project", + name="presentation_address", + field=models.URLField(blank=True, null=True), + ), + migrations.AlterField( + model_name="project", + name="region", + field=models.CharField(blank=True, max_length=256, null=True), + ), + migrations.AlterField( + model_name="project", + name="short_description", + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/projects/models.py b/projects/models.py index 7b1898d4..890aa516 100644 --- a/projects/models.py +++ b/projects/models.py @@ -31,24 +31,24 @@ class Project(models.Model): """ name = models.CharField(max_length=256, null=True, blank=True) - description = models.TextField(blank=True) - short_description = models.TextField(blank=True) - region = models.CharField(max_length=256, blank=True) + description = models.TextField(null=True, blank=True) + short_description = models.TextField(null=True, blank=True) + region = models.CharField(max_length=256, null=True, blank=True) step = models.PositiveSmallIntegerField(choices=VERBOSE_STEPS, null=True, blank=True) industry = models.ForeignKey( Industry, on_delete=models.SET_NULL, null=True, + blank=True, related_name="projects", ) - presentation_address = models.URLField(blank=True) - image_address = models.URLField(blank=True) + presentation_address = models.URLField(null=True, blank=True) + image_address = models.URLField(null=True, blank=True) leader = models.ForeignKey( User, - on_delete=models.SET_NULL, - null=True, + on_delete=models.CASCADE, related_name="leaders_projects", # projects in which this user is the leader ) @@ -85,10 +85,7 @@ class Achievement(models.Model): status = models.CharField(max_length=256, null=False) project = models.ForeignKey( - Project, - on_delete=models.SET_NULL, - null=True, - related_name="achievements", + Project, on_delete=models.SET_NULL, null=True, related_name="achievements" ) objects = AchievementManager() diff --git a/projects/serializers.py b/projects/serializers.py index 1bd35dc5..079ee817 100644 --- a/projects/serializers.py +++ b/projects/serializers.py @@ -3,7 +3,6 @@ from industries.models import Industry from projects.models import Project, Achievement, Collaborator from projects.validators import validate_project -from users.models import CustomUser from vacancy.serializers import ProjectVacancyListSerializer @@ -95,10 +94,6 @@ class Meta: class ProjectListSerializer(serializers.ModelSerializer): - def __init__(self, *args, **kwargs): - # might be unnecessary - self.max_collaborator_count = kwargs.pop("max_collaborator_count", 4) - super().__init__(*args, **kwargs) collaborators = serializers.SerializerMethodField(method_name="get_collaborators") collaborator_count = serializers.SerializerMethodField( @@ -111,8 +106,9 @@ def get_collaborator_count(cls, obj): return len(obj.collaborator_set.all()) def get_collaborators(self, obj): + max_collaborator_count = 4 return CollaboratorSerializer( - instance=obj.collaborator_set.all()[: self.max_collaborator_count], many=True + instance=obj.collaborator_set.all()[:max_collaborator_count], many=True ).data class Meta: @@ -144,15 +140,13 @@ def validate(self, data): super().validate(data) return validate_project(data) - def create(self, validated_data): - industry = Industry.objects.get(id=validated_data.pop("industry")) - leader = CustomUser.objects.get(id=validated_data.pop("leader")) - project = Project.objects.create( - **validated_data, - industry=industry, - leader=leader, - ) - return project + # def create(self, validated_data): + # project = Project.objects.create( + # **validated_data, + # industry_id=validated_data.pop("industry"), + # leader_id=validated_data.pop("leader"), + # ) + # return project class ProjectIndustrySerializer(serializers.ModelSerializer): diff --git a/projects/views.py b/projects/views.py index 716c49dd..d86e9f47 100644 --- a/projects/views.py +++ b/projects/views.py @@ -30,7 +30,7 @@ def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) # Doesn't work if not explicitly set like this - serializer.validated_data["leader"] = request.user.id + serializer.validated_data["leader"] = request.user self.perform_create(serializer) headers = self.get_success_headers(serializer.data) From f7ef5c736f505046cf58d40f48604e7d36340663 Mon Sep 17 00:00:00 2001 From: Mikhail Khromov Date: Fri, 18 Nov 2022 14:54:39 +0300 Subject: [PATCH 7/7] removed ProjectListSerializer.create() --- projects/serializers.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/projects/serializers.py b/projects/serializers.py index 079ee817..023e223e 100644 --- a/projects/serializers.py +++ b/projects/serializers.py @@ -140,14 +140,6 @@ def validate(self, data): super().validate(data) return validate_project(data) - # def create(self, validated_data): - # project = Project.objects.create( - # **validated_data, - # industry_id=validated_data.pop("industry"), - # leader_id=validated_data.pop("leader"), - # ) - # return project - class ProjectIndustrySerializer(serializers.ModelSerializer): id = serializers.IntegerField()