diff --git a/README.md b/README.md new file mode 100644 index 0000000..e80b6fd --- /dev/null +++ b/README.md @@ -0,0 +1,125 @@ +# Project usage guide +## How to run the project locally? + +### ๐Ÿ’พ Downloading the project to the computer +#### Git clone +```bash +git clone https://github.com/dev-KPI/iatp-dev-website-backend-python.git +``` + + +### ๐Ÿ”จ Dependency installation +To use this project without installation of everything by yourself just use +#### Libs installation +```bash +pip3 install -r requirements.txt +``` + +### โšก๏ธ Connect your database +Create your database. And make changes to the settings +##### Connect to DB +```bash +sudo -u postgres psql +``` +##### Create DB +```bash +CREATE DATABASE my_database; +``` +##### Changes settings +``` +DATABASES = { + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": "my_database", + "USER": "", + "PASSWORD": "", + "HOST": "", + "PORT": "", + } +} +``` + + +### ๐Ÿ” Get your secret key +Get your secret key. And make changes to the settings +##### Get your secret key +``` +https://djecrety.ir/ +``` +##### Changes settings +###### Before +``` +SECRET_KEY = os.getenv("SECRET_KEY") +``` +###### After +``` +SECRET_KEY = "" +``` + + +### ๐Ÿ—ƒ๏ธ Makemigrations +Run the command in the directory with file manage.py +##### Makemigrations writes model changes to separate migration files, similar to commits. Create makemigrations +```bash +python manage.py makemigrations +``` + +### ๐Ÿ—„๏ธ Migrate +Run the command in the directory with file manage.py +##### Migrate applies these changes to the database. Create migrate +```bash +python manage.py migrate +``` + + +### ๐Ÿ‘‘ Superuser +Run the command in the directory with file manage.py. Follow the instructions +##### Create superuser +```bash +python manage.py createsuperuser +``` + +### ๐Ÿš€ Start +Run the command in the directory with file manage.py +##### Run project +```bash +python manage.py runserver +``` + +## How to deploy a project remotely? + +### ๐Ÿ’ฝ Install flyctl +Flyctl is a command-line utility that lets you work with the Fly.io platform, from creating your account to deploying your applications +##### Install flyctl +```bash +curl -L https://fly.io/install.sh | sh +``` + + +### ๐Ÿ“– Sign up +If itโ€™s your first time using Fly.io, youโ€™ll need to sign up for an account. +##### Sign up +```bash +flyctl auth signup +``` + +### ๐Ÿ“ฎ Preparing Django application +To prepare for the fly.io you donโ€™t normally need anything. +The fly.io cli will take care of everything. +From creating the docker file to fly configuration file. +##### Preparing Django application +```bash +flyctl launch +``` + +### ๐Ÿš€ Deploy +So for the deployment steps all we need to do is +put our .env files into fly instance and +then issue a deploy command. +##### Deploy +``` +flyctl secrets import < .env +``` +```bash +flyctl deploy +``` \ No newline at end of file diff --git a/api/apps.py b/api/apps.py index 66656fd..878e7d5 100644 --- a/api/apps.py +++ b/api/apps.py @@ -2,5 +2,5 @@ class ApiConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'api' + default_auto_field = "django.db.models.BigAutoField" + name = "api" diff --git a/api/migrations/0003_specialization_project_programming_language_and_more.py b/api/migrations/0003_specialization_project_programming_language_and_more.py new file mode 100644 index 0000000..a06bf9c --- /dev/null +++ b/api/migrations/0003_specialization_project_programming_language_and_more.py @@ -0,0 +1,35 @@ +# Generated by Django 4.0.6 on 2022-08-18 09:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0002_language_member_programming_language'), + ] + + operations = [ + migrations.CreateModel( + name='Specialization', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('specialization', models.CharField(max_length=32)), + ], + ), + migrations.AddField( + model_name='project', + name='programming_language', + field=models.ManyToManyField(to='api.language'), + ), + migrations.AddField( + model_name='member', + name='specialization', + field=models.ManyToManyField(to='api.specialization'), + ), + migrations.AddField( + model_name='project', + name='specialization', + field=models.ManyToManyField(to='api.specialization'), + ), + ] diff --git a/api/migrations/0004_remove_member_github_member_sociallinks.py b/api/migrations/0004_remove_member_github_member_sociallinks.py new file mode 100644 index 0000000..5b680c3 --- /dev/null +++ b/api/migrations/0004_remove_member_github_member_sociallinks.py @@ -0,0 +1,27 @@ +# Generated by Django 4.0.6 on 2022-09-04 13:54 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0003_specialization_project_programming_language_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='member', + name='github_member', + ), + migrations.CreateModel( + name='SocialLinks', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('social_link', models.CharField(choices=[('GH', 'Github'), ('GL', 'Gitlab'), ('TG', 'Telegram'), ('LD', 'Linkenid')], default='GH', max_length=2)), + ('link', models.URLField(max_length=256)), + ('member', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='api.member')), + ], + ), + ] diff --git a/api/migrations/0005_member_photo_url_alter_sociallinks_social_link.py b/api/migrations/0005_member_photo_url_alter_sociallinks_social_link.py new file mode 100644 index 0000000..a1d0ced --- /dev/null +++ b/api/migrations/0005_member_photo_url_alter_sociallinks_social_link.py @@ -0,0 +1,23 @@ +# Generated by Django 4.0.6 on 2022-10-14 21:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0004_remove_member_github_member_sociallinks'), + ] + + operations = [ + migrations.AddField( + model_name='member', + name='photo_url', + field=models.URLField(max_length=256, null=True), + ), + migrations.AlterField( + model_name='sociallinks', + name='social_link', + field=models.CharField(choices=[('GH', 'Github'), ('GL', 'Gitlab'), ('TG', 'Telegram'), ('LD', 'Linkenid'), ('DS', 'Discord')], default='GH', max_length=2), + ), + ] diff --git a/api/migrations/0006_alter_project_members_alter_sociallinks_member.py b/api/migrations/0006_alter_project_members_alter_sociallinks_member.py new file mode 100644 index 0000000..3c6f0b3 --- /dev/null +++ b/api/migrations/0006_alter_project_members_alter_sociallinks_member.py @@ -0,0 +1,24 @@ +# Generated by Django 4.0.6 on 2022-10-17 18:05 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0005_member_photo_url_alter_sociallinks_social_link'), + ] + + operations = [ + migrations.AlterField( + model_name='project', + name='members', + field=models.ManyToManyField(related_name='projects', to='api.member'), + ), + migrations.AlterField( + model_name='sociallinks', + name='member', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='social_links', to='api.member'), + ), + ] diff --git a/api/migrations/0007_project_photo_url.py b/api/migrations/0007_project_photo_url.py new file mode 100644 index 0000000..1156be2 --- /dev/null +++ b/api/migrations/0007_project_photo_url.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.6 on 2022-10-18 11:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0006_alter_project_members_alter_sociallinks_member'), + ] + + operations = [ + migrations.AddField( + model_name='project', + name='photo_url', + field=models.URLField(max_length=256, null=True), + ), + ] diff --git a/api/migrations/0008_remove_project_github_project_githublinks.py b/api/migrations/0008_remove_project_github_project_githublinks.py new file mode 100644 index 0000000..66216ae --- /dev/null +++ b/api/migrations/0008_remove_project_github_project_githublinks.py @@ -0,0 +1,27 @@ +# Generated by Django 4.0.6 on 2022-10-19 08:15 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0007_project_photo_url'), + ] + + operations = [ + migrations.RemoveField( + model_name='project', + name='github_project', + ), + migrations.CreateModel( + name='GitHubLinks', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=64)), + ('link', models.URLField(max_length=256)), + ('project', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='github_links', to='api.project')), + ], + ), + ] diff --git a/api/models.py b/api/models.py index 1063cab..833f6be 100644 --- a/api/models.py +++ b/api/models.py @@ -1,8 +1,16 @@ from django.db import models + # Create your models here. +class Specialization(models.Model): + specialization = models.CharField(max_length=32) + + def __str__(self): + return self.specialization + + class Language(models.Model): language = models.CharField(max_length=32) @@ -11,26 +19,64 @@ def __str__(self): class Member(models.Model): + photo_url = models.URLField(max_length=256, null=True) first_name = models.CharField(max_length=32) last_name = models.CharField(max_length=32) date_of_birth = models.DateField(blank=True) summary = models.TextField(max_length=1024, help_text="Enter your summary") programming_language = models.ManyToManyField(Language) + specialization = models.ManyToManyField(Specialization) email = models.EmailField(max_length=256) - github_member = models.URLField(max_length=200) def __str__(self): return "{0} {1}".format(self.last_name, self.first_name) class Project(models.Model): + photo_url = models.URLField(max_length=256, null=True) title = models.CharField(max_length=64) description = models.TextField( max_length=1000, help_text="Enter project description" ) - members = models.ManyToManyField(Member) - github_project = models.URLField(max_length=256) + members = models.ManyToManyField(Member, related_name="projects") + specialization = models.ManyToManyField(Specialization) + programming_language = models.ManyToManyField(Language) def __str__(self): return self.title + +class SocialLinks(models.Model): + GITHUB = "GH" + GITLAB = "GL" + TELEGRAM = "TG" + LINKENID = "LD" + DISCORD = "DS" + SOCIAL_LINK_CHOICES = [ + (GITHUB, "Github"), + (GITLAB, "Gitlab"), + (TELEGRAM, "Telegram"), + (LINKENID, "Linkenid"), + (DISCORD, "Discord"), + ] + social_link = models.CharField( + max_length=2, choices=SOCIAL_LINK_CHOICES, default=GITHUB + ) + link = models.URLField(max_length=256) + member = models.ForeignKey( + Member, on_delete=models.CASCADE, null=True, related_name="social_links" + ) + + def __str__(self): + return self.social_link + + +class GitHubLinks(models.Model): + title = models.CharField(max_length=64) + link = models.URLField(max_length=256) + project = models.ForeignKey( + Project, on_delete=models.CASCADE, null=True, related_name="github_links" + ) + + def __str__(self): + return self.title diff --git a/api/serializers.py b/api/serializers.py index 9163cd5..be05985 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -1,20 +1,114 @@ from rest_framework import serializers -from api.models import Member, Project, Language + +from api.models import ( + Member, + Project, + Language, + Specialization, + SocialLinks, + GitHubLinks, +) + + +class MemberCreateSerializer(serializers.ModelSerializer): + class Meta: + model = Member + fields = "__all__" + + +class ProjectCreateSerializer(serializers.ModelSerializer): + class Meta: + model = Project + fields = "__all__" class LanguageSerializer(serializers.ModelSerializer): class Meta: model = Language - fields = '__all__' + fields = "__all__" + + +class SocialLinksSerializer(serializers.ModelSerializer): + class Meta: + model = SocialLinks + fields = "__all__" + + +class SpecializationSerializer(serializers.ModelSerializer): + class Meta: + model = Specialization + fields = "__all__" + + +class GitHubLinksSerializer(serializers.ModelSerializer): + class Meta: + model = GitHubLinks + fields = "__all__" + + +class GitHubLinksforSerializer(serializers.ModelSerializer): + class Meta: + model = GitHubLinks + fields = ["id", "title", "link"] + + +class SocialLinksforMemberSerializer(serializers.ModelSerializer): + class Meta: + model = SocialLinks + fields = ["id", "social_link", "link"] -class MemberSerializer(serializers.HyperlinkedModelSerializer): +class ProjectsforMemberSerializer(serializers.ModelSerializer): + class Meta: + model = Project + fields = ["id", "title"] + + +class MemberDetailSerializer(serializers.ModelSerializer): + specialization = serializers.StringRelatedField(many=True) + programming_language = serializers.StringRelatedField(many=True) + social_links = SocialLinksforMemberSerializer(many=True) + projects = ProjectsforMemberSerializer(many=True) + + counter_projects = serializers.SerializerMethodField("count") + + def count(self, member): + member_id = member.id + return len(Project.objects.filter(members=member_id).values()) + class Meta: model = Member - fields = '__all__' + fields = [ + "id", + "photo_url", + "first_name", + "last_name", + "date_of_birth", + "summary", + "programming_language", + "specialization", + "email", + "social_links", + "projects", + "counter_projects", + ] + +class ProjectDetailSerializer(serializers.ModelSerializer): + specialization = serializers.StringRelatedField(many=True) + programming_language = serializers.StringRelatedField(many=True) + members = serializers.StringRelatedField(many=True) + github_links = GitHubLinksforSerializer(many=True) -class ProjectSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Project - fields = '__all__' + fields = [ + "id", + "photo_url", + "title", + "description", + "members", + "specialization", + "programming_language", + "github_links", + ] diff --git a/api/tests.py b/api/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/api/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/api/tests/__init__.py b/api/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/tests/test_api.py b/api/tests/test_api.py new file mode 100644 index 0000000..0b58f60 --- /dev/null +++ b/api/tests/test_api.py @@ -0,0 +1,121 @@ +from django.urls import reverse +from rest_framework import status +from rest_framework.test import APITestCase + +from api.models import Language, Specialization, Member, Project, SocialLinks, GitHubLinks +from api.serializers import ( + LanguageSerializer, + SpecializationSerializer, + MemberCreateSerializer, + ProjectCreateSerializer, + SocialLinksSerializer, + GitHubLinksSerializer +) + + +class ApiTestCase(APITestCase): + def setUp(self): + self.language_1 = Language.objects.create(id=1, language="Java") + self.language_2 = Language.objects.create(id=2, language="Python") + self.specialization = Specialization.objects.create( + id=1, specialization="Desktop" + ) + self.member = Member.objects.create( + id=1, + first_name="Dima", + last_name="Rezenkov", + date_of_birth="2004-03-05", + summary="From Ukraine", + email="rezenkovdmitro@gmail.com", + ) + self.member.programming_language.add(self.language_1) + self.member.specialization.add(self.specialization) + self.project = Project.objects.create( + title="Dive-into", + description="Our first project", + ) + self.project.specialization.add(self.specialization) + self.project.programming_language.add(self.language_1) + self.project.members.add(self.member) + self.link = SocialLinks.objects.create( + id=1, social_link="GH", link="https://github.com", member=self.member + ) + self.githublink = GitHubLinks.objects.create( + id=2, title="Backend", link="https://github.com", project=self.project + ) + + def test_get_language(self): + url = reverse("language-list") + response = self.client.get(url) + serializer_data = LanguageSerializer( + [self.language_1, self.language_2], many=True + ).data + self.assertEqual(status.HTTP_200_OK, response.status_code) + self.assertEqual(serializer_data, response.data) + + def test_get_specialization(self): + url = reverse("specialization-list") + response = self.client.get(url) + serializer_data = SpecializationSerializer( + [self.specialization], many=True + ).data + self.assertEqual(status.HTTP_200_OK, response.status_code) + self.assertEqual(serializer_data, response.data) + + def test_get_member(self): + url = reverse("members-list") + response = self.client.get(url) + serializer_data = MemberCreateSerializer([self.member], many=True).data + self.assertEqual(status.HTTP_200_OK, response.status_code) + self.assertEqual(serializer_data, response.data) + + def test_get_project(self): + url = reverse("projects-list") + response = self.client.get(url) + serializer_data = ProjectCreateSerializer([self.project], many=True).data + self.assertEqual(status.HTTP_200_OK, response.status_code) + self.assertEqual(serializer_data, response.data) + + def test_get_social_link(self): + url = reverse("sociallinks-list") + response = self.client.get(url) + serializer_data = SocialLinksSerializer([self.link], many=True).data + self.assertEqual(status.HTTP_200_OK, response.status_code) + self.assertEqual(serializer_data, response.data) + + def test_get_github_link(self): + url = reverse("githublinks-list") + response = self.client.get(url) + serializer_data = GitHubLinksSerializer([self.githublink], many=True).data + self.assertEqual(status.HTTP_200_OK, response.status_code) + self.assertEqual(serializer_data, response.data) + + def test_get_member_filter(self): + url = reverse("members-list") + response = self.client.get( + url, data={"programming_language": [1], "specialization": [1]} + ) + serializer_data = MemberCreateSerializer([self.member], many=True).data + self.assertEqual(status.HTTP_200_OK, response.status_code) + self.assertEqual(serializer_data, response.data) + + def test_get_project_filter(self): + url = reverse("projects-list") + response = self.client.get(url, data={"members": [1], "specialization": [1]}) + serializer_data = ProjectCreateSerializer([self.project], many=True).data + self.assertEqual(status.HTTP_200_OK, response.status_code) + self.assertEqual(serializer_data, response.data) + + def test_get_member_search(self): + url = reverse("members-list") + response = self.client.get(url, data={"search": "Rezenkov"}) + serializer_data = MemberCreateSerializer([self.member], many=True).data + self.assertEqual(status.HTTP_200_OK, response.status_code) + self.assertEqual(serializer_data, response.data) + + def test_get_project_search(self): + url = reverse("projects-list") + response = self.client.get(url, data={"search": "Dive-into"}) + serializer_data = ProjectCreateSerializer([self.project], many=True).data + self.assertEqual(status.HTTP_200_OK, response.status_code) + self.assertEqual(serializer_data, response.data) diff --git a/api/tests/test_serializers.py b/api/tests/test_serializers.py new file mode 100644 index 0000000..30f38ea --- /dev/null +++ b/api/tests/test_serializers.py @@ -0,0 +1,157 @@ +from django.test import TestCase +from api.models import Language, Specialization, Member, Project, SocialLinks, GitHubLinks +from api.serializers import ( + LanguageSerializer, + SpecializationSerializer, + MemberCreateSerializer, + ProjectCreateSerializer, + SocialLinksSerializer, + GitHubLinksSerializer, +) + + +class SpecializationSerializerTestCase(TestCase): + def test_ok_specialization(self): + specialization = Specialization.objects.create(id=1, specialization="Desktop") + data = SpecializationSerializer(specialization).data + expected_data = [{"id": 1, "specialization": "Desktop"}] + self.assertEqual(expected_data[0], data) + + +class LanguageSerializerTestCase(TestCase): + def test_ok_language(self): + language = Language.objects.create(id=1, language="Java") + data = LanguageSerializer(language).data + expected_data = [{"id": 1, "language": "Java"}] + self.assertEqual(expected_data[0], data) + + +class ProjectSerializerTestCase(TestCase): + def test_ok_project(self): + project = Project.objects.create( + id=1, + photo_url="http://127.0.0.1:8000/docs#/", + title="Dive-into", + description="Our first project", + ) + language = Language.objects.create(id=1, language="Java") + specialization = Specialization.objects.create(id=1, specialization="Android") + member = Member.objects.create( + id=1, + first_name="Dima", + last_name="Rezenkov", + date_of_birth="2004-03-05", + summary="From Ukraine", + email="rezenkovdmitro@gmail.com", + ) + project.programming_language.add(language) + project.specialization.add(specialization) + project.members.add(member) + data = ProjectCreateSerializer(project).data + expected_data = [ + { + "id": 1, + "photo_url": "http://127.0.0.1:8000/docs#/", + "title": "Dive-into", + "description": "Our first project", + "members": [1], + "specialization": [1], + "programming_language": [1], + } + ] + self.assertEqual(expected_data[0], data) + + +class MemberSerializerTestCase(TestCase): + def test_ok_member(self): + language = Language.objects.create(id=1, language="JS") + specialization = Specialization.objects.create(id=1, specialization="WEB") + member = Member.objects.create( + photo_url="https://drive.google.com/file/d/1KzK3R5CktEDWbexP2FsbyIn28R4qsbdF/view?usp=sharing", + first_name="Dima", + last_name="Rezenkov", + date_of_birth="2004-03-05", + summary="From Ukraine", + email="rezenkovdmitro@gmail.com", + ) + member.programming_language.add(language) + member.specialization.add(specialization) + data = MemberCreateSerializer(member).data + expected_data = [ + { + "id": 1, + "photo_url": "https://drive.google.com/file/d/1KzK3R5CktEDWbexP2FsbyIn28R4qsbdF/view?usp=sharing", + "first_name": "Dima", + "last_name": "Rezenkov", + "date_of_birth": "2004-03-05", + "summary": "From Ukraine", + "email": "rezenkovdmitro@gmail.com", + "programming_language": [1], + "specialization": [1], + } + ] + self.assertEqual(expected_data[0], data) + + +class SocialLinksSerializersTestCase(TestCase): + def test_ok_sociallinks(self): + language = Language.objects.create(id=1, language="JS") + specialization = Specialization.objects.create(id=1, specialization="WEB") + member = Member.objects.create( + id=1, + first_name="Dima", + last_name="Rezenkov", + date_of_birth="2004-03-05", + summary="From Ukraine", + email="rezenkovdmitro@gmail.com", + ) + member.programming_language.add(language) + member.specialization.add(specialization) + link = SocialLinks.objects.create( + id=1, social_link="GH", link="https://github.com", member=member + ) + data = SocialLinksSerializer(link).data + expected_data = [ + { + "id": 1, + "social_link": "GH", + "link": "https://github.com", + "member": 1, + } + ] + self.assertEqual(expected_data[0], data) + + +class GitHubLinksSerializerTestCase(TestCase): + def test_ok_githublinks(self): + project = Project.objects.create( + photo_url="http://127.0.0.1:8000/docs#/", + title="Dive-into", + description="Our first project", + ) + language = Language.objects.create(id=1, language="Java") + specialization = Specialization.objects.create(id=1, specialization="Android") + member = Member.objects.create( + id=1, + first_name="Dima", + last_name="Rezenkov", + date_of_birth="2004-03-05", + summary="From Ukraine", + email="rezenkovdmitro@gmail.com", + ) + project.programming_language.add(language) + project.specialization.add(specialization) + project.members.add(member) + githublink = GitHubLinks.objects.create( + id=1, title="Backend", link="https://github.com", project=project + ) + data = GitHubLinksSerializer(githublink).data + expected_data = [ + { + "id": 1, + "title": "Backend", + "link": "https://github.com", + "project": 1, + } + ] + self.assertEqual(expected_data[0], data) diff --git a/api/urls.py b/api/urls.py index e00743e..4a8d241 100644 --- a/api/urls.py +++ b/api/urls.py @@ -3,9 +3,14 @@ from rest_framework.routers import DefaultRouter router = DefaultRouter() -router.register(r'members', views.MemberViewSet) -router.register(r'projects', views.ProjectViewSet) -router.register(r'languages', views.LanguageViewSet) +router.register(r"members", views.MemberCreateViewSet, basename="members") +router.register(r"projects", views.ProjectCreateViewSet, basename="projects") +router.register(r"languages", views.LanguageViewSet) +router.register(r"sociallinks", views.SocialLinksViewSet) +router.register(r"specializations", views.SpecializationViewSet) +router.register(r"githublinks", views.GitHubLinksViewSet, basename="githublinks") +router.register(r"members-details", views.MemberDetailViewSet) +router.register(r"projects-details", views.ProjectDetailViewSet) urlpatterns = [ path("", include(router.urls)), diff --git a/api/views.py b/api/views.py index 1d7370d..347fd6a 100644 --- a/api/views.py +++ b/api/views.py @@ -1,27 +1,77 @@ from rest_framework import viewsets from rest_framework.filters import SearchFilter from django_filters.rest_framework import DjangoFilterBackend -from api.serializers import MemberSerializer, ProjectSerializer, LanguageSerializer -from api.models import Member, Project, Language +from rest_framework.permissions import IsAuthenticated +from api.serializers import ( + MemberDetailSerializer, + ProjectDetailSerializer, + LanguageSerializer, + SpecializationSerializer, + SocialLinksSerializer, + MemberCreateSerializer, + ProjectCreateSerializer, + GitHubLinksSerializer, +) +from api.models import ( + Member, + Project, + Language, + Specialization, + SocialLinks, + GitHubLinks, +) -class MemberViewSet(viewsets.ModelViewSet): +class MemberCreateViewSet(viewsets.ModelViewSet): + permission_classes = [IsAuthenticated] queryset = Member.objects.all() - serializer_class = MemberSerializer - filter_backends = [DjangoFilterBackend, SearchFilter] - filterset_fields = ['programming_language', ] - search_fields = ['last_name', ] + serializer_class = MemberCreateSerializer -class ProjectViewSet(viewsets.ModelViewSet): +class ProjectCreateViewSet(viewsets.ModelViewSet): + permission_classes = [IsAuthenticated] queryset = Project.objects.all() - serializer_class = ProjectSerializer - filter_backends = [DjangoFilterBackend, SearchFilter] - filterset_fields = ['members', ] - search_fields = ['title', ] + serializer_class = ProjectCreateSerializer class LanguageViewSet(viewsets.ModelViewSet): + permission_classes = [IsAuthenticated] queryset = Language.objects.all() serializer_class = LanguageSerializer + + +class SpecializationViewSet(viewsets.ModelViewSet): + permission_classes = [IsAuthenticated] + queryset = Specialization.objects.all() + serializer_class = SpecializationSerializer + + +class GitHubLinksViewSet(viewsets.ModelViewSet): + permission_classes = [IsAuthenticated] + queryset = GitHubLinks.objects.all() + serializer_class = GitHubLinksSerializer + + +class SocialLinksViewSet(viewsets.ModelViewSet): + permission_classes = [IsAuthenticated] + queryset = SocialLinks.objects.all() + serializer_class = SocialLinksSerializer + + +class MemberDetailViewSet(viewsets.ReadOnlyModelViewSet): + queryset = Member.objects.all() + serializer_class = MemberDetailSerializer + filter_backends = [DjangoFilterBackend, SearchFilter] + filterset_fields = ["programming_language", "specialization"] + search_fields = [ + "last_name", + ] + + +class ProjectDetailViewSet(viewsets.ReadOnlyModelViewSet): + queryset = Project.objects.all() + serializer_class = ProjectDetailSerializer + filter_backends = [DjangoFilterBackend, SearchFilter] + filterset_fields = ["members", "specialization"] + search_fields = ["title"] diff --git a/manage.py b/manage.py index b48f52c..9d93e21 100644 --- a/manage.py +++ b/manage.py @@ -6,7 +6,7 @@ def main(): """Run administrative tasks.""" - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'site_dive_into.settings') + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "site_dive_into.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: @@ -18,5 +18,5 @@ def main(): execute_from_command_line(sys.argv) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/requirements.txt b/requirements.txt index 58152c6..8ff4e4d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,13 @@ asgiref==3.5.2 -Django==4.0.6 +dj-database-url==1.0.0 +Django==4.1.10 +django-cors-headers==3.13.0 +django-filter==22.1 djangorestframework==3.13.1 +gunicorn==20.1.0 +Pillow==10.0.1 psycopg2==2.9.3 pytz==2022.1 -sqlparse==0.4.2 -tzdata==2022.1 \ No newline at end of file +sqlparse==0.4.4 +tzdata==2022.1 +whitenoise==6.2.0 diff --git a/site_dive_into/asgi.py b/site_dive_into/asgi.py index 5dc5622..2304f54 100644 --- a/site_dive_into/asgi.py +++ b/site_dive_into/asgi.py @@ -11,6 +11,6 @@ from django.core.asgi import get_asgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'site_dive_into.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "site_dive_into.settings") application = get_asgi_application() diff --git a/site_dive_into/settings.py b/site_dive_into/settings.py index 514e781..a6dcca9 100644 --- a/site_dive_into/settings.py +++ b/site_dive_into/settings.py @@ -15,7 +15,6 @@ # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent - # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ @@ -24,53 +23,54 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] - +ALLOWED_HOSTS = ["*"] +CORS_ORIGIN_ALLOW_ALL = True # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django_filters', - 'api', - 'rest_framework', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django_filters", + "api", + "rest_framework", + "corsheaders", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "corsheaders.middleware.CorsMiddleware", ] -ROOT_URLCONF = 'site_dive_into.urls' +ROOT_URLCONF = "site_dive_into.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, ] -WSGI_APPLICATION = 'site_dive_into.wsgi.application' - +WSGI_APPLICATION = "site_dive_into.wsgi.application" # Database # https://docs.djangoproject.com/en/4.0/ref/settings/#databases @@ -86,48 +86,45 @@ } } - # Password validation # https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] - # Internationalization # https://docs.djangoproject.com/en/4.0/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = "UTC" USE_I18N = True USE_TZ = True - # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/4.0/howto/static-files/ -STATIC_URL = 'static/' +STATIC_URL = "static/" # Default primary key field type # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" REST_FRAMEWORK = { - 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'] -} \ No newline at end of file + "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"] +} diff --git a/site_dive_into/urls.py b/site_dive_into/urls.py index 2cf0e72..696ac6d 100644 --- a/site_dive_into/urls.py +++ b/site_dive_into/urls.py @@ -17,6 +17,6 @@ from django.urls import path, include urlpatterns = [ - path('admin/', admin.site.urls), - path('', include('api.urls')), + path("admin/", admin.site.urls), + path("", include("api.urls")), ] diff --git a/site_dive_into/wsgi.py b/site_dive_into/wsgi.py index e600ccd..5b70401 100644 --- a/site_dive_into/wsgi.py +++ b/site_dive_into/wsgi.py @@ -11,6 +11,6 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'site_dive_into.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "site_dive_into.settings") application = get_wsgi_application()