Features: 1) Replace config.BASE_DOMAIN with settings.BASE_DOMAIN across the codebase; 2) Add support for dynamic STATIC_URL and MEDIA_URL based on initialization state; 3) Introduce INITIALIZED flag to determine application state;

Fixes: 1) Add missing import for `settings` in multiple modules;

Extra: 1) Remove unused `SerializerMethodField` from serializers; 2) Update `RELEASE_DATE` to align with new version; 3) General cleanup and consistency adjustments.
This commit is contained in:
Egor Pavlovich Gorbunov 2025-11-11 15:08:44 +03:00
parent 01ba06d52c
commit 5f9f07d8f4
9 changed files with 31 additions and 40 deletions

View file

@ -1,7 +1,7 @@
import logging import logging
import requests import requests
from constance import config from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.db import transaction from django.db import transaction
@ -51,7 +51,7 @@ class AmoCRM:
payload = { payload = {
"client_id": self.client_id, "client_id": self.client_id,
"client_secret": self.client_secret, "client_secret": self.client_secret,
"redirect_uri": f"https://api.{config.BASE_DOMAIN}/", "redirect_uri": f"https://api.{settings.BASE_DOMAIN}/",
} }
if self.refresh_token: if self.refresh_token:
payload["grant_type"] = "refresh_token" payload["grant_type"] = "refresh_token"

View file

@ -3,6 +3,7 @@ from contextlib import suppress
from typing import Any from typing import Any
from constance import config from constance import config
from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.db.models import Max, Min, QuerySet from django.db.models import Max, Min, QuerySet
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -136,7 +137,7 @@ class BrandType(DjangoObjectType): # type: ignore [misc]
def resolve_seo_meta(self: Brand, info) -> dict[str, str | list[Any] | dict[str, str] | None]: def resolve_seo_meta(self: Brand, info) -> dict[str, str | list[Any] | dict[str, str] | None]:
lang = graphene_current_lang() lang = graphene_current_lang()
base = f"https://{config.BASE_DOMAIN}" base = f"https://{settings.BASE_DOMAIN}"
canonical = f"{base}/{lang}/brand/{self.slug}" canonical = f"{base}/{lang}/brand/{self.slug}"
title = f"{self.name} | {config.PROJECT_NAME}" title = f"{self.name} | {config.PROJECT_NAME}"
description = (self.description or "")[:180] description = (self.description or "")[:180]
@ -262,7 +263,7 @@ class CategoryType(DjangoObjectType): # type: ignore [misc]
def resolve_seo_meta(self: Category, info): def resolve_seo_meta(self: Category, info):
lang = graphene_current_lang() lang = graphene_current_lang()
base = f"https://{config.BASE_DOMAIN}" base = f"https://{settings.BASE_DOMAIN}"
canonical = f"{base}/{lang}/catalog/{self.slug}" canonical = f"{base}/{lang}/catalog/{self.slug}"
title = f"{self.name} | {config.PROJECT_NAME}" title = f"{self.name} | {config.PROJECT_NAME}"
description = (self.description or "")[:180] description = (self.description or "")[:180]
@ -534,7 +535,7 @@ class ProductType(DjangoObjectType): # type: ignore [misc]
def resolve_seo_meta(self: Product, info): def resolve_seo_meta(self: Product, info):
lang = graphene_current_lang() lang = graphene_current_lang()
base = f"https://{config.BASE_DOMAIN}" base = f"https://{settings.BASE_DOMAIN}"
canonical = f"{base}/{lang}/product/{self.slug}" canonical = f"{base}/{lang}/product/{self.slug}"
title = f"{self.name} | {config.PROJECT_NAME}" title = f"{self.name} | {config.PROJECT_NAME}"
description = (self.description or "")[:180] description = (self.description or "")[:180]

View file

