Skip to content

Commit

Permalink
Merge pull request #16 from Abramov0Alexandr/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Abramov0Alexandr authored Sep 29, 2023
2 parents 242dabe + 1780793 commit ae012bc
Show file tree
Hide file tree
Showing 39 changed files with 1,236 additions and 6 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
141 changes: 140 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,140 @@
# marketplace-API-template
# marketplace-API-template

Данное проект предназначен для реализации HTTP запросов и направлен на использование в маркетплейсах.

## Стек технологий:
- python
- django
- djangorestframework
- python-dotenv
- psycopg2-binary
- djangorestframework-simplejwt
- drf-yasg
- coverage
- flake8
- django-extensions
- ipython
- django-filter
- pillow
- pyparsing
- pydot
- django-cors-headers


## Установка
Прежде чем начать использовать Marketplace API, убедитесь, что у вас установлен
интерпретатор Python c версией не ниже 3.9:

Клонируйте репозиторий с помощью следующей команды:
```bash
git clone git@github.com:Abramov0Alexandr/marketplace-API-template.git
```

Перейдите в директорию проекта:
```bash
cd marketplace-API-template
```

Активируйте виртуальное окружение Poetry и установите зависимости:

```bash
poetry init
```

```bash
poetry shell
```

```bash
poetry install
```

Создайте и примените миграции для базы данных:

```bash
python manage.py migrate
```


Запустите сервер:
```bash
python manage.py runserver
```

Теперь приложение доступно по адресу http://localhost:8000/.


## Регистрация
Для регистрации в сервисе необходимо отправить POST-запрос на эндпоинт users/create/.
В зависимости от выбора регистрации пользователя необходимо передать следующие данные:

1. Регистрация как обычный пользователь:
- first_name: Ваше имя.
- last_name: Ваша фамилия.
- patronymic (необязательно): Ваше отчество (если есть).
- email: Ваша почта.
- password: Пароль для входа.
- password_confirmation: Подтверждение пароля.
- is_seller: Оставьте поле пустым или укажите false, чтобы зарегистрироваться как обычный пользователь.
- phone (необязательно): Номер телефона пользователя.


2. Регистрация в качестве продавца:
- email: Ваша почта.
- shop_name: Название магазина.
- shop_preview (необязательно): Лого или превью магазина.
- password: Пароль для входа.
- password_confirmation: Подтверждение пароля.
- is_seller: Укажите true, чтобы зарегистрироваться как продавец.


## Возможности API
Наш проект предоставляет следующие возможности через API:

1. Идентификация и авторизация:
- Регистрация новых пользователей (как обычных, так и продавцов).
- Авторизация пользователей.
- Получение JWT-токенов для аутентификации.


2. Создание и управление товарами:
- Создание нового товара.
- Просмотр списка товаров.
- Просмотр детальной информации о товаре.
- Редактирование и удаление товаров (доступно только продавцам).
- Снятие или вывод товара в продажу (доступно только продавцам).


## Схемы моделей и их взаимосвязи
Полная схема моделей

![Полная схема моделей](models_schemes/full_scheme.png)

Схема кастомных моделей

![Схема кастомных моделей](models_schemes/my_project_subsystem.png)

## Документация
Документацию к API вы можете найти перейдя по следующим ссылкам:<br>
http://127.0.0.1:8000/swagger/ <br>
http://127.0.0.1:8000/redoc/

## Тестирование
Для запуска тестов используйте следующую команду:

```bash
python manage.py test
```

## Лицензия
Marketplace API распространяется по [MIT License](https://opensource.org/licenses/MIT).

## Контакты

Спасибо за использование Marketplace API! Если у вас есть какие-либо вопросы или предложения, не стесняйтесь обращаться к нам.

Автор: [Alexandr Abramov <https://github.com/Abramov0Alexandr>]

Связь: [alexandr.abramovv@gmail.com <https://github.com/Abramov0Alexandr>]

GitHub: [https://github.com/Abramov0Alexandr]
33 changes: 30 additions & 3 deletions config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,14 @@
'django.contrib.messages',
'django.contrib.staticfiles',

'rest_framework',
'django_extensions',
'django_filters',
'drf_yasg',
'corsheaders',

'custom_user.apps.CustomUserConfig',
'products.apps.ProductsConfig',
]


Expand All @@ -56,16 +61,24 @@
# Setting the permission policy
# https://www.django-rest-framework.org/api-guide/permissions/#setting-the-permission-policy

# Integration with Django Rest Framework is provided through a DRF-specific FilterSet and a filter backend.
# These may be found in the rest_framework sub-package.
# https://django-filter.readthedocs.io/en/stable/guide/rest_framework.html

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
),

'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.IsAuthenticated',
'rest_framework.permissions.AllowAny',
]
'rest_framework.permissions.IsAuthenticated',
# 'rest_framework.permissions.AllowAny',
],

