From e06e13f99c8dafb3e19900ec94c35c85be2731ae Mon Sep 17 00:00:00 2001 From: Oz Onuk Date: Sun, 19 Dec 2021 02:03:14 +0000 Subject: [PATCH 1/5] README.md updated --- ecommerce/README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ecommerce/README.md b/ecommerce/README.md index 83c3722..9bac9de 100644 --- a/ecommerce/README.md +++ b/ecommerce/README.md @@ -20,4 +20,14 @@ It contains billing address model, shipping address model, order bank account mo ### Payments -It contains bank model and bank account model. \ No newline at end of file +It contains bank model and bank account model. + +## Customer Registration +To register a new customer go to endpoint at + + /api/register/ + +## Add to Basket +To add a product into your basket go to endpoint at + + api/baskets/add_to_basket/ \ No newline at end of file From ea09dbdcf46f7532c176e85be34e7c15961c48e8 Mon Sep 17 00:00:00 2001 From: Oz Onuk Date: Sun, 19 Dec 2021 02:04:29 +0000 Subject: [PATCH 2/5] Customer Registration endpoint implemented --- ecommerce/customers/serializers.py | 20 +++++++++++++++++--- ecommerce/customers/views.py | 21 +++++++++++++++------ ecommerce/ecommerce/settings.py | 13 ++++--------- ecommerce/ecommerce/urls.py | 4 ++-- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/ecommerce/customers/serializers.py b/ecommerce/customers/serializers.py index ffad735..5b247c8 100644 --- a/ecommerce/customers/serializers.py +++ b/ecommerce/customers/serializers.py @@ -1,24 +1,39 @@ +from django.contrib.auth.password_validation import validate_password +from django.core.validators import EmailValidator from django.utils.translation import gettext_lazy as _ from django.db.transaction import atomic from rest_framework import serializers from rest_framework.exceptions import ValidationError +from rest_framework.validators import UniqueValidator from customers.models import Customer, Address, City, Country class CustomerSerializer(serializers.ModelSerializer): - class Meta: model = Customer fields = ("id", "first_name", "last_name", "email", "is_staff", "is_active", "date_joined") class ProfileSerializer(serializers.ModelSerializer): - class Meta: model = Customer fields = ("first_name", "last_name", "email") +# Serializer used for customer registration purposes. Does email and password validation +class RegisterSerializer(serializers.ModelSerializer): + email = serializers.EmailField(label=_("Email"), write_only=True, required=True, validators=[ + EmailValidator, UniqueValidator(queryset=Customer.objects.all())]) + password = serializers.CharField(label=_("Password"), write_only=True, required=True, validators=[validate_password]) + + class Meta: + model = Customer + fields = ("first_name", "last_name", "email", "password") + + def create(self, validated_data): + new_customer = Customer.objects.create_user(**validated_data) + return new_customer + class CountrySerializer(serializers.ModelSerializer): class Meta: @@ -27,7 +42,6 @@ class Meta: class CitySerializer(serializers.ModelSerializer): - class Meta: model = City fields = ("id", "name", "country") diff --git a/ecommerce/customers/views.py b/ecommerce/customers/views.py index e2d05b6..4508342 100644 --- a/ecommerce/customers/views.py +++ b/ecommerce/customers/views.py @@ -1,5 +1,6 @@ from django.shortcuts import get_object_or_404 from rest_framework import viewsets, permissions, mixins +from rest_framework.generics import CreateAPIView from rest_framework.viewsets import GenericViewSet from core.mixins import DetailedViewSetMixin @@ -8,7 +9,7 @@ from customers.models import Customer, Address, City, Country from customers.serializers import CustomerSerializer, AddressSerializer, CitySerializer, \ CountrySerializer, \ - AddressDetailedSerializer, CityDetailedSerializer, ProfileSerializer + AddressDetailedSerializer, CityDetailedSerializer, ProfileSerializer, RegisterSerializer class AdminCustomerViewSet(viewsets.ModelViewSet): @@ -31,16 +32,25 @@ def get_object(self): obj = get_object_or_404(queryset, **filter_kwargs) return obj +# End point for customer registration +class RegisterCustomerViewSet(viewsets.ModelViewSet): + permission_classes = () + http_method_names = ['post'] + queryset = Customer.objects.all() + serializer_class = RegisterSerializer + class CountryViewSet(viewsets.ModelViewSet): - permission_classes = () + permission_classes = ( + permissions.IsAuthenticated,) queryset = Country.objects.all() serializer_class = CountrySerializer filterset_class = CountryFilter class CityViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): - permission_classes = () + permission_classes = ( + permissions.IsAuthenticated,) queryset = City.objects.all() serializer_class = CitySerializer filterset_class = CityFilter @@ -51,6 +61,8 @@ class CityViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): class AddressViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): + permission_classes = ( + permissions.IsAuthenticated,) queryset = Address.objects.all() serializer_class = AddressSerializer filterset_class = AddressFilter @@ -63,6 +75,3 @@ def get_queryset(self): queryset = super().get_queryset() user = self.request.user return queryset.filter(customer=user) - - - diff --git a/ecommerce/ecommerce/settings.py b/ecommerce/ecommerce/settings.py index 594485d..51c257c 100644 --- a/ecommerce/ecommerce/settings.py +++ b/ecommerce/ecommerce/settings.py @@ -32,7 +32,6 @@ ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=[]) - # Application definition INSTALLED_APPS = [ @@ -54,9 +53,9 @@ ] REST_FRAMEWORK = { - 'DEFAULT_PERMISSION_CLASSES': [ - 'rest_framework.permissions.IsAuthenticated', - ], + #'DEFAULT_PERMISSION_CLASSES': [ + # 'rest_framework.permissions.IsAuthenticated', + #], 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'], 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.SessionAuthentication', @@ -101,13 +100,11 @@ WSGI_APPLICATION = 'ecommerce.wsgi.application' - # Database # https://docs.djangoproject.com/en/3.2/ref/settings/#databases DATABASES = {'default': env.db('DATABASE_URL')} - # Password validation # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators @@ -126,7 +123,6 @@ }, ] - # Internationalization # https://docs.djangoproject.com/en/3.2/topics/i18n/ @@ -151,7 +147,6 @@ def gettext_noop(s): USE_TZ = True - # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.2/howto/static-files/ @@ -164,4 +159,4 @@ def gettext_noop(s): DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' AUTH_USER_MODEL = "customers.Customer" -LOCALE_PATHS = (str(BASE_DIR / "locale/"), ) +LOCALE_PATHS = (str(BASE_DIR / "locale/"),) diff --git a/ecommerce/ecommerce/urls.py b/ecommerce/ecommerce/urls.py index 28278ef..5a0d28e 100644 --- a/ecommerce/ecommerce/urls.py +++ b/ecommerce/ecommerce/urls.py @@ -21,7 +21,7 @@ from baskets.views import BasketItemViewSet, BasketViewSet from core.views import APITokenObtainPairView from customers.views import AddressViewSet, CityViewSet, \ - CountryViewSet, AdminCustomerViewSet, MyProfileViewSet + CountryViewSet, AdminCustomerViewSet, MyProfileViewSet, RegisterCustomerViewSet from ecommerce.router import router from orders.views import OrderItemViewSet, OrderViewSet, BillingAddressViewSet, ShippingAddressViewSet, \ OrderBankAccountViewSet @@ -47,7 +47,6 @@ router.register("admin-products", AdminProductViewSet, basename="admin-product") router.register("admin-customers", AdminCustomerViewSet, basename="admin-customer") - urlpatterns = [ path("api/", include(router.urls)), path('admin/', admin.site.urls), @@ -55,6 +54,7 @@ path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), path('api/profile/', MyProfileViewSet.as_view( {"get": "retrieve", "put": "update", "patch": "partial_update"}), name='profile'), + path('api/register/', RegisterCustomerViewSet.as_view({"post": "create"}), name='register_customer'), ] if settings.DEBUG: From 3ab00282e894eba0909c178aa1373ca79e0704ee Mon Sep 17 00:00:00 2001 From: Oz Onuk Date: Sun, 19 Dec 2021 02:05:14 +0000 Subject: [PATCH 3/5] End point for add to basket feature implemented --- ecommerce/baskets/serializers.py | 2 - ecommerce/baskets/views.py | 66 +++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/ecommerce/baskets/serializers.py b/ecommerce/baskets/serializers.py index 9dc4c30..90239f4 100644 --- a/ecommerce/baskets/serializers.py +++ b/ecommerce/baskets/serializers.py @@ -6,14 +6,12 @@ class BasketItemSerializer(serializers.ModelSerializer): - class Meta: model = BasketItem fields = ("id", "basket", "product", "quantity", "price") class BasketSerializer(serializers.ModelSerializer): - class Meta: model = Basket fields = ("id", "customer", "status") diff --git a/ecommerce/baskets/views.py b/ecommerce/baskets/views.py index ee32571..f96259c 100644 --- a/ecommerce/baskets/views.py +++ b/ecommerce/baskets/views.py @@ -1,12 +1,17 @@ -from rest_framework import viewsets +from rest_framework import viewsets, status +from rest_framework.decorators import action +from rest_framework.response import Response from baskets.filters import BasketItemFilter, BasketFilter from baskets.models import BasketItem, Basket -from baskets.serializers import BasketItemSerializer, BasketSerializer, BasketItemDetailedSerializer, BasketDetailedSerializer +from baskets.serializers import BasketItemSerializer, BasketSerializer, BasketItemDetailedSerializer, \ + BasketDetailedSerializer from core.mixins import DetailedViewSetMixin class BasketItemViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): + http_method_names = ["get", "post"] + permission_classes = () queryset = BasketItem.objects.all() serializer_class = BasketItemSerializer filterset_class = BasketItemFilter @@ -15,6 +20,13 @@ class BasketItemViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): "detailed": BasketItemDetailedSerializer, } + def get_queryset(self): + queryset = super().get_queryset() + user = self.request.user + if user.is_staff: # to enable admin to see all baskets items. + return queryset + return queryset.filter(basket__customer=user) + class BasketViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): queryset = Basket.objects.all() @@ -23,4 +35,54 @@ class BasketViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): serializer_action_classes = { "detailed_list": BasketDetailedSerializer, "detailed": BasketDetailedSerializer, + "add_to_basket": BasketSerializer, } + + def get_queryset(self): + queryset = super().get_queryset() + user = self.request.user + + if user.is_staff: # to enable admin to see all baskets. + return queryset + + return queryset.filter(customer=user) + + @action(detail=False, methods=['post']) + def add_to_basket(self, request): + serializer = BasketItemSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + """ + @action(detail=False, methods=['post', 'get'], http_method_names = ['get', 'post']) + def add_basket_item(self, request): + + if self.request.method == 'GET': + return self.list(request) + elif self.request.method == 'POST': + serializer = BasketItemSerializer(data=request.data) + if serializer.is_valid(): + user_id = self.request.user.id + product = serializer.validated_data.get("product") + quantity = serializer.validated_data.get("quantity") + price = serializer.validated_data.get("price") + basket = Basket.objects.filter(customer__id=user_id, status="open").first() + + if not basket: + basket = Basket.objects.create(customer__id=user_id, status="open") + + basket_item = BasketItem.objects.filter(basket__customer__id=user__id, product=product, + price=float(str(price))).first() + if not basket_item: + basket_item = BasketItem.objects.create( + basket=basket, product=product, quantity=quantity, price=float(str(price)) + ) + else: + basket_item.quantity += float(quantity) + basket.item.save() + serializer_detailed_data = BasketItemDetailedSerializer(basket_item).data + return Response(dict(serializer_detailed_data)) + + +""" From 6abff7dfa9e185a7ddf0eded9738115a469b414c Mon Sep 17 00:00:00 2001 From: Oz Onuk Date: Sun, 19 Dec 2021 02:06:16 +0000 Subject: [PATCH 4/5] permissions get_queryset added for necessary viewsets --- ecommerce/orders/views.py | 27 ++++++++++++++++++++++++++- ecommerce/payments/views.py | 6 +++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/ecommerce/orders/views.py b/ecommerce/orders/views.py index dde7137..944aa43 100644 --- a/ecommerce/orders/views.py +++ b/ecommerce/orders/views.py @@ -1,4 +1,4 @@ -from rest_framework import viewsets +from rest_framework import viewsets, permissions from core.mixins import DetailedViewSetMixin from orders.filters import OrderItemFilter, OrderFilter, BillingAddressFilter, ShippingAddressFilter, \ @@ -10,6 +10,8 @@ class OrderItemViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): + permission_classes = ( + permissions.IsAuthenticated,) queryset = OrderItem.objects.all() serializer_class = OrderItemSerializer filterset_class = OrderItemFilter @@ -18,8 +20,15 @@ class OrderItemViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): "detailed": OrderItemDetailedSerializer, } + def get_queryset(self): + queryset = super().get_queryset() + user = self.request.user + return queryset.filter(order__customer=user) + class OrderViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): + permission_classes = ( + permissions.IsAuthenticated,) queryset = Order.objects.all() serializer_class = OrderSerializer filterset_class = OrderFilter @@ -28,8 +37,15 @@ class OrderViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): "detailed": OrderDetailedSerializer, } + def get_queryset(self): + queryset = super().get_queryset() + user = self.request.user + return queryset.filter(customer=user) + class BillingAddressViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): + permission_classes = ( + permissions.IsAuthenticated,) queryset = BillingAddress.objects.all() serializer_class = BillingAddressSerializer filterset_class = BillingAddressFilter @@ -40,6 +56,8 @@ class BillingAddressViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): class ShippingAddressViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): + permission_classes = ( + permissions.IsAuthenticated,) queryset = ShippingAddress.objects.all() serializer_class = ShippingAddressSerializer filterset_class = ShippingAddressFilter @@ -50,6 +68,8 @@ class ShippingAddressViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): class OrderBankAccountViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): + permission_classes = ( + permissions.IsAuthenticated,) queryset = OrderBankAccount.objects.all() serializer_class = OrderBankAccountSerializer filterset_class = OrderBankAccountFilter @@ -57,3 +77,8 @@ class OrderBankAccountViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): "detailed_list": OrderBankAccountDetailedSerializer, "detailed": OrderBankAccountDetailedSerializer, } + + def get_queryset(self): + queryset = super().get_queryset() + user = self.request.user + return queryset.filter(customer=user) diff --git a/ecommerce/payments/views.py b/ecommerce/payments/views.py index 09b28a7..297f094 100644 --- a/ecommerce/payments/views.py +++ b/ecommerce/payments/views.py @@ -1,4 +1,4 @@ -from rest_framework import viewsets +from rest_framework import viewsets, permissions from core.mixins import DetailedViewSetMixin from payments.filters import BankAccountFilter, BankFilter @@ -7,6 +7,8 @@ class BankAccountViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): + permission_classes = ( + permissions.IsAuthenticated,) queryset = BankAccount.objects.all() serializer_class = BankAccountSerializer filterset_class = BankAccountFilter @@ -17,6 +19,8 @@ class BankAccountViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): class BankViewSet(viewsets.ModelViewSet): + permission_classes = ( + permissions.IsAuthenticated,) queryset = Bank.objects.all() serializer_class = BankSerializer filterset_class = BankFilter From 44aab1557360e6492f7aece3970617d96a809331 Mon Sep 17 00:00:00 2001 From: Oz Onuk Date: Sun, 19 Dec 2021 02:12:43 +0000 Subject: [PATCH 5/5] Unnecessary comments cleaned --- ecommerce/baskets/views.py | 31 ------------------------------- ecommerce/customers/views.py | 1 + 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/ecommerce/baskets/views.py b/ecommerce/baskets/views.py index f96259c..ac70e2d 100644 --- a/ecommerce/baskets/views.py +++ b/ecommerce/baskets/views.py @@ -54,35 +54,4 @@ def add_to_basket(self, request): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - """ - @action(detail=False, methods=['post', 'get'], http_method_names = ['get', 'post']) - def add_basket_item(self, request): - - if self.request.method == 'GET': - return self.list(request) - elif self.request.method == 'POST': - serializer = BasketItemSerializer(data=request.data) - if serializer.is_valid(): - user_id = self.request.user.id - product = serializer.validated_data.get("product") - quantity = serializer.validated_data.get("quantity") - price = serializer.validated_data.get("price") - basket = Basket.objects.filter(customer__id=user_id, status="open").first() - if not basket: - basket = Basket.objects.create(customer__id=user_id, status="open") - - basket_item = BasketItem.objects.filter(basket__customer__id=user__id, product=product, - price=float(str(price))).first() - if not basket_item: - basket_item = BasketItem.objects.create( - basket=basket, product=product, quantity=quantity, price=float(str(price)) - ) - else: - basket_item.quantity += float(quantity) - basket.item.save() - serializer_detailed_data = BasketItemDetailedSerializer(basket_item).data - return Response(dict(serializer_detailed_data)) - - -""" diff --git a/ecommerce/customers/views.py b/ecommerce/customers/views.py index 4508342..7210faa 100644 --- a/ecommerce/customers/views.py +++ b/ecommerce/customers/views.py @@ -32,6 +32,7 @@ def get_object(self): obj = get_object_or_404(queryset, **filter_kwargs) return obj + # End point for customer registration class RegisterCustomerViewSet(viewsets.ModelViewSet): permission_classes = ()