Skip to content

Commit

Permalink
Merge pull request #361 from Pet-projects-CodePET/refactor/validate_f…
Browse files Browse the repository at this point in the history
…ields

fix: Внес изменения:
  • Loading branch information
Badmajor authored Aug 2, 2024
2 parents ca5bd73 + 71a7929 commit 4d308e9
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 30 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ repos:
- id: pytest
name: pytest
description: 'Run tests with pytest'
entry: bash -c 'cd src/backend/; poetry run pytest -vv'
entry: 'poetry run pytest -vv'
language: python
types: [ python ]
require_serial: true
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ extend-exclude = """
"""

[tool.pytest.ini_options]
pythonpath = "scr/backend"
pythonpath = "src/backend"
DJANGO_SETTINGS_MODULE= "config.settings.local"
testpaths = ["tests",]
python_files = ['test_*.py', ]
16 changes: 16 additions & 0 deletions src/backend/api/v1/profile/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import html
from typing import ClassVar, Optional

import bleach
from django.core.validators import RegexValidator
from django.db.models import Q
from rest_framework import serializers
Expand Down Expand Up @@ -224,6 +226,11 @@ class Meta(BaseProfileSerializer.Meta):
)
read_only_fields = fields

def to_representation(self, instance):
rep = super().to_representation(instance)
rep["about"] = html.unescape(rep["about"])
return rep


class ProfileMeWriteSerializer(ProfileMeReadSerializer):
"""Сериализатор для обновления профиля его владельцем."""
Expand All @@ -244,3 +251,12 @@ class ProfileMeWriteSerializer(ProfileMeReadSerializer):

class Meta(ProfileMeReadSerializer.Meta):
read_only_fields = ("user_id", "specialists")

def validate_about(self, value):
"""
Метод валидации и защиты от потенциально вредоносных
HTML-тегов и атрибутов.
"""

safe_about = bleach.clean(value)
return safe_about
62 changes: 39 additions & 23 deletions src/backend/api/v1/projects/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,22 +90,10 @@ class Meta:
"project_specialists",
"status",
)

def _get_base_fields(self):
"""Метод получения полей создателя и владельца."""

return {
"creator": serializers.SlugRelatedField(
slug_field="username", read_only=True
),
}

def get_fields(self):
"""метод получения полей сериализатора."""

fields = super().get_fields()
fields.update(self._get_base_fields())
return fields
read_only_fields: ClassVar[Tuple[str, ...]] = (
"creator",
"owner",
)


class ReadParticipantSerializer(CustomModelSerializer):
Expand Down Expand Up @@ -140,6 +128,9 @@ class ReadProjectSerializer(RecruitmentStatusMixin, BaseProjectSerializer):
recruitment_status = serializers.SerializerMethodField()
is_favorite = serializers.SerializerMethodField(read_only=True)
owner = serializers.SerializerMethodField()
creator = serializers.SlugRelatedField(
slug_field="username", read_only=True
)
project_participants = ReadParticipantSerializer(many=True)
unique_project_participants_skills = serializers.SerializerMethodField()

