Skip to content

Commit

Permalink
Merge pull request #221 from volunteers-for-city-projects/fix/is_favo…
Browse files Browse the repository at this point in the history
…rite_project

Fixed adding a project to favorites and is_favorite in ProjectGetSeri…
  • Loading branch information
ArtemKAF authored Nov 20, 2023
2 parents 84c9eae + 7abdd0a commit 370b586
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 45 deletions.
2 changes: 1 addition & 1 deletion backend/api/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ def is_valid(self, *, raise_exception=False):
errors_db
}
)
return bool(self._errors)
return not bool(self._errors)
48 changes: 40 additions & 8 deletions backend/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from djoser.serializers import UserCreateSerializer, UserSerializer
from drf_extra_fields.fields import Base64ImageField
from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator
from taggit.models import Tag

from api.utils import NonEmptyBase64ImageField, create_user
Expand All @@ -19,6 +20,7 @@
Category,
Organization,
Project,
ProjectFavorite,
ProjectIncomes,
ProjectParticipants,
Volunteer,
Expand Down Expand Up @@ -165,10 +167,19 @@ class ProjectGetSerializer(serializers.ModelSerializer):

event_address = AddressSerializer(read_only=True)
skills = SkillsSerializer(many=True, read_only=True)
is_favorited = serializers.BooleanField(default=False)
is_favorited = serializers.SerializerMethodField()
status = serializers.SerializerMethodField()
city = serializers.SlugRelatedField(slug_field='name', read_only=True)

def get_is_favorited(self, obj):
request = self.context.get('request')
if request and request.user.is_authenticated:
return ProjectFavorite.objects.filter(
user=request.user,
project=obj,
).exists()
return False

def get_status(self, data):
OPEN = 'open'
READY = 'ready_for_feedback'
Expand Down Expand Up @@ -752,19 +763,40 @@ class Meta:
)


class VolunteerFavoriteGetSerializer(serializers.ModelSerializer):
# class ProjectFavoriteGetSerializer(serializers.ModelSerializer):
# """
# Сериализатор для отображения избранных проектов.
# """
#
# class Meta:
# model = Project
# fields = (
# 'id',
# 'name',
# 'picture',
# 'organization',
# )


class ProjectFavoriteSerializer(IsValidModifyErrorForFrontendMixin,
serializers.ModelSerializer):
"""
Сериализатор для отображения избранных проектов волонтера.
Сериализатор для отображения избранных проектов.
"""

class Meta:
model = Project
model = ProjectFavorite
fields = (
'id',
'name',
'picture',
'organization',
'user',
'project',
)
validators = [
UniqueTogetherValidator(
ProjectFavorite.objects.all(),
fields=('user', 'project'),
message='Этот проект уже присутствует в избранном!',
)
]


class CurrentUserSerializer(UserSerializer):
Expand Down
54 changes: 25 additions & 29 deletions backend/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
Category,
Organization,
Project,
ProjectFavorite,
ProjectIncomes,
Volunteer,
VolunteerFavorite,
)

from .filters import (
Expand Down Expand Up @@ -58,14 +58,14 @@
PlatformAboutSerializer,
PreviewNewsSerializer,
ProjectCategorySerializer,
ProjectFavoriteSerializer,
ProjectGetSerializer,
ProjectIncomesGetSerializer,
ProjectIncomesSerializer,
ProjectSerializer,
SkillsSerializer,
TagSerializer,
VolunteerCreateSerializer,
VolunteerFavoriteGetSerializer,
VolunteerGetSerializer,
VolunteerUpdateSerializer,
)
Expand Down Expand Up @@ -229,53 +229,49 @@ def destroy(self, request, *args, **kwargs):
instance.delete()
return Response(status=status.HTTP_204_NO_CONTENT)

