Стартавая модель задачника
This commit is contained in:
4
config/asgi.py
Normal file
4
config/asgi.py
Normal file
@@ -0,0 +1,4 @@
|
||||
import os
|
||||
from django.core.asgi import get_asgi_application
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
|
||||
application = get_asgi_application()
|
||||
48
config/local_settings.py_orig
Normal file
48
config/local_settings.py_orig
Normal file
@@ -0,0 +1,48 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from django.core.management.utils import get_random_secret_key
|
||||
|
||||
#BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
TEMPLATES_DIR = os.path.join(BASE_DIR, "templates")
|
||||
STATICFILES_DIR = os.path.join(BASE_DIR, "staticfiles")
|
||||
STATIC_DIR = os.path.join(BASE_DIR, "static")
|
||||
MEDIA_DIR = os.path.join(BASE_DIR, "media")
|
||||
LOGS_DIR = os.path.join(BASE_DIR, "logs")
|
||||
SECRET_KEY = '***********************'
|
||||
DEBUG = True
|
||||
#DEBUG = False
|
||||
ALLOWED_HOSTS = []
|
||||
# For SQLlITE3 database
|
||||
DB_CONFIG_SQLL = {
|
||||
'ENGINE': os.getenv('DB_ENGINE', 'django.db.backends.sqlite3'),
|
||||
'NAME': os.getenv('DB_NAME', 'db.sqlite3'),
|
||||
}
|
||||
# For PostgreSQL database (comment it if not needed)
|
||||
DB_CONFIG_PSQL = {
|
||||
'ENGINE': os.getenv('DB_ENGINE', 'django.db.backends.postgresql'),
|
||||
'HOST': os.getenv('DB_HOST', '127.0.0.1'),
|
||||
'PORT': os.getenv('DB_PORT', 5432),
|
||||
'NAME': os.getenv('DB_NAME', '_db'),
|
||||
'USER': os.getenv('DB_USER', 'admdb'),
|
||||
'PASSWORD': os.getenv('DB_PASS', '**********')
|
||||
}
|
||||
|
||||
CORS_ORIGIN_WHITELIST = [
|
||||
'http://localhost:8000',
|
||||
'https://localhost:8000',
|
||||
'http://localhost:8001',
|
||||
'https://localhost:8001',
|
||||
'http://127.0.0.1:8000',
|
||||
'https://127.0.0.1:8000',
|
||||
'http://127.0.0.1:8001',
|
||||
'https://127.0.0.1:8001',
|
||||
'https://google.com',
|
||||
'http://google.com',
|
||||
]
|
||||
CSRF_TRUSTED_ORIGINS = [
|
||||
'http://127.0.0.1:8001',
|
||||
'https://127.0.0.1:8001',
|
||||
'http://127.0.0.1:8000',
|
||||
'https://127.0.0.1:8000',
|
||||
]
|
||||
49
config/logging.py
Normal file
49
config/logging.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import os
|
||||
from config.local_settings import LOGS_DIR
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '{asctime} {levelname} {module} {process:d} {thread:d} '
|
||||
'{message}',
|
||||
'style': '{',
|
||||
},
|
||||
'simple': {
|
||||
'format': '{asctime} {levelname} {message}',
|
||||
'style': '{',
|
||||
},
|
||||
}, # formatters
|
||||
'handlers': {
|
||||
'console': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'simple',
|
||||
},
|
||||
'file': {
|
||||
'level': 'DEBUG',
|
||||
'formatter': 'verbose',
|
||||
'class': 'logging.handlers.TimedRotatingFileHandler',
|
||||
'filename': os.path.join(LOGS_DIR, "debug.log"),
|
||||
'when': 'midnight',
|
||||
'backupCount': 30,
|
||||
},
|
||||
}, # handlers
|
||||
'loggers': {
|
||||
'': { # root logger
|
||||
'handlers': ['console', 'file'],
|
||||
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO').upper(),
|
||||
},
|
||||
'customauth': {
|
||||
'handlers': ['console', 'file'],
|
||||
'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG').upper(),
|
||||
'propagate': False, # required to eliminate duplication on root
|
||||
},
|
||||
# 'app_name': {
|
||||
# 'handlers': ['console', 'file'],
|
||||
# 'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG').upper(),
|
||||
# 'propagate': False, # required to eliminate duplication on root
|
||||
# },
|
||||
}, # loggers
|
||||
} # logging
|
||||
184
config/settings.py
Normal file
184
config/settings.py
Normal file
@@ -0,0 +1,184 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from datetime import timedelta
|
||||
# Вставка из файла конфика, нужно данный файл вынесити за пределы проекта в ENV часть.
|
||||
from .local_settings import (
|
||||
SECRET_KEY,
|
||||
DEBUG,
|
||||
ALLOWED_HOSTS,
|
||||
DB_CONFIG,
|
||||
TEMPLATES_DIR,
|
||||
STATICFILES_DIR,
|
||||
STATIC_DIR,
|
||||
MEDIA_DIR,
|
||||
LOGS_DIR,
|
||||
CORS_ORIGIN_WHITELIST,
|
||||
CSRF_TRUSTED_ORIGINS
|
||||
)
|
||||
from .logging import LOGGING
|
||||
# Списки сокращений
|
||||
SETTINGS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
BASE_DIR = os.path.dirname(SETTINGS_DIR)
|
||||
TEMPLATES_DIR = os.getenv('TEMPLATES_DIR', TEMPLATES_DIR)
|
||||
STATICFILES_DIR = os.getenv('STATICFILES_DIR', STATICFILES_DIR)
|
||||
STATIC_DIR = os.getenv('STATIC_DIR', STATIC_DIR)
|
||||
MEDIA_DIR = os.getenv('MEDIA_DIR', MEDIA_DIR)
|
||||
LOGS_DIR = os.getenv('LOGS_DIR', LOGS_DIR)
|
||||
SECRET_KEY = SECRET_KEY
|
||||
DEBUG = DEBUG
|
||||
ALLOWED_HOSTS = ALLOWED_HOSTS
|
||||
# Определение приложения
|
||||
# Встроенные в framework
|
||||
DJANGO_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
]
|
||||
# Стронние приложения
|
||||
THIRD_PARTY_APPS = [
|
||||
'rest_framework',
|
||||
'rest_framework_simplejwt',
|
||||
'django_filters',
|
||||
'taggit',
|
||||
'drf_yasg',
|
||||
'corsheaders',
|
||||
]
|
||||
# Внетренние приложения
|
||||
LOCAL_APPS = [
|
||||
'todos.apps.TodosConfig',
|
||||
]
|
||||
# Сборщик всех приложений в один список
|
||||
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
ROOT_URLCONF = 'config.urls'
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [TEMPLATES_DIR, ],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
WSGI_APPLICATION = 'config.wsgi.application'
|
||||
# База данныех для изменеия используйте один теговое обозначение
|
||||
# DB_CONFIG_SQLL = SQLlITE3
|
||||
# DB_CONFIG_PSQL = PostgreSQL
|
||||
DATABASES = {
|
||||
'default': os.getenv('DB_CONFIG_SQLL', DB_CONFIG_SQLL)
|
||||
}
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',},
|
||||
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',},
|
||||
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',},
|
||||
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',},
|
||||
]
|
||||
LANGUAGE_CODE = 'ru-ru'
|
||||
TIME_ZONE = 'Europe/Moscow'
|
||||
USE_I18N = True
|
||||
USE_TZ = True
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
STATIC_URL = '/static/'
|
||||
STATIC_ROOT = STATIC_DIR
|
||||
STATICFILES_DIRS = [STATICFILES_DIR, ]
|
||||
MEDIA_URL = '/media/'
|
||||
MEDIA_ROOT = MEDIA_DIR
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
# Logging ---------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/5.0/topics/logging/
|
||||
if os.getenv('DISABLE_LOGGING', False): # только для celery в jenkins ci
|
||||
LOGGING_CONFIG = None
|
||||
LOGGING = LOGGING
|
||||
# REST API
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
|
||||
#'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
|
||||
#'PAGE_SIZE': 2,
|
||||
#ХЗ почему с этими параметрами не работает. Нужно более подробно изучить проблему.
|
||||
'DEFAULT_RENDERER_CLASSES': [
|
||||
'rest_framework.renderers.JSONRenderer',
|
||||
'rest_framework.renderers.BrowsableAPIRenderer',
|
||||
],
|
||||
'DEFAULT_PERMISSION_CLASSES': [
|
||||
'rest_framework.permissions.AllowAny',
|
||||
'rest_framework.permissions.IsAuthenticated',
|
||||
],
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||
'rest_framework_simplejwt.authentication.JWTAuthentication',
|
||||
'rest_framework.authentication.BasicAuthentication',
|
||||
'rest_framework.authentication.SessionAuthentication',
|
||||
]
|
||||
}
|
||||
"""
|
||||
# JWT token settings
|
||||
# По умолчанию был такой пример, узучить более подробно
|
||||
SIMPLE_JWT = {
|
||||
'ACCESS_TOKEN_LIFETIME': timedelta(days=7),
|
||||
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
|
||||
'ROTATE_REFRESH_TOKENS': True,
|
||||
'UPDATE_LAST_LOGIN': True,
|
||||
'AUTH_HEADER_TYPES': ('Token',)
|
||||
}
|
||||
"""
|
||||
SIMPLE_JWT = {
|
||||
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
|
||||
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
|
||||
'ROTATE_REFRESH_TOKENS': False,
|
||||
'BLACKLIST_AFTER_ROTATION': False,
|
||||
'UPDATE_LAST_LOGIN': False,
|
||||
|
||||
'ALGORITHM': 'HS256',
|
||||
'SIGNING_KEY': SECRET_KEY,
|
||||
'VERIFYING_KEY': None,
|
||||
'AUDIENCE': None,
|
||||
'ISSUER': None,
|
||||
'JWK_URL': None,
|
||||
'LEEWAY': 0,
|
||||
|
||||
'AUTH_HEADER_TYPES': ('JWT',),
|
||||
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
|
||||
'USER_ID_FIELD': 'id',
|
||||
'USER_ID_CLAIM': 'user_id',
|
||||
'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
|
||||
|
||||
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
|
||||
'TOKEN_TYPE_CLAIM': 'token_type',
|
||||
|
||||
'JTI_CLAIM': 'jti',
|
||||
|
||||
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
|
||||
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
|
||||
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
|
||||
}
|
||||
# https://whitenoise.readthedocs.io/en/stable/
|
||||
# Радикально упрощенное обслуживание статических файлов для веб-приложений Python.
|
||||
STORAGES = {
|
||||
'staticfiles': {
|
||||
'BACKEND': 'whitenoise.storage.CompressedManifestStaticFilesStorage',
|
||||
},
|
||||
}
|
||||
# приложение для обработки заголовков сервера, необходимых для совместного использования ресурсов между источниками (CORS)
|
||||
CORS_ORIGIN_ALLOW_ALL=True
|
||||
CORS_ORIGIN_WHITELIST = CORS_ORIGIN_WHITELIST
|
||||
CSRF_TRUSTED_ORIGINS = CSRF_TRUSTED_ORIGINS
|
||||
# При выходе из учётной записи вас направит на данный url
|
||||
LOGOUT_REDIRECT_URL = "main"
|
||||
35
config/urls.py
Normal file
35
config/urls.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
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, TokenVerifyView
|
||||
# Машиночитаемая [схема] описывает, какие ресурсы доступны через API
|
||||
schema_view = get_schema_view(
|
||||
openapi.Info(
|
||||
title="??? API",
|
||||
default_version='v1',
|
||||
description="??? API Documentation",
|
||||
),
|
||||
public=True,
|
||||
permission_classes=(permissions.AllowAny,),
|
||||
)
|
||||
# Префикс для понтов, можно без него
|
||||
api_prefix = 'api'
|
||||
# Кортеж адресов
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),# Адрес админ панели
|
||||
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-redoc'), # API сборник адресов
|
||||
path(f'{api_prefix}/api-auth/', include('rest_framework.urls')), # API rest, допилить виюшку
|
||||
path(f'{api_prefix}/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), # Присвоение токена
|
||||
path(f'{api_prefix}/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), # Перевыдача токена
|
||||
path(f'{api_prefix}/token/verify/', TokenVerifyView.as_view(), name='token_verify'), # Верифекация токена
|
||||
path(f'{api_prefix}/', include('todos.urls')),
|
||||
path(f'{api_prefix}/todo/', TodoAPIList.as_view()),
|
||||
]
|
||||
# Обслуживание файлов, загруженных пользователем во время разработки
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
4
config/wsgi.py
Normal file
4
config/wsgi.py
Normal file
@@ -0,0 +1,4 @@
|
||||
import os
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
|
||||
application = get_wsgi_application()
|
||||
19
manage.py
Normal file
19
manage.py
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
def main():
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
31
requirements.txt
Normal file
31
requirements.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
asgiref==3.7.2
|
||||
certifi==2023.11.17
|
||||
charset-normalizer==3.3.2
|
||||
coreapi==2.3.3
|
||||
coreschema==0.0.4
|
||||
Django==4.2.7
|
||||
django-cors-headers==4.3.1
|
||||
django-filter==23.5
|
||||
django-taggit==5.0.1
|
||||
djangorestframework==3.14.0
|
||||
djangorestframework-simplejwt==5.3.1
|
||||
drf-yasg==1.21.7
|
||||
idna==3.6
|
||||
inflection==0.5.1
|
||||
itypes==1.2.0
|
||||
Jinja2==3.1.3
|
||||
Markdown==3.5.2
|
||||
MarkupSafe==2.1.4
|
||||
packaging==23.2
|
||||
pillow==10.2.0
|
||||
psycopg2==2.9.9
|
||||
PyJWT==2.8.0
|
||||
pytz==2023.3.post1
|
||||
PyYAML==6.0.1
|
||||
requests==2.31.0
|
||||
sqlparse==0.4.4
|
||||
typing_extensions==4.9.0
|
||||
tzdata==2023.4
|
||||
uritemplate==4.1.1
|
||||
urllib3==2.1.0
|
||||
whitenoise==6.6.0
|
||||
62
system/accounts/admin.py
Normal file
62
system/accounts/admin.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# Системные приложения
|
||||
import logging
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from django.contrib.admin.models import LogEntry, DELETION
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.urls import reverse
|
||||
# Внутриние приложения
|
||||
from system.accounts.models import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class ProfileInline(admin.StackedInline):
|
||||
model = User
|
||||
can_delete = False
|
||||
max_num = 1
|
||||
verbose_name = 'Profile'
|
||||
verbose_name_plural = 'Profile'
|
||||
fk_name = 'user'
|
||||
|
||||
@admin.register(User)
|
||||
class UserAdmin(UserAdmin):
|
||||
ordering = ('email', )
|
||||
list_display = (
|
||||
'id', 'email', 'is_staff', 'is_superuser', 'is_active', 'date_joined',
|
||||
'last_updated', 'last_login'
|
||||
#'verified_email', 'accepted_terms', 'read_terms',
|
||||
)
|
||||
search_fields = ('email',)
|
||||
list_filter = (
|
||||
'is_staff', 'is_superuser', 'is_active',
|
||||
#'verified_email'
|
||||
)
|
||||
readonly_fields = (
|
||||
'last_login', 'last_updated', 'date_joined'
|
||||
#'email_token',
|
||||
)
|
||||
list_display_links = ('id', 'email')
|
||||
fieldsets = (
|
||||
(None, {'fields': ('email', 'password')}),
|
||||
('Permissions', {'fields': ('groups', 'user_permissions')}),
|
||||
('Roles', {'fields': ('is_staff', 'is_superuser', 'is_active')}),
|
||||
#('Additional', {'fields': (
|
||||
#'verified_email', 'email_token', 'accepted_terms', 'read_terms'
|
||||
#)}),
|
||||
('Profile', {'fields': (
|
||||
'first_name','last_name', 'gender', 'bio', 'image',
|
||||
'address', 'followers'
|
||||
)}),
|
||||
('Dates', {'fields': ('last_login', 'last_updated', 'date_joined')})
|
||||
)
|
||||
add_fieldsets = (
|
||||
(None, {
|
||||
'classes': ('wide', ),
|
||||
'fields': (
|
||||
'email', 'password1', 'password2'
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
#inlines = (ProfileInline, )
|
||||
7
system/accounts/apps.py
Normal file
7
system/accounts/apps.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class AccountsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'system.accounts'
|
||||
verbose_name = 'Управление пользователями'
|
||||
|
||||
68
system/accounts/models.py
Normal file
68
system/accounts/models.py
Normal file
@@ -0,0 +1,68 @@
|
||||
from __future__ import annotations
|
||||
from django.contrib.auth.models import AbstractUser, BaseUserManager
|
||||
from django.db import models
|
||||
# Управление учётными записями
|
||||
class UserManager(BaseUserManager):
|
||||
# Создание пользователя
|
||||
def create_user(
|
||||
self, email: str, password: str | None = None, **other_fields
|
||||
) -> User:
|
||||
user = User(email=email, **other_fields)
|
||||
if password:
|
||||
user.set_password(password)
|
||||
else:
|
||||
user.set_unusable_password()
|
||||
user.save()
|
||||
return user
|
||||
# Создание супер пользователя
|
||||
def create_superuser(self, email: str, password: str | None = None, **other_fields) -> User:
|
||||
other_fields.setdefault("is_staff", True)
|
||||
other_fields.setdefault("is_superuser", True)
|
||||
other_fields.setdefault("is_active", True)
|
||||
if other_fields.get("is_staff") is not True:
|
||||
raise ValueError("Superuser must be assigned to is_staff=True.")
|
||||
if other_fields.get("is_superuser") is not True:
|
||||
raise ValueError("Superuser must be assigned to is_superuser=True.")
|
||||
return self.create_user(email, password, **other_fields)
|
||||
# Модель учётной записи
|
||||
class User(AbstractUser):
|
||||
GENDER_CHOICES = (
|
||||
('n', 'Не указано'),
|
||||
('m', 'Мужчина'),
|
||||
('f', 'Женщина'),
|
||||
)
|
||||
first_name: str = models.CharField(max_length=60, null=True, blank=True, verbose_name='Имя')
|
||||
last_name: str = models.CharField(max_length=60, null=True, blank=True, verbose_name='Фамилия')
|
||||
gender: str = models.CharField(verbose_name='Пол', max_length=10, choices=GENDER_CHOICES, default='n')
|
||||
email: str = models.EmailField(verbose_name='Email Address', unique=True)
|
||||
username: str = models.CharField(max_length=60, verbose_name='Ник')
|
||||
bio: str = models.TextField(blank=True, verbose_name='О себе')
|
||||
image: str | None = models.URLField(null=True, blank=True, verbose_name='Аватар')
|
||||
|
||||
followers = models.ManyToManyField("self", blank=True, symmetrical=False, verbose_name='Подписчики')
|
||||
|
||||
address: str = models.CharField(verbose_name='Адрес', max_length=255, blank=True, null=True)
|
||||
#avatar = models.ImageField(upload_to=avatar_upload_path, null=True, blank=True)
|
||||
#country = models.ForeignKey('chatroom.Country', on_delete=models.SET_NULL, null=True)
|
||||
#state = models.ForeignKey('chatroom.State', on_delete=models.SET_NULL, null=True)
|
||||
date_joined = models.DateTimeField(verbose_name='Дата регистрации', blank=True, null=True, auto_now_add=True)
|
||||
last_updated = models.DateTimeField(verbose_name='Последнее обновление', blank=True, null=True, auto_now=True)
|
||||
last_login = models.DateTimeField(verbose_name='Последняя авторизация', blank=True, null=True, auto_now=True)
|
||||
|
||||
EMAIL_FIELD = "email"
|
||||
USERNAME_FIELD = "email"
|
||||
REQUIRED_FIELDS: list[str] = []
|
||||
|
||||
objects = UserManager()
|
||||
|
||||
def get_full_name(self) -> str:
|
||||
if self.first_name and self.last_name:
|
||||
return f"{self.first_name} {self.last_name}"
|
||||
else:
|
||||
return self.username
|
||||
|
||||
def get_short_name(self) -> str:
|
||||
if self.first_name and self.last_name:
|
||||
return f"{self.first_name[0]}{self.last_name}"
|
||||
else:
|
||||
return self.username
|
||||
40
system/accounts/serializers.py
Normal file
40
system/accounts/serializers.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from rest_framework import serializers
|
||||
from system.accounts.models import User
|
||||
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('username', 'email', 'first_name', 'last_name', 'password', 'bio', 'image')
|
||||
extra_kwargs = {'password': {'write_only': True}}
|
||||
|
||||
def create(self, validated_data):
|
||||
password = validated_data.pop('password')
|
||||
user = User(
|
||||
**validated_data
|
||||
)
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
return user
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
for key, value in validated_data.items():
|
||||
if key == 'password':
|
||||
instance.set_password(value)
|
||||
else:
|
||||
setattr(instance, key, value)
|
||||
instance.save()
|
||||
return instance
|
||||
|
||||
|
||||
class ProfileSerializer(serializers.ModelSerializer):
|
||||
following = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('username', 'first_name', 'last_name', 'bio', 'image', 'following')
|
||||
|
||||
def get_following(self, obj):
|
||||
user = self.context.get('request').user
|
||||
if user.is_authenticated:
|
||||
return obj.followers.filter(pk=user.id).exists()
|
||||
return False
|
||||
2
system/accounts/tests.py
Normal file
2
system/accounts/tests.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from django.test import TestCase
|
||||
# Создайте первуый тест
|
||||
18
system/accounts/urls.py
Normal file
18
system/accounts/urls.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from django.contrib.auth.views import LogoutView
|
||||
from django.contrib.auth import views
|
||||
from django.urls import path
|
||||
from system.accounts import views
|
||||
|
||||
profile_router = DefaultRouter(trailing_slash=False)
|
||||
profile_router.register('profiles', views.ProfileDetailView)
|
||||
|
||||
urlpatterns = [
|
||||
path('users/login', views.account_login, name='account-login'),
|
||||
path('users', views.account_registration, name="account-registration"),
|
||||
path('user', views.UserView.as_view(), name='user-account'),
|
||||
path('signout/', views.signout, name='signout'),
|
||||
path('logout/', views.signout, name='logout'),
|
||||
path('', include(profile_router.urls))
|
||||
]
|
||||
135
system/accounts/views.py
Normal file
135
system/accounts/views.py
Normal file
@@ -0,0 +1,135 @@
|
||||
from rest_framework.decorators import api_view, action
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status, views, viewsets
|
||||
from django.contrib.auth import authenticate
|
||||
from django.shortcuts import redirect
|
||||
from django.contrib.auth import logout
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
|
||||
|
||||
from system.accounts.models import User
|
||||
from system.accounts.serializers import UserSerializer, ProfileSerializer
|
||||
|
||||
|
||||
@api_view(['POST', ])
|
||||
def account_registration(request):
|
||||
try:
|
||||
user_data = request.data.get('user')
|
||||
|
||||
serializer = UserSerializer(data=user_data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
return Response({"user": serializer.data}, status=status.HTTP_201_CREATED)
|
||||
|
||||
except Exception:
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@api_view(['POST', ])
|
||||
def account_login(request):
|
||||
try:
|
||||
user_data = request.data.get('user')
|
||||
user = authenticate(email=user_data['email'], password=user_data['password'])
|
||||
serializer = UserSerializer(user)
|
||||
jwt_token = RefreshToken.for_user(user)
|
||||
serializer_data = serializer.data
|
||||
serializer_data['token'] = str(jwt_token.access_token)
|
||||
response_data = {
|
||||
"user": serializer_data,
|
||||
}
|
||||
return Response(response_data, status=status.HTTP_202_ACCEPTED)
|
||||
|
||||
except Exception:
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class UserView(views.APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get(self, request, format=None):
|
||||
user = self.request.user
|
||||
serializer = UserSerializer(user)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
def put(self, request, format=None, pk=None):
|
||||
user = self.request.user
|
||||
user_data = request.data.get('user')
|
||||
|
||||
user.email = user_data['email']
|
||||
user.bio = user_data['bio']
|
||||
user.image = user_data['image']
|
||||
user.save()
|
||||
|
||||
serializer = UserSerializer(user)
|
||||
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class ProfileDetailView(viewsets.ModelViewSet):
|
||||
queryset = User.objects.all()
|
||||
serializer_class = ProfileSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
lookup_field = 'username'
|
||||
http_method_names = ['get', 'post', 'delete']
|
||||
|
||||
def get_permissions(self):
|
||||
if self.action == 'list':
|
||||
return [IsAuthenticatedOrReadOnly(), ]
|
||||
return super().get_permissions()
|
||||
|
||||
def list(self, request, username=None, *args, **kwargs):
|
||||
try:
|
||||
profile = User.objects.get(username=username)
|
||||
serializer = self.get_serializer(profile)
|
||||
return Response({"profile": serializer.data})
|
||||
|
||||
except Exception:
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"Invalid User"
|
||||
]
|
||||
}})
|
||||
|
||||
@action(detail=True, methods=['post', 'delete'])
|
||||
def follow(self, request, username=None, *args, **kwargs):
|
||||
if request.method == 'POST':
|
||||
|
||||
profile = self.get_object()
|
||||
follower = request.user
|
||||
if profile == follower:
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"Invalid follow Request"
|
||||
]
|
||||
}}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
profile.followers.add(follower)
|
||||
serializer = self.get_serializer(profile)
|
||||
return Response({"profile": serializer.data})
|
||||
|
||||
elif request.method == 'DELETE':
|
||||
|
||||
profile = self.get_object()
|
||||
follower = request.user
|
||||
if profile == follower:
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"Invalid follow Request"
|
||||
]
|
||||
}}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if not profile.followers.filter(pk=follower.id).exists():
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"Invalid follow Request"
|
||||
]
|
||||
}}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
profile.followers.remove(follower)
|
||||
serializer = self.get_serializer(profile)
|
||||
return Response({"profile": serializer.data})
|
||||
|
||||
|
||||
def signout(request):
|
||||
logout(request)
|
||||
return redirect("main")
|
||||
10
system/main/admin.py
Normal file
10
system/main/admin.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.apps import apps
|
||||
from django.contrib import admin
|
||||
#
|
||||
models = apps.get_models()
|
||||
# код что отображает все модели всех apps
|
||||
for model in models:
|
||||
try:
|
||||
admin.site.register(model)
|
||||
except admin.sites.AlreadyRegistered:
|
||||
pass
|
||||
6
system/main/apps.py
Normal file
6
system/main/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class MainConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'system.main'
|
||||
verbose_name = 'Главное приложение'
|
||||
2
system/main/models.py
Normal file
2
system/main/models.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from django.db import models
|
||||
# Создайте первую модель
|
||||
2
system/main/tests.py
Normal file
2
system/main/tests.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from django.test import TestCase
|
||||
# Создайте первуый тест
|
||||
0
system/main/urls.py
Normal file
0
system/main/urls.py
Normal file
8
system/main/views.py
Normal file
8
system/main/views.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import View
|
||||
# Простое отображение страницы заглушки.
|
||||
class MainView(View):
|
||||
template_name = 'index.html'
|
||||
|
||||
def get(self, request):
|
||||
return render(request, self.template_name)
|
||||
2
todos/admin.py
Normal file
2
todos/admin.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from django.contrib import admin
|
||||
#
|
||||
7
todos/apps.py
Normal file
7
todos/apps.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.apps import AppConfig
|
||||
# Настрока аппаса
|
||||
class TodosConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'todos'
|
||||
verbose_name = 'Ежедневник'
|
||||
|
||||
111
todos/models.py
Normal file
111
todos/models.py
Normal file
@@ -0,0 +1,111 @@
|
||||
import markdown
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from taggit.managers import TaggableManager
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.utils.text import slugify
|
||||
from django.urls import reverse
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
TASK_STATUS = (
|
||||
('N', 'Новая'),
|
||||
('D', 'Удалено'),
|
||||
('S', 'Ожидание'),
|
||||
('Y', 'Выполнена'),
|
||||
)
|
||||
|
||||
PRIORITY = (
|
||||
('L', 'Низкий'),
|
||||
('W', 'Средний'),
|
||||
('H', 'Высокий'),
|
||||
)
|
||||
|
||||
class TodoQuerySet(models.QuerySet):
|
||||
def with_favorites(self, user: AnonymousUser | User) -> models.QuerySet:
|
||||
return self.annotate(
|
||||
num_favorites=models.Count("favorites"),
|
||||
# true if user is authenticated
|
||||
is_favorite=models.Exists(
|
||||
get_user_model().objects.filter(
|
||||
pk=user.id, favorites=models.OuterRef("pk")
|
||||
),
|
||||
)
|
||||
if user.is_authenticated
|
||||
else models.Value(False, output_field=models.BooleanField()),
|
||||
)
|
||||
|
||||
|
||||
TodoManager = models.Manager.from_queryset(TodoQuerySet)
|
||||
|
||||
|
||||
class Todo(models.Model):
|
||||
author = models.ForeignKey(settings.User, on_delete=models.CASCADE)
|
||||
title = models.CharField(max_length=150, unique=True, verbose_name='Задача')
|
||||
summary = models.TextField(blank=True, verbose_name='Краткое описание')
|
||||
content = models.TextField(blank=True, verbose_name='Описание')
|
||||
#
|
||||
is_active = models.BooleanField(default=True, verbose_name='Активная')
|
||||
is_deleted = models.BooleanField(default=False, verbose_name='Удаление')
|
||||
priority = models.CharField(max_length=1, choices=PRIORITY, default='W', verbose_name='Приоритет')
|
||||
status = models.CharField(max_length=1, choices=TASK_STATUS, default='N', verbose_name='Статус')
|
||||
is_complete = models.BooleanField(default=False)
|
||||
#
|
||||
start_time = models.DateTimeField(verbose_name='Дата начала')
|
||||
end_time = models.DateTimeField(verbose_name='Крайний срок')
|
||||
created = models.DateTimeField(auto_now_add=True, verbose_name='Время создания')
|
||||
updated = models.DateTimeField(auto_now_add=True, verbose_name='Время обновления')
|
||||
#
|
||||
substacles = models.ManyToManyField("Todo", blank=True, verbose_name='Подзадачи')
|
||||
#
|
||||
favorites = models.ManyToManyField(
|
||||
settings.AUTH_USER_MODEL, blank=True, related_name="favorites",
|
||||
verbose_name = 'Ответственный'
|
||||
)
|
||||
suppliers = models.ManyToManyField(
|
||||
settings.AUTH_USER_MODEL, blank=True, related_name="suppliers",
|
||||
verbose_name='Постановщик'
|
||||
)
|
||||
supporters = models.ManyToManyField(
|
||||
settings.AUTH_USER_MODEL, blank=True, related_name="supporters",
|
||||
verbose_name='Соисполнители'
|
||||
)
|
||||
observers = models.ManyToManyField(
|
||||
settings.AUTH_USER_MODEL, blank=True, related_name="observers",
|
||||
verbose_name='Наблюдатели'
|
||||
)
|
||||
|
||||
#
|
||||
tags = TaggableManager(blank=True)
|
||||
slug = models.SlugField(unique=True, max_length=255)
|
||||
|
||||
objects = TodoManager()
|
||||
|
||||
class Meta:
|
||||
#unique_together = ["event", "user"]
|
||||
verbose_name='Задача'
|
||||
verbose_name_plural='Задачи'
|
||||
|
||||
def __str__(self):
|
||||
#return f"{self.author.first_name} - {self.title}"
|
||||
return f"{self.title}"
|
||||
|
||||
# @property
|
||||
# def slug(self) -> str:
|
||||
# return slugify(self.title)
|
||||
def save(self, *args, **kwargs):
|
||||
self.slug = slugify(self.title)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def get_absolute_url(self) -> str:
|
||||
return reverse(
|
||||
"todo_detail",
|
||||
kwargs={
|
||||
"todo_id": self.id,
|
||||
"slug": self.slug,
|
||||
}
|
||||
)
|
||||
|
||||
def as_markdown(self) -> str:
|
||||
return markdown.markdown(self.content, safe_mode="escape", extensions=["extra"])
|
||||
105
todos/serializers.py
Normal file
105
todos/serializers.py
Normal file
@@ -0,0 +1,105 @@
|
||||
from rest_framework import serializers
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth import get_user_model
|
||||
from taggit.models import Tag
|
||||
from taggit.serializers import (TagListSerializerField,
|
||||
TaggitSerializer)
|
||||
|
||||
from todos.models import Todo
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class AuthorSerializer(serializers.ModelSerializer):
|
||||
following = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('username', 'bio', 'image', 'following')
|
||||
|
||||
def get_following(self, obj):
|
||||
user = self.context.get('request').user
|
||||
if user.is_authenticated:
|
||||
return obj.followers.filter(pk=user.id).exists()
|
||||
return False
|
||||
|
||||
class TodosSerializer(serializers.ModelSerializer):
|
||||
slug = serializers.SlugField(required=False)
|
||||
#titles = serializers.CharField(source='title')
|
||||
|
||||
class Meta:
|
||||
model = Todo
|
||||
fields = "__all__"
|
||||
|
||||
class TodoSerializer(TaggitSerializer, serializers.ModelSerializer):
|
||||
slug = serializers.SlugField(required=False)
|
||||
description = serializers.CharField(source='summary')
|
||||
body = serializers.CharField(source='content')
|
||||
tagList = TagListSerializerField(source='tags', required=False)
|
||||
createdAt = serializers.DateTimeField(source='created', format='%Y-%m-%dT%H:%M:%S.%fZ', required=False)
|
||||
updatedAt = serializers.DateTimeField(source='updated', format='%Y-%m-%dT%H:%M:%S.%fZ', required=False)
|
||||
startTd = serializers.DateTimeField(source='start_time', format='%Y-%m-%d %H:%M', required=False)
|
||||
endTd = serializers.DateTimeField(source='end_time', format='%Y-%m-%d %H:%M', required=False)
|
||||
|
||||
favorited = serializers.SerializerMethodField()
|
||||
favoritesCount = serializers.SerializerMethodField()
|
||||
#supplierd = serializers.SerializerMethodField()
|
||||
#suppliersCount = serializers.SerializerMethodField()
|
||||
#supporterd = serializers.SerializerMethodField()
|
||||
#supportersCount = serializers.SerializerMethodField()
|
||||
#observerd = serializers.SerializerMethodField()
|
||||
#observersCount = serializers.SerializerMethodField()
|
||||
author = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Todo
|
||||
fields = ['id', 'slug', 'title', 'description', 'body', 'tagList',
|
||||
'is_active', 'is_deleted', 'priority', 'status', 'is_complete',
|
||||
'startTd', 'endTd', 'createdAt', 'updatedAt',
|
||||
#'favorited', 'favoritesCount', 'supplierd', 'suppliersCount', 'supporterd', 'supportersCount', 'observerd', 'observersCount',
|
||||
'favorited', 'favoritesCount',
|
||||
'author']
|
||||
read_only_fields = ['slug', 'createdAt', 'updatedAt', 'author']
|
||||
|
||||
def get_author(self, obj):
|
||||
request = self.context.get('request')
|
||||
# serializer = AuthorSerializer(request.user, context={'request': request})
|
||||
serializer = AuthorSerializer(obj.author, context={'request': request})
|
||||
return serializer.data
|
||||
|
||||
def get_favorited(self, instance):
|
||||
request = self.context.get('request')
|
||||
if request and request.user.is_authenticated:
|
||||
return instance.favorites.filter(pk=request.user.pk).exists()
|
||||
return False
|
||||
|
||||
def get_favoritesCount(self, instance):
|
||||
return instance.favorites.count()
|
||||
|
||||
def create(self, validated_data):
|
||||
tags = validated_data.pop('tags')
|
||||
todo = Todo(
|
||||
author=self.context['request'].user,
|
||||
**validated_data
|
||||
)
|
||||
todo.save()
|
||||
todo.tags.add(*tags)
|
||||
return todo
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
tags = validated_data.pop('tags')
|
||||
for key, value in validated_data.items():
|
||||
setattr(instance, key, value)
|
||||
instance.save()
|
||||
|
||||
instance.tags.clear()
|
||||
instance.tags.add(*tags)
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
class TagSerializer(serializers.Serializer):
|
||||
tags = serializers.ListField(
|
||||
child=serializers.CharField()
|
||||
)
|
||||
|
||||
2
todos/tests.py
Normal file
2
todos/tests.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from django.test import TestCase
|
||||
# Создайте первуый тест
|
||||
19
todos/urls.py
Normal file
19
todos/urls.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
#from rest_framework.urlpatterns import format_suffix_patterns
|
||||
|
||||
from todos import views
|
||||
|
||||
todo_router = DefaultRouter(trailing_slash=False)
|
||||
todo_router.register('todos', views.TodoView, basename='todos')
|
||||
todo_router.register('tags', views.TagView)
|
||||
|
||||
urlpatterns = [
|
||||
path('', include(todo_router.urls)),
|
||||
path('todo/json/', views.TodoListView),
|
||||
#path('todo/<slug:slug>', views.TodoDetailView)
|
||||
path('todo/<int:id>', views.TodoDetailView)
|
||||
]
|
||||
|
||||
#urlpatterns = format_suffix_patterns(urlpatterns)
|
||||
247
todos/views.py
Normal file
247
todos/views.py
Normal file
@@ -0,0 +1,247 @@
|
||||
|
||||
from rest_framework import viewsets, status, mixins, generics
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
|
||||
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.decorators import api_view
|
||||
from taggit.models import Tag
|
||||
from django.http import JsonResponse
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
|
||||
from system.accounts.models import User
|
||||
from todos.models import Todo
|
||||
from todos.serializers import TodoSerializer, TagSerializer, TodosSerializer
|
||||
from todos.filters import TodoFilter
|
||||
|
||||
@api_view(['GET', 'POST'])
|
||||
#@authentication_classes([SessionAuthentication, BasicAuthentication])
|
||||
#@permission_classes([IsAuthenticated])
|
||||
def TodoListView(request, format=None):
|
||||
|
||||
if request.method == 'GET':
|
||||
todos = Todo.objects.all()
|
||||
serializer = TodosSerializer(todos, many=True)
|
||||
return JsonResponse({'todos': serializer.data})
|
||||
|
||||
if request.method == 'POST':
|
||||
serializer = TodosSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
@api_view(['GET', 'PUT', 'DELETE'])
|
||||
def TodoDetailView(request, id, format=None):
|
||||
|
||||
try:
|
||||
todo = Todo.objects.get(pk=id)
|
||||
except Todo.DoesNotExist:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
if request.method == 'GET':
|
||||
serializer = TodosSerializer(todo)#, many=False)
|
||||
return Response(serializer.data)
|
||||
|
||||
elif request.method == 'PUT':
|
||||
serializer = TodosSerializer(todo, data=request.data)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return Response(serializer.data)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
elif request.method == 'DELETE':
|
||||
todo.delete()
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
class TodoAPIListPagination(PageNumberPagination):
|
||||
page_size = 3
|
||||
page_size_query_param = 'page_size'
|
||||
max_page_size = 2
|
||||
|
||||
class TodoAPIList(generics.ListCreateAPIView):
|
||||
queryset = Todo.objects.all()
|
||||
serializer_class = TodosSerializer
|
||||
#authentication_classes = [SessionAuthentication, BasicAuthentication]
|
||||
permission_classes = (IsAuthenticatedOrReadOnly, IsAuthenticated)
|
||||
pagination_class = TodoAPIListPagination
|
||||
|
||||
|
||||
class TodoView(viewsets.ModelViewSet):
|
||||
queryset = Todo.objects.all()
|
||||
serializer_class = TodoSerializer
|
||||
#authentication_classes = [SessionAuthentication, BasicAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
lookup_field = 'slug'
|
||||
filterset_class = TodoFilter
|
||||
http_method_names = ['get', 'post', 'put', 'delete']
|
||||
|
||||
def get_permissions(self):
|
||||
if self.action == 'retrieve' or self.action == 'list':
|
||||
return [IsAuthenticatedOrReadOnly()]
|
||||
|
||||
return super().get_permissions()
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
try:
|
||||
todo_data = request.data.get('todo')
|
||||
serializer = self.get_serializer(data=todo_data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
self.perform_create(serializer)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response({"todo": serializer.data}, status=status.HTTP_201_CREATED)
|
||||
|
||||
except Exception:
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"Bad Request"
|
||||
]
|
||||
}}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
@action(detail=True, methods=['post', 'delete'])
|
||||
def favorite(self, request, slug, *args, **kwargs):
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
todo = Todo.objects.get(slug=slug)
|
||||
|
||||
if todo.favorites.filter(id=request.user.id).exists():
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"Already Favourited Todo"
|
||||
]
|
||||
}})
|
||||
|
||||
todo.favorites.add(request.user)
|
||||
serializer = self.get_serializer(todo)
|
||||
return Response({"todo": serializer.data})
|
||||
|
||||
except Exception:
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"Bad Request"
|
||||
]
|
||||
}}, status=status.HTTP_404_NOT_FOUND)
|
||||
else:
|
||||
try:
|
||||
|
||||
todo = Todo.objects.get(slug=slug)
|
||||
if todo.favorites.get(id=request.user.id):
|
||||
todo.favorites.remove(request.user.id)
|
||||
serializer = self.get_serializer(todo)
|
||||
return Response({"todo": serializer.data})
|
||||
|
||||
else:
|
||||
raise Exception
|
||||
|
||||
except Exception:
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"Bad Request"
|
||||
]
|
||||
}}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
@action(detail=False)
|
||||
def feed(self, request, *args, **kwargs):
|
||||
try:
|
||||
followed_authors = User.objects.filter(followers=request.user)
|
||||
queryset = self.get_queryset()
|
||||
todos = queryset.filter(
|
||||
author__in=followed_authors).order_by('-created')
|
||||
queryset = self.filter_queryset(todos)
|
||||
|
||||
serializer = self.get_serializer(queryset, many=True)
|
||||
response = {
|
||||
'comments': serializer.data,
|
||||
'todoCount': len(serializer.data)
|
||||
}
|
||||
return Response(response)
|
||||
|
||||
except Exception:
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"Bad Request"
|
||||
]
|
||||
}}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def retrieve(self, request, slug, *args, **kwargs):
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
todo = queryset.get(slug=slug)
|
||||
serializer = self.get_serializer(todo)
|
||||
|
||||
return Response({"todo": serializer.data})
|
||||
|
||||
except Exception:
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"Bad Request"
|
||||
]
|
||||
}}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def update(self, request, slug, *args, **kwargs):
|
||||
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
todo = queryset.get(slug=slug)
|
||||
|
||||
if request.user != todo.author:
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"UnAuthorized Action"
|
||||
]
|
||||
}}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
request_data = request.data.get('todo')
|
||||
serializer = self.get_serializer(todo, data=request_data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
self.perform_update(serializer)
|
||||
|
||||
return Response({"todo": serializer.data})
|
||||
|
||||
except Exception:
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"Bad Request"
|
||||
]
|
||||
}}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def destroy(self, request, slug, *args, **kwargs):
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
todo = queryset.get(slug=slug)
|
||||
|
||||
if request.user != todo.author:
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"UnAuthorized Action"
|
||||
]
|
||||
}}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
todo.delete()
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
except Exception:
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"Bad Request"
|
||||
]
|
||||
}}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
|
||||
class TagView(viewsets.GenericViewSet, mixins.ListModelMixin):
|
||||
queryset = Tag.objects.all()
|
||||
serializer_class = TagSerializer
|
||||
http_method_names = ['get', ]
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
try:
|
||||
queryset = self.get_queryset()
|
||||
tags = [element.name for element in queryset]
|
||||
serializer = self.get_serializer({'tags': tags})
|
||||
return Response(serializer.data)
|
||||
|
||||
except Exception:
|
||||
return Response({"errors": {
|
||||
"body": [
|
||||
"Bad Request"
|
||||
]
|
||||
}}, status=status.HTTP_404_NOT_FOUND)
|
||||
Reference in New Issue
Block a user