From 5623d01134187eff519179df932c443fa6226dd1 Mon Sep 17 00:00:00 2001 From: Mikan <72257910+Mikan-DS@users.noreply.github.com> Date: Mon, 19 May 2025 12:54:50 +0300 Subject: [PATCH] begin api --- .gitignore | 1 + education/filters.py | 57 ++++++++++++++++++++++++++++++ education/serializers.py | 75 ++++++++++++++++++++++++++++++++++++++++ education/urls.py | 22 ++++++++++++ education/views.py | 74 +++++++++++++++++++++++++++++++++++++-- requirements.txt | 12 ++++++- tisbiProFi/settings.py | 23 ++++++++++++ tisbiProFi/urls.py | 32 +++++++++++++++-- 8 files changed, 291 insertions(+), 5 deletions(-) create mode 100644 .gitignore create mode 100644 education/filters.py create mode 100644 education/serializers.py create mode 100644 education/urls.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3eae80e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/db.sqlite3 diff --git a/education/filters.py b/education/filters.py new file mode 100644 index 0000000..9e5fe83 --- /dev/null +++ b/education/filters.py @@ -0,0 +1,57 @@ +import django_filters +from .models import AcademicYear, Department, DepartmentHead, Specialty, Group, Student + +class AcademicYearFilter(django_filters.FilterSet): + year = django_filters.CharFilter(lookup_expr="icontains") + + class Meta: + model = AcademicYear + fields = ["year"] + + +class DepartmentFilter(django_filters.FilterSet): + name = django_filters.CharFilter(lookup_expr="icontains") + + class Meta: + model = Department + fields = ["name"] + + +class DepartmentHeadFilter(django_filters.FilterSet): + department_name = django_filters.CharFilter(field_name="department__name", lookup_expr="icontains") + head_name = django_filters.CharFilter(field_name="head__first_name", lookup_expr="icontains") + + class Meta: + model = DepartmentHead + fields = ["department_name", "head_name"] + + +class SpecialtyFilter(django_filters.FilterSet): + name = django_filters.CharFilter(lookup_expr="icontains") + code = django_filters.CharFilter(lookup_expr="icontains") + department_name = django_filters.CharFilter(field_name="department__name", lookup_expr="icontains") + + class Meta: + model = Specialty + fields = ["name", "code", "department_name"] + + +class GroupFilter(django_filters.FilterSet): + name = django_filters.CharFilter(lookup_expr="icontains") + curator_name = django_filters.CharFilter(field_name="curator__first_name", lookup_expr="icontains") + specialty_name = django_filters.CharFilter(field_name="specialty__name", lookup_expr="icontains") + + class Meta: + model = Group + fields = ["name", "curator_name", "specialty_name"] + + +class StudentFilter(django_filters.FilterSet): + first_name = django_filters.CharFilter(field_name="account__first_name", lookup_expr="icontains") + last_name = django_filters.CharFilter(field_name="account__last_name", lookup_expr="icontains") + group_name = django_filters.CharFilter(field_name="group__name", lookup_expr="icontains") + specialty_name = django_filters.CharFilter(field_name="group__specialty__name", lookup_expr="icontains") + + class Meta: + model = Student + fields = ["first_name", "last_name", "group_name", "specialty_name"] diff --git a/education/serializers.py b/education/serializers.py new file mode 100644 index 0000000..e2d6c30 --- /dev/null +++ b/education/serializers.py @@ -0,0 +1,75 @@ +from rest_framework import serializers +from .models import AcademicYear, Department, DepartmentHead, Specialty, Group, Student + +class AcademicYearSerializer(serializers.ModelSerializer): + class Meta: + model = AcademicYear + fields = "__all__" + + +class DepartmentSerializer(serializers.ModelSerializer): + class Meta: + model = Department + fields = "__all__" + + +class DepartmentHeadSerializer(serializers.ModelSerializer): + head_name = serializers.SerializerMethodField() + + class Meta: + model = DepartmentHead + fields = ["id", "department", "head", "head_name"] + + def get_head_name(self, obj): + return f"{obj.head.first_name} {obj.head.last_name}" + + +class SpecialtySerializer(serializers.ModelSerializer): + class Meta: + model = Specialty + fields = "__all__" + + +class GroupSerializer(serializers.ModelSerializer): + curator_name = serializers.SerializerMethodField() + + class Meta: + model = Group + fields = ["id", "name", "curator", "specialty", "curator_name"] + + def get_curator_name(self, obj): + if obj.curator: + return f"{obj.curator.first_name} {obj.curator.last_name}" + return None + + +class StudentSerializer(serializers.ModelSerializer): + first_name = serializers.CharField(source="account.first_name") + last_name = serializers.CharField(source="account.last_name") + middle_name = serializers.CharField(source="account.middle_name") + phone = serializers.CharField(source="account.phone") + email = serializers.CharField(source="account.email") + + class Meta: + model = Student + fields = [ + "id", + "group", + "first_name", + "last_name", + "middle_name", + "phone", + "email", + ] + + def update(self, instance, validated_data): + account_data = validated_data.pop("account", {}) + instance.account.first_name = account_data.get("first_name", instance.account.first_name) + instance.account.last_name = account_data.get("last_name", instance.account.last_name) + instance.account.middle_name = account_data.get("middle_name", instance.account.middle_name) + instance.account.phone = account_data.get("phone", instance.account.phone) + instance.account.email = account_data.get("email", instance.account.email) + instance.account.save() + return super().update(instance, validated_data) + + diff --git a/education/urls.py b/education/urls.py new file mode 100644 index 0000000..8efd01e --- /dev/null +++ b/education/urls.py @@ -0,0 +1,22 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from .views import ( + AcademicYearViewSet, + DepartmentViewSet, + DepartmentHeadViewSet, + SpecialtyViewSet, + GroupViewSet, + StudentViewSet, +) + +router = DefaultRouter() +router.register(r"academic-years", AcademicYearViewSet, basename="academic-year") +router.register(r"departments", DepartmentViewSet, basename="department") +router.register(r"department-heads", DepartmentHeadViewSet, basename="department-head") +router.register(r"specialties", SpecialtyViewSet, basename="specialty") +router.register(r"groups", GroupViewSet, basename="group") +router.register(r"students", StudentViewSet, basename="student") + +urlpatterns = [ + path("", include(router.urls)), +] \ No newline at end of file diff --git a/education/views.py b/education/views.py index 91ea44a..6dac682 100644 --- a/education/views.py +++ b/education/views.py @@ -1,3 +1,73 @@ -from django.shortcuts import render +from rest_framework import viewsets +from rest_framework.filters import SearchFilter, OrderingFilter +from django_filters.rest_framework import DjangoFilterBackend +from .models import AcademicYear, Department, DepartmentHead, Specialty, Group, Student +from .serializers import ( + AcademicYearSerializer, + DepartmentSerializer, + DepartmentHeadSerializer, + SpecialtySerializer, + GroupSerializer, + StudentSerializer, +) +from .filters import ( + AcademicYearFilter, + DepartmentFilter, + DepartmentHeadFilter, + SpecialtyFilter, + GroupFilter, + StudentFilter, +) -# Create your views here. +class AcademicYearViewSet(viewsets.ModelViewSet): + queryset = AcademicYear.objects.all() + serializer_class = AcademicYearSerializer + filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] + filterset_class = AcademicYearFilter + search_fields = ["year"] + ordering_fields = ["year"] + + +class DepartmentViewSet(viewsets.ModelViewSet): + queryset = Department.objects.all() + serializer_class = DepartmentSerializer + filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] + filterset_class = DepartmentFilter + search_fields = ["name"] + ordering_fields = ["name"] + + +class DepartmentHeadViewSet(viewsets.ModelViewSet): + queryset = DepartmentHead.objects.all() + serializer_class = DepartmentHeadSerializer + filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] + filterset_class = DepartmentHeadFilter + search_fields = ["department__name", "head__first_name"] + ordering_fields = ["department", "head"] + + +class SpecialtyViewSet(viewsets.ModelViewSet): + queryset = Specialty.objects.all() + serializer_class = SpecialtySerializer + filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] + filterset_class = SpecialtyFilter + search_fields = ["name", "code", "department__name"] + ordering_fields = ["name", "code"] + + +class GroupViewSet(viewsets.ModelViewSet): + queryset = Group.objects.all() + serializer_class = GroupSerializer + filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] + filterset_class = GroupFilter + search_fields = ["name", "curator__first_name", "specialty__name"] + ordering_fields = ["name", "specialty"] + + +class StudentViewSet(viewsets.ModelViewSet): + queryset = Student.objects.all() + serializer_class = StudentSerializer + filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter] + filterset_class = StudentFilter + search_fields = ["account__first_name", "account__last_name", "group__name"] + ordering_fields = ["group", "account__last_name"] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 387a9eb..eb61c0d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,11 @@ -django~=4.2.21 \ No newline at end of file +django~=4.2.21 +django-cors-headers +djangorestframework +drf-yasg +Pillow +requests +django-autoslug +transliterate +pytest-django +pytest +django-filter \ No newline at end of file diff --git a/tisbiProFi/settings.py b/tisbiProFi/settings.py index ed8f66c..7141711 100644 --- a/tisbiProFi/settings.py +++ b/tisbiProFi/settings.py @@ -39,6 +39,10 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'corsheaders', + 'rest_framework', + "django_filters", + 'drf_yasg', "core.apps.CoreConfig", 'users.apps.UsersConfig', "employees.apps.EmployeesConfig", @@ -51,6 +55,7 @@ INSTALLED_APPS = [ MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', @@ -149,3 +154,21 @@ MEDIA_ROOT = BASE_DIR / 'media' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' AUTH_USER_MODEL = "users.Account" + + +CORS_ORIGIN_WHITELIST = ['http://localhost:8000', + 'http://localhost:3000'] +CSRF_TRUSTED_ORIGINS = ['http://localhost:8000', + 'http://localhost:3000'] + +CORS_ALLOW_CREDENTIALS = True + +REST_FRAMEWORK = { + "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning", + "DEFAULT_VERSION": "v1", + + "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", + "PAGE_SIZE": 10, + + "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"], +} diff --git a/tisbiProFi/urls.py b/tisbiProFi/urls.py index 508d059..4c0407f 100644 --- a/tisbiProFi/urls.py +++ b/tisbiProFi/urls.py @@ -14,9 +14,37 @@ Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ +from django.conf import settings +from django.conf.urls.static import static from django.contrib import admin -from django.urls import path +from django.urls import path, include +from drf_yasg import openapi +from drf_yasg.views import get_schema_view +from rest_framework import permissions + +schema_view = get_schema_view( + openapi.Info( + title="ProFi API", + default_version='v1', + description="API for ProFi (v1)", + contact=openapi.Contact(email="support@novelplus.ru"), + + + ), + public=True, + permission_classes=[permissions.AllowAny], +) + urlpatterns = [ path('admin/', admin.site.urls), -] + path('api/v1/education/', include("education.urls")), + + path('docs/swagger/', schema_view.without_ui(cache_timeout=0), name='schema-json'), + path('docs/swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), + path('docs/redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), + +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + +if settings.DEBUG: + urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)