config
This commit is contained in:
parent
b1867a9a07
commit
da7d25d29e
7 changed files with 131 additions and 12 deletions
|
|
@ -1,4 +1,12 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from .models import Config
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ['key', 'value', 'value_type', 'get_value', 'description', 'comment']
|
||||||
|
|
||||||
|
admin.site.register(Config, ConfigAdmin)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
31
apps/core/decorators.py
Normal file
31
apps/core/decorators.py
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from apps.core.models import Config, ConfigValueChoices
|
||||||
|
from utils.exceptions import ServiceUnavailable
|
||||||
|
|
||||||
|
|
||||||
|
def service_availability(key:str):
|
||||||
|
def with_params(view_func):
|
||||||
|
@wraps(view_func)
|
||||||
|
def wrapper(self, request, *args, **kwargs):
|
||||||
|
print(key)
|
||||||
|
print(f"Executing {view_func.__name__} action")
|
||||||
|
|
||||||
|
service_is_available = Config.get_value_of(f"SERVICE_IS_AVAILABLE_{key.upper()}", "True", ConfigValueChoices.BOOLEAN)
|
||||||
|
config = Config.objects.get(key=f"SERVICE_IS_AVAILABLE_{key.upper()}")
|
||||||
|
if not service_is_available:
|
||||||
|
raise ServiceUnavailable(
|
||||||
|
detail={
|
||||||
|
# "code": 'service_unavailable_at_now',
|
||||||
|
# "timestamp": timezone.now().isoformat(),
|
||||||
|
"message": config.description or "این سرویس در حال حاضر در دسترس نیست"
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return view_func(self, request, *args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
return with_params
|
||||||
31
apps/core/migrations/0001_initial.py
Normal file
31
apps/core/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Generated by Django 5.1.4 on 2026-03-29 13:38
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Config',
|
||||||
|
fields=[
|
||||||
|
('uuid', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, db_index=True)),
|
||||||
|
('key', models.CharField(db_index=True, max_length=255, unique=True, verbose_name='key')),
|
||||||
|
('value', models.TextField(blank=True, null=True, verbose_name='value')),
|
||||||
|
('value_type', models.CharField(choices=[('INT', 'Integer'), ('FLOAT', 'Float'), ('BOOLEAN', 'Boolean'), ('STRING', 'String'), ('DATE', 'Date'), ('DATETIME', 'DateTime'), ('JSON', 'JSON')], max_length=64)),
|
||||||
|
('description', models.TextField(blank=True, null=True, verbose_name='description')),
|
||||||
|
('comment', models.TextField(blank=True, null=True, verbose_name='comment')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
0
apps/core/migrations/__init__.py
Normal file
0
apps/core/migrations/__init__.py
Normal file
|
|
@ -6,6 +6,7 @@ from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
|
||||||
|
|
||||||
class BaseModel(models.Model):
|
class BaseModel(models.Model):
|
||||||
id = None
|
id = None
|
||||||
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, unique=True, db_index=True)
|
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, unique=True, db_index=True)
|
||||||
|
|
@ -16,7 +17,6 @@ class BaseModel(models.Model):
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BasePGModel(models.Model):
|
class BasePGModel(models.Model):
|
||||||
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
|
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True, db_index=True)
|
updated_at = models.DateTimeField(auto_now=True, db_index=True)
|
||||||
|
|
@ -39,8 +39,64 @@ class BasePGModel(models.Model):
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigValueChoices(models.TextChoices):
|
||||||
|
INT = 'INT', _('Integer')
|
||||||
|
FLOAT = 'FLOAT', _('Float')
|
||||||
|
BOOLEAN = 'BOOLEAN', _('Boolean')
|
||||||
|
STRING = 'STRING', _('String')
|
||||||
|
DATE = 'DATE', _('Date')
|
||||||
|
DATETIME = 'DATETIME', _('DateTime')
|
||||||
|
JSON = 'JSON', _('JSON')
|
||||||
|
|
||||||
|
|
||||||
|
class Config(BaseModel):
|
||||||
|
key = models.CharField(_('key'), max_length=255, unique=True, db_index=True)
|
||||||
|
value = models.TextField(_('value'), null=True, blank=True)
|
||||||
|
value_type = models.CharField(choices=ConfigValueChoices.choices, max_length=64)
|
||||||
|
description = models.TextField(_('description'), null=True, blank=True)
|
||||||
|
comment = models.TextField(_('comment'), null=True, blank=True)
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
return Config.get_value_of(self.key)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def type_cast(value, value_type):
|
||||||
|
|
||||||
|
try:
|
||||||
|
if value_type == ConfigValueChoices.STRING.value:
|
||||||
|
value = str(value)
|
||||||
|
|
||||||
|
elif value_type == ConfigValueChoices.BOOLEAN.value:
|
||||||
|
value = value.lower() == 'true'
|
||||||
|
|
||||||
|
elif value_type == ConfigValueChoices.DATE.value:
|
||||||
|
value = datetime.fromisoformat(value)
|
||||||
|
|
||||||
|
elif value_type == ConfigValueChoices.DATETIME.value:
|
||||||
|
value = datetime.fromisoformat(value)
|
||||||
|
|
||||||
|
elif value_type == ConfigValueChoices.JSON.value:
|
||||||
|
value = json.loads(value)
|
||||||
|
|
||||||
|
elif value_type == ConfigValueChoices.INT.value:
|
||||||
|
value = int(value)
|
||||||
|
|
||||||
|
elif value_type == ConfigValueChoices.FLOAT.value:
|
||||||
|
value = float(value)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
value = None
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_value_of(key, default=None, casting_type=None):
|
||||||
|
# TODO: use cache
|
||||||
|
config, created = Config.objects.get_or_create(key=key, defaults={'value': default})
|
||||||
|
|
||||||
|
if config.value is None:
|
||||||
|
return default
|
||||||
|
|
||||||
|
value = Config.type_cast(config.value, casting_type or config.value_type)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ from rest_framework.permissions import AllowAny
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
from utils.exceptions import StatusError
|
from apps.core.decorators import service_availability
|
||||||
|
from utils.exceptions import ServiceUnavailable
|
||||||
|
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
@ -22,16 +23,8 @@ class HomeView(TemplateView):
|
||||||
class StatusView(APIView):
|
class StatusView(APIView):
|
||||||
permission_classes = [AllowAny]
|
permission_classes = [AllowAny]
|
||||||
|
|
||||||
|
@service_availability('status')
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
if self.request.GET.get('raise_error'):
|
|
||||||
raise StatusError(
|
|
||||||
# code='service_unavailable_at_now',
|
|
||||||
detail={
|
|
||||||
# "code": 'service_unavailable_at_now',
|
|
||||||
"timestamp": timezone.now().isoformat(),
|
|
||||||
"message": "برخی سرویسهای خارجی در دسترس نیستند لطفا بعدا تلاش کنید"
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ class UnprocessableEntity(APIException):
|
||||||
default_detail = 'The request was well-formed but cannot be processed due to semantic errors.'
|
default_detail = 'The request was well-formed but cannot be processed due to semantic errors.'
|
||||||
default_code = 'unprocessable_entity'
|
default_code = 'unprocessable_entity'
|
||||||
|
|
||||||
class StatusError(APIException):
|
class ServiceUnavailable(APIException):
|
||||||
status_code = status.HTTP_503_SERVICE_UNAVAILABLE
|
status_code = status.HTTP_503_SERVICE_UNAVAILABLE
|
||||||
default_ = _('SERVICE_UNAVAILABLE')
|
default_ = _('SERVICE_UNAVAILABLE')
|
||||||
default_code = 'service_unavailable'
|
default_code = 'service_unavailable'
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue