more utils
This commit is contained in:
parent
68f0dd13db
commit
7a7f58ba69
20 changed files with 410 additions and 170 deletions
0
apps/sample/__init__.py
Normal file
0
apps/sample/__init__.py
Normal file
9
apps/sample/admin.py
Normal file
9
apps/sample/admin.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
from functools import update_wrapper
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Sample
|
||||
|
||||
|
||||
admin.site.register(Sample)
|
||||
|
||||
6
apps/sample/apps.py
Normal file
6
apps/sample/apps.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class SampleConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'apps.sample'
|
||||
11
apps/sample/filters.py
Normal file
11
apps/sample/filters.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import django_filters
|
||||
|
||||
from .models import Sample
|
||||
|
||||
|
||||
class SampleFilter(django_filters.FilterSet):
|
||||
class Meta:
|
||||
model = Sample
|
||||
fields = {
|
||||
'data': ['exact'],
|
||||
}
|
||||
12
apps/sample/forms.py
Normal file
12
apps/sample/forms.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .models import Sample
|
||||
|
||||
|
||||
class SampleForm(forms.ModelForm):
|
||||
# state = forms.ChoiceField(required=False, disabled=True)
|
||||
class Meta:
|
||||
model = Sample
|
||||
fields = ['data', 'user', 'application']
|
||||
readonly_fields = ['user', 'application']
|
||||
0
apps/sample/migrations/__init__.py
Normal file
0
apps/sample/migrations/__init__.py
Normal file
21
apps/sample/models.py
Normal file
21
apps/sample/models.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
from django.conf import settings
|
||||
from django.db import models
|
||||
from oauth2_provider.settings import oauth2_settings
|
||||
|
||||
from utils.models import BaseModel
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class SampleQuerySet(models.QuerySet):
|
||||
pass
|
||||
|
||||
|
||||
class Sample(BaseModel):
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
|
||||
application = models.ForeignKey(oauth2_settings.APPLICATION_MODEL, on_delete=models.PROTECT,
|
||||
related_name='+', null=True, blank=True)
|
||||
|
||||
data = models.JSONField(null=True, blank=True)
|
||||
|
||||
objects = SampleQuerySet.as_manager()
|
||||
14
apps/sample/serializers.py
Normal file
14
apps/sample/serializers.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from .models import Sample
|
||||
|
||||
|
||||
class SampleSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Sample
|
||||
fields = (
|
||||
"user",
|
||||
"application",
|
||||
"data"
|
||||
)
|
||||
read_only_fields = ('user','application')
|
||||
7
apps/sample/tasks.py
Normal file
7
apps/sample/tasks.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
from main import celery_app
|
||||
|
||||
|
||||
@celery_app.task
|
||||
def sample_application_task(sample_uuid):
|
||||
from .models import Sample
|
||||
sample = Sample.objects.get(uuid=sample_uuid)
|
||||
3
apps/sample/tests.py
Normal file
3
apps/sample/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
19
apps/sample/urls.py
Normal file
19
apps/sample/urls.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from rest_framework import routers
|
||||
from rest_framework.routers import DefaultRouter, DynamicRoute, Route
|
||||
|
||||
from utils.router import ProfileRouter
|
||||
from . import views
|
||||
from django.urls import NoReverseMatch, path, re_path, include
|
||||
|
||||
app_name = 'sample'
|
||||
|
||||
application_router = DefaultRouter()
|
||||
application_router.register(r'', views.ApplicationSampleViewSet, basename='application' )
|
||||
|
||||
user_router = ProfileRouter()
|
||||
user_router.register('', views.UserSampleViewSet, basename='user')
|
||||
|
||||
urlpatterns = [
|
||||
path('api/v1/application/<user_uuid>/', include(application_router.urls), name='application'),
|
||||
path('api/v1/user/', include(user_router.urls), name='user'),
|
||||
]
|
||||
63
apps/sample/views.py
Normal file
63
apps/sample/views.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
from django.http import Http404
|
||||
from django.shortcuts import render
|
||||
from oauth2_provider.contrib.rest_framework import TokenMatchesOASRequirements
|
||||
from rest_framework import viewsets, mixins
|
||||
from rest_framework.generics import get_object_or_404
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from .models import PushUser, PushMessage
|
||||
from .serializers import PushUserSerializer, PushMessageSerializer
|
||||
from apps.gooyal_oauth2.models import Application
|
||||
from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
|
||||
from apps.gooyal_oauth2.utils import get_application
|
||||
from ..users.models import User
|
||||
|
||||
|
||||
from django.shortcuts import render
|
||||
|
||||
from django.http import Http404
|
||||
from django.shortcuts import render
|
||||
from django.urls import reverse
|
||||
from oauth2_provider.contrib.rest_framework import TokenMatchesOASRequirements
|
||||
from rest_framework import viewsets, mixins
|
||||
from rest_framework.generics import get_object_or_404
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from .models import Sample
|
||||
from .serializers import SampleSerializer
|
||||
from apps.gooyal_oauth2.models import Application
|
||||
from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
|
||||
from apps.gooyal_oauth2.utils import get_application
|
||||
from ..users.models import User
|
||||
|
||||
|
||||
class ApplicationSampleViewSet(mixins.ListModelMixin,
|
||||
mixins.CreateModelMixin,
|
||||
GenericViewSet):
|
||||
queryset = Sample.objects.all()
|
||||
serializer_class = SampleSerializer
|
||||
|
||||
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
||||
required_alternate_scopes = {
|
||||
"POST": [["service_template.application.user-sample:submit"]],
|
||||
"GET": [["service_template.application.user-sample:list-retrieve"]],
|
||||
}
|
||||
|
||||
def perform_create(self, serializer):
|
||||
user_uuid = self.kwargs.get('user_uuid')
|
||||
user = User.objects.get(uuid=user_uuid)
|
||||
application = get_application(self.request)
|
||||
instance = serializer.save(user=user, application=application)
|
||||
|
||||
|
||||
class UserSampleViewSet(mixins.RetrieveModelMixin,
|
||||
GenericViewSet):
|
||||
queryset = Sample.objects.all()
|
||||
serializer_class = SampleSerializer
|
||||
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
||||
required_alternate_scopes = {
|
||||
"GET": [["service_template.user.self-sample:list-retrieve"]],
|
||||
}
|
||||
|
||||
|
|
@ -1,16 +1,42 @@
|
|||
from .base_settings import *
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
"""
|
||||
Django settings for main project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 5.1.4.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.1/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.1/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from decouple import config
|
||||
|
||||
SECRET_KEY = config('SECRET_KEY', default=SECRET_KEY)
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = config('SECRET_KEY', default='django-insecure-ync*tk)t*bz3zzbza&xvw6cl+wk6+@gk^@)2)-pp=^&v%@knib')
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = config('DEBUG', default=DEBUG, cast=bool)
|
||||
DEBUG = config('DEBUG', default=True, cast=bool)
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
|
||||
INSTALLED_APPS += [
|
||||
# pip installed apps
|
||||
'drf_spectacular',
|
||||
'oauth2_provider',
|
||||
|
|
@ -27,6 +53,7 @@ INSTALLED_APPS += [
|
|||
'utils',
|
||||
'apps.users',
|
||||
'apps.gooyal_oauth2',
|
||||
'apps.activity',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
|
@ -40,7 +67,83 @@ MIDDLEWARE = [
|
|||
'oauth2_provider.middleware.OAuth2TokenMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'utils.exceptions.ErrorMiddleware',
|
||||
]
|
||||
ROOT_URLCONF = 'main.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [BASE_DIR / 'templates'],
|
||||
'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 = 'main.wsgi.application'
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
"ENGINE": "django.db.backends.postgresql",
|
||||
'NAME': config('DB_NAME'),
|
||||
'USER': config('DB_USER'),
|
||||
'PASSWORD': config('DB_PASSWORD'),
|
||||
'HOST': config('DB_HOST', '127.0.0.1'),
|
||||
'PORT': config('DB_PORT', ''),
|
||||
}
|
||||
}
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
|
||||
|
||||
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',
|
||||
},
|
||||
]
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.1/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
#######################
|
||||
|
||||
|
||||
OAUTH2_PROVIDER_APPLICATION_MODEL = "gooyal_oauth2.Application"
|
||||
OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = "gooyal_oauth2.AccessToken"
|
||||
|
|
@ -48,12 +151,12 @@ OAUTH2_PROVIDER_ID_TOKEN_MODEL = "gooyal_oauth2.IDToken"
|
|||
OAUTH2_PROVIDER_GRANT_MODEL = "gooyal_oauth2.Grant"
|
||||
OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "gooyal_oauth2.RefreshToken"
|
||||
|
||||
BASE_OAUTH2_PROVIDER_PUBLIC_URL = config("BASE_OAUTH2_PROVIDER_PUBLIC_URL")
|
||||
BASE_OAUTH2_PROVIDER_PRIVATE_URL = config("BASE_OAUTH2_PROVIDER_PRIVATE_URL")
|
||||
OAUTH2_PROVIDER_PUBLIC_URL = config("OAUTH2_PROVIDER_PUBLIC_URL")
|
||||
OAUTH2_PROVIDER_PRIVATE_URL = config("OAUTH2_PROVIDER_PRIVATE_URL")
|
||||
|
||||
CLIENT_ID = config('CLIENT_ID')
|
||||
CLIENT_SECRET = config('CLIENT_SECRET')
|
||||
SCOPES = config('SCOPES', default='')
|
||||
OAUTH2_CLIENT_ID = config('OAUTH2_CLIENT_ID')
|
||||
OAUTH2_CLIENT_SECRET = config('OAUTH2_CLIENT_SECRET')
|
||||
OAUTH2_SCOPES = config('OAUTH2_SCOPES', default='')
|
||||
OAUTH2_PROVIDER = {
|
||||
# this is the list of available scopes
|
||||
# 'SCOPES_BACKEND_CLASS': 'apps.gooyal_oauth2.scopes.Scopes',
|
||||
|
|
@ -62,9 +165,9 @@ OAUTH2_PROVIDER = {
|
|||
# 'groups': 'Access to your groups',
|
||||
# 'introspection': 'Introspect token scope'},
|
||||
'OAUTH2_VALIDATOR_CLASS': 'apps.gooyal_oauth2.validators.OAuth2Validator',
|
||||
'RESOURCE_SERVER_INTROSPECTION_URL': BASE_OAUTH2_PROVIDER_PRIVATE_URL + '/introspect/',
|
||||
'RESOURCE_SERVER_INTROSPECTION_URL': OAUTH2_PROVIDER_PRIVATE_URL + '/introspect/',
|
||||
# 'RESOURCE_SERVER_AUTH_TOKEN': '3yUqsWtwKYKHnfivFcJu', # OR this but not both:
|
||||
'RESOURCE_SERVER_INTROSPECTION_CREDENTIALS': (CLIENT_ID, CLIENT_SECRET),
|
||||
'RESOURCE_SERVER_INTROSPECTION_CREDENTIALS': (OAUTH2_CLIENT_ID, OAUTH2_CLIENT_SECRET),
|
||||
}
|
||||
|
||||
REST_FRAMEWORK = {
|
||||
|
|
@ -84,41 +187,24 @@ REST_FRAMEWORK = {
|
|||
'DEFAULT_THROTTLE_RATES': {
|
||||
# 'otp_min': '1/min',
|
||||
# 'otp_day': '20/day',
|
||||
}
|
||||
},
|
||||
'EXCEPTION_HANDLER': 'utils.exceptions.ExceptionHandler',
|
||||
}
|
||||
|
||||
|
||||
TEMPLATES[0]['DIRS'] += [BASE_DIR / 'templates']
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'oauth2_provider.backends.OAuth2Backend',
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
)
|
||||
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
"ENGINE": "django.contrib.gis.db.backends.postgis",
|
||||
'NAME': config('DB_NAME'),
|
||||
'USER': config('DB_USER'),
|
||||
'PASSWORD': config('DB_PASSWORD'),
|
||||
'HOST': config('DB_HOST', '127.0.0.1'),
|
||||
'PORT': config('DB_PORT', ''),
|
||||
}
|
||||
}
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
|
||||
LOCALE_PATHS = [
|
||||
BASE_DIR / 'locale',
|
||||
]
|
||||
|
||||
|
||||
MEDIA_URL = 'media/'
|
||||
MEDIA_ROOT = BASE_DIR / 'media'
|
||||
|
||||
|
||||
if DEBUG == True:
|
||||
STATICFILES_DIRS = (
|
||||
BASE_DIR / 'static',
|
||||
|
|
@ -126,8 +212,6 @@ if DEBUG == True:
|
|||
else:
|
||||
STATIC_ROOT = BASE_DIR / 'static'
|
||||
|
||||
|
||||
|
||||
LOGIN_URL = '/users/login/request/'
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
|
||||
|
|
@ -135,7 +219,7 @@ CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
|
|||
CRISPY_TEMPLATE_PACK = "bootstrap5"
|
||||
|
||||
SPECTACULAR_SETTINGS = {
|
||||
'TITLE': 'merchants',
|
||||
'TITLE': 'service',
|
||||
'DESCRIPTION': 'service api reference',
|
||||
'VERSION': '1.0.0',
|
||||
'SERVE_INCLUDE_SCHEMA': False,
|
||||
|
|
@ -191,8 +275,10 @@ LOGGING = {
|
|||
'timeout': 1, # Post request timeout, default is 0.5. Optional
|
||||
'url': 'https://loki.winsoo.ir/loki/api/v1/push', # Loki url. Defaults to localhost. Optional.
|
||||
# 'auth': ("user", "password"), # Basic auth to authenticate with loki. Default is None (i.e. no auth). Optional
|
||||
'tags': { "app": "ads", "env": "production" }, # Tags / Labels to attach to the log. Optional, but strongly encoraged to use.
|
||||
'mode': 'thread', # Push mode. Can be 'sync' or 'thread'. Sync is blocking, thread is non-blocking. Defaults to sync. Optional.
|
||||
'tags': {"app": "ads", "env": "production"},
|
||||
# Tags / Labels to attach to the log. Optional, but strongly encoraged to use.
|
||||
'mode': 'thread',
|
||||
# Push mode. Can be 'sync' or 'thread'. Sync is blocking, thread is non-blocking. Defaults to sync. Optional.
|
||||
},
|
||||
# 'file': {
|
||||
# 'level': 'DEBUG',
|
||||
|
|
@ -233,8 +319,10 @@ STORAGES = {
|
|||
MINIO_ENDPOINT = config('MINIO_ENDPOINT', default='drive.gooyal.com')
|
||||
MINIO_USE_HTTPS = config('MINIO_USE_HTTPS', default=True, cast=bool)
|
||||
|
||||
MINIO_EXTERNAL_ENDPOINT = config('MINIO_EXTERNAL_ENDPOINT', default='drive.gooyal.com') # Default is same as MINIO_ENDPOINT
|
||||
MINIO_EXTERNAL_ENDPOINT_USE_HTTPS = config('MINIO_EXTERNAL_ENDPOINT_USE_HTTPS', default=True, cast=bool) # Default is same as MINIO_USE_HTTPS
|
||||
MINIO_EXTERNAL_ENDPOINT = config('MINIO_EXTERNAL_ENDPOINT',
|
||||
default='drive.gooyal.com') # Default is same as MINIO_ENDPOINT
|
||||
MINIO_EXTERNAL_ENDPOINT_USE_HTTPS = config('MINIO_EXTERNAL_ENDPOINT_USE_HTTPS', default=True,
|
||||
cast=bool) # Default is same as MINIO_USE_HTTPS
|
||||
MINIO_REGION = None # Default is set to None
|
||||
MINIO_ACCESS_KEY = config('MINIO_ACCESS_KEY')
|
||||
MINIO_SECRET_KEY = config('MINIO_SECRET_KEY')
|
||||
|
|
@ -272,14 +360,11 @@ MINIO_HTTP_CLIENT: urllib3.poolmanager.PoolManager = urllib3.PoolManager(
|
|||
)
|
||||
)
|
||||
|
||||
|
||||
REDIS_BASE_URL = config('REDIS_BASE_URL')
|
||||
|
||||
|
||||
|
||||
CELERY={
|
||||
"broker_url" :REDIS_BASE_URL,
|
||||
"result_backend" : REDIS_BASE_URL,
|
||||
CELERY = {
|
||||
"broker_url": REDIS_BASE_URL,
|
||||
"result_backend": REDIS_BASE_URL,
|
||||
}
|
||||
|
||||
CELERY_TASK_TRACK_STARTED = True
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
from .base_settings import *
|
||||
from .settings import *
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
"""
|
||||
Django settings for main project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 5.1.4.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/5.1/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.1/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-ync*tk)t*bz3zzbza&xvw6cl+wk6+@gk^@)2)-pp=^&v%@knib'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'main.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'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 = 'main.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
|
||||
|
||||
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',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.1/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
54
utils/exceptions.py
Normal file
54
utils/exceptions.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import logging
|
||||
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.views import exception_handler as drf_exception_handler
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class ExceptionHandler:
|
||||
|
||||
@classmethod
|
||||
def __call__(cls, exc, context):
|
||||
# پاسخ پیشفرض DRF را دریافت میکنیم
|
||||
response = drf_exception_handler(exc, context)
|
||||
|
||||
if response is not None:
|
||||
# ساختار دلخواه خود را تعریف میکنیم
|
||||
response_data = {
|
||||
'success': False,
|
||||
'status_code': response.status_code,
|
||||
'message': 'An error occurred',
|
||||
'details': {}
|
||||
}
|
||||
|
||||
if isinstance(exc, APIException):
|
||||
response_data['message'] = exc.detail if isinstance(exc.detail, str) else "Validation error"
|
||||
response_data['details'] = exc.detail if isinstance(exc.detail, dict) else {}
|
||||
|
||||
response.data = response_data
|
||||
|
||||
return response
|
||||
|
||||
|
||||
# در فایل middleware.py
|
||||
from django.http import JsonResponse
|
||||
|
||||
class ErrorMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
return response
|
||||
|
||||
def process_exception(self, request, exception):
|
||||
logger.exception(exception)
|
||||
return JsonResponse(
|
||||
{
|
||||
"success": False,
|
||||
"status_code": 500,
|
||||
"message": "Internal server error",
|
||||
"details": str(exception) # فقط در حالت توسعه! در تولید بهتر است لاگ شود.
|
||||
},
|
||||
status=500
|
||||
)
|
||||
14
utils/i18n.py
Normal file
14
utils/i18n.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
from django.utils import translation
|
||||
|
||||
class ForceAdminLanguageMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
if request.path.startswith('/admin/'):
|
||||
translation.activate('en')
|
||||
request.LANGUAGE_CODE = 'en'
|
||||
else:
|
||||
translation.activate('fa')
|
||||
request.LANGUAGE_CODE = 'fa'
|
||||
return self.get_response(request)
|
||||
47
utils/router.py
Normal file
47
utils/router.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
from rest_framework.routers import DefaultRouter, Route, DynamicRoute
|
||||
|
||||
|
||||
class ProfileRouter(DefaultRouter):
|
||||
routes = [
|
||||
# List route.
|
||||
Route(
|
||||
url=r'^{prefix}{trailing_slash}$',
|
||||
mapping={
|
||||
'get': 'list',
|
||||
'post': 'create'
|
||||
},
|
||||
name='{basename}-list',
|
||||
detail=False,
|
||||
initkwargs={'suffix': 'List'}
|
||||
),
|
||||
# Dynamically generated list routes. Generated using
|
||||
# @action(detail=False) decorator on methods of the viewset.
|
||||
DynamicRoute(
|
||||
url=r'^{prefix}/{url_path}{trailing_slash}$',
|
||||
name='{basename}-{url_name}',
|
||||
detail=False,
|
||||
initkwargs={}
|
||||
),
|
||||
# Detail route.
|
||||
Route(
|
||||
url=r'^{prefix}{trailing_slash}$',
|
||||
mapping={
|
||||
'get': 'retrieve',
|
||||
'put': 'update',
|
||||
'patch': 'partial_update',
|
||||
'delete': 'destroy'
|
||||
},
|
||||
name='{basename}-detail',
|
||||
detail=True,
|
||||
initkwargs={'suffix': 'Instance'}
|
||||
),
|
||||
# Dynamically generated detail routes. Generated using
|
||||
# @action(detail=True) decorator on methods of the viewset.
|
||||
DynamicRoute(
|
||||
url=r'^{prefix}/{url_path}{trailing_slash}$',
|
||||
name='{basename}-{url_name}',
|
||||
detail=True,
|
||||
initkwargs={}
|
||||
),
|
||||
]
|
||||
|
||||
Loading…
Add table
Reference in a new issue