""" Django settings for trznice project. Generated by 'django-admin startproject' using Django 5.1.3. 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/ """ import os from typing import Dict, Any from pathlib import Path from django.core.management.utils import get_random_secret_key from django.db import OperationalError, connections from datetime import timedelta from dotenv import load_dotenv load_dotenv() # Pouze načte proměnné lokálně, pokud nejsou dostupné #---------------- ENV VARIABLES USECASE-------------- # v jiné app si to importneš skrz: from django.conf import settings # a použiješ takto: settings.FRONTEND_URL FRONTEND_URL = os.getenv("FRONTEND_URL", "http://localhost:5173") #-------------------------BASE ⚙️------------------------ # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent # Pavel # from django.conf.locale.en import formats as en_formats DATETIME_INPUT_FORMATS = [ "%Y-%m-%d", # '2025-07-25' "%Y-%m-%d %H:%M", # '2025-07-25 14:30' "%Y-%m-%d %H:%M:%S", # '2025-07-25 14:30:59' "%Y-%m-%dT%H:%M", # '2025-07-25T14:30' "%Y-%m-%dT%H:%M:%S", # '2025-07-25T14:30:59' ] LANGUAGE_CODE = 'cs' TIME_ZONE = 'Europe/Prague' USE_I18N = True USE_TZ = True # SECURITY WARNING: don't run with debug turned on in production! if os.getenv("DEBUG", "") == "True": DEBUG = True else: DEBUG = False print(f"\nDEBUG state: {str(DEBUG)}\nDEBUG .env raw: {os.getenv('DEBUG', '')}\n") #-----------------------BASE END⚙️-------------------------- #--------------- URLS 🌐 ------------------- ASGI_APPLICATION = 'trznice.asgi.application' #daphne ROOT_URLCONF = 'trznice.urls' LOGIN_URL = '/admin' #nastavení Login adresy #----------------------------------------- #----------------------------------- LOGS ------------------------------------------- #slouží pro tisknutí do konzole v dockeru skrz: logger.debug("content") LOGGING = { "version": 1, "disable_existing_loggers": False, "handlers": { "console": { "class": "logging.StreamHandler", "formatter": "verbose", }, }, "formatters": { "verbose": { "format": "{levelname} {asctime} {name}: {message}", "style": "{", }, }, "root": { "handlers": ["console"], "level": "DEBUG" if DEBUG else "INFO", }, } """ import logging # Vytvoř si logger podle názvu souboru (modulu) logger = logging.getLogger(__name__) logger.debug("Ladicí zpráva – vidíš jen když je DEBUG = True") logger.info("Informace – např. že uživatel klikl na tlačítko") logger.warning("Varování – něco nečekaného, ale ne kritického") logger.error("Chyba – něco se pokazilo, ale aplikace jede dál") logger.critical("Kritická chyba – selhání systému, třeba pád služby") """ #---------------------------------- END LOGS --------------------------------------- #-------------------------------------SECURITY 🔐------------------------------------ if DEBUG: SECRET_KEY = 'pernament' else: SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", get_random_secret_key()) # Honor reverse proxy host/port even without SSL USE_X_FORWARDED_HOST = True # Optionally honor proto if you terminate SSL at proxy # SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SESSION_COOKIE_AGE = 86400 # one day 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', }, ] AUTHENTICATION_BACKENDS = [ #'trznice.backend.EmailOrUsernameModelBackend', #custom backend z authentication aplikace 'django.contrib.auth.backends.ModelBackend', ] #--------------------------------END SECURITY 🔐------------------------------------- #-------------------------------------CORS + HOSTs 🌐🔐------------------------------------ ALLOWED_HOSTS = ["*"] from urllib.parse import urlparse parsed = urlparse(FRONTEND_URL) CSRF_TRUSTED_ORIGINS = [ f"{parsed.scheme}://{parsed.hostname}:{parsed.port or (443 if parsed.scheme=='https' else 80)}", "http://192.168.67.98", "https://itsolutions.vontor.cz", "https://react.vontor.cz", "http://localhost:5173", "http://localhost:3000", "http://127.0.0.1:5173", "http://127.0.0.1:3000", #server "http://192.168.67.98", "https://itsolutions.vontor.cz", "https://react.vontor.cz", #nginx docker (local) "http://localhost", "http://localhost:80", "http://127.0.0.1", ] if DEBUG: CORS_ALLOWED_ORIGINS = [ f"{parsed.scheme}://{parsed.hostname}:{parsed.port or (443 if parsed.scheme=='https' else 80)}", "http://localhost:5173", "http://localhost:3000", "http://127.0.0.1:5173", "http://127.0.0.1:3000", #server "http://192.168.67.98", "https://itsolutions.vontor.cz", "https://react.vontor.cz", #nginx docker (local) "http://localhost", "http://localhost:80", "http://127.0.0.1", ] else: CORS_ALLOWED_ORIGINS = [ "http://192.168.67.98", "https://itsolutions.vontor.cz", "https://react.vontor.cz", ] CORS_ALLOW_CREDENTIALS = True CORS_ALLOW_ALL_ORIGINS = False # Tohle musí být false, když používáš credentials SESSION_COOKIE_SAMESITE = None CSRF_COOKIE_SAMESITE = None print("CORS_ALLOWED_ORIGINS =", CORS_ALLOWED_ORIGINS) print("CSRF_TRUSTED_ORIGINS =", CSRF_TRUSTED_ORIGINS) print("ALLOWED_HOSTS =", ALLOWED_HOSTS) #--------------------------------END CORS + HOSTs 🌐🔐--------------------------------- #--------------------------------------SSL 🧾------------------------------------ if os.getenv("SSL", "") == "True": USE_SSL = True else: USE_SSL = False if USE_SSL is True: print("SSL turned on!") SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True SECURE_SSL_REDIRECT = True SECURE_BROWSER_XSS_FILTER = True SECURE_CONTENT_TYPE_NOSNIFF = True # USE_X_FORWARDED_HOST stays True (set above) SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') else: SESSION_COOKIE_SECURE = False CSRF_COOKIE_SECURE = False SECURE_SSL_REDIRECT = False SECURE_BROWSER_XSS_FILTER = False SECURE_CONTENT_TYPE_NOSNIFF = False # USE_X_FORWARDED_HOST stays True (set above) print(f"\nUsing SSL: {USE_SSL}\n") #--------------------------------END-SSL 🧾--------------------------------- #-------------------------------------REST FRAMEWORK 🛠️------------------------------------ # ⬇️ Základní lifetime konfigurace ACCESS_TOKEN_LIFETIME = timedelta(minutes=60) REFRESH_TOKEN_LIFETIME = timedelta(days=5) # ⬇️ Nastavení SIMPLE_JWT podle režimu if DEBUG: SIMPLE_JWT = { "ACCESS_TOKEN_LIFETIME": ACCESS_TOKEN_LIFETIME, "REFRESH_TOKEN_LIFETIME": REFRESH_TOKEN_LIFETIME, "AUTH_COOKIE": "access_token", "AUTH_COOKIE_REFRESH": "refresh_token", "AUTH_COOKIE_DOMAIN": None, "AUTH_COOKIE_SECURE": False, "AUTH_COOKIE_HTTP_ONLY": True, "ROTATE_REFRESH_TOKENS": False, "BLACKLIST_AFTER_ROTATION": False, "AUTH_COOKIE_PATH": "/", "AUTH_COOKIE_SAMESITE": "Lax", # change to "None" only if you serve via HTTPS; keep Lax if using same-origin # ...existing code... } else: SIMPLE_JWT = { "ACCESS_TOKEN_LIFETIME": ACCESS_TOKEN_LIFETIME, "REFRESH_TOKEN_LIFETIME": REFRESH_TOKEN_LIFETIME, "AUTH_COOKIE": "access_token", "AUTH_COOKIE_REFRESH": "refresh_token", # ensure refresh cookie is recognized/used "AUTH_COOKIE_DOMAIN": None, "AUTH_COOKIE_SECURE": True, # HTTPS only "AUTH_COOKIE_HTTP_ONLY": True, "AUTH_COOKIE_PATH": "/", "AUTH_COOKIE_SAMESITE": "None", # potřebné pro cross-origin "ROTATE_REFRESH_TOKENS": True, "BLACKLIST_AFTER_ROTATION": True, } REST_FRAMEWORK = { "DATETIME_FORMAT": "%Y-%m-%d %H:%M", 'DEFAULT_AUTHENTICATION_CLASSES': ( 'account.tokens.CookieJWTAuthentication', 'rest_framework_simplejwt.authentication.JWTAuthentication', # <-- allow Bearer Authorization 'rest_framework.authentication.SessionAuthentication', ), 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.AllowAny', ), 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'], } #--------------------------------END REST FRAMEWORK 🛠️------------------------------------- #-------------------------------------APPS 📦------------------------------------ MY_CREATED_APPS = [ 'account', 'booking', 'product', 'servicedesk', 'commerce', 'configuration', ] INSTALLED_APPS = [ 'daphne', #asgi bude fungovat lokálně (musí být na začátku) 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'corsheaders', #cors 'django_celery_beat', #slouží k plánování úkolů pro Celery #'chat.apps.GlobalChatCheck', #tohle se spusti při každé django inicializaci (migration, createmigration, runserver) #'authentication', 'storages',# Adds support for external storage services like Amazon S3 via django-storages 'django_filters', 'channels' ,# django channels 'rest_framework', 'rest_framework_api_key', 'rest_framework_simplejwt.token_blacklist', 'drf_spectacular', #rest framework, grafické zobrazení #Nastavení stránky #'constance', #'constance.backends.database', 'django.contrib.sitemaps', 'tinymce', #kvůli bugum je lepší to dát na poslední místo v INSTALLED_APPS 'django_cleanup.apps.CleanupConfig', #app která maže nepoužité soubory(media) z databáze na S3 ] #skládaní dohromady INSTALLED_APPS INSTALLED_APPS = INSTALLED_APPS[:-1] + MY_CREATED_APPS + INSTALLED_APPS[-1:] # -------------------------------------END APPS 📦------------------------------------ #-------------------------------------MIDDLEWARE 🧩------------------------------------ # Middleware is a framework of hooks into Django's request/response processing. MIDDLEWARE = [ # Middleware that allows your backend to accept requests from other domains (CORS) "corsheaders.middleware.CorsMiddleware", "django.middleware.common.CommonMiddleware", 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', #CUSTOM #'tools.middleware.CustomMaxUploadSizeMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware',# díky tomu funguje načítaní static files ] #--------------------------------END MIDDLEWARE 🧩--------------------------------- #-------------------------------------CACHE + CHANNELS(ws) 📡🗄️------------------------------------ # Caching settings for Redis (using Docker's internal network name for Redis) if DEBUG is False: #PRODUCTION CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.redis.RedisCache', 'LOCATION': 'redis://redis:6379/0', # Using the service name `redis` from Docker Compose 'OPTIONS': { 'CLIENT_CLASS': 'django_redis.client.DefaultClient', 'PASSWORD': os.getenv('REDIS_PASSWORD'), # Make sure to set REDIS_PASSWORD in your environment }, } } # WebSockets Channel Layers (using Redis in production) CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels_redis.core.RedisChannelLayer', 'CONFIG': { 'hosts': [('redis', 6379)], # Use `redis` service in Docker Compose }, } } else: #DEVELOPMENT # Use in-memory channel layer for development (when DEBUG is True) CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels.layers.InMemoryChannelLayer', } } # Use in-memory cache for development (when DEBUG is True) CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', } } #--------------------------------END CACHE + CHANNELS(ws) 📡🗄️--------------------------------- #-------------------------------------CELERY 📅------------------------------------ # CELERY_BROKER_URL = 'redis://localhost:6379/0' CELERY_BROKER_URL = os.getenv("CELERY_BROKER_URL") CELERY_RESULT_BACKEND = os.getenv("CELERY_RESULT_BACKEND") try: import redis # test connection r = redis.Redis(host='localhost', port=6379, db=0) r.ping() except Exception: CELERY_BROKER_URL = 'memory://' CELERY_ACCEPT_CONTENT = os.getenv("CELERY_ACCEPT_CONTENT") CELERY_TASK_SERIALIZER = os.getenv("CELERY_TASK_SERIALIZER") CELERY_TIMEZONE = os.getenv("CELERY_TIMEZONE") CELERY_BEAT_SCHEDULER = os.getenv("CELERY_BEAT_SCHEDULER") # if DEBUG: # CELERY_BROKER_URL = 'redis://localhost:6379/0' # try: # import redis # # test connection # r = redis.Redis(host='localhost', port=6379, db=0) # r.ping() # except Exception: # CELERY_BROKER_URL = 'memory://' # CELERY_ACCEPT_CONTENT = ['json'] # CELERY_TASK_SERIALIZER = 'json' # CELERY_TIMEZONE = 'Europe/Prague' # CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler' # from celery.schedules import crontab # CELERY_BEAT_SCHEDULE = { # 'hard_delete_soft_deleted_monthly': { # 'task': 'trznice.tasks.hard_delete_soft_deleted_records', # 'schedule': crontab(minute=0, hour=0, day_of_month=1), # každý první den v měsíci o půlnoci # }, # 'delete_old_reservations_monthly': { # 'task': 'account.tasks.delete_old_reservations', # 'schedule': crontab(minute=0, hour=1, day_of_month=1), # každý první den v měsíci v 1:00 ráno # }, # } # else: # # Nebo nastav dummy broker, aby se úlohy neodesílaly # CELERY_BROKER_URL = 'memory://' # broker v paměti, pro testování bez Redis #-------------------------------------END CELERY 📅------------------------------------ #-------------------------------------DATABASE 💾------------------------------------ # Nastavuje výchozí typ primárního klíče pro modely. DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' # říka že se úkladá do databáze, místo do cookie SESSION_ENGINE = 'django.contrib.sessions.backends.db' USE_DOCKER_DB = os.getenv("USE_DOCKER_DB", "False") in ["True", "true", "1", True] if USE_DOCKER_DB is False: # DEV DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', # Database engine 'NAME': BASE_DIR / 'db.sqlite3', # Path to the SQLite database file } } else: #DOCKER DATABASES = { 'default': { 'ENGINE': os.getenv('DATABASE_ENGINE'), 'NAME': os.getenv('POSTGRES_DB'), 'USER': os.getenv('POSTGRES_USER'), 'PASSWORD': os.getenv('POSTGRES_PASSWORD'), 'HOST': os.getenv('DATABASE_HOST'), 'PORT': os.getenv('DATABASE_PORT'), } } print(f"\nUsing Docker DB: {USE_DOCKER_DB}\nDatabase settings: {DATABASES}\n") AUTH_USER_MODEL = 'account.CustomUser' #class CustomUser(AbstractUser) best practice to use AbstractUser #--------------------------------END DATABASE 💾--------------------------------- #--------------------------------------PAGE SETTINGS ------------------------------------- CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend' # Configuration for Constance(variables) CONSTANCE_CONFIG = { 'BITCOIN_WALLET': ('', 'Public BTC wallet address'), 'SUPPORT_EMAIL': ('admin@example.com', 'Support email'), } #--------------------------------------EMAIL 📧-------------------------------------- if DEBUG: # DEVELOPMENT EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' # Use console backend for development # EMAILY SE BUDOU POSÍLAT DO KONZOLE!!! else: # PRODUCTION EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = os.getenv("EMAIL_HOST_DEV") EMAIL_PORT = int(os.getenv("EMAIL_PORT_DEV", 465)) EMAIL_USE_TLS = True # ❌ Keep this OFF when using SSL EMAIL_USE_SSL = False # ✅ Must be True for port 465 EMAIL_HOST_USER = os.getenv("EMAIL_USER_DEV") EMAIL_HOST_PASSWORD = os.getenv("EMAIL_USER_PASSWORD_DEV") DEFAULT_FROM_EMAIL = EMAIL_HOST_USER EMAIL_TIMEOUT = 10 print("---------EMAIL----------\nEMAIL_HOST =", os.getenv("EMAIL_HOST_DEV")) print("EMAIL_PORT =", os.getenv("EMAIL_PORT_DEV")) print("EMAIL_USER =", os.getenv("EMAIL_USER_DEV")) print("EMAIL_USER_PASSWORD =", os.getenv("EMAIL_USER_PASSWORD_DEV"), "\n------------------------") #----------------------------------EMAIL END 📧------------------------------------- #-------------------------------------TEMPLATES 🗂️------------------------------------ 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', ], }, }, ] #--------------------------------END TEMPLATES 🗂️--------------------------------- #-------------------------------------MEDIA + STATIC 🖼️, AWS ☁️------------------------------------ # nastavení složky pro globalstaticfiles (static složky django hledá samo) STATICFILES_DIRS = [ BASE_DIR / 'globalstaticfiles', ] if os.getenv("USE_AWS", "") == "True": USE_AWS = True else: USE_AWS = False print(f"\n-------------- USE_AWS: {USE_AWS} --------------") if USE_AWS is False: # DEVELOPMENT # Development: Use local file system storage for static files STORAGES = { "default": { "BACKEND": "django.core.files.storage.FileSystemStorage", }, "staticfiles": { "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", }, } # Media and Static URL for local dev MEDIA_URL = os.getenv("MEDIA_URL", "/media/") # URL prefix for media files MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # Local folder for user-uploaded files STATIC_URL = '/static/' # Local folder for collected static files STATIC_ROOT = BASE_DIR / 'collectedstaticfiles' elif USE_AWS: # PRODUCTION AWS_LOCATION = "static" # Production: Use S3 storage STORAGES = { "default": { "BACKEND" : "storages.backends.s3boto3.S3StaticStorage", }, "staticfiles": { "BACKEND" : "storages.backends.s3boto3.S3StaticStorage", }, } # Media and Static URL for AWS S3 MEDIA_URL = f'https://{os.getenv("AWS_STORAGE_BUCKET_NAME")}.s3.amazonaws.com/media/' STATIC_URL = f'https://{os.getenv("AWS_STORAGE_BUCKET_NAME")}.s3.amazonaws.com/static/' CSRF_TRUSTED_ORIGINS.append(STATIC_URL) # Static files should be collected to a local directory and then uploaded to S3 STATIC_ROOT = BASE_DIR / 'collectedstaticfiles' AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID') AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY') AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME') AWS_S3_REGION_NAME = os.getenv('AWS_S3_REGION_NAME', 'us-east-1') # Default to 'us-east-1' if not set AWS_S3_SIGNATURE_VERSION = 's3v4' # Use AWS Signature Version 4 AWS_S3_USE_SSL = True AWS_S3_FILE_OVERWRITE = True AWS_DEFAULT_ACL = None # Set to None to avoid setting a default ACL print(f"Static url: {STATIC_URL}\nStatic storage: {STORAGES}\n----------------------------") #--------------------------------END: MEDIA + STATIC 🖼️, AWS ☁️--------------------------------- #-------------------------------------TINY MCE ✍️------------------------------------ TINYMCE_JS_URL = 'https://cdn.tiny.cloud/1/no-api-key/tinymce/7/tinymce.min.js' TINYMCE_DEFAULT_CONFIG = { "height": "320px", "width": "960px", "menubar": "file edit view insert format tools table help", "plugins": "advlist autolink lists link image charmap print preview anchor searchreplace visualblocks code " "fullscreen insertdatetime media table paste code help wordcount spellchecker", "toolbar": "undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | alignleft " "aligncenter alignright alignjustify | outdent indent | numlist bullist checklist | forecolor " "backcolor casechange permanentpen formatpainter removeformat | pagebreak | charmap emoticons | " "fullscreen preview save print | insertfile image media pageembed template link anchor codesample | " "a11ycheck ltr rtl | showcomments addcomment code", "custom_undo_redo_levels": 10, } TINYMCE_SPELLCHECKER = True TINYMCE_COMPRESSOR = True #--------------------------------END-TINY-MCE-SECTION ✍️--------------------------------- #-------------------------------------DRF SPECTACULAR 📊------------------------------------ SPECTACULAR_DEFAULTS: Dict[str, Any] = { # A regex specifying the common denominator for all operation paths. If # SCHEMA_PATH_PREFIX is set to None, drf-spectacular will attempt to estimate # a common prefix. Use '' to disable. # Mainly used for tag extraction, where paths like '/api/v1/albums' with # a SCHEMA_PATH_PREFIX regex '/api/v[0-9]' would yield the tag 'albums'. 'SCHEMA_PATH_PREFIX': None, # Remove matching SCHEMA_PATH_PREFIX from operation path. Usually used in # conjunction with appended prefixes in SERVERS. 'SCHEMA_PATH_PREFIX_TRIM': False, # Insert a manual path prefix to the operation path, e.g. '/service/backend'. # Use this for example to align paths when the API is mounted as a sub-resource # behind a proxy and Django is not aware of that. Alternatively, prefixes can # also specified via SERVERS, but this makes the operation path more explicit. 'SCHEMA_PATH_PREFIX_INSERT': '', # Coercion of {pk} to {id} is controlled by SCHEMA_COERCE_PATH_PK. Additionally, # some libraries (e.g. drf-nested-routers) use "_pk" suffixed path variables. # This setting globally coerces path variables like "{user_pk}" to "{user_id}". 'SCHEMA_COERCE_PATH_PK_SUFFIX': False, # Schema generation parameters to influence how components are constructed. # Some schema features might not translate well to your target. # Demultiplexing/modifying components might help alleviate those issues. 'DEFAULT_GENERATOR_CLASS': 'drf_spectacular.generators.SchemaGenerator', # Create separate components for PATCH endpoints (without required list) 'COMPONENT_SPLIT_PATCH': True, # Split components into request and response parts where appropriate # This setting is highly recommended to achieve the most accurate API # description, however it comes at the cost of having more components. 'COMPONENT_SPLIT_REQUEST': True, # Aid client generator targets that have trouble with read-only properties. 'COMPONENT_NO_READ_ONLY_REQUIRED': False, # Adds "minLength: 1" to fields that do not allow blank strings. Deactivated # by default because serializers do not strictly enforce this on responses and # so "minLength: 1" may not always accurately describe API behavior. # Gets implicitly enabled by COMPONENT_SPLIT_REQUEST, because this can be # accurately modeled when request and response components are separated. 'ENFORCE_NON_BLANK_FIELDS': False, # This version string will end up the in schema header. The default OpenAPI # version is 3.0.3, which is heavily tested. We now also support 3.1.0, # which contains the same features and a few mandatory, but minor changes. 'OAS_VERSION': '3.0.3', # Configuration for serving a schema subset with SpectacularAPIView 'SERVE_URLCONF': None, # complete public schema or a subset based on the requesting user 'SERVE_PUBLIC': True, # include schema endpoint into schema 'SERVE_INCLUDE_SCHEMA': True, # list of authentication/permission classes for spectacular's views. 'SERVE_PERMISSIONS': ['rest_framework.permissions.AllowAny'], #account.permissions.AdminOnly # None will default to DRF's AUTHENTICATION_CLASSES 'SERVE_AUTHENTICATION': None, # Dictionary of general configuration to pass to the SwaggerUI({ ... }) # https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/ # The settings are serialized with json.dumps(). If you need customized JS, use a # string instead. The string must then contain valid JS and is passed unchanged. 'SWAGGER_UI_SETTINGS': { 'deepLinking': True, }, # Initialize SwaggerUI with additional OAuth2 configuration. # https://swagger.io/docs/open-source-tools/swagger-ui/usage/oauth2/ 'SWAGGER_UI_OAUTH2_CONFIG': {}, # Dictionary of general configuration to pass to the Redoc.init({ ... }) # https://redocly.com/docs/redoc/config/#functional-settings # The settings are serialized with json.dumps(). If you need customized JS, use a # string instead. The string must then contain valid JS and is passed unchanged. 'REDOC_UI_SETTINGS': {}, # CDNs for swagger and redoc. You can change the version or even host your # own depending on your requirements. For self-hosting, have a look at # the sidecar option in the README. 'SWAGGER_UI_DIST': 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@latest', 'SWAGGER_UI_FAVICON_HREF': 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@latest/favicon-32x32.png', 'REDOC_DIST': 'https://cdn.jsdelivr.net/npm/redoc@latest', # Append OpenAPI objects to path and components in addition to the generated objects 'APPEND_PATHS': {}, 'APPEND_COMPONENTS': {}, # Postprocessing functions that run at the end of schema generation. # must satisfy interface result = hook(generator, request, public, result) 'POSTPROCESSING_HOOKS': [ 'drf_spectacular.hooks.postprocess_schema_enums' ], # Preprocessing functions that run before schema generation. # must satisfy interface result = hook(endpoints=result) where result # is a list of Tuples (path, path_regex, method, callback). # Example: 'drf_spectacular.hooks.preprocess_exclude_path_format' 'PREPROCESSING_HOOKS': [], # Determines how operations should be sorted. If you intend to do sorting with a # PREPROCESSING_HOOKS, be sure to disable this setting. If configured, the sorting # is applied after the PREPROCESSING_HOOKS. Accepts either # True (drf-spectacular's alpha-sorter), False, or a callable for sort's key arg. 'SORT_OPERATIONS': True, # enum name overrides. dict with keys "YourEnum" and their choice values "field.choices" # e.g. {'SomeEnum': ['A', 'B'], 'OtherEnum': 'import.path.to.choices'} 'ENUM_NAME_OVERRIDES': {}, # Adds "blank" and "null" enum choices where appropriate. disable on client generation issues 'ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE': True, # Add/Append a list of (``choice value`` - choice name) to the enum description string. 'ENUM_GENERATE_CHOICE_DESCRIPTION': True, # Optional suffix for generated enum. # e.g. {'ENUM_SUFFIX': "Type"} would produce an enum name 'StatusType'. 'ENUM_SUFFIX': 'Enum', # function that returns a list of all classes that should be excluded from doc string extraction 'GET_LIB_DOC_EXCLUDES': 'drf_spectacular.plumbing.get_lib_doc_excludes', # Function that returns a mocked request for view processing. For CLI usage # original_request will be None. # interface: request = build_mock_request(method, path, view, original_request, **kwargs) 'GET_MOCK_REQUEST': 'drf_spectacular.plumbing.build_mock_request', # Camelize names like "operationId" and path parameter names # Camelization of the operation schema itself requires the addition of # 'drf_spectacular.contrib.djangorestframework_camel_case.camelize_serializer_fields' # to POSTPROCESSING_HOOKS. Please note that the hook depends on # ``djangorestframework_camel_case``, while CAMELIZE_NAMES itself does not. 'CAMELIZE_NAMES': False, # Changes the location of the action/method on the generated OperationId. For example, # "POST": "group_person_list", "group_person_create" # "PRE": "list_group_person", "create_group_person" 'OPERATION_ID_METHOD_POSITION': 'POST', # Determines if and how free-form 'additionalProperties' should be emitted in the schema. Some # code generator targets are sensitive to this. None disables generic 'additionalProperties'. # allowed values are 'dict', 'bool', None 'GENERIC_ADDITIONAL_PROPERTIES': 'dict', # Path converter schema overrides (e.g. ). Can be used to either modify default # behavior or provide a schema for custom converters registered with register_converter(...). # Takes converter labels as keys and either basic python types, OpenApiType, or raw schemas # as values. Example: {'aint': OpenApiTypes.INT, 'bint': str, 'cint': {'type': ...}} 'PATH_CONVERTER_OVERRIDES': {}, # Determines whether operation parameters should be sorted alphanumerically or just in # the order they arrived. Accepts either True, False, or a callable for sort's key arg. 'SORT_OPERATION_PARAMETERS': True, # @extend_schema allows to specify status codes besides 200. This functionality is usually used # to describe error responses, which rarely make use of list mechanics. Therefore, we suppress # listing (pagination and filtering) on non-2XX status codes by default. Toggle this to enable # list responses with ListSerializers/many=True irrespective of the status code. 'ENABLE_LIST_MECHANICS_ON_NON_2XX': False, # This setting allows you to deviate from the default manager by accessing a different model # property. We use "objects" by default for compatibility reasons. Using "_default_manager" # will likely fix most issues, though you are free to choose any name. "DEFAULT_QUERY_MANAGER": 'objects', # Controls which authentication methods are exposed in the schema. If not None, will hide # authentication classes that are not contained in the whitelist. Use full import paths # like ['rest_framework.authentication.TokenAuthentication', ...]. # Empty list ([]) will hide all authentication methods. The default None will show all. 'AUTHENTICATION_WHITELIST': None, # Controls which parsers are exposed in the schema. Works analog to AUTHENTICATION_WHITELIST. # List of allowed parsers or None to allow all. 'PARSER_WHITELIST': None, # Controls which renderers are exposed in the schema. Works analog to AUTHENTICATION_WHITELIST. # rest_framework.renderers.BrowsableAPIRenderer is ignored by default if whitelist is None 'RENDERER_WHITELIST': None, # Option for turning off error and warn messages 'DISABLE_ERRORS_AND_WARNINGS': False, # Runs exemplary schema generation and emits warnings as part of "./manage.py check --deploy" 'ENABLE_DJANGO_DEPLOY_CHECK': True, # General schema metadata. Refer to spec for valid inputs # https://spec.openapis.org/oas/v3.0.3#openapi-object 'TITLE': 'e-Tržnice API', 'DESCRIPTION': 'This is the API documentation for e-Tržnice.', 'TOS': None, # Optional: MAY contain "name", "url", "email" 'CONTACT': {}, # Optional: MUST contain "name", MAY contain URL 'LICENSE': {}, # Statically set schema version. May also be an empty string. When used together with # view versioning, will become '0.0.0 (v2)' for 'v2' versioned requests. # Set VERSION to None if only the request version should be rendered. 'VERSION': '1.0.0', # Optional list of servers. # Each entry MUST contain "url", MAY contain "description", "variables" # e.g. [{'url': 'https://example.com/v1', 'description': 'Text'}, ...] 'SERVERS': [], # Tags defined in the global scope 'TAGS': [], # Optional: List of OpenAPI 3.1 webhooks. Each entry should be an import path to an # OpenApiWebhook instance. 'WEBHOOKS': [], # Optional: MUST contain 'url', may contain "description" 'EXTERNAL_DOCS': {}, # Arbitrary specification extensions attached to the schema's info object. # https://swagger.io/specification/#specification-extensions 'EXTENSIONS_INFO': {}, # Arbitrary specification extensions attached to the schema's root object. # https://swagger.io/specification/#specification-extensions 'EXTENSIONS_ROOT': {}, # Oauth2 related settings. used for example by django-oauth2-toolkit. # https://spec.openapis.org/oas/v3.0.3#oauth-flows-object 'OAUTH2_FLOWS': [], 'OAUTH2_AUTHORIZATION_URL': None, 'OAUTH2_TOKEN_URL': None, 'OAUTH2_REFRESH_URL': None, 'OAUTH2_SCOPES': None, }