first commit
This commit is contained in:
0
core/apps/shared/__init__.py
Normal file
0
core/apps/shared/__init__.py
Normal file
1
core/apps/shared/admin/__init__.py
Normal file
1
core/apps/shared/admin/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .settings import * # noqa
|
||||
20
core/apps/shared/admin/settings.py
Normal file
20
core/apps/shared/admin/settings.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from django.contrib import admin
|
||||
from unfold.admin import ModelAdmin, StackedInline
|
||||
from core.apps.shared.models import SettingsModel, OptionsModel
|
||||
from unfold.contrib.forms.widgets import ArrayWidget
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
|
||||
|
||||
class OptionsInline(StackedInline):
|
||||
model = OptionsModel
|
||||
extra = 1
|
||||
formfield_overrides = {
|
||||
ArrayField: {"widget": ArrayWidget},
|
||||
}
|
||||
|
||||
|
||||
@admin.register(SettingsModel)
|
||||
class SettingsAdmin(ModelAdmin):
|
||||
list_display = ["id", "key"]
|
||||
inlines = [OptionsInline]
|
||||
|
||||
6
core/apps/shared/apps.py
Normal file
6
core/apps/shared/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ModuleConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "core.apps.shared"
|
||||
17
core/apps/shared/enums/__init__.py
Normal file
17
core/apps/shared/enums/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class BaseEnum(Enum):
|
||||
|
||||
def choices(self):
|
||||
return [(x.name, x.value) for x in self]
|
||||
|
||||
|
||||
class GenderEnum(BaseEnum):
|
||||
MALE = "male"
|
||||
FEMALE = "female"
|
||||
|
||||
|
||||
class RoleEnum(BaseEnum):
|
||||
ADMIN = "admin"
|
||||
USER = "user"
|
||||
41
core/apps/shared/migrations/0001_initial.py
Normal file
41
core/apps/shared/migrations/0001_initial.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# Generated by Django 5.1.3 on 2025-07-11 15:00
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SettingsModel',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('key', models.CharField(verbose_name='key')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Settings',
|
||||
'verbose_name_plural': 'Settings',
|
||||
'db_table': 'settings',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='OptionsModel',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('key', models.CharField(verbose_name='key')),
|
||||
('value', models.CharField(verbose_name='value')),
|
||||
('settings', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='options', to='shared.settingsmodel', verbose_name='settings')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Options',
|
||||
'verbose_name_plural': 'Options',
|
||||
'db_table': 'options',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,46 @@
|
||||
# Generated by Django 5.1.3 on 2025-07-12 05:19
|
||||
|
||||
import django.contrib.postgres.fields
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('shared', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='settingsmodel',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='settingsmodel',
|
||||
name='description',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='description'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='settingsmodel',
|
||||
name='is_public',
|
||||
field=models.BooleanField(default=False, verbose_name='is public'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='settingsmodel',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='optionsmodel',
|
||||
name='key',
|
||||
field=models.CharField(max_length=255, verbose_name='key'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='optionsmodel',
|
||||
name='value',
|
||||
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=255, verbose_name='value'), size=None, verbose_name='value'),
|
||||
),
|
||||
]
|
||||
0
core/apps/shared/migrations/__init__.py
Normal file
0
core/apps/shared/migrations/__init__.py
Normal file
1
core/apps/shared/models/__init__.py
Normal file
1
core/apps/shared/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .settings import * # noqa
|
||||
31
core/apps/shared/models/settings.py
Normal file
31
core/apps/shared/models/settings.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_core.models import AbstractBaseModel
|
||||
|
||||
|
||||
class SettingsModel(AbstractBaseModel):
|
||||
key = models.CharField(_("key"))
|
||||
is_public = models.BooleanField(_("is public"), default=False)
|
||||
description = models.TextField(_("description"), blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "settings"
|
||||
verbose_name = _("Settings")
|
||||
verbose_name_plural = _("Settings")
|
||||
|
||||
|
||||
class OptionsModel(models.Model):
|
||||
settings = models.ForeignKey(
|
||||
"SettingsModel", verbose_name=_("settings"), on_delete=models.CASCADE, related_name="options"
|
||||
)
|
||||
key = models.CharField(_("key"), max_length=255)
|
||||
value = ArrayField(
|
||||
models.CharField(_("value"), max_length=255),
|
||||
verbose_name=_("value"),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = "options"
|
||||
verbose_name = _("Options")
|
||||
verbose_name_plural = _("Options")
|
||||
1
core/apps/shared/serializers/__init__.py
Normal file
1
core/apps/shared/serializers/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .settings import * # noqa
|
||||
1
core/apps/shared/serializers/settings/__init__.py
Normal file
1
core/apps/shared/serializers/settings/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .settings import * # noqa
|
||||
7
core/apps/shared/serializers/settings/settings.py
Normal file
7
core/apps/shared/serializers/settings/settings.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
class ListLanguageSerializer(serializers.Serializer):
|
||||
code = serializers.CharField(read_only=True)
|
||||
name = serializers.CharField(read_only=True)
|
||||
is_default = serializers.BooleanField(read_only=True, default=False)
|
||||
1
core/apps/shared/tests/__init__.py
Normal file
1
core/apps/shared/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .test_settings import * # noqa
|
||||
20
core/apps/shared/tests/test_settings.py
Normal file
20
core/apps/shared/tests/test_settings.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import pytest
|
||||
from django.urls import reverse
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def api_client():
|
||||
return APIClient()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def settings_urls():
|
||||
return {
|
||||
"languages": reverse("settings-languages"),
|
||||
}
|
||||
|
||||
|
||||
def test_languages(api_client, settings_urls):
|
||||
response = api_client.get(settings_urls["languages"])
|
||||
assert response.status_code == 200
|
||||
11
core/apps/shared/urls.py
Normal file
11
core/apps/shared/urls.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from .views import SettingsView
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register("settings", SettingsView, basename="settings")
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path("", include(router.urls)),
|
||||
]
|
||||
1
core/apps/shared/utils/__init__.py
Normal file
1
core/apps/shared/utils/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .settings import * # noqa
|
||||
17
core/apps/shared/utils/settings.py
Normal file
17
core/apps/shared/utils/settings.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from core.apps.shared.models import OptionsModel
|
||||
from typing import Optional
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
def get_config(settings: str, key: str, default=None) -> Optional[str]:
|
||||
config = OptionsModel.objects.filter(settings__key=settings, key=key)
|
||||
if not config.exists():
|
||||
return default
|
||||
return config.first().value
|
||||
|
||||
|
||||
def get_exchange_rate():
|
||||
exchange_rate = get_config("currency", "exchange_rate")
|
||||
if exchange_rate is None:
|
||||
raise Exception(_("USD kursi kiritilmagan iltimos adminga murojat qiling"))
|
||||
return float(exchange_rate[0])
|
||||
1
core/apps/shared/views/__init__.py
Normal file
1
core/apps/shared/views/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .settings import * # noqa
|
||||
53
core/apps/shared/views/settings.py
Normal file
53
core/apps/shared/views/settings.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from django_core.mixins import BaseViewSetMixin
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
from django.conf import settings
|
||||
from rest_framework.response import Response
|
||||
from ..serializers import ListLanguageSerializer
|
||||
from drf_spectacular.utils import extend_schema, OpenApiResponse
|
||||
from core.apps.shared.models import SettingsModel
|
||||
|
||||
|
||||
@extend_schema(tags=["settings"])
|
||||
class SettingsView(BaseViewSetMixin, GenericViewSet):
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ["languages"]:
|
||||
return ListLanguageSerializer
|
||||
return ListLanguageSerializer
|
||||
|
||||
@extend_schema(responses={200: OpenApiResponse(response=ListLanguageSerializer(many=True))})
|
||||
@action(methods=["GET"], detail=False, url_path="languages", url_name="languages")
|
||||
def languages(self, request):
|
||||
return Response(self.get_serializer(settings.JST_LANGUAGES, many=True).data)
|
||||
|
||||
@extend_schema(
|
||||
summary="Get public settings",
|
||||
responses={
|
||||
200: OpenApiResponse(
|
||||
response={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"example_key": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"example_key": {"type": "array", "items": {"type": "string"}, "example": [12300.50]}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
)
|
||||
@action(methods=["GET"], detail=False, url_path="config", url_name="config")
|
||||
def config(self, request):
|
||||
config = SettingsModel.objects.filter(is_public=True)
|
||||
response = {}
|
||||
for item in config:
|
||||
config_value = {}
|
||||
for option in item.options.all():
|
||||
config_value[option.key] = option.value
|
||||
response[item.key] = config_value
|
||||
return Response(data=response)
|
||||
Reference in New Issue
Block a user