From 5673ff0527388343d4a23d6ced60f15971918bbf Mon Sep 17 00:00:00 2001 From: Ruzal Date: Thu, 19 Oct 2023 19:16:04 +0300 Subject: [PATCH 1/2] Create model: ProjectIncomes, create serizlizers: ProjectParticipantSerializer, ProjectIncomesSerializer, VolunteerProfileSerializer --- backend/api/serializers.py | 97 +++++++++++++++++-- backend/api/urls.py | 9 +- backend/api/views.py | 9 ++ backend/backend/settings.py | 25 +++-- backend/projects/admin.py | 17 ++++ ...0006_alter_category_slug_projectincomes.py | 28 ++++++ backend/projects/models.py | 41 +++++++- 7 files changed, 204 insertions(+), 22 deletions(-) create mode 100644 backend/projects/migrations/0006_alter_category_slug_projectincomes.py diff --git a/backend/api/serializers.py b/backend/api/serializers.py index 18bc0b7..5b690f2 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -11,7 +11,14 @@ Skills, Valuation, ) -from projects.models import Organization, Project, Volunteer, VolunteerSkills +from projects.models import ( + Organization, + Project, + Volunteer, + VolunteerSkills, + ProjectParticipants, + ProjectIncomes, +) from users.models import User @@ -36,8 +43,12 @@ class PlatformAboutSerializer(serializers.ModelSerializer): class Meta: model = PlatformAbout fields = ( - 'about_us', 'platform_email', 'valuations', - 'projects_count', 'volunteers_count', 'organizers_count' + 'about_us', + 'platform_email', + 'valuations', + 'projects_count', + 'volunteers_count', + 'organizers_count', ) def get_projects_count(self, obj): @@ -208,9 +219,16 @@ class UserSerializer(serializers.ModelSerializer): """ Сериализатор для получения пользователя. """ + class Meta: model = User - fields = ('id', 'first_name', 'second_name', 'last_name', 'email',) + fields = ( + 'id', + 'first_name', + 'second_name', + 'last_name', + 'email', + ) class UserCreateSerializer(serializers.ModelSerializer): @@ -220,14 +238,20 @@ class UserCreateSerializer(serializers.ModelSerializer): class Meta: model = User - fields = ('first_name', 'second_name', 'last_name', - 'email', 'password',) + fields = ( + 'first_name', + 'second_name', + 'last_name', + 'email', + 'password', + ) class VolunteerGetSerializer(serializers.ModelSerializer): """ Сериализатор для отображения волонтера. """ + user = UserSerializer() skills = SkillsSerializer(many=True) @@ -240,10 +264,10 @@ class VolunteerCreateSerializer(serializers.ModelSerializer): """ Сериализатор для создания волонтера. """ + user = UserCreateSerializer() skills = serializers.PrimaryKeyRelatedField( - queryset=Skills.objects.all(), - many=True + queryset=Skills.objects.all(), many=True ) def create_skills(self, skills, volunteer): @@ -292,6 +316,7 @@ class OrganizationGetSerializer(serializers.ModelSerializer): """ Сериализатор для отображения организации-организатора. """ + contact_person = UserSerializer() class Meta: @@ -303,6 +328,7 @@ class OgranizationCreateSerializer(serializers.ModelSerializer): """ Сериализатор для создания организации-организатора. """ + contact_person = UserCreateSerializer() @transaction.atomic @@ -310,8 +336,7 @@ def create(self, validated_data): user_data = validated_data.pop('contact_person') user = User.objects.create_user(role=User.ORGANIZER, **user_data) organization = Organization.objects.create( - contact_person=user, - **validated_data + contact_person=user, **validated_data ) return organization @@ -335,3 +360,55 @@ def update(self, instance, validated_data): class Meta: model = Organization exclude = ('id',) + + +class ProjectParticipantSerializer(serializers.Serializer): + """ + Сериализатор для списока участников. + """ + + class Meta: + model = ProjectParticipants + fields = ( + 'project', + 'volunteer', + ) + + +class ProjectIncomesSerializer(serializers.Serializer): + """ + Сериализатор для заявок волонтеров. + """ + + class Meta: + model = ProjectIncomes + fields = ( + 'project', + 'volunteer', + 'status_incomes', + ) + + +class VolunteerProfileSerializer(serializers.Serializer): + """ + Сериализатор для личного кабинета. + """ + + volunteer = VolunteerGetSerializer() + user = UserSerializer() + skills = SkillsSerializer(many=True) + participating_projects = ProjectSerializer(many=True) + applied_projects = ProjectSerializer(many=True) + participants = ProjectParticipantSerializer(many=True) + project_incomes = ProjectIncomesSerializer(many=True) + + class Meta: + fields = ( + 'volunteer', + 'user', + 'skills', + 'participating_projects', + 'applied_projects', + 'participants', + 'project_incomes', + ) diff --git a/backend/api/urls.py b/backend/api/urls.py index 700fcad..6d783f4 100644 --- a/backend/api/urls.py +++ b/backend/api/urls.py @@ -11,6 +11,7 @@ SearchListView, SkillsViewSet, VolunteerViewSet, + VolunteerProfileView, ) router = DefaultRouter() @@ -18,7 +19,8 @@ router.register(r'projects', ProjectViewSet, basename='projects') router.register(r'volunteers', VolunteerViewSet, basename='volunteers') router.register( - r'organizations', OrganizationViewSet, basename='organizations') + r'organizations', OrganizationViewSet, basename='organizations' +) router.register(r'cities', CityViewSet) router.register(r'skills', SkillsViewSet) @@ -29,4 +31,9 @@ path('platform_about/', PlatformAboutView.as_view()), path('feedback/', FeedbackCreateView.as_view()), path('search/', SearchListView.as_view()), + path( + 'volunteer/profile/', + VolunteerProfileView.as_view(), + name='volunteer-profile', + ), ] diff --git a/backend/api/views.py b/backend/api/views.py index 79e243a..4d58e09 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -28,6 +28,7 @@ SkillsSerializer, VolunteerCreateSerializer, VolunteerGetSerializer, + VolunteerProfileSerializer, ) # from .filters import SearchFilter @@ -143,3 +144,11 @@ class SearchListView(generics.ListAPIView): filter_backends = [DjangoFilterBackend, filters.SearchFilter] # filterset_class = SearchFilter search_fields = ['name', 'description', 'event_purpose'] + + +class VolunteerProfileView(generics.RetrieveAPIView): + queryset = Volunteer.objects.all() + serializer_class = VolunteerProfileSerializer + + def get_object(self): + return self.request.user.volunteer diff --git a/backend/backend/settings.py b/backend/backend/settings.py index dfba84c..f512879 100644 --- a/backend/backend/settings.py +++ b/backend/backend/settings.py @@ -20,8 +20,12 @@ ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', '127.0.0.1').split(',') CSRF_TRUSTED_ORIGINS = [ - 'https://*.better-together.acceleratorpracticum.ru/', 'https://*.80.87.109.180', 'https://*.127.0.0.1', - 'http://*.better-together.acceleratorpracticum.ru/', 'http://*.80.87.109.180', 'http://*.127.0.0.1', + 'https://*.better-together.acceleratorpracticum.ru/', + 'https://*.80.87.109.180', + 'https://*.127.0.0.1', + 'http://*.better-together.acceleratorpracticum.ru/', + 'http://*.80.87.109.180', + 'http://*.127.0.0.1', ] # Application definition @@ -162,7 +166,9 @@ ], 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 10, - 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend', ], + 'DEFAULT_FILTER_BACKENDS': [ + 'django_filters.rest_framework.DjangoFilterBackend', + ], # 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema', } @@ -191,14 +197,12 @@ SWAGGER_SETTINGS = { 'SECURITY_DEFINITIONS': { - 'Token': { # авторизация в джанго по токену + 'Token': { # авторизация в джанго по токену 'type': 'apiKey', 'name': 'Authorization', - 'in': 'header' + 'in': 'header', }, - 'Basic': { # базова авторизация - 'type': 'basic' - } + 'Basic': {'type': 'basic'}, # базова авторизация }, 'USE_SESSION_AUTH': True, # кнопка джанго логин можно отключить поменяв False 'JSON_EDITOR': True, @@ -217,6 +221,7 @@ MAX_LEN_TEXT_IN_ADMIN = 50 MAX_LEN_NAME = 200 +MAX_LEN_SLUG = 50 LEN_OGRN = 13 MESSAGE_PHONE_REGEX = 'Номер должен начинаться с +7 и содержать {} цифр.' MESSAGE_EMAIL_VALID = ( @@ -234,7 +239,9 @@ MIN_LEN_NAME_USER = 2 MAX_LEN_NAME_USER = 40 -MESSAGE_NAME_USER_VALID = f'Длина поля от {MIN_LEN_NAME_USER} до {MAX_LEN_NAME_USER} символов' +MESSAGE_NAME_USER_VALID = ( + f'Длина поля от {MIN_LEN_NAME_USER} до {MAX_LEN_NAME_USER} символов' +) MESSAGE_NAME_USER_CYRILLIC = 'Введите имя кириллицей' OGRN_ERROR_MESSAGE = 'ОГРН должен состоять из 13 цифр.' diff --git a/backend/projects/admin.py b/backend/projects/admin.py index d23cb07..f4876a7 100644 --- a/backend/projects/admin.py +++ b/backend/projects/admin.py @@ -6,6 +6,7 @@ Project, Volunteer, VolunteerSkills, + ProjectIncomes, ) @@ -110,3 +111,19 @@ class ProjectAdmin(ModelAdmin): ) save_on_top = True empty_value_display = '-пусто-' + + +@register(ProjectIncomes) +class ProjectIncomesAdmin(ModelAdmin): + list_display = ( + 'project', + 'volunteer', + 'status_incomes', + ) + search_fields = ( + 'name', + 'slug', + 'status_incomes', + ) + list_filter = ('project',) + save_on_top = True diff --git a/backend/projects/migrations/0006_alter_category_slug_projectincomes.py b/backend/projects/migrations/0006_alter_category_slug_projectincomes.py new file mode 100644 index 0000000..388f704 --- /dev/null +++ b/backend/projects/migrations/0006_alter_category_slug_projectincomes.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.6 on 2023-10-19 12:41 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0005_alter_volunteer_date_of_birth'), + ] + + operations = [ + migrations.AlterField( + model_name='category', + name='slug', + field=models.SlugField(unique=True, verbose_name='Слаг'), + ), + migrations.CreateModel( + name='ProjectIncomes', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status_incomes', models.CharField(blank=True, choices=[('application_submitted', 'Одобрено'), ('rejected', 'На рассмотрении'), ('accepted', 'Принята')], default=None, null=True, verbose_name='Статус заявки волонтера')), + ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_incomes', to='projects.project', verbose_name='Проект')), + ('volunteer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='project_incomes', to='projects.volunteer', verbose_name='Волонтер')), + ], + ), + ] diff --git a/backend/projects/models.py b/backend/projects/models.py index 2a40c2f..333eb6c 100644 --- a/backend/projects/models.py +++ b/backend/projects/models.py @@ -153,8 +153,8 @@ class Category(models.Model): ) slug = models.SlugField( unique=True, - max_length=30, - verbose_name='Идентификатор', + max_length=settings.MAX_LEN_SLUG, + verbose_name='Слаг', ) description = models.TextField( blank=False, @@ -308,3 +308,40 @@ def __str__(self): return settings.PROJECTPARTICIPANTS.format( self.project, self.volunteer ) + + +class ProjectIncomes(models.Model): + """ + Модель представляет собой заявки волонтеров на участие в проекте. + """ + + APPLICATION_SUBMITTED = 'application_submitted' + REJECTED = 'rejected' + ACCEPTED = 'accepted' + + STATUS_INCOMES = [ + (APPLICATION_SUBMITTED, 'Одобрено'), + (REJECTED, 'На рассмотрении'), + (ACCEPTED, 'Принята'), + ] + project = models.ForeignKey( + Project, + blank=False, + on_delete=models.CASCADE, + related_name='project_incomes', + verbose_name='Проект', + ) + volunteer = models.ForeignKey( + Volunteer, + blank=False, + on_delete=models.CASCADE, + related_name='project_incomes', + verbose_name='Волонтер', + ) + status_incomes = models.CharField( + choices=STATUS_INCOMES, + null=True, + blank=True, + default=None, + verbose_name='Статус заявки волонтера', + ) From 7a211ffcc50a348fb84b89022a8a2f194115e3fa Mon Sep 17 00:00:00 2001 From: Ruzal Date: Fri, 20 Oct 2023 09:26:04 +0300 Subject: [PATCH 2/2] Create view VolunteerProfileView --- backend/api/serializers.py | 9 ++++++--- backend/api/urls.py | 3 +-- backend/api/views.py | 13 ++++++++----- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/backend/api/serializers.py b/backend/api/serializers.py index 5b690f2..bfae13b 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -389,26 +389,29 @@ class Meta: ) +# в разработке class VolunteerProfileSerializer(serializers.Serializer): """ - Сериализатор для личного кабинета. + Сериализатор для личного кабинета волонтера. """ volunteer = VolunteerGetSerializer() user = UserSerializer() skills = SkillsSerializer(many=True) participating_projects = ProjectSerializer(many=True) - applied_projects = ProjectSerializer(many=True) + projects = ProjectSerializer(many=True) participants = ProjectParticipantSerializer(many=True) project_incomes = ProjectIncomesSerializer(many=True) + phone = serializers.CharField(source='volunteer.phone') class Meta: fields = ( 'volunteer', 'user', + 'phone', 'skills', 'participating_projects', - 'applied_projects', + 'projects', 'participants', 'project_incomes', ) diff --git a/backend/api/urls.py b/backend/api/urls.py index 6d783f4..a300e3c 100644 --- a/backend/api/urls.py +++ b/backend/api/urls.py @@ -32,8 +32,7 @@ path('feedback/', FeedbackCreateView.as_view()), path('search/', SearchListView.as_view()), path( - 'volunteer/profile/', + 'volunteer/profile//', VolunteerProfileView.as_view(), - name='volunteer-profile', ), ] diff --git a/backend/api/views.py b/backend/api/views.py index 4d58e09..5ef5b1c 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -146,9 +146,12 @@ class SearchListView(generics.ListAPIView): search_fields = ['name', 'description', 'event_purpose'] +# в разработке class VolunteerProfileView(generics.RetrieveAPIView): - queryset = Volunteer.objects.all() - serializer_class = VolunteerProfileSerializer - - def get_object(self): - return self.request.user.volunteer + def get(self, request, volunteer_id, format=None): + try: + volunteer = Volunteer.objects.get(id=volunteer_id) + serializer = VolunteerProfileSerializer(volunteer) + return Response(serializer.data) + except Volunteer.DoesNotExist: + return Response({'error': 'Волонтер не найден'}, status=404)