**Features**: 1) Add unicode_slugify_function for advanced slugify handling; 2) Replace default slugify method with unicode_slugify_function in models; 3) Introduce python-slugify as a project dependency for Unicode slugification;

**Fixes**: 1) Remove redundant `slugify` import from `core/models.py`;

**Extra**: 1) Update `poetry.lock` and `pyproject.toml` to lock `python-slugify@8.0.4` and refine dependency definitions; 2) Improve readability of debug logging in `TweakedAutoSlugField`.
This commit is contained in:
Egor Pavlovich Gorbunov 2025-06-21 22:49:34 +03:00
parent 9eb4ee72df
commit 5d7075ee2e
4 changed files with 40 additions and 8 deletions

View file

@ -35,7 +35,6 @@ from django.http import Http404
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_bytes from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode from django.utils.http import urlsafe_base64_encode
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_extensions.db.fields import AutoSlugField from django_extensions.db.fields import AutoSlugField
from django_prometheus.models import ExportModelOperationsMixin from django_prometheus.models import ExportModelOperationsMixin
@ -51,7 +50,7 @@ from core.utils import (
get_product_uuid_as_path, get_product_uuid_as_path,
get_random_code, get_random_code,
) )
from core.utils.db import TweakedAutoSlugField from core.utils.db import TweakedAutoSlugField, unicode_slugify_function
from core.utils.lists import FAILED_STATUSES from core.utils.lists import FAILED_STATUSES
from core.validators import validate_category_image_dimensions from core.validators import validate_category_image_dimensions
from evibes.settings import CURRENCY_CODE from evibes.settings import CURRENCY_CODE
@ -220,7 +219,7 @@ class Category(ExportModelOperationsMixin("category"), NiceModel, MPTTModel):
"parent__name", "parent__name",
"name", "name",
), ),
slugify_function=slugify, slugify_function=unicode_slugify_function,
allow_unicode=True, allow_unicode=True,
unique=True, unique=True,
editable=False, editable=False,
@ -297,7 +296,7 @@ class Brand(ExportModelOperationsMixin("brand"), NiceModel):
max_length=88, max_length=88,
overwrite=True, overwrite=True,
null=True, null=True,
slugify_function=slugify, slugify_function=unicode_slugify_function,
verbose_name=_("brand slug"), verbose_name=_("brand slug"),
) )
priority: int = PositiveIntegerField( # type: ignore priority: int = PositiveIntegerField( # type: ignore
@ -376,7 +375,7 @@ class Product(ExportModelOperationsMixin("product"), NiceModel):
overwrite=True, overwrite=True,
allow_unicode=True, allow_unicode=True,
unique=True, unique=True,
slugify_function=slugify, slugify_function=unicode_slugify_function,
editable=False, editable=False,
null=True, null=True,
) )

View file

@ -4,9 +4,11 @@ from django.db.models import Model
from django.db.models.constants import LOOKUP_SEP from django.db.models.constants import LOOKUP_SEP
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_extensions.db.fields import AutoSlugField from django_extensions.db.fields import AutoSlugField
from slugify import slugify
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def list_to_queryset(model: Model, data: list): def list_to_queryset(model: Model, data: list):
if not isinstance(model, Model): if not isinstance(model, Model):
raise ValueError(_(f"{model} must be model")) raise ValueError(_(f"{model} must be model"))
@ -17,6 +19,17 @@ def list_to_queryset(model: Model, data: list):
return model.objects.filter(pk__in=pk_list) return model.objects.filter(pk__in=pk_list)
def unicode_slugify_function(content):
return slugify(
text=content,
allow_unicode=True,
max_length=88,
word_boundary=True,
save_order=True,
regex_pattern=r"/",
)
class TweakedAutoSlugField(AutoSlugField): class TweakedAutoSlugField(AutoSlugField):
def get_slug_fields(self, model_instance, lookup_value): def get_slug_fields(self, model_instance, lookup_value):
if callable(lookup_value): if callable(lookup_value):
@ -25,7 +38,9 @@ class TweakedAutoSlugField(AutoSlugField):
lookup_value_path = lookup_value.split(LOOKUP_SEP) lookup_value_path = lookup_value.split(LOOKUP_SEP)
attr = model_instance attr = model_instance
logger.debug(f"get_slug_fields: lookup_value_path is {lookup_value_path} and attr is {attr}") logger.debug(
f"get_slug_fields: lookup_value_path is {lookup_value_path} and attr is {attr}"
)
for elem in lookup_value_path: for elem in lookup_value_path:
try: try:

19
poetry.lock generated
View file

@ -4039,6 +4039,23 @@ files = [
[package.extras] [package.extras]
dev = ["backports.zoneinfo", "black", "build", "freezegun", "mdx_truly_sane_lists", "mike", "mkdocs", "mkdocs-awesome-pages-plugin", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-material (>=8.5)", "mkdocstrings[python]", "msgspec", "mypy", "orjson", "pylint", "pytest", "tzdata", "validate-pyproject[all]"] dev = ["backports.zoneinfo", "black", "build", "freezegun", "mdx_truly_sane_lists", "mike", "mkdocs", "mkdocs-awesome-pages-plugin", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-material (>=8.5)", "mkdocstrings[python]", "msgspec", "mypy", "orjson", "pylint", "pytest", "tzdata", "validate-pyproject[all]"]
[[package]]
name = "python-slugify"
version = "8.0.4"
description = "A Python slugify application that also handles Unicode"
optional = false
python-versions = ">=3.7"
files = [
{file = "python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856"},
{file = "python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8"},
]
[package.dependencies]
text-unidecode = ">=1.3"
[package.extras]
unidecode = ["Unidecode (>=1.1.1)"]
[[package]] [[package]]
name = "pytz" name = "pytz"
version = "2025.2" version = "2025.2"
@ -5166,4 +5183,4 @@ worker = ["celery", "celery-prometheus-exporter", "django-celery-beat", "django-
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = ">=3.12,<3.13" python-versions = ">=3.12,<3.13"
content-hash = "2af805bf62515a47ea6f4649913482e4af8f8831a1236e1fa1400ba82dd9b4e0" content-hash = "659721d93224050ebe572b017a80d45826b288764427c24bd07fbd35b704fa48"

View file

@ -65,6 +65,7 @@ polib = "1.2.0"
poetry-core = "2.1.3" poetry-core = "2.1.3"
PyJWT = "2.9.0" PyJWT = "2.9.0"
python = ">=3.12,<3.13" python = ">=3.12,<3.13"
python-slugify = "8.0.4"
psutil = "6.1.1" psutil = "6.1.1"
psycopg2 = "2.9.10" psycopg2 = "2.9.10"
pydantic = "2.9.2" pydantic = "2.9.2"
@ -76,7 +77,7 @@ ruff = "0.12.0"
sentry-sdk = { extras = ["django", "celery", "opentelemetry", "redis"], version = "2.27.0" } sentry-sdk = { extras = ["django", "celery", "opentelemetry", "redis"], version = "2.27.0" }
six = "1.17.0" six = "1.17.0"
swapper = "1.4.0" swapper = "1.4.0"
yapf = "^0.43.0" yapf = "0.43.0"
zeep = "4.3.1" zeep = "4.3.1"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]