def add_to(self, volunteer, project, errors):
def create_favorite(self, serializer_class, user, project):
"""
Добавить проект в избранное.
"""
_, created = VolunteerFavorite.objects.get_or_create(
volunteer=volunteer, project=project
serializer = serializer_class(
data={'user': user, 'project': project, }
)
if serializer.is_valid():
serializer.save()
return Response(status=status.HTTP_201_CREATED)
return Response(
serializer.errors,
status=status.HTTP_400_BAD_REQUEST,
)
if not created:
return Response(
{'errors': errors},
status=status.HTTP_400_BAD_REQUEST,
)
serializer = VolunteerFavoriteGetSerializer(instance=project)
return Response(serializer.data, status=status.HTTP_201_CREATED)

def delete_from(self, volunteer, project, errors):
def delete_favorite(self, model, user, project):
"""
Удалить проект из избранного.
"""
cnt_deleted, _ = VolunteerFavorite.objects.filter(
volunteer=volunteer, project=project
).delete()

if cnt_deleted == 0:
return Response(
{'errors': errors},
status=status.HTTP_400_BAD_REQUEST,
)
model.objects.filter(user=user, project=project).delete()
return Response(status=status.HTTP_204_NO_CONTENT)

@action(
['POST', 'DELETE'],
detail=True,
permission_classes=(IsVolunteer,),
permission_classes=[IsVolunteer | IsOrganizer],
serializer_class=ProjectFavoriteSerializer,
)
def favorite(self, request, **kwargs):
"""
Избранные проекты волонтера.
"""
project = get_object_or_404(Project, pk=kwargs.get('pk'))
volunteer = get_object_or_404(Volunteer, user=request.user)
if request.method == 'POST':
return self.add_to(
volunteer, project, 'Данный проект уже есть в избранном!'
return self.create_favorite(
serializer_class=self.serializer_class,
user=request.user.pk,
project=project.pk
)
return self.delete_from(
volunteer, project, 'Данного проекта нет в избранном!'
return self.delete_favorite(
model=ProjectFavorite,
user=request.user.pk,
project=kwargs.get('pk'),
)

@action(
Expand Down Expand Up @@ -543,7 +539,7 @@ class ProjectMeViewSet(viewsets.GenericViewSet, mixins.ListModelMixin):
def get_queryset(self):
if self.request.user.is_volunteer:
volunteer = get_object_or_404(Volunteer, user=self.request.user.id)
from_volunteer_favorite = VolunteerFavorite.objects.filter(
from_volunteer_favorite = ProjectFavorite.objects.filter(
project=OuterRef('pk'), volunteer=volunteer
)
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Generated by Django 4.2.6 on 2023-11-19 21:04

import datetime
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('projects', '0004_alter_projectincomes_cover_letter'),
]

operations = [
migrations.CreateModel(
name='ProjectFavorite',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='projects.project', verbose_name='Проект')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Пользователь')),
],
options={
'verbose_name': 'Избранный проект',
'verbose_name_plural': 'Избранные проекты',
'default_related_name': 'project_favorite',
},
),
migrations.AlterField(
model_name='volunteer',
name='date_of_birth',
field=models.DateField(help_text='Введите дату в формате "ГГГГ-ММ-ДД", пример: "2000-01-01".', validators=[django.core.validators.MinValueValidator(limit_value=datetime.date(1900, 1, 1)), django.core.validators.MaxValueValidator(limit_value=datetime.date(2023, 11, 19))], verbose_name='Дата рождения'),
),
migrations.DeleteModel(
name='VolunteerFavorite',
),
migrations.AddConstraint(
model_name='projectfavorite',
constraint=models.UniqueConstraint(fields=('user', 'project'), name='projects_projectfavorite_unique_project_in_favorite'),
),
]
35 changes: 28 additions & 7 deletions backend/projects/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,21 +532,42 @@ def __str__(self):
)


class VolunteerFavorite(models.Model):
class ProjectFavorite(models.Model):
"""
Модель избранных проектов волонтеров.
Модель избранных проектов пользователей.
При добавлении проекта в избранное все поля обязательны для заполнения.
Attributes:
user(int):
Поле ForeignKey на пользователя, у которого проект в избранном.
project(int):
Поле ForeignKey на проект, добавленный в избранное.
"""

volunteer = models.ForeignKey(Volunteer, on_delete=models.CASCADE)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
user = models.ForeignKey(
User,
verbose_name='Пользователь',
on_delete=models.CASCADE,
)
project = models.ForeignKey(
Project,
verbose_name='Проект',
on_delete=models.CASCADE,
)

class Meta:
verbose_name = 'Избранный проект'
verbose_name_plural = 'Избранные проекты'
default_related_name = 'project_favorite'
constraints = (
models.UniqueConstraint(
fields=('volunteer', 'project'),
name='unique_volunteer_favorites',
fields=('user', 'project'),
name='%(app_label)s_%(class)s_unique_project_in_favorite',
),
)

def __str__(self):
return f'{self.volunteer} {self.project}'
return (
f'Проект {self.project.name} в избранном у {self.user}'
)

0 comments on commit 370b586

Please sign in to comment.