Expand All @@ -158,9 +149,10 @@ def get_is_favorite(self, project) -> bool:
В противном случе возвращает False.
Для неавторизованных пользователей всегда возвращает False.
"""
user = self.context["request"].user
if user.is_authenticated:
return project.favorited_by.filter(id=user.id).exists()
request = self.context.get("request", None)
if user := getattr(request, "user", None):
if user.is_authenticated:
return project.favorited_by.filter(id=user.id).exists()
return False

def get_owner(self, project):
Expand All @@ -184,6 +176,11 @@ def get_unique_project_participants_skills(self, obj):
)
return list(set(all_skills))

def to_representation(self, instance):
rep = super().to_representation(instance)
rep["description"] = html.unescape(rep["description"])
return rep


class WriteProjectSerializer(
ToRepresentationOnlyIdMixin,
Expand All @@ -202,7 +199,6 @@ class Meta(BaseProjectSerializer.Meta):
"ended": {"required": True},
"busyness": {"required": True},
"status": {"required": False},
"owner": {"required": False},
}

def validate_status(self, value) -> int:
Expand All @@ -214,6 +210,15 @@ def validate_status(self, value) -> int:
)
return value

def validate_description(self, value):
"""
Метод валидации и защиты от потенциально вредоносных
HTML-тегов и атрибутов.
"""

safe_description = bleach.clean(value)
return safe_description


class ShortProjectSpecialistSerializer(BaseProjectSpecialistSerializer):
"""Сериализатор для чтения специалиста необходимого проекту краткий."""
Expand Down Expand Up @@ -465,9 +470,9 @@ class ReadRetrieveParticipationRequestSerializer(
class Meta(ReadListParticipationRequestSerializer.Meta):
fields: ClassVar[Tuple[str, ...]] = (
*ReadListParticipationRequestSerializer.Meta.fields,
"answer",
"cover_letter",
"created",
"answer", # какая-то дичь происходит, зачем нам тут видеть и ответ
"cover_letter", # и сопроводительное письмо не понимаю,
"created", # не хочу разбираться сейчас.
)

def to_representation(self, instance):
Expand Down Expand Up @@ -545,6 +550,11 @@ class Meta(ReadRetrieveParticipationRequestSerializer.Meta):
"author",
)

def to_representation(self, instance):
rep = super().to_representation(instance)
rep["cover_letter"] = html.unescape(rep["cover_letter"])
return rep


class WriteInvitationToProjectSerializer(
ToRepresentationOnlyIdMixin, BaseParticipationRequestSerializer
Expand Down Expand Up @@ -591,6 +601,12 @@ def validate(self, attrs) -> OrderedDict:
raise serializers.ValidationError(errors)
return attrs

def validate_cover_letter(self, value):
escaped_html = bleach.clean(
value
) # защита потенциально вредоносных HTML-тегов и атрибутов
return escaped_html


class PartialWriteInvitationToProjectSerializer(
CustomModelSerializer, ToRepresentationOnlyIdMixin
Expand Down
1 change: 0 additions & 1 deletion src/backend/api/v1/projects/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ def perform_create(self, serializer):
"""
Метод подготовки данных для процесса предварительного создания проекта.
"""

serializer.save(
creator=self.request.user,
owner=self.request.user,
Expand Down
8 changes: 8 additions & 0 deletions src/backend/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,11 @@ def user(django_user_model):
def user_client(user, client):
client.force_login(user)
return client


@pytest.fixture
def profile(user):
profile = user.profile
profile.name = "profile_name"
profile.save()
return profile
3 changes: 2 additions & 1 deletion src/backend/tests/profile/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@


@pytest.fixture
def profile(user):
def profile_new(user):
"""Профайл с незаполненной информацией"""
return user.profile


Expand Down
6 changes: 3 additions & 3 deletions src/backend/tests/profile/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ def test_create_profile_with_user():


@pytest.mark.django_db
def test_update_profiles_data(profile, update_data):
def test_update_profiles_data(profile_new, update_data):
"""Тест обновления информации в профиле"""
assert Profile.objects.filter(**update_data).count() == 0
for field, value in update_data.items():
setattr(profile, field, value)
profile.save()
setattr(profile_new, field, value)
profile_new.save()
assert Profile.objects.filter(**update_data).count() == 1
File renamed without changes.
12 changes: 12 additions & 0 deletions src/backend/tests/projects/test_serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from api.v1.projects.serializers import ReadProjectSerializer


def test_owner_field(profile, project):
valid_data = {
"id": profile.user.id,
"username": profile.user.username,
"name": profile.name,
"avatar": (profile.avatar.url if profile.avatar else None),
}
serializer = ReadProjectSerializer(instance=project)
assert serializer.data["owner"] == valid_data

0 comments on commit 4d308e9

Please sign in to comment.