Add choices API endpoint and OpenAPI client setup
Introduces a new /api/choices/ endpoint for fetching model choices with multilingual labels. Updates Django models to use 'cz#' prefix for Czech labels. Adds OpenAPI client generation via orval, refactors frontend API structure, and provides documentation and helper scripts for dynamic choices and OpenAPI usage.
This commit is contained in:
@@ -42,9 +42,9 @@ class CustomUser(SoftDeleteModel, AbstractUser):
|
||||
)
|
||||
|
||||
class Role(models.TextChoices):
|
||||
ADMIN = "admin", "Admin"
|
||||
MANAGER = "mod", "Moderator"
|
||||
CUSTOMER = "regular", "Regular"
|
||||
ADMIN = "admin", "cz#Administrátor"
|
||||
MANAGER = "mod", "cz#Moderator"
|
||||
CUSTOMER = "regular", "cz#Regular"
|
||||
|
||||
role = models.CharField(max_length=20, choices=Role.choices, default=Role.CUSTOMER)
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ from rest_framework_simplejwt.authentication import JWTAuthentication
|
||||
#NEMĚNIT CUSTOM SBÍRANÍ COOKIE TOKENU
|
||||
class CookieJWTAuthentication(JWTAuthentication):
|
||||
def authenticate(self, request):
|
||||
|
||||
raw_token = request.COOKIES.get('access_token')
|
||||
|
||||
if not raw_token:
|
||||
|
||||
@@ -103,12 +103,12 @@ class ProductImage(models.Model):
|
||||
|
||||
class Order(models.Model):
|
||||
class Status(models.TextChoices):
|
||||
CREATED = "created", "Vytvořeno"
|
||||
CANCELLED = "cancelled", "Zrušeno"
|
||||
COMPLETED = "completed", "Dokončeno"
|
||||
CREATED = "created", "cz#Vytvořeno"
|
||||
CANCELLED = "cancelled", "cz#Zrušeno"
|
||||
COMPLETED = "completed", "cz#Dokončeno"
|
||||
|
||||
REFUNDING = "refunding", "Vrácení v procesu"
|
||||
REFUNDED = "refunded", "Vráceno"
|
||||
REFUNDING = "refunding", "cz#Vrácení v procesu"
|
||||
REFUNDED = "refunded", "cz#Vráceno"
|
||||
|
||||
status = models.CharField(
|
||||
max_length=20, choices=Status.choices, null=True, blank=True, default=Status.CREATED
|
||||
@@ -205,15 +205,15 @@ class Order(models.Model):
|
||||
|
||||
class Carrier(models.Model):
|
||||
class SHIPPING(models.TextChoices):
|
||||
ZASILKOVNA = "packeta", "Zásilkovna"
|
||||
STORE = "store", "Osobní odběr"
|
||||
ZASILKOVNA = "packeta", "cz#Zásilkovna"
|
||||
STORE = "store", "cz#Osobní odběr"
|
||||
shipping_method = models.CharField(max_length=20, choices=SHIPPING.choices, default=SHIPPING.STORE)
|
||||
|
||||
class STATE(models.TextChoices):
|
||||
PREPARING = "ordered", "Objednávka se připravuje"
|
||||
SHIPPED = "shipped", "Odesláno"
|
||||
DELIVERED = "delivered", "Doručeno"
|
||||
READY_TO_PICKUP = "ready_to_pickup", "Připraveno k vyzvednutí"
|
||||
PREPARING = "ordered", "cz#Objednávka se připravuje"
|
||||
SHIPPED = "shipped", "cz#Odesláno"
|
||||
DELIVERED = "delivered", "cz#Doručeno"
|
||||
READY_TO_PICKUP = "ready_to_pickup", "cz#Připraveno k vyzvednutí"
|
||||
#RETURNING = "returning", "Vracení objednávky"
|
||||
state = models.CharField(max_length=20, choices=STATE.choices, default=STATE.PREPARING)
|
||||
|
||||
@@ -276,9 +276,9 @@ class Carrier(models.Model):
|
||||
|
||||
class Payment(models.Model):
|
||||
class PAYMENT(models.TextChoices):
|
||||
SHOP = "shop", "Platba v obchodě"
|
||||
STRIPE = "stripe", "Bankovní převod"
|
||||
CASH_ON_DELIVERY = "cash_on_delivery", "Dobírka"
|
||||
SHOP = "shop", "cz#Platba v obchodě"
|
||||
STRIPE = "stripe", "cz#Bankovní převod"
|
||||
CASH_ON_DELIVERY = "cash_on_delivery", "cz#Dobírka"
|
||||
payment_method = models.CharField(max_length=30, choices=PAYMENT.choices, default=PAYMENT.SHOP)
|
||||
|
||||
#FIXME: potvrdit že logika platby funguje správně
|
||||
@@ -472,10 +472,10 @@ class Refund(models.Model):
|
||||
order = models.ForeignKey(Order, related_name="refunds", on_delete=models.CASCADE)
|
||||
|
||||
class Reason(models.TextChoices):
|
||||
RETUNING_PERIOD = "retuning_before_fourteen_day_period", "Vrácení před uplynutím 14-ti denní lhůty"
|
||||
DAMAGED_PRODUCT = "damaged_product", "Poškozený produkt"
|
||||
WRONG_ITEM = "wrong_item", "Špatná položka"
|
||||
OTHER = "other", "Jiný důvod"
|
||||
RETUNING_PERIOD = "retuning_before_fourteen_day_period", "cz#Vrácení před uplynutím 14-ti denní lhůty"
|
||||
DAMAGED_PRODUCT = "damaged_product", "cz#Poškozený produkt"
|
||||
WRONG_ITEM = "wrong_item", "cz#Špatná položka"
|
||||
OTHER = "other", "cz#Jiný důvod"
|
||||
reason_choice = models.CharField(max_length=30, choices=Reason.choices)
|
||||
reason_text = models.TextField(blank=True)
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@ class ShopConfiguration(models.Model):
|
||||
addition_of_coupons_amount = models.BooleanField(default=False, help_text="Sčítání slevových kupónů v objednávce (ano/ne), pokud ne tak se použije pouze nejvyšší slevový kupón")
|
||||
|
||||
class CURRENCY(models.TextChoices):
|
||||
CZK = "CZK", "Czech Koruna"
|
||||
EUR = "EUR", "Euro"
|
||||
CZK = "CZK", "cz#Czech Koruna"
|
||||
EUR = "EUR", "cz#Euro"
|
||||
currency = models.CharField(max_length=10, default=CURRENCY.CZK, choices=CURRENCY.choices)
|
||||
|
||||
class Meta:
|
||||
|
||||
12
backend/thirdparty/stripe/models.py
vendored
12
backend/thirdparty/stripe/models.py
vendored
@@ -7,12 +7,12 @@ from .client import StripeClient
|
||||
|
||||
class StripeModel(models.Model):
|
||||
class STATUS_CHOICES(models.TextChoices):
|
||||
PENDING = "pending", "Čeká se na platbu"
|
||||
PAID = "paid", "Zaplaceno"
|
||||
FAILED = "failed", "Neúspěšné"
|
||||
CANCELLED = "cancelled", "Zrušeno"
|
||||
REFUNDING = "refunding", "Platba se vrací"
|
||||
REFUNDED = "refunded", "Platba úspěšně vrácena"
|
||||
PENDING = "pending", "cz#Čeká se na platbu"
|
||||
PAID = "paid", "cz#Zaplaceno"
|
||||
FAILED = "failed", "cz#Neúspěšné"
|
||||
CANCELLED = "cancelled", "cz#Zrušeno"
|
||||
REFUNDING = "refunding", "cz#Platba se vrací"
|
||||
REFUNDED = "refunded", "cz#Platba úspěšně vrácena"
|
||||
|
||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES.choices, default=STATUS_CHOICES.PENDING)
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ Including another URLconf
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from .views import choices
|
||||
|
||||
from drf_spectacular.views import (
|
||||
SpectacularAPIView,
|
||||
@@ -31,6 +32,7 @@ urlpatterns = [
|
||||
|
||||
|
||||
path('admin/', admin.site.urls),
|
||||
path("api/choices/", choices, name="choices"),
|
||||
path('api/account/', include('account.urls')),
|
||||
path('api/commerce/', include('commerce.urls')),
|
||||
#path('api/advertisments/', include('advertisements.urls')),
|
||||
|
||||
85
backend/vontor_cz/views.py
Normal file
85
backend/vontor_cz/views.py
Normal file
@@ -0,0 +1,85 @@
|
||||
import json
|
||||
from django.apps import apps
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.response import Response
|
||||
from drf_spectacular.utils import extend_schema, OpenApiParameter
|
||||
from rest_framework.permissions import AllowAny
|
||||
|
||||
@extend_schema(
|
||||
description="Vrátí všechny možné hodnoty pro ChoiceField s podporou vícejazyčných labelů. "
|
||||
"Umožňuje načíst více modelů a polí najednou.",
|
||||
parameters=[
|
||||
OpenApiParameter(
|
||||
name="fields",
|
||||
description=(
|
||||
"JSON pole objektů {model: 'ModelName', field: 'field_name'} "
|
||||
"např. '[{\"model\": \"User\", \"field\": \"role\"}, {\"model\": \"Carrier\", \"field\": \"shipping_method\"}]'"
|
||||
),
|
||||
required=True,
|
||||
type=str,
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="lang",
|
||||
description="Jazyk pro labely (např. 'cz', 'en')",
|
||||
required=False,
|
||||
type=str,
|
||||
),
|
||||
],
|
||||
responses={
|
||||
200: {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"value": {"type": "string", "description": "Logická hodnota pro backend"},
|
||||
"label": {"type": "string", "description": "Human-readable label podle zvoleného jazyka"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
@api_view(["GET"])
|
||||
def choices(request):
|
||||
"""
|
||||
Endpoint pro získání choices pro dropdowny s podporou vícejazyčných labelů.
|
||||
Formát labelu v modelu: 'lang#Label', např. 'cz#Administrátor'.
|
||||
Podporuje více modelů a polí najednou.
|
||||
"""
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
fields_param = request.query_params.get("fields")
|
||||
lang = request.query_params.get("lang", None)
|
||||
|
||||
try:
|
||||
fields_list = json.loads(fields_param)
|
||||
except Exception:
|
||||
return Response({"error": "Invalid 'fields' parameter, must be valid JSON"}, status=400)
|
||||
|
||||
result = {}
|
||||
for item in fields_list:
|
||||
model_name = item.get("model")
|
||||
field_name = item.get("field")
|
||||
if not model_name or not field_name:
|
||||
continue
|
||||
|
||||
model = apps.get_model("app_name", model_name)
|
||||
field = model._meta.get_field(field_name)
|
||||
|
||||
choices_data = []
|
||||
for value, label in field.choices:
|
||||
if "#" in label and lang:
|
||||
label_parts = label.split("#", 1)
|
||||
if label_parts[0] == lang:
|
||||
label = label_parts[1]
|
||||
else:
|
||||
label = label_parts[1] # fallback na defaultní text (po #)
|
||||
elif "#" in label:
|
||||
label = label.split("#", 1)[1]
|
||||
choices_data.append({"value": value, "label": label})
|
||||
|
||||
result[f"{model_name}.{field_name}"] = choices_data
|
||||
|
||||
return Response(result)
|
||||
Reference in New Issue
Block a user