Skip to content

Commit

Permalink
Merge pull request #15 from Abramov0Alexandr/feature
Browse files Browse the repository at this point in the history
Feature
  • Loading branch information
Abramov0Alexandr authored Sep 29, 2023
2 parents 6793d60 + e0d867b commit 1780793
Show file tree
Hide file tree
Showing 16 changed files with 403 additions and 15 deletions.
10 changes: 10 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[flake8]

max-line-length = 120

exclude =
# The autogenerated migrations data, ignore it
migrations

# Excluding virtual env folders
.venv
48 changes: 48 additions & 0 deletions custom_user/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Generated by Django 4.2.5 on 2023-09-28 22:09

import custom_user.services
import custom_user.user_manager
from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

initial = True

dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]

operations = [
migrations.CreateModel(
name='CustomUser',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('first_name', models.CharField(blank=True, max_length=150, null=True, verbose_name='Имя')),
('last_name', models.CharField(blank=True, max_length=150, null=True, verbose_name='Фамилия')),
('patronymic', models.CharField(blank=True, max_length=150, null=True, verbose_name='Отчество')),
('phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='Телефон')),
('shop_name', models.CharField(blank=True, max_length=200, null=True, verbose_name='Название магазина')),
('shop_preview', models.ImageField(blank=True, null=True, upload_to=custom_user.services.shop_preview_upload_path, verbose_name='Превью магазина')),
('email', models.EmailField(max_length=254, unique=True, verbose_name='Email')),
('is_active', models.BooleanField(default=True, verbose_name='Статус активации')),
('is_seller', models.BooleanField(choices=[(0, 'Посетитель'), (1, 'Продавец')], default=0, verbose_name='Статус клиента')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', custom_user.user_manager.CustomUserManager()),
],
),
]
4 changes: 2 additions & 2 deletions custom_user/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def create(self, validated_data):
:return: Создается новый экземпляр класса CustomUser.
"""

password_confirmation = validated_data.pop('password_confirmation', None)
validated_data.pop('password_confirmation', None)
new_common_user = CustomUser.objects.create_user(**validated_data)
return new_common_user

Expand All @@ -63,6 +63,6 @@ class Meta:
validators = [PasswordValidation()]

def create(self, validated_data):
password_confirmation = validated_data.pop('password_confirmation', None)
validated_data.pop('password_confirmation', None)
new_seller = CustomUser.objects.create_user(**validated_data)
return new_seller
File renamed without changes.
60 changes: 60 additions & 0 deletions custom_user/tests/test_common_user_creation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from rest_framework import status
from rest_framework.reverse import reverse
from rest_framework.test import APITestCase


class CommonUserCreate(APITestCase):

def test_success_creation(self):
"""Тесткейс успешного создания нового пользователя"""

users_data: dict = {
"first_name": "Alex",
"last_name": "Abramov",
"email": "common@user.ru",
"phone": "800000000",
"password": "123654",
"password_confirmation": "123654"
}

response = self.client.post(reverse('custom_user:custom_user_create'), data=users_data)

self.assertEqual(response.status_code, 200)
self.assertEqual(response.json(),
{'message': 'Регистрация успешно завершена!',
'status': status.HTTP_201_CREATED})

def test_failed_password_confirmation(self):
"""Тесткейс при неверной передачи значения password_confirmation"""

users_data: dict = {
"first_name": "Ivan",
"last_name": "Ivanov",
"email": "common@user.ru",
"phone": "800000000",
"password": "123654",
"password_confirmation": "000000"
}

response = self.client.post(reverse('custom_user:custom_user_create'), data=users_data)

self.assertEqual(response.status_code, 400)
self.assertEqual(response.json(),
{'non_field_errors': ['Пароль и его подтверждение не совпадают']})

def test_no_password_confirmation(self):
"""Тесткейс при отсутствии значения password_confirmation"""

users_data: dict = {
"first_name": "Ivan",
"last_name": "Ivanov",
"email": "common@user.ru",
"phone": "800000000",
"password": "123654"
}

response = self.client.post(reverse('custom_user:custom_user_create'), data=users_data)

self.assertEqual(response.status_code, 400)
self.assertEqual(response.json(),
{'password_confirmation': ['Обязательное поле.']})
58 changes: 58 additions & 0 deletions custom_user/tests/test_seller_creation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from rest_framework import status
from rest_framework.reverse import reverse
from rest_framework.test import APITestCase


class CommonUserCreateTestCase(APITestCase):

def test_success_creation(self):
"""Тесткейс успешного создания нового продавца"""

seller_data: dict = {
"is_seller": True,
"shop_name": "ASOS",
"email": "asos@shop.com",
"password": "123654",
"password_confirmation": "123654"
}

response = self.client.post(reverse('custom_user:custom_user_create'), data=seller_data)

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.json(),
{'message': 'Вы зарегистрированы в качестве продавца и можете размещать товары на площадке',
'shop_title': 'ASOS',
'status': 201})

def test_failed_password_confirmation(self):
"""Тесткейс при неверной передачи значения password_confirmation"""

seller_data: dict = {
"is_seller": True,
"shop_name": "ASOS",
"email": "asos@shop.com",
"password": "123654",
"password_confirmation": "000000"
}

response = self.client.post(reverse('custom_user:custom_user_create'), data=seller_data)

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(),
{'non_field_errors': ['Пароль и его подтверждение не совпадают']})

def test_no_password_confirmation(self):
"""Тесткейс при отсутствии значения password_confirmation"""

seller_data: dict = {
"is_seller": True,
"shop_name": "ASOS",
"email": "asos@shop.com",
"password": "123654",
}

response = self.client.post(reverse('custom_user:custom_user_create'), data=seller_data)

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.json(),
{'password_confirmation': ['Обязательное поле.']})
5 changes: 0 additions & 5 deletions custom_user/user_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from django.apps import apps
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import UserManager

Expand All @@ -12,10 +11,6 @@ def _create_user(self, email, password, **extra_fields):
"""

email = self.normalize_email(email)

GlobalUserModel = apps.get_model(
self.model._meta.app_label, self.model._meta.object_name
)
user = self.model(email=email, **extra_fields)
user.password = make_password(password)
user.save(using=self._db)
Expand Down
8 changes: 4 additions & 4 deletions custom_user/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
from rest_framework.permissions import IsAdminUser, AllowAny
from rest_framework.response import Response
from custom_user.models import CustomUser
from custom_user.serializers import CustomersListSerializer, VisitorSerializer, SellerSerializer
from custom_user import serializers


class CustomersListView(generics.ListAPIView):
"""Контроллер для отображения списка зарегистрированных пользователей."""

serializer_class = CustomersListSerializer
serializer_class = serializers.CustomersListSerializer
queryset = CustomUser.objects.all()
filter_backends = [DjangoFilterBackend]
filterset_fields = ('is_seller', )
Expand All @@ -29,9 +29,9 @@ def get_serializer_class(self):
is_seller = self.request.data.get('is_seller', None)

if is_seller:
return SellerSerializer
return serializers.SellerSerializer

return VisitorSerializer
return serializers.VisitorSerializer

def create(self, request, *args, **kwargs):
is_seller = request.data.get('is_seller', False)
Expand Down
31 changes: 31 additions & 0 deletions products/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 4.2.5 on 2023-09-28 22:09

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='Product',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('product_title', models.CharField(max_length=255, verbose_name='Наименование товара')),
('price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Цена товара')),
('is_active_sale', models.BooleanField(choices=[(True, 'В продаже'), (False, 'Снят с продажи')], default=True, verbose_name='Статус товара')),
('seller', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='seller', to=settings.AUTH_USER_MODEL, verbose_name='Продавец')),
],
options={
'verbose_name': 'Товар',
'verbose_name_plural': 'Товары',
},
),
]
3 changes: 2 additions & 1 deletion products/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ class Product(models.Model):
]