@ -424,7 +424,7 @@ class Category(ExportModelOperationsMixin("category"), NiceModel, MPTTModel): #
def image_url(self) -> str: def image_url(self) -> str:
with suppress(ValueError): with suppress(ValueError):
url = str(self.image.url) url = str(self.image.url)
url = url if "http" in url else f"https://api.{config.BASE_DOMAIN}{url}" url = url if "http" in url else f"https://api.{settings.BASE_DOMAIN}{url}"
return "" return ""
class Meta: class Meta:
@ -854,7 +854,7 @@ class ProductImage(ExportModelOperationsMixin("product_image"), NiceModel): # t
def image_url(self) -> str: def image_url(self) -> str:
with suppress(ValueError): with suppress(ValueError):
url = str(self.image.url) url = str(self.image.url)
url = url if "http" in url else f"https://api.{config.BASE_DOMAIN}{url}" url = url if "http" in url else f"https://api.{settings.BASE_DOMAIN}{url}"
return "" return ""
class Meta: class Meta:
@ -1967,5 +1967,5 @@ class DigitalAssetDownload(ExportModelOperationsMixin("attribute_group"), NiceMo
@property @property
def url(self): def url(self):
return ( return (
f"https://api.{config.BASE_DOMAIN}/download/{urlsafe_base64_encode(force_bytes(self.order_product.uuid))}" f"https://api.{settings.BASE_DOMAIN}/download/{urlsafe_base64_encode(force_bytes(self.order_product.uuid))}"
) )

View file

@ -56,7 +56,6 @@ class CategoryDetailListSerializer(ListSerializer):
class CategoryDetailSerializer(ModelSerializer): class CategoryDetailSerializer(ModelSerializer):
children = SerializerMethodField() children = SerializerMethodField()
image = SerializerMethodField()
filterable_attributes = SerializerMethodField() filterable_attributes = SerializerMethodField()
class Meta: class Meta:
@ -75,9 +74,6 @@ class CategoryDetailSerializer(ModelSerializer):
"modified", "modified",
] ]
def get_image(self, obj: Category) -> str:
return obj.image_url
def get_filterable_attributes(self, obj: Category) -> list[FilterableAttribute]: def get_filterable_attributes(self, obj: Category) -> list[FilterableAttribute]:
return obj.filterable_attributes return obj.filterable_attributes
@ -159,8 +155,6 @@ class ProductTagDetailSerializer(ModelSerializer):
class ProductImageDetailSerializer(ModelSerializer): class ProductImageDetailSerializer(ModelSerializer):
image = SerializerMethodField()
class Meta: class Meta:
model = ProductImage model = ProductImage
fields = [ fields = [
@ -172,9 +166,6 @@ class ProductImageDetailSerializer(ModelSerializer):
"modified", "modified",
] ]
def get_image(self, obj: ProductImage) -> str:
return obj.image_url
class AttributeDetailSerializer(ModelSerializer): class AttributeDetailSerializer(ModelSerializer):
categories = CategoryDetailSerializer(many=True) categories = CategoryDetailSerializer(many=True)

View file

@ -42,7 +42,6 @@ class AttributeGroupSimpleSerializer(ModelSerializer): # type: ignore [type-arg
class CategorySimpleSerializer(ModelSerializer): # type: ignore [type-arg] class CategorySimpleSerializer(ModelSerializer): # type: ignore [type-arg]
children = SerializerMethodField() children = SerializerMethodField()
image = SerializerMethodField()
class Meta: class Meta:
model = Category model = Category
@ -54,9 +53,6 @@ class CategorySimpleSerializer(ModelSerializer): # type: ignore [type-arg]
"children", "children",
] ]
def get_image(self, obj: Category) -> str:
return obj.image_url
def get_children(self, obj: Category) -> dict[str, Any]: def get_children(self, obj: Category) -> dict[str, Any]:
request = self.context.get("request") request = self.context.get("request")
if request is not None and request.user.has_perm("view_category"): if request is not None and request.user.has_perm("view_category"):

View file

@ -1,5 +1,5 @@
from constance import config from django.conf import settings
def get_flag_by_language(language: str) -> str: def get_flag_by_language(language: str) -> str:
return f"https://api.{config.BASE_DOMAIN}/static/flags/{language}.png" return f"https://api.{settings.BASE_DOMAIN}/static/flags/{language}.png"