'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend',
),
}

# Some of Simple JWT’s behavior can be customized through settings variables in settings.py
Expand All @@ -80,6 +93,7 @@
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
Expand Down Expand Up @@ -140,6 +154,19 @@
]


# CORS integration
# https://pypi.org/project/django-cors-headers/#description (CSRF Integration)

CORS_ALLOWED_ORIGINS = [
"https://read-only.example.com",
"https://read-and-write.example.com",
]

CSRF_TRUSTED_ORIGINS = [
"https://read-and-write.example.com",
]


# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/

Expand Down
25 changes: 24 additions & 1 deletion config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,37 @@
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from django.urls import path, include
from drf_yasg import openapi
from drf_yasg.views import get_schema_view
from rest_framework import permissions
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView


schema_view = get_schema_view(
openapi.Info(
title="Marketplace API template",
default_version='v1',
description="Here you can find all the information about requests and check them",
terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(url="https://github.com/Abramov0Alexandr"),
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=(permissions.AllowAny,),
)


urlpatterns = [
path('admin/', admin.site.urls),
path('users/', include('custom_user.urls', namespace='custom_user')),
path('products/', include('products.urls', namespace='products')),

# authorization urls
path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),

# documentation
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]
Empty file added custom_user/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions custom_user/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.contrib import admin
from custom_user.models import CustomUser


@admin.register(CustomUser)
class UserAdmin(admin.ModelAdmin):
list_display = ('id', 'email', 'first_name', 'last_name', 'shop_name', 'is_seller', )
list_display_links = ('email', )
list_filter = ('is_seller', )
7 changes: 7 additions & 0 deletions custom_user/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.apps import AppConfig


class CustomUserConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'custom_user'
verbose_name = 'Зарегистрированные пользователи'
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()),
],
),
]
Empty file.
45 changes: 45 additions & 0 deletions custom_user/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
from custom_user.services import shop_preview_upload_path
from custom_user.user_manager import CustomUserManager


NULLABLE = {'blank': True, 'null': True}


class CustomUser(AbstractUser):
"""
Расширение стандартной модели пользователя в соответствии с требованиями текущего проекта.
"""

class UserStatus(models.IntegerChoices):
"""
Вспомогательный класс для определения статуса посетителя магазина.
По умолчанию, каждый новый пользователь имеет статус 'Посетитель'.
"""

COMMON_USER = 0, "Посетитель"
SELLER = 1, "Продавец"

username = None

# Поля, используемые для регистрации посетителя сайта
first_name = models.CharField(max_length=150, verbose_name='Имя', **NULLABLE)
last_name = models.CharField(max_length=150, verbose_name='Фамилия', **NULLABLE)
patronymic = models.CharField(max_length=150, verbose_name='Отчество', **NULLABLE)
phone = models.CharField(max_length=20, verbose_name='Телефон', **NULLABLE)

# Поля, используемые для регистрации продавца
shop_name = models.CharField(max_length=200, verbose_name='Название магазина', **NULLABLE)
shop_preview = models.ImageField(upload_to=shop_preview_upload_path, verbose_name='Превью магазина', **NULLABLE)

# Общие поля для каждого вида пользователя
email = models.EmailField(unique=True, verbose_name='Email')
is_active = models.BooleanField(default=True, verbose_name='Статус активации')
is_seller = models.BooleanField(choices=UserStatus.choices, default=UserStatus.COMMON_USER,
verbose_name='Статус клиента')

objects = CustomUserManager()

USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
Loading

0 comments on commit ae012bc

Please sign in to comment.