From 0da3ec4446f98d7d8417d351516a38f74ae412d0 Mon Sep 17 00:00:00 2001 From: "ahmetbahadircol@gmail.com" Date: Thu, 16 Dec 2021 15:29:16 +0300 Subject: [PATCH 1/4] Added register url to add new user to Customers --- ecommerce/customers/serializers.py | 10 +++++++++- ecommerce/customers/views.py | 20 +++++++++++++++++++- ecommerce/ecommerce/urls.py | 3 ++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/ecommerce/customers/serializers.py b/ecommerce/customers/serializers.py index ffad735..2d96994 100644 --- a/ecommerce/customers/serializers.py +++ b/ecommerce/customers/serializers.py @@ -20,6 +20,13 @@ class Meta: fields = ("first_name", "last_name", "email") +class RegisterSerializer(serializers.ModelSerializer): + + class Meta: + model = Customer + fields = ("first_name", "last_name", "email") + + class CountrySerializer(serializers.ModelSerializer): class Meta: model = Country @@ -55,7 +62,8 @@ def validate(self, attrs): return validated_data - def validate_full_name(self, value): + @staticmethod + def validate_full_name(value): if len(value) < 10: raise ValidationError(detail=_("Full name length must be bigger than 10")) return value diff --git a/ecommerce/customers/views.py b/ecommerce/customers/views.py index e2d05b6..2dbf845 100644 --- a/ecommerce/customers/views.py +++ b/ecommerce/customers/views.py @@ -1,3 +1,4 @@ +from django.contrib.auth import logout from django.shortcuts import get_object_or_404 from rest_framework import viewsets, permissions, mixins from rest_framework.viewsets import GenericViewSet @@ -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): @@ -65,4 +66,21 @@ def get_queryset(self): return queryset.filter(customer=user) +class RegisterViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, GenericViewSet): + """ + Register new customer endpoint + """ + queryset = Customer.objects.all() + serializer_class = RegisterSerializer + permission_classes = () + http_method_names = ["get", "put"] + def get_object(self): + """ + Each customer must see own profile + :return: Customer Object + """ + queryset = self.get_queryset() + filter_kwargs = {"id": self.request.user.id} + obj = get_object_or_404(queryset, **filter_kwargs) + return obj diff --git a/ecommerce/ecommerce/urls.py b/ecommerce/ecommerce/urls.py index 28278ef..3d269aa 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, RegisterViewSet from ecommerce.router import router from orders.views import OrderItemViewSet, OrderViewSet, BillingAddressViewSet, ShippingAddressViewSet, \ OrderBankAccountViewSet @@ -55,6 +55,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/', RegisterViewSet.as_view({"put": "create", "get": "retrieve"}), name='register') ] if settings.DEBUG: From 7bf753a53d20c9d7d61040f89dab6173b0a251b9 Mon Sep 17 00:00:00 2001 From: "ahmetbahadircol@gmail.com" Date: Thu, 16 Dec 2021 16:48:02 +0300 Subject: [PATCH 2/4] Customized password field in serialzer and viewset --- ecommerce/customers/serializers.py | 25 ++++++++++++++++++++++++- ecommerce/customers/views.py | 1 + 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/ecommerce/customers/serializers.py b/ecommerce/customers/serializers.py index 2d96994..73cbd48 100644 --- a/ecommerce/customers/serializers.py +++ b/ecommerce/customers/serializers.py @@ -1,3 +1,4 @@ +from django.contrib.auth.password_validation import validate_password from django.utils.translation import gettext_lazy as _ from django.db.transaction import atomic from rest_framework import serializers @@ -6,6 +7,7 @@ from customers.models import Customer, Address, City, Country + class CustomerSerializer(serializers.ModelSerializer): class Meta: @@ -21,10 +23,30 @@ class Meta: class RegisterSerializer(serializers.ModelSerializer): + """ + Register Serializer for create new user + """ + password = serializers.CharField( + write_only=True, required=True, style={'input_type': 'password', 'placeholder': 'Password'}, + validators=[validate_password]) + password2 = serializers.CharField(write_only=True, required=True) + + def create(self, validated_data): + + customer = Customer.objects.create( + first_name=validated_data['first_name'], + last_name=validated_data['last_name'], + email=validated_data['email'], + ) + customer.set_password(validated_data['password']) + customer.save() + + return customer class Meta: model = Customer - fields = ("first_name", "last_name", "email") + fields = ("first_name", "last_name", "email", "password", "password2") + class CountrySerializer(serializers.ModelSerializer): @@ -76,3 +98,4 @@ class AddressDetailedSerializer(AddressSerializer): class CityDetailedSerializer(CitySerializer): country = CountrySerializer() + diff --git a/ecommerce/customers/views.py b/ecommerce/customers/views.py index 2dbf845..92c9db1 100644 --- a/ecommerce/customers/views.py +++ b/ecommerce/customers/views.py @@ -84,3 +84,4 @@ def get_object(self): filter_kwargs = {"id": self.request.user.id} obj = get_object_or_404(queryset, **filter_kwargs) return obj + From 7f5dc05dba440f332056f8b84eae30d7b949aa37 Mon Sep 17 00:00:00 2001 From: "ahmetbahadircol@gmail.com" Date: Sat, 18 Dec 2021 02:20:04 +0300 Subject: [PATCH 3/4] Users can see their own baskets. Register endpoint has update feature now. Basket endpoint has not finished yet. --- ecommerce/baskets/views.py | 18 ++++++++++++++++-- ecommerce/customers/serializers.py | 18 +++++++++--------- ecommerce/customers/views.py | 3 ++- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/ecommerce/baskets/views.py b/ecommerce/baskets/views.py index ee32571..d0fb085 100644 --- a/ecommerce/baskets/views.py +++ b/ecommerce/baskets/views.py @@ -1,9 +1,12 @@ -from rest_framework import viewsets +from rest_framework import viewsets, permissions +from rest_framework.generics import get_object_or_404 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 +from rest_framework.decorators import action class BasketItemViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): @@ -17,6 +20,7 @@ class BasketItemViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): class BasketViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): + permission_classes = () queryset = Basket.objects.all() serializer_class = BasketSerializer filterset_class = BasketFilter @@ -24,3 +28,13 @@ class BasketViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): "detailed_list": BasketDetailedSerializer, "detailed": BasketDetailedSerializer, } + + def get_object(self): + """ + Each customer must see own Basket + :return: Basket Object + """ + queryset = self.get_queryset() + filter_kwargs = {"id": self.request.user.id} + obj = get_object_or_404(queryset, **filter_kwargs) + return obj diff --git a/ecommerce/customers/serializers.py b/ecommerce/customers/serializers.py index 73cbd48..a623745 100644 --- a/ecommerce/customers/serializers.py +++ b/ecommerce/customers/serializers.py @@ -7,16 +7,13 @@ 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") @@ -32,10 +29,9 @@ class RegisterSerializer(serializers.ModelSerializer): password2 = serializers.CharField(write_only=True, required=True) def create(self, validated_data): - customer = Customer.objects.create( - first_name=validated_data['first_name'], - last_name=validated_data['last_name'], + first_name=validated_data.get('first_name'), + last_name=validated_data.get('last_name'), email=validated_data['email'], ) customer.set_password(validated_data['password']) @@ -43,12 +39,18 @@ def create(self, validated_data): return customer + def update(self, instance, validated_data): + instance.first_name = validated_data.get('first_name', instance.first_name) + instance.last_name = validated_data.get('last_name', instance.last_name) + instance.email = validated_data.get('email', instance.email) + instance.save() + return instance + class Meta: model = Customer fields = ("first_name", "last_name", "email", "password", "password2") - class CountrySerializer(serializers.ModelSerializer): class Meta: model = Country @@ -56,7 +58,6 @@ class Meta: class CitySerializer(serializers.ModelSerializer): - class Meta: model = City fields = ("id", "name", "country") @@ -98,4 +99,3 @@ class AddressDetailedSerializer(AddressSerializer): class CityDetailedSerializer(CitySerializer): country = CountrySerializer() - diff --git a/ecommerce/customers/views.py b/ecommerce/customers/views.py index 92c9db1..c0eb649 100644 --- a/ecommerce/customers/views.py +++ b/ecommerce/customers/views.py @@ -52,6 +52,7 @@ class CityViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): class AddressViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): + permission_classes = (permissions.IsAuthenticated,) queryset = Address.objects.all() serializer_class = AddressSerializer filterset_class = AddressFilter @@ -73,7 +74,7 @@ class RegisterViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, Generi queryset = Customer.objects.all() serializer_class = RegisterSerializer permission_classes = () - http_method_names = ["get", "put"] + http_method_names = ["get", "put", "patch", "options"] def get_object(self): """ From c3516085890123c7ac66d9d7013fcba71c9b49a4 Mon Sep 17 00:00:00 2001 From: "ahmetbahadircol@gmail.com" Date: Sat, 18 Dec 2021 13:33:22 +0300 Subject: [PATCH 4/4] Basket.basket is blank = True now. Addded add_product action endpoint to create new basket/basketitem --- ecommerce/baskets/models.py | 2 +- ecommerce/baskets/views.py | 57 +++++++++++++++++++++++++++++++------ 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/ecommerce/baskets/models.py b/ecommerce/baskets/models.py index 4f109d5..107fe17 100644 --- a/ecommerce/baskets/models.py +++ b/ecommerce/baskets/models.py @@ -28,7 +28,7 @@ class BasketItem(BaseAbstractModel): """ Basket item model """ - basket = models.ForeignKey(Basket, verbose_name=_("Basket"), on_delete=models.PROTECT) + basket = models.ForeignKey(Basket, verbose_name=_("Basket"), on_delete=models.PROTECT, blank=True) product = models.ForeignKey(Product, verbose_name=_("Product"), on_delete=models.PROTECT) quantity = models.PositiveIntegerField(verbose_name=_("Quantity")) price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name=_("Price")) diff --git a/ecommerce/baskets/views.py b/ecommerce/baskets/views.py index d0fb085..8789a2b 100644 --- a/ecommerce/baskets/views.py +++ b/ecommerce/baskets/views.py @@ -1,5 +1,5 @@ -from rest_framework import viewsets, permissions -from rest_framework.generics import get_object_or_404 +from rest_framework import viewsets +from rest_framework.response import Response from baskets.filters import BasketItemFilter, BasketFilter from baskets.models import BasketItem, Basket @@ -8,8 +8,12 @@ from core.mixins import DetailedViewSetMixin from rest_framework.decorators import action +from customers.models import Customer + class BasketItemViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): + permission_classes = () + http_method_names = ["get"] queryset = BasketItem.objects.all() serializer_class = BasketItemSerializer filterset_class = BasketItemFilter @@ -18,23 +22,58 @@ class BasketItemViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): "detailed": BasketItemDetailedSerializer, } + def get_queryset(self): + queryset = super().get_queryset() + user = self.request.user.id + return queryset.filter(basket__customer__id=user) + class BasketViewSet(DetailedViewSetMixin, viewsets.ModelViewSet): permission_classes = () + http_method_names = ["get", "post", "options"] queryset = Basket.objects.all() serializer_class = BasketSerializer filterset_class = BasketFilter serializer_action_classes = { "detailed_list": BasketDetailedSerializer, "detailed": BasketDetailedSerializer, + "add_product": BasketItemSerializer, } - def get_object(self): + def get_queryset(self): + queryset = super().get_queryset() + user = self.request.user.id + return queryset.filter(basket__customer__id=user) + + @action(detail=False, methods=["post", "options"]) + def add_product(self, request, pk=None, *args, **kwargs): """ - Each customer must see own Basket - :return: Basket Object + Create a new endpoint for adding product to basket item + Check if Basket is exist otherwise create a new one + :param request: request + :param pk: None + :param args: + :param kwargs: + :return: Dictionary """ - queryset = self.get_queryset() - filter_kwargs = {"id": self.request.user.id} - obj = get_object_or_404(queryset, **filter_kwargs) - return obj + serializer = BasketItemSerializer(data=request.data) + if serializer.is_valid(): + user = self.request.user.id + customer_obj = Customer.objects.filter(id=user).first() + product = serializer.validated_data.get("product") + quantity = serializer.validated_data.get("quantity") + price = serializer.validated_data.get("price") + basket = Basket.objects.filter(customer=customer_obj, status="open").first() + if not basket: + basket = Basket.objects.create(customer=customer_obj, status="open") + basket_item = BasketItem.objects.filter(basket__customer=user, product=product, price=float(str(price))) + if not basket_item: + basket_item = BasketItem.objects.create(basket=basket, product=product, quantity=quantity, price=float(str(price))) + else: + old_qty = basket_item[0].quantity + new_qty = old_qty + float(quantity) + basket_item[0].quantity = new_qty + basket_item[0].save() + serializer_detailed_data = BasketItemDetailedSerializer(basket_item[0]).data + return Response(serializer_detailed_data) +