From 56ad8588bf668a297ebbdb89572369d0668886dd Mon Sep 17 00:00:00 2001 From: Armin Patel <72291325+arminpatel@users.noreply.github.com> Date: Wed, 5 Jul 2023 13:21:07 +0530 Subject: [PATCH 1/8] feat(backend): setup authentication (#46) --- backend/auth/urls.py | 8 ++++++++ backend/core/settings.py | 13 +++++++++++++ backend/core/urls.py | 18 ++---------------- backend/requirements.txt | 2 ++ 4 files changed, 25 insertions(+), 16 deletions(-) create mode 100644 backend/auth/urls.py diff --git a/backend/auth/urls.py b/backend/auth/urls.py new file mode 100644 index 0000000..14c329b --- /dev/null +++ b/backend/auth/urls.py @@ -0,0 +1,8 @@ +from django.urls import path +from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView + + +urlpatterns = [ + path('', TokenObtainPairView.as_view(), name='token_obtain_pair'), + path('refresh/', TokenRefreshView.as_view(), name='token_refresh'), +] diff --git a/backend/core/settings.py b/backend/core/settings.py index d01d28e..0e5ef4c 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -30,9 +30,22 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'rest_framework', 'corsheaders', + 'rest_framework_simplejwt', ] +REST_FRAMEWORK = { + # Use Django's standard `django.contrib.auth` permissions, + # or allow read-only access for unauthenticated users. + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly' + ], + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework_simplejwt.authentication.JWTAuthentication', + ) +} + MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', 'django.middleware.security.SecurityMiddleware', diff --git a/backend/core/urls.py b/backend/core/urls.py index 3b2f61e..f8c2b62 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -1,22 +1,8 @@ -""" -URL configuration for core project. - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/4.2/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -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.contrib import admin from django.urls import path +from django.conf.urls import include urlpatterns = [ path('admin/', admin.site.urls), + path('api/token/', include('auth.urls')), ] diff --git a/backend/requirements.txt b/backend/requirements.txt index 9aaa714..e72f807 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -2,6 +2,8 @@ asgiref==3.7.2 Django==4.2.2 django-cors-headers==4.1.0 djangorestframework==3.14.0 +djangorestframework-simplejwt==5.2.2 +PyJWT==2.7.0 python-dotenv==1.0.0 pytz==2023.3 sqlparse==0.4.4 From 15b7fd829609f428f84b3d2d77c210d00e322eee Mon Sep 17 00:00:00 2001 From: Priyansh Mehta <96513964+Priyansh61@users.noreply.github.com> Date: Fri, 7 Jul 2023 16:32:05 +0530 Subject: [PATCH 2/8] feat(backend) : added account models (#50) * updated requirements * created account app * updated setting.py * updated url to incorporate routes * accounts directory changed * duplicacy of core removed * Removed username field * removed routers --- backend/accounts/__init__.py | 0 backend/accounts/admin.py | 18 ++++++++++ backend/accounts/apps.py | 6 ++++ backend/accounts/models.py | 69 ++++++++++++++++++++++++++++++++++++ backend/accounts/tests.py | 3 ++ backend/accounts/views.py | 3 ++ backend/core/settings.py | 32 ++++++++++++++++- backend/requirements.txt | 7 +++- 8 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 backend/accounts/__init__.py create mode 100644 backend/accounts/admin.py create mode 100644 backend/accounts/apps.py create mode 100644 backend/accounts/models.py create mode 100644 backend/accounts/tests.py create mode 100644 backend/accounts/views.py diff --git a/backend/accounts/__init__.py b/backend/accounts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/accounts/admin.py b/backend/accounts/admin.py new file mode 100644 index 0000000..19ff587 --- /dev/null +++ b/backend/accounts/admin.py @@ -0,0 +1,18 @@ +from django.contrib import admin +from django.contrib.auth.admin import UserAdmin +from .models import Account +# Register your models here. + +class AccountAdmin(UserAdmin): + list_display = ('email', 'first_name', 'last_name', 'last_login', 'date_joined', 'is_active', 'is_staff') + list_display_links = ('email', 'first_name', 'last_name') + readonly_fields = ('last_login', 'date_joined') + ordering = ('-date_joined',) + + filter_horizontal = () + list_filter = () + fieldsets = () + +admin.site.register(Account,AccountAdmin) + + diff --git a/backend/accounts/apps.py b/backend/accounts/apps.py new file mode 100644 index 0000000..3e3c765 --- /dev/null +++ b/backend/accounts/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AccountsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'accounts' diff --git a/backend/accounts/models.py b/backend/accounts/models.py new file mode 100644 index 0000000..378428b --- /dev/null +++ b/backend/accounts/models.py @@ -0,0 +1,69 @@ +from django.db import models +from django.contrib.auth.models import AbstractBaseUser, BaseUserManager + +# Create your models here. + +class MyAccountManager(BaseUserManager): + def create_user(self, first_name, last_name, email, password=None): + if not email: + raise ValueError("Users must have an email address") + + user = self.model( + email = self.normalize_email(email), + first_name = first_name, + last_name = last_name + ) + + user.set_password(password) + user.save(using=self._db) + + return user + + def create_superuser(self, first_name, last_name, email, password): + user = self.create_user( + email = self.normalize_email(email), + first_name = first_name, + last_name = last_name, + password = password + ) + + user.is_admin = True + user.is_active = True + user.is_staff = True + user.is_superadmin = True + + user.save(using=self._db) + + return user + + + +class Account(AbstractBaseUser): + first_name = models.CharField(max_length=50) + last_name = models.CharField(max_length=50) + email = models.EmailField(unique=True) + phone_number = models.CharField(max_length=50) + + # required + date_joined = models.DateTimeField(auto_now_add=True) + last_login = models.DateTimeField(auto_now=True) + is_admin = models.BooleanField(default=False) + is_staff = models.BooleanField(default=False) + is_superadmin = models.BooleanField(default=False) + is_active = models.BooleanField(default=False) + + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = ['first_name', 'last_name'] + + objects = MyAccountManager() + + def __str__(self): + return self.email + + def has_perm(self, perm, obj=None): + return self.is_admin + + def has_module_perms(self, add_label): + return True + + diff --git a/backend/accounts/tests.py b/backend/accounts/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/accounts/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/accounts/views.py b/backend/accounts/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/accounts/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/backend/core/settings.py b/backend/core/settings.py index 0e5ef4c..a442bec 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -30,9 +30,13 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'rest_framework', 'corsheaders', 'rest_framework_simplejwt', + 'django_extensions', + 'rest_framework', + 'django_filters', + 'core', + 'accounts', ] REST_FRAMEWORK = { @@ -77,6 +81,8 @@ WSGI_APPLICATION = 'core.wsgi.application' +AUTH_USER_MODEL = 'accounts.Account' + # Database # https://docs.djangoproject.com/en/4.2/ref/settings/#databases @@ -129,3 +135,27 @@ # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +REST_FRAMEWORK = { + 'EXCEPTION_HANDLER': 'rest_framework_json_api.exceptions.exception_handler', + 'DEFAULT_PARSER_CLASSES': ( + 'rest_framework_json_api.parsers.JSONParser', + ), + 'DEFAULT_RENDERER_CLASSES': ( + 'rest_framework_json_api.renderers.JSONRenderer', + 'rest_framework.renderers.BrowsableAPIRenderer' + ), + 'DEFAULT_METADATA_CLASS': 'rest_framework_json_api.metadata.JSONAPIMetadata', + 'DEFAULT_FILTER_BACKENDS': ( + 'rest_framework_json_api.filters.QueryParameterValidationFilter', + 'rest_framework_json_api.filters.OrderingFilter', + 'rest_framework_json_api.django_filters.DjangoFilterBackend', + 'rest_framework.filters.SearchFilter', + ), + 'SEARCH_PARAM': 'filter[search]', + 'TEST_REQUEST_RENDERER_CLASSES': ( + 'rest_framework_json_api.renderers.JSONRenderer', + ), + 'TEST_REQUEST_DEFAULT_FORMAT': 'vnd.api+json' +} + diff --git a/backend/requirements.txt b/backend/requirements.txt index e72f807..560f1e7 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,9 +1,14 @@ asgiref==3.7.2 -Django==4.2.2 +Django==4.1 django-cors-headers==4.1.0 +django-extensions==3.2.1 +django-filter==22.1 djangorestframework==3.14.0 djangorestframework-simplejwt==5.2.2 PyJWT==2.7.0 +djangorestframework-jsonapi==6.0.0 +inflection==0.5.1 +tzdata==2022.6 python-dotenv==1.0.0 pytz==2023.3 sqlparse==0.4.4 From 7b56a27cc99363219fa3868636e7a3ecbac4c5ef Mon Sep 17 00:00:00 2001 From: Armin Patel <72291325+arminpatel@users.noreply.github.com> Date: Fri, 21 Jul 2023 19:17:58 +0530 Subject: [PATCH 3/8] fix(backend): linting account and build file (#79) * fix(backend): fix account and build file * fix linting * remove json_api package * fix(backend): add address field to account model --- backend/accounts/admin.py | 40 +++++++++++++++++++++++++++++++++----- backend/accounts/models.py | 33 +++++++++++++++---------------- backend/accounts/tests.py | 3 --- backend/accounts/views.py | 3 --- backend/build.py | 30 +++++++++++++++------------- backend/core/settings.py | 24 +---------------------- backend/requirements.txt | 3 +-- 7 files changed, 69 insertions(+), 67 deletions(-) delete mode 100644 backend/accounts/tests.py delete mode 100644 backend/accounts/views.py diff --git a/backend/accounts/admin.py b/backend/accounts/admin.py index 19ff587..7064228 100644 --- a/backend/accounts/admin.py +++ b/backend/accounts/admin.py @@ -1,18 +1,48 @@ from django.contrib import admin +from django import forms from django.contrib.auth.admin import UserAdmin from .models import Account -# Register your models here. + + +class AccountAddForm(forms.ModelForm): + class Meta: + model = Account + fields = ('email', + 'first_name', + 'last_name', + 'password', + 'is_active', + 'is_staff') + class AccountAdmin(UserAdmin): - list_display = ('email', 'first_name', 'last_name', 'last_login', 'date_joined', 'is_active', 'is_staff') + add_form = AccountAddForm + list_display = ('email', + 'first_name', + 'last_name', + 'last_login', + 'date_joined', + 'is_active', + 'is_staff') + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('email', + 'first_name', + 'last_name', + 'password', + 'is_active', + 'is_staff') + }), + ) + list_display_links = ('email', 'first_name', 'last_name') readonly_fields = ('last_login', 'date_joined') ordering = ('-date_joined',) - + filter_horizontal = () list_filter = () fieldsets = () -admin.site.register(Account,AccountAdmin) - +admin.site.register(Account, AccountAdmin) diff --git a/backend/accounts/models.py b/backend/accounts/models.py index 378428b..8fb9a88 100644 --- a/backend/accounts/models.py +++ b/backend/accounts/models.py @@ -3,28 +3,29 @@ # Create your models here. + class MyAccountManager(BaseUserManager): def create_user(self, first_name, last_name, email, password=None): if not email: raise ValueError("Users must have an email address") - + user = self.model( - email = self.normalize_email(email), - first_name = first_name, - last_name = last_name + email=self.normalize_email(email), + first_name=first_name, + last_name=last_name ) - user.set_password(password) + user.set_password(password) user.save(using=self._db) return user - + def create_superuser(self, first_name, last_name, email, password): user = self.create_user( - email = self.normalize_email(email), - first_name = first_name, - last_name = last_name, - password = password + email=self.normalize_email(email), + first_name=first_name, + last_name=last_name, + password=password ) user.is_admin = True @@ -35,14 +36,14 @@ def create_superuser(self, first_name, last_name, email, password): user.save(using=self._db) return user - class Account(AbstractBaseUser): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) email = models.EmailField(unique=True) - phone_number = models.CharField(max_length=50) + phone_number = models.CharField(max_length=50, blank=True) + address = models.CharField(max_length=128, blank=True) # required date_joined = models.DateTimeField(auto_now_add=True) @@ -58,12 +59,10 @@ class Account(AbstractBaseUser): objects = MyAccountManager() def __str__(self): - return self.email - + return str(self.email) + def has_perm(self, perm, obj=None): return self.is_admin - + def has_module_perms(self, add_label): return True - - diff --git a/backend/accounts/tests.py b/backend/accounts/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/backend/accounts/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/backend/accounts/views.py b/backend/accounts/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/backend/accounts/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. diff --git a/backend/build.py b/backend/build.py index 4882691..f93c2a6 100644 --- a/backend/build.py +++ b/backend/build.py @@ -1,20 +1,22 @@ -import os import sys import subprocess import random + def in_venv(): return sys.prefix != sys.base_prefix + # Copy content from .env.template to .env -try : +try: # Generate a random secret key - django_secret = ''.join (random.SystemRandom ().choice ('abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)') for i in range (50)) + django_secret = ''.join(random.SystemRandom().choice( + 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)') for i in range(50)) - with open('.env', 'w') as env : - env.write ('\n') - env.write ('DJANGO_SECRET=' + "'" + django_secret + "'") + with open('.env', 'w') as env: + env.write('\n') + env.write('DJANGO_SECRET=' + "'" + django_secret + "'") # Check for venv if in_venv(): @@ -24,30 +26,30 @@ def in_venv(): exit() # Print the python version - subprocess.run (['python3', '--version']) + subprocess.run(['python3', '--version']) # Install dependencies print('Installing dependencies...') - subprocess.run (['pip3', 'install', '-r', 'requirements.txt']) + subprocess.run(['pip3', 'install', '-r', 'requirements.txt']) print("Dependencies installed!") # Make Migrations print('Making migrations...') - subprocess.run (['python3', 'manage.py', 'makemigrations']) + subprocess.run(['python3', 'manage.py', 'makemigrations', 'accounts']) + subprocess.run(['python3', 'manage.py', 'makemigrations']) print("Migrations made!") # Migrate the database print('Migrating the database...') - subprocess.run (['python3', 'manage.py', 'migrate']) + subprocess.run(['python3', 'manage.py', 'migrate']) print("Database migrated!") # Run the server print('Running the server...') - subprocess.run (['python3', 'manage.py', 'runserver']) + subprocess.run(['python3', 'manage.py', 'runserver']) -except KeyboardInterrupt : +except KeyboardInterrupt: print("Process interrupted!") - -print("Build Stopped!") +print("Build Stopped!") diff --git a/backend/core/settings.py b/backend/core/settings.py index a442bec..850861c 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -136,26 +136,4 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' -REST_FRAMEWORK = { - 'EXCEPTION_HANDLER': 'rest_framework_json_api.exceptions.exception_handler', - 'DEFAULT_PARSER_CLASSES': ( - 'rest_framework_json_api.parsers.JSONParser', - ), - 'DEFAULT_RENDERER_CLASSES': ( - 'rest_framework_json_api.renderers.JSONRenderer', - 'rest_framework.renderers.BrowsableAPIRenderer' - ), - 'DEFAULT_METADATA_CLASS': 'rest_framework_json_api.metadata.JSONAPIMetadata', - 'DEFAULT_FILTER_BACKENDS': ( - 'rest_framework_json_api.filters.QueryParameterValidationFilter', - 'rest_framework_json_api.filters.OrderingFilter', - 'rest_framework_json_api.django_filters.DjangoFilterBackend', - 'rest_framework.filters.SearchFilter', - ), - 'SEARCH_PARAM': 'filter[search]', - 'TEST_REQUEST_RENDERER_CLASSES': ( - 'rest_framework_json_api.renderers.JSONRenderer', - ), - 'TEST_REQUEST_DEFAULT_FORMAT': 'vnd.api+json' -} - +REST_FRAMEWORK = {} diff --git a/backend/requirements.txt b/backend/requirements.txt index 560f1e7..c15be1a 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,12 +1,11 @@ asgiref==3.7.2 -Django==4.1 +Django==4.2 django-cors-headers==4.1.0 django-extensions==3.2.1 django-filter==22.1 djangorestframework==3.14.0 djangorestframework-simplejwt==5.2.2 PyJWT==2.7.0 -djangorestframework-jsonapi==6.0.0 inflection==0.5.1 tzdata==2022.6 python-dotenv==1.0.0 From 9f051c8dc37674426e0e6d61aa68fd3190201cf8 Mon Sep 17 00:00:00 2001 From: Armin Patel <72291325+arminpatel@users.noreply.github.com> Date: Fri, 21 Jul 2023 19:39:59 +0530 Subject: [PATCH 4/8] feat(backend): add endpoints for accounts (#82) * feat(backend): add endpoints for account * fix linting and build errors * fix linting and build errors --- backend/accounts/models.py | 11 +-- backend/accounts/serializers.py | 33 +++++++++ backend/accounts/tests.py | 122 ++++++++++++++++++++++++++++++++ backend/accounts/urls.py | 7 ++ backend/accounts/views.py | 29 ++++++++ backend/core/urls.py | 1 + backend/pytest.ini | 3 + backend/requirements.txt | 11 ++- backend/tests.py | 0 9 files changed, 210 insertions(+), 7 deletions(-) create mode 100644 backend/accounts/serializers.py create mode 100644 backend/accounts/tests.py create mode 100644 backend/accounts/urls.py create mode 100644 backend/accounts/views.py create mode 100644 backend/pytest.ini create mode 100644 backend/tests.py diff --git a/backend/accounts/models.py b/backend/accounts/models.py index 8fb9a88..e2a53c5 100644 --- a/backend/accounts/models.py +++ b/backend/accounts/models.py @@ -1,8 +1,7 @@ from django.db import models +from django.core.validators import RegexValidator from django.contrib.auth.models import AbstractBaseUser, BaseUserManager -# Create your models here. - class MyAccountManager(BaseUserManager): def create_user(self, first_name, last_name, email, password=None): @@ -39,11 +38,13 @@ def create_superuser(self, first_name, last_name, email, password): class Account(AbstractBaseUser): + phone_regex = RegexValidator(regex=r'^(\+91)?\d{10}$', message="Invalid phone number") + first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) email = models.EmailField(unique=True) - phone_number = models.CharField(max_length=50, blank=True) - address = models.CharField(max_length=128, blank=True) + phone_number = models.CharField(max_length=13, validators=[phone_regex], blank=True) + address = models.CharField(max_length=128, blank=True) # required date_joined = models.DateTimeField(auto_now_add=True) @@ -51,7 +52,7 @@ class Account(AbstractBaseUser): is_admin = models.BooleanField(default=False) is_staff = models.BooleanField(default=False) is_superadmin = models.BooleanField(default=False) - is_active = models.BooleanField(default=False) + is_active = models.BooleanField(default=True) USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['first_name', 'last_name'] diff --git a/backend/accounts/serializers.py b/backend/accounts/serializers.py new file mode 100644 index 0000000..9b1c643 --- /dev/null +++ b/backend/accounts/serializers.py @@ -0,0 +1,33 @@ +from rest_framework import serializers +from .models import Account + + +class AccountSerializer(serializers.ModelSerializer): + class Meta: + model = Account + fields = ['id', + 'first_name', + 'last_name', + 'email', + 'password', + 'address', + 'phone_number', + 'date_joined', + 'last_login'] + extra_kwargs = { + 'password': {'write_only': True}, + 'date_joined': {'read_only': True}, + 'last_login': {'read_only': True}, + } + + def create(self, validated_data): + password = validated_data.pop('password', None) + + if not password: + raise serializers.ValidationError('Password is required') + + account = self.Meta.model(**validated_data) + account.set_password(password) + account.save() + + return account diff --git a/backend/accounts/tests.py b/backend/accounts/tests.py new file mode 100644 index 0000000..749d140 --- /dev/null +++ b/backend/accounts/tests.py @@ -0,0 +1,122 @@ +import pytest +from rest_framework.test import APIClient +from rest_framework import status +from .models import Account + +account_data = { + "email": "test@email.com", + "password": "testpassword", + "first_name": "test", + "last_name": "account", + "phone_number": "1234567890", + "address": "test address", + } + + +@pytest.fixture +def api_client(): + account = Account.objects.create(**account_data) + client = APIClient() + client.force_authenticate(user=account) + return client + + +@pytest.mark.django_db +def testCreateAccountView_validAccountDetails_accountCreationSuccesful(): + # Arrange + client = APIClient() + + # Act + response = client.post('/api/accounts/', account_data, format='json') + + print(response.data) + + # Assert + assert response.status_code == status.HTTP_201_CREATED + assert Account.objects.count() == 1 + assert response.data['email'] == account_data['email'] + + # check that password is not returned and the stored password is hashed + assert 'password' not in response.data + assert Account.objects.get().password != account_data['password'] + + +@pytest.mark.django_db +@pytest.mark.parametrize('field', ['email', 'password', 'first_name', 'last_name']) +def testCreateAccountView_missingRequiredDetails_returnsBadRequest(field): + # Arrange + client = APIClient() + data = account_data.copy() + data.pop(field) + + # Act + response = client.post('/api/accounts/', data, format='json') + + # Assert + assert response.status_code == status.HTTP_400_BAD_REQUEST + + +@pytest.mark.django_db +@pytest.mark.parametrize('new_email', ['test@email.com', 'testemail']) +def testCreateAccountView_invalidOrDuplicateEmail_returnsBadRequest(new_email): + # Arrange + client = APIClient() + client.post('/api/accounts/', account_data, format='json') + data = account_data.copy() + data['email'] = new_email + + # Act + response = client.post('/api/accounts/', data, format='json') + + # Assert + assert response.status_code == status.HTTP_400_BAD_REQUEST + + +@pytest.mark.django_db +def testRetrieveLoggedInAccountView_userLoggedIn_returnsAccountDetails(api_client): + # Act + response = api_client.get('/api/accounts/me/', format='json') + + # Assert + assert response.status_code == status.HTTP_200_OK + assert response.data['email'] == account_data['email'] + + +@pytest.mark.django_db +def testRetrieveLoggedInAccountView_userNotLoggedIn_returnsForbidden(): + # Arrange + client = APIClient() + + # Act + response = client.get('/api/accounts/me/', format='json') + + # Assert + assert response.status_code == status.HTTP_403_FORBIDDEN + + +@pytest.mark.django_db +@pytest.mark.parametrize('field', ['email', 'first_name', 'last_name', 'address']) +def testUpdateLogInAccountView_userLoggedIn_updateSuccesful(api_client, field): + # Arrange + data = { + field: f'new{account_data[field]}' + } + + # Act + response = api_client.put('/api/accounts/me/', data, format='json') + + # Assert + assert response.status_code == status.HTTP_200_OK + assert response.data[field] == data[field] + + +@pytest.mark.django_db +def testUpdateLogInAccountView_userNotLoggedIn_returnsForbidden(): + # Arrange + client = APIClient() + + # Act + response = client.put('/api/accounts/me/', {}, format='json') + + # Assert + assert response.status_code == status.HTTP_403_FORBIDDEN diff --git a/backend/accounts/urls.py b/backend/accounts/urls.py new file mode 100644 index 0000000..7f02571 --- /dev/null +++ b/backend/accounts/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from .views import CreateAccountView, RetrieveUpdateLoggedInAccountView + +urlpatterns = [ + path('', CreateAccountView.as_view()), + path('me/', RetrieveUpdateLoggedInAccountView.as_view()), +] diff --git a/backend/accounts/views.py b/backend/accounts/views.py new file mode 100644 index 0000000..68990ea --- /dev/null +++ b/backend/accounts/views.py @@ -0,0 +1,29 @@ +from rest_framework.generics import CreateAPIView +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.permissions import IsAuthenticated +from .serializers import AccountSerializer +from .models import Account + + +class CreateAccountView(CreateAPIView): + """ View to create accounts """ + queryset = Account.objects.all() + serializer_class = AccountSerializer + + +class RetrieveUpdateLoggedInAccountView(APIView): + """ View to retrieve and change logged in account """ + permission_classes = (IsAuthenticated,) + + def get(self, request): + serializer = AccountSerializer(request.user) + return Response(serializer.data) + + def put(self, request): + # let update be partial + serializer = AccountSerializer(request.user, data=request.data, partial=True) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data) + return Response(serializer.errors) diff --git a/backend/core/urls.py b/backend/core/urls.py index f8c2b62..51d142c 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -5,4 +5,5 @@ urlpatterns = [ path('admin/', admin.site.urls), path('api/token/', include('auth.urls')), + path('api/accounts/', include('accounts.urls')), ] diff --git a/backend/pytest.ini b/backend/pytest.ini new file mode 100644 index 0000000..0d3adea --- /dev/null +++ b/backend/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +DJANGO_SETTINGS_MODULE = core.settings +python_files = **/tests.py **/test_*.py **/*_test.py diff --git a/backend/requirements.txt b/backend/requirements.txt index c15be1a..41c9b7c 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -5,10 +5,17 @@ django-extensions==3.2.1 django-filter==22.1 djangorestframework==3.14.0 djangorestframework-simplejwt==5.2.2 -PyJWT==2.7.0 +exceptiongroup==1.1.2 inflection==0.5.1 -tzdata==2022.6 +iniconfig==2.0.0 +packaging==23.1 +pluggy==1.2.0 +PyJWT==2.7.0 +pytest==7.4.0 +pytest-django==4.5.2 python-dotenv==1.0.0 pytz==2023.3 sqlparse==0.4.4 +tomli==2.0.1 typing_extensions==4.6.3 +tzdata==2022.6 diff --git a/backend/tests.py b/backend/tests.py new file mode 100644 index 0000000..e69de29 From 18dbbd730f80a3e35445c4396468aec64f1b1309 Mon Sep 17 00:00:00 2001 From: Priyansh Mehta <96513964+Priyansh61@users.noreply.github.com> Date: Sun, 23 Jul 2023 02:14:35 +0530 Subject: [PATCH 5/8] feat (backend) : Added Product Models (#87) * Merge branch develop into Priyansh61/product-model * Done requested changes --- backend/core/settings.py | 3 ++ backend/core/urls.py | 4 +- backend/products/__init__.py | 0 backend/products/admin.py | 30 +++++++++++++++ backend/products/apps.py | 6 +++ backend/products/models.py | 73 ++++++++++++++++++++++++++++++++++++ backend/products/tests.py | 3 ++ backend/products/views.py | 3 ++ backend/requirements.txt | 1 + 9 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 backend/products/__init__.py create mode 100644 backend/products/admin.py create mode 100644 backend/products/apps.py create mode 100644 backend/products/models.py create mode 100644 backend/products/tests.py create mode 100644 backend/products/views.py diff --git a/backend/core/settings.py b/backend/core/settings.py index 850861c..143d011 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -37,6 +37,7 @@ 'django_filters', 'core', 'accounts', + 'products', ] REST_FRAMEWORK = { @@ -137,3 +138,5 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' REST_FRAMEWORK = {} +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') \ No newline at end of file diff --git a/backend/core/urls.py b/backend/core/urls.py index 51d142c..2d79c10 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -1,9 +1,11 @@ from django.contrib import admin from django.urls import path from django.conf.urls import include +from django.conf.urls.static import static +from django.conf import settings urlpatterns = [ path('admin/', admin.site.urls), path('api/token/', include('auth.urls')), path('api/accounts/', include('accounts.urls')), -] +]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/backend/products/__init__.py b/backend/products/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/products/admin.py b/backend/products/admin.py new file mode 100644 index 0000000..4e7f4d7 --- /dev/null +++ b/backend/products/admin.py @@ -0,0 +1,30 @@ +from django.contrib import admin +from .models import Product,ProductVariation,ProductColor,ProductSize,ProductImage + +# Register your models here. + +class ProductAdmin(admin.ModelAdmin) : + list_display = ("name","description","category",'primary_variant',"created_at","updated_at") + fields = ("name","description","category","primary_variant",) + +class ProductVariationAdmin(admin.ModelAdmin) : + list_display = ("Product","ProductSize","ProductColor","price","quantity") + +class ProductColorAdmin(admin.ModelAdmin) : + list_display = ("color",) + +class ProductSizeAdmin(admin.ModelAdmin) : + list_display = ("size",) + +class ProductImageAdmin(admin.ModelAdmin) : + list_display = ("product_variation_id","image") + + +admin.site.register(Product,ProductAdmin) +admin.site.register(ProductVariation,ProductVariationAdmin) +admin.site.register(ProductColor,ProductColorAdmin) +admin.site.register(ProductSize,ProductSizeAdmin) +admin.site.register(ProductImage,ProductImageAdmin) + + + diff --git a/backend/products/apps.py b/backend/products/apps.py new file mode 100644 index 0000000..145a2ac --- /dev/null +++ b/backend/products/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ProductsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'products' diff --git a/backend/products/models.py b/backend/products/models.py new file mode 100644 index 0000000..5dfe54e --- /dev/null +++ b/backend/products/models.py @@ -0,0 +1,73 @@ +from django.db import models + +# Create your models here. + +category_choices = [ + ('clothing','Clothing'), + ('rsvp','RSVP'), +] + +class Product(models.Model): + name = models.CharField(max_length=200) + description = models.TextField(max_length=600, + blank=True) + category = models.CharField(max_length=100, + choices=category_choices) + primary_variant = models.OneToOneField( + 'ProductVariation', + on_delete=models.CASCADE, + default=None, + null=True, + blank=True,) + + # Need to add Primary Variant and seller Id + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.name + + +class ProductVariation(models.Model): + Product = models.ForeignKey(Product, + on_delete=models.CASCADE, + related_name='product_variations') + ProductColor = models.ForeignKey('ProductColor', + on_delete=models.CASCADE) + ProductSize = models.ForeignKey('ProductSize', + on_delete=models.CASCADE) + price = models.IntegerField() + quantity = models.IntegerField() + cover_image = models.ForeignKey('ProductImage', + on_delete=models.CASCADE, + default=None, null=True, + blank=True,) + is_active = models.BooleanField(default=True) + + def __str__(self): + return f'{self.Product.name} {self.ProductColor.color} {self.ProductSize.size}' + + +class ProductColor(models.Model): + color = models.CharField(max_length=100, unique=True) + + def __str__(self): + return self.color + +class ProductSize(models.Model): + size = models.CharField(max_length=100, unique=True) + + def __str__(self): + return self.size + + +class ProductImage(models.Model): + product_variation_id = models.ForeignKey(ProductVariation, + on_delete=models.CASCADE, + related_name='product_images') + image = models.ImageField(upload_to='product_images') + + required = ['image'] + + def __str__(self): + return f'{self.product_variation_id.Product.name} {self.product_variation_id.ProductColor.color} {self.product_variation_id.ProductSize.size}' diff --git a/backend/products/tests.py b/backend/products/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/products/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/products/views.py b/backend/products/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/backend/products/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/backend/requirements.txt b/backend/requirements.txt index 41c9b7c..dea3ff6 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -15,6 +15,7 @@ pytest==7.4.0 pytest-django==4.5.2 python-dotenv==1.0.0 pytz==2023.3 +Pillow==10.0.0 sqlparse==0.4.4 tomli==2.0.1 typing_extensions==4.6.3 From 53b051e6eb750c656cb00c8762ccafc525734ff7 Mon Sep 17 00:00:00 2001 From: Priyansh61 <21bec080@iiitdmj.ac.in> Date: Thu, 16 Nov 2023 18:22:36 +0530 Subject: [PATCH 6/8] Added Evnet Model Signed-off-by: Priyansh61 <21bec080@iiitdmj.ac.in> --- backend/accounts/serializers.py | 2 +- backend/core/settings.py | 1 + backend/core/urls.py | 1 + backend/events/__init__.py | 0 backend/events/admin.py | 11 +++++++++ backend/events/apps.py | 6 +++++ backend/events/models.py | 41 +++++++++++++++++++++++++++++++++ backend/events/serializers.py | 12 ++++++++++ backend/events/tests.py | 3 +++ backend/events/urls.py | 10 ++++++++ backend/events/views.py | 9 ++++++++ 11 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 backend/events/__init__.py create mode 100644 backend/events/admin.py create mode 100644 backend/events/apps.py create mode 100644 backend/events/models.py create mode 100644 backend/events/serializers.py create mode 100644 backend/events/tests.py create mode 100644 backend/events/urls.py create mode 100644 backend/events/views.py diff --git a/backend/accounts/serializers.py b/backend/accounts/serializers.py index 9b1c643..286ef96 100644 --- a/backend/accounts/serializers.py +++ b/backend/accounts/serializers.py @@ -19,7 +19,7 @@ class Meta: 'date_joined': {'read_only': True}, 'last_login': {'read_only': True}, } - + def create(self, validated_data): password = validated_data.pop('password', None) diff --git a/backend/core/settings.py b/backend/core/settings.py index 143d011..db8236c 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -38,6 +38,7 @@ 'core', 'accounts', 'products', + 'events', ] REST_FRAMEWORK = { diff --git a/backend/core/urls.py b/backend/core/urls.py index 2d79c10..99ffd8e 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -8,4 +8,5 @@ path('admin/', admin.site.urls), path('api/token/', include('auth.urls')), path('api/accounts/', include('accounts.urls')), + path('api/events/', include('events.urls')), ]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/backend/events/__init__.py b/backend/events/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/events/admin.py b/backend/events/admin.py new file mode 100644 index 0000000..54a36c8 --- /dev/null +++ b/backend/events/admin.py @@ -0,0 +1,11 @@ +from django.contrib import admin +from .models import Event +# Register your models here. + + +class EventAdmin(admin.ModelAdmin) : + list_display = ('title', 'date', 'time', 'location', 'ticket_price', 'event_type') + search_fields = ('title', 'description', 'location', 'email') + list_filter = ('event_type', 'date', 'time') + +admin.site.register(Event, EventAdmin) \ No newline at end of file diff --git a/backend/events/apps.py b/backend/events/apps.py new file mode 100644 index 0000000..20f48f2 --- /dev/null +++ b/backend/events/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class EventsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'events' diff --git a/backend/events/models.py b/backend/events/models.py new file mode 100644 index 0000000..3390580 --- /dev/null +++ b/backend/events/models.py @@ -0,0 +1,41 @@ +from django.db import models + +# Create your models here. + +event_type_choices = [ + ('virtual','Virtual'), + ('physical','Physical'), +] +class Event (models.Model) : + title = models.CharField(max_length=200) + organizer_id = models.OneToOneField( + 'accounts.Account', + on_delete=models.CASCADE, + default=None, + null=True, + blank=True, + ) + description = models.TextField(max_length=600, + blank=True) + email = models.EmailField(max_length=254, + blank=True) + phone = models.CharField(max_length=15, blank=True) + date = models.DateField( + blank = False, + null = False, + ) + time = models.TimeField( + blank = False, + null = False, + ) + event_type = models.CharField(max_length=100, + choices=event_type_choices) + + location = models.CharField(max_length=200) + ticket_price = models.DecimalField(max_digits=10, decimal_places=2) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + + def __str__(self): + return self.title \ No newline at end of file diff --git a/backend/events/serializers.py b/backend/events/serializers.py new file mode 100644 index 0000000..f08eb3a --- /dev/null +++ b/backend/events/serializers.py @@ -0,0 +1,12 @@ +from rest_framework import serializers +from .models import Event + +class EventSerializer(serializers.ModelSerializer) : + class Meta: + model = Event + fields = '__all__' + extra_kwargs = { + 'created_at': {'read_only': True}, + 'updated_at': {'read_only': True}, + } + \ No newline at end of file diff --git a/backend/events/tests.py b/backend/events/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/events/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/events/urls.py b/backend/events/urls.py new file mode 100644 index 0000000..d21fb85 --- /dev/null +++ b/backend/events/urls.py @@ -0,0 +1,10 @@ +from django.urls import path, include +from .views import EventViewSet +from rest_framework.routers import DefaultRouter + +router = DefaultRouter() +router.register('', EventViewSet, basename='event') + +urlpatterns = [ + path('', include(router.urls)), +] diff --git a/backend/events/views.py b/backend/events/views.py new file mode 100644 index 0000000..0237e77 --- /dev/null +++ b/backend/events/views.py @@ -0,0 +1,9 @@ +from rest_framework import viewsets +from .models import Event +from .serializers import EventSerializer + +# Create your views here. + +class EventViewSet(viewsets.ModelViewSet): + queryset = Event.objects.all() + serializer_class = EventSerializer From 5fc0da340c140f1efc2f23480ca821ca53360bf8 Mon Sep 17 00:00:00 2001 From: Priyansh61 <21bec080@iiitdmj.ac.in> Date: Thu, 16 Nov 2023 19:47:31 +0530 Subject: [PATCH 7/8] Added Tickets App Signed-off-by: Priyansh61 <21bec080@iiitdmj.ac.in> --- backend/core/settings.py | 1 + backend/core/urls.py | 1 + backend/tickets/__init__.py | 0 backend/tickets/admin.py | 11 ++++++++++ backend/tickets/apps.py | 6 ++++++ backend/tickets/models.py | 37 ++++++++++++++++++++++++++++++++++ backend/tickets/serializers.py | 8 ++++++++ backend/tickets/tests.py | 3 +++ backend/tickets/urls.py | 10 +++++++++ backend/tickets/views.py | 9 +++++++++ 10 files changed, 86 insertions(+) create mode 100644 backend/tickets/__init__.py create mode 100644 backend/tickets/admin.py create mode 100644 backend/tickets/apps.py create mode 100644 backend/tickets/models.py create mode 100644 backend/tickets/serializers.py create mode 100644 backend/tickets/tests.py create mode 100644 backend/tickets/urls.py create mode 100644 backend/tickets/views.py diff --git a/backend/core/settings.py b/backend/core/settings.py index db8236c..787c9e0 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -39,6 +39,7 @@ 'accounts', 'products', 'events', + 'tickets', ] REST_FRAMEWORK = { diff --git a/backend/core/urls.py b/backend/core/urls.py index 99ffd8e..7e2404b 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -9,4 +9,5 @@ path('api/token/', include('auth.urls')), path('api/accounts/', include('accounts.urls')), path('api/events/', include('events.urls')), + path('api/tickets/', include('tickets.urls')), ]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/backend/tickets/__init__.py b/backend/tickets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/tickets/admin.py b/backend/tickets/admin.py new file mode 100644 index 0000000..ea49674 --- /dev/null +++ b/backend/tickets/admin.py @@ -0,0 +1,11 @@ +from django.contrib import admin +from .models import Ticket + + +# Register your models here. +class TicketAdmin(admin.ModelAdmin) : + list_display = ('event', 'buyer', 'purchase_date', 'status') + search_fields = ('event', 'buyer', 'purchase_date', 'status') + list_filter = ('event', 'buyer', 'purchase_date', 'status') + +admin.site.register(Ticket, TicketAdmin) \ No newline at end of file diff --git a/backend/tickets/apps.py b/backend/tickets/apps.py new file mode 100644 index 0000000..45a7d76 --- /dev/null +++ b/backend/tickets/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class TicketsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'tickets' diff --git a/backend/tickets/models.py b/backend/tickets/models.py new file mode 100644 index 0000000..174229f --- /dev/null +++ b/backend/tickets/models.py @@ -0,0 +1,37 @@ +from django.db import models + +# Create your models here. + +status_enums = [ + ('pending','Pending'), + ('approved','Approved'), + ('rejected','Rejected'), + ('cancelled','Cancelled'), +] + +class Ticket (models.Model) : + event = models.ForeignKey( + 'events.Event', + on_delete=models.CASCADE, + default=None, + null=False, + blank=False, + ) + buyer = models.OneToOneField( + 'accounts.Account', + on_delete=models.CASCADE, + default=None, + null=False, + blank=False, + ) + response = models.JSONField(default=dict) + purchase_date = models.DateTimeField(auto_now_add=True) + status = models.CharField(max_length=100, + choices=status_enums) + + + + def __str__(self): + return f'{self.event.title} {self.buyer.first_name} {self.buyer.last_name}' + + diff --git a/backend/tickets/serializers.py b/backend/tickets/serializers.py new file mode 100644 index 0000000..a24727e --- /dev/null +++ b/backend/tickets/serializers.py @@ -0,0 +1,8 @@ +from rest_framework import serializers +from .models import Ticket + +class TicketSerializer(serializers.ModelSerializer) : + class Meta: + model = Ticket + fields = '__all__' + \ No newline at end of file diff --git a/backend/tickets/tests.py b/backend/tickets/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/tickets/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/tickets/urls.py b/backend/tickets/urls.py new file mode 100644 index 0000000..5dc91cc --- /dev/null +++ b/backend/tickets/urls.py @@ -0,0 +1,10 @@ +from django.urls import path,include +from rest_framework.routers import DefaultRouter +from .views import TicketViewSet + +router = DefaultRouter() +router.register('', TicketViewSet, basename='ticket') + +urlpatterns = [ + path('', include(router.urls)), +] \ No newline at end of file diff --git a/backend/tickets/views.py b/backend/tickets/views.py new file mode 100644 index 0000000..20851c4 --- /dev/null +++ b/backend/tickets/views.py @@ -0,0 +1,9 @@ +from rest_framework import viewsets +from .models import Ticket +from .serializers import TicketSerializer + +# Create your views here. + +class TicketViewSet(viewsets.ModelViewSet): + queryset = Ticket.objects.all() + serializer_class = TicketSerializer \ No newline at end of file From d9913ab31f195482b7e585118cfdabc614878d29 Mon Sep 17 00:00:00 2001 From: Priyansh61 <21bec080@iiitdmj.ac.in> Date: Thu, 16 Nov 2023 19:55:49 +0530 Subject: [PATCH 8/8] Added Event Form Model Signed-off-by: Priyansh61 <21bec080@iiitdmj.ac.in> --- backend/events/admin.py | 9 ++++++++- backend/events/models.py | 16 +++++++++++++++- backend/events/serializers.py | 6 +++++- backend/events/views.py | 6 +++++- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/backend/events/admin.py b/backend/events/admin.py index 54a36c8..b5019ee 100644 --- a/backend/events/admin.py +++ b/backend/events/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from .models import Event +from .models import Event, Event_Form # Register your models here. @@ -8,4 +8,11 @@ class EventAdmin(admin.ModelAdmin) : search_fields = ('title', 'description', 'location', 'email') list_filter = ('event_type', 'date', 'time') +class Event_FormAdmin(admin.ModelAdmin) : + list_display = ('event', 'form_fields') + search_fields = ('event', 'form_fields') + list_filter = ('event', 'form_fields') + +admin.site.register(Event_Form, Event_FormAdmin) + admin.site.register(Event, EventAdmin) \ No newline at end of file diff --git a/backend/events/models.py b/backend/events/models.py index 3390580..2b93ad6 100644 --- a/backend/events/models.py +++ b/backend/events/models.py @@ -38,4 +38,18 @@ class Event (models.Model) : def __str__(self): - return self.title \ No newline at end of file + return self.title + + +class Event_Form (models.Model) : + event = models.OneToOneField( + 'events.Event', + on_delete=models.CASCADE, + default=None, + null=False, + blank=False, + ) + form_fields = models.JSONField(default=dict) + + def __str__(self): + return self.event.title \ No newline at end of file diff --git a/backend/events/serializers.py b/backend/events/serializers.py index f08eb3a..7c13a69 100644 --- a/backend/events/serializers.py +++ b/backend/events/serializers.py @@ -9,4 +9,8 @@ class Meta: 'created_at': {'read_only': True}, 'updated_at': {'read_only': True}, } - \ No newline at end of file + +class EventFormSerializer(serializers.ModelSerializer) : + class Meta: + model = Event + fields = '__all__' diff --git a/backend/events/views.py b/backend/events/views.py index 0237e77..29cca53 100644 --- a/backend/events/views.py +++ b/backend/events/views.py @@ -1,9 +1,13 @@ from rest_framework import viewsets from .models import Event -from .serializers import EventSerializer +from .serializers import EventSerializer, EventFormSerializer # Create your views here. class EventViewSet(viewsets.ModelViewSet): queryset = Event.objects.all() serializer_class = EventSerializer + +class EventFormViewSet(viewsets.ModelViewSet): + queryset = Event.objects.all() + serializer_class = EventFormSerializer