View file

@ -9,8 +9,8 @@ def org_schema():
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "Organization", "@type": "Organization",
"name": config.COMPANY_NAME, "name": config.COMPANY_NAME,
"url": f"https://{config.BASE_DOMAIN}/", "url": f"https://{settings.BASE_DOMAIN}/",
"logo": f"https://{config.BASE_DOMAIN}/static/logo.png", "logo": f"https://{settings.BASE_DOMAIN}/static/logo.png",
} }
@ -19,10 +19,10 @@ def website_schema():
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "WebSite", "@type": "WebSite",
"name": config.PROJECT_NAME, "name": config.PROJECT_NAME,
"url": f"https://{config.BASE_DOMAIN}/", "url": f"https://{settings.BASE_DOMAIN}/",
"potentialAction": { "potentialAction": {
"@type": "SearchAction", "@type": "SearchAction",
"target": f"https://{config.BASE_DOMAIN}/search?q={{query}}", "target": f"https://{settings.BASE_DOMAIN}/search?q={{query}}",
"query-input": "required name=query", "query-input": "required name=query",
}, },
} }
@ -56,7 +56,7 @@ def product_schema(product, images, rating=None):
"priceCurrency": settings.CURRENCY_CODE, "priceCurrency": settings.CURRENCY_CODE,
"availability": "https://schema.org/InStock" if stock.quantity > 0 else "https://schema.org/OutOfStock", "availability": "https://schema.org/InStock" if stock.quantity > 0 else "https://schema.org/OutOfStock",
"sku": stock.sku, "sku": stock.sku,
"url": f"https://{config.BASE_DOMAIN}/product/{product.slug}", "url": f"https://{settings.BASE_DOMAIN}/product/{product.slug}",
} }
) )
data = { data = {

View file

@ -271,7 +271,7 @@ class CategoryViewSet(EvibesViewSet):
title = f"{category.name} | {config.PROJECT_NAME}" title = f"{category.name} | {config.PROJECT_NAME}"
description = (category.description or "")[:180] description = (category.description or "")[:180]
canonical = f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{category.slug}" canonical = f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{category.slug}"
og_image = request.build_absolute_uri(category.image.url) if getattr(category, "image", None) else "" og_image = request.build_absolute_uri(category.image.url) if getattr(category, "image", None) else ""
og = { og = {
@ -283,10 +283,10 @@ class CategoryViewSet(EvibesViewSet):
} }
tw = {"card": "summary_large_image", "title": title, "description": description} tw = {"card": "summary_large_image", "title": title, "description": description}
crumbs = [("Home", f"https://{config.BASE_DOMAIN}/")] crumbs = [("Home", f"https://{settings.BASE_DOMAIN}/")]
if category.get_ancestors().exists(): if category.get_ancestors().exists():
for c in category.get_ancestors(): for c in category.get_ancestors():
crumbs.append((c.name, f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{c.slug}")) crumbs.append((c.name, f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{c.slug}"))
crumbs.append((category.name, canonical)) crumbs.append((category.name, canonical))
json_ld = [org_schema(), website_schema(), breadcrumb_schema(crumbs), category_schema(category, canonical)] json_ld = [org_schema(), website_schema(), breadcrumb_schema(crumbs), category_schema(category, canonical)]
@ -303,7 +303,7 @@ class CategoryViewSet(EvibesViewSet):
.distinct()[:24] .distinct()[:24]
) )
for p in qs: for p in qs:
product_urls.append(f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/product/{p.slug}") product_urls.append(f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/product/{p.slug}")
if product_urls: if product_urls:
json_ld.append(item_list_schema(product_urls)) json_ld.append(item_list_schema(product_urls))
@ -388,7 +388,7 @@ class BrandViewSet(EvibesViewSet):
title = f"{brand.name} | {config.PROJECT_NAME}" title = f"{brand.name} | {config.PROJECT_NAME}"
description = (brand.description or "")[:180] description = (brand.description or "")[:180]
canonical = f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/brand/{brand.slug}" canonical = f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/brand/{brand.slug}"
logo_url = ( logo_url = (
request.build_absolute_uri(brand.big_logo.url) request.build_absolute_uri(brand.big_logo.url)
@ -408,7 +408,7 @@ class BrandViewSet(EvibesViewSet):
tw = {"card": "summary_large_image", "title": title, "description": description} tw = {"card": "summary_large_image", "title": title, "description": description}
crumbs = [ crumbs = [
("Home", f"https://{config.BASE_DOMAIN}/"), ("Home", f"https://{settings.BASE_DOMAIN}/"),
(brand.name, canonical), (brand.name, canonical),
] ]
@ -528,7 +528,7 @@ class ProductViewSet(EvibesViewSet):
rating = {"value": p.rating, "count": p.feedbacks_count} rating = {"value": p.rating, "count": p.feedbacks_count}
title = f"{p.name} | {config.PROJECT_NAME}" title = f"{p.name} | {config.PROJECT_NAME}"
description = (p.description or "")[:180] description = (p.description or "")[:180]
canonical = f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/product/{p.slug}" canonical = f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/product/{p.slug}"
og = { og = {
"title": title, "title": title,
"description": description, "description": description,
@ -538,10 +538,10 @@ class ProductViewSet(EvibesViewSet):
} }
tw = {"card": "summary_large_image", "title": title, "description": description} tw = {"card": "summary_large_image", "title": title, "description": description}
crumbs = [("Home", f"https://{config.BASE_DOMAIN}/")] crumbs = [("Home", f"https://{settings.BASE_DOMAIN}/")]
if p.category: if p.category:
for c in p.category.get_ancestors(include_self=True): for c in p.category.get_ancestors(include_self=True):
crumbs.append((c.name, f"https://{config.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{c.slug}")) crumbs.append((c.name, f"https://{settings.BASE_DOMAIN}/{settings.LANGUAGE_CODE}/catalog/{c.slug}"))
crumbs.append((p.name, canonical)) crumbs.append((p.name, canonical))
json_ld = [org_schema(), website_schema()] json_ld = [org_schema(), website_schema()]

View file

@ -7,13 +7,16 @@ from typing import Any
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
EVIBES_VERSION = "2025.4" EVIBES_VERSION = "2025.4"
RELEASE_DATE = datetime(2025, 9, 13) RELEASE_DATE = datetime(2025, 11, 9)
BASE_DIR = Path(__file__).resolve().parent.parent.parent BASE_DIR = Path(__file__).resolve().parent.parent.parent
INITIALIZED = (BASE_DIR / ".initialized").exists()
SECRET_KEY = getenv("SECRET_KEY", "SUPER_SECRET_KEY") SECRET_KEY = getenv("SECRET_KEY", "SUPER_SECRET_KEY")
DEBUG = bool(int(getenv("DEBUG", "1"))) DEBUG = bool(int(getenv("DEBUG", "1")))
BASE_DOMAIN: str = getenv("EVIBES_BASE_DOMAIN", "localhost")
ALLOWED_HOSTS: set[str] = { ALLOWED_HOSTS: set[str] = {
"app", "app",
"worker", "worker",
@ -294,10 +297,10 @@ TIME_ZONE: str = getenv("TIME_ZONE", "Europe/London")
WHITENOISE_MANIFEST_STRICT: bool = False WHITENOISE_MANIFEST_STRICT: bool = False
STATIC_URL: str = "/static/" STATIC_URL: str = f"https://api.{BASE_DOMAIN}/static/" if INITIALIZED else "static/"
STATIC_ROOT: Path = BASE_DIR / "static" STATIC_ROOT: Path = BASE_DIR / "static"
MEDIA_URL: str = "/media/" MEDIA_URL: str = f"https://api.{BASE_DOMAIN}/media/" if INITIALIZED else "media/"
MEDIA_ROOT: Path = BASE_DIR / "media" MEDIA_ROOT: Path = BASE_DIR / "media"
AUTH_USER_MODEL: str = "vibes_auth.User" AUTH_USER_MODEL: str = "vibes_auth.User"