product_title = models.CharField(max_length=255, verbose_name='Наименование товара')
seller = models.ForeignKey(get_user_model(), on_delete=models.PROTECT, related_name='seller', verbose_name='Продавец')
seller = models.ForeignKey(get_user_model(), on_delete=models.PROTECT,
related_name='seller', verbose_name='Продавец')
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='Цена товара')
is_active_sale = models.BooleanField(default=True, choices=SALE_STATUS, verbose_name='Статус товара')

Expand Down
3 changes: 0 additions & 3 deletions products/tests.py

This file was deleted.

Empty file added products/tests/__init__.py
Empty file.
40 changes: 40 additions & 0 deletions products/tests/test_change_sale_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from rest_framework import status
from rest_framework.test import APITestCase, APIClient
from custom_user.models import CustomUser
from products.models import Product


class ProductChangeSaleStatusTestCase(APITestCase):

def setUp(self) -> None:
"""Предварительное наполнение БД для дальнейших тестов."""

self.seller = CustomUser.objects.create(
is_seller=True,
shop_name='ASOS',
email='asos@shop.com',
password='123654'
)

self.product = Product.objects.create(
seller=self.seller,
product_title='Product Title',
price='1000.00'
)

"""Имитация авторизации пользователя по JWT токену."""
self.client = APIClient()
self.client.force_authenticate(user=self.seller)

def test_change_status(self):

response = self.client.patch(f'/products/change_status/{self.product.id}/')

self.assertEqual(response.status_code, status.HTTP_200_OK)

self.assertEqual(response.json(),
{"Product info": {
"sale status": "False",
"message": "Product Title снят с продажи",
"status": 200
}})
42 changes: 42 additions & 0 deletions products/tests/test_detail_product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from rest_framework import status
from rest_framework.test import APITestCase, APIClient
from custom_user.models import CustomUser
from products.models import Product


class ProductDetailTestCase(APITestCase):

def setUp(self) -> None:
"""Предварительное наполнение БД для дальнейших тестов."""

self.seller = CustomUser.objects.create(
is_seller=True,
shop_name='ASOS',
email='asos@shop.com',
password='123654'
)

self.product = Product.objects.create(
seller=self.seller,
product_title='Product Title',
price='1000.00'
)

"""Имитация авторизации пользователя по JWT токену."""
self.client = APIClient()
self.client.force_authenticate(user=self.seller)

def test_retrieve_product(self):

response = self.client.get(f'/products/detail/{self.product.id}/')

self.assertEqual(response.status_code, status.HTTP_200_OK)

self.assertEqual(response.json(),
{'id': self.product.id,
'shop_title': self.seller.shop_name,
'seller': self.seller.email,
'product_title': self.product.product_title,
'price': self.product.price,
'is_active_sale': True}
)
Loading

0 comments on commit 1780793

Please sign in to comment.