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 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..ac70e2d 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,23 @@ 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) + 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..7210faa 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): @@ -32,15 +33,25 @@ def get_object(self): return obj -class CountryViewSet(viewsets.ModelViewSet): +# 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 = ( + 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 +62,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 +76,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: 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