Как сделать мои urlpatterns более умными, чтобы избежать громоздкого кода в моих шаблонах?
На данный момент я определил свои URL таким образом, что они могут включать различные комбинации категории (всегда включена), attribute_slugs (1 и/или 2) и brand_slug, все с использованием urlpatterns, определенных ниже.
В моем файле views.py я убеждаюсь, что каждый slug существует в соответствующей модели, иначе выводится ошибка 404. Я также убеждаюсь, что атрибуты имеют определенный порядок, чтобы избежать идентичных страниц, где атрибуты просто поменялись местами в URL. Затем контекст отправляется в шаблон.
В моем шаблоне для категорий я реализую фильтр электронной коммерции, основанный на контексте. Например, фильтр может показывать ссылки на атрибуты, содержащиеся в товарах на данной конкретной странице. Так, если вы находитесь на странице: /shoes/, то фильтр по цвету имеет варианты: /shoes/green, /shoes/blue/, /shoes/black/ и т.д. Этот фильтр создается с помощью стандартного тега шаблона { % url % } с параметрами, основанными на контексте.
Это работает хорошо, но есть одна проблема. Она основана на большом количестве логических операторов в шаблоне, что громоздко. Например, есть if-выражение для проверки наличия 0 атрибутов, 1 атрибута и 2 атрибутов на текущей странице. Я также проверяю то же самое для брендов, то есть в шаблоне много if-выражений (помните, что они умножаются). Я должен это сделать, потому что мне нужно написать разные версии тега шаблона { % url % % } в зависимости от контекста страницы. В частности, параметры тега шаблона меняются в зависимости от страницы.
Например, если данная страница не включает никаких атрибутов, то кликабельный URL для любого атрибута должен быть: {% url 'products:product_categories' slug=category.slug attribute_slug=ATTRIBUTEVALUE %}. Другими словами: Устанавливается только первый attribute_slug.
С другой стороны, если на странице уже есть один атрибут, то проверяется порядок следования нового и старого атрибута, и затем новый атрибут добавляется в URL, например, так: {% url 'products:product_categories' slug=category.slug attribute_slug=ATTRIBUTEVALUE attribute_slug2=ATTRIBUTEVALUE2 %}.
Теперь это кажется относительно простым, когда я объясняю это таким образом. Но есть много различных случаев, которые делают это более громоздким, например, учитывая brand_slug, и учитывая, что у меня также есть функциональность, используемая для удаления определенного атрибута или бренда из URL.
В заключение, мой вопрос: Есть ли лучший подход urlpatterns и динамических параметров в URL тегах шаблона?
Одна мысль, которая у меня возникла, заключается в том, чтобы передавать произвольное количество аргументов в теге шаблона URL (подобно **args или **kwargs в классическом pythonic). Это выглядело бы примерно так: {% url 'products:product_categories' *KWARGS %}. Я просто не нашел способа, чтобы это работало.
Есть ли что-нибудь более умное, что вы можете придумать?
Заранее спасибо, и наилучшие пожелания.
# urls.py
urlpatterns = [
# Category + Attributes
re_path(r"^vk/(?P<slug>[\w-]+)/$", ProductCategoryListView.as_view(), name="product_categories"),
re_path(r"^vk/(?P<slug>[\w-]+)/(?P<attribute_slug>[\w-]+){1}/$", ProductCategoryListView.as_view(), name="product_categories"),
re_path(r"^vk/(?P<slug>[\w-]+)/(?P<attribute_slug>[\w-]+){1}/(?P<attribute_slug2>[\w-]+){1}/$", ProductCategoryListView.as_view(), name="product_categories"),
# Category + Brand + Attributes
re_path(r"^vk/(?P<slug>[\w-]+)/(?P<brand_slug>[\w-]+)/$", ProductCategoryListView.as_view(), name="product_categories"),
re_path(r"^vk/(?P<slug>[\w-]+)/(?P<brand_slug>[\w-]+)/(?P<attribute_slug>[\w-]+){1}/$", ProductCategoryListView.as_view(), name="product_categories"),
re_path(r"^vk/(?P<slug>[\w-]+)/(?P<brand_slug>[\w-]+)/(?P<attribute_slug>[\w-]+){1}/(?P<attribute_slug2>[\w-]+){1}/$", ProductCategoryListView.as_view(), name="product_categories"),
]
Запись вышеупомянутого обсуждения, как было предложено.
По какой-то причине передача параметров как GET-параметров с помощью строки запроса исключена. Они должны быть в URL.
Мое предложение состоит в том, чтобы разобрать только постоянный(ые) параметр(ы) в URLconfig. Для остальных, сверните их в один строковый параметр, наложив синтаксическое ограничение на то, какие значения они могут иметь, а какие нет. Что-то вроде http://.../attr:attrval;brand:somebrand;category:one;... разобранное как, скажем, otherparams.
Чтобы впоследствии минимизировать дублирование кода, можно написать общий код Mixin для всех представлений на основе классов, которые будут использовать эту технику. Подкласс метода setup:
class OtherparamsMixin( object):
def setup(self, request, *args, **kwargs):
super().setup( request, *args, **kwargs)
self.otherparams = {}
if 'otherparams' in self.kwargs:
params = self.kwargs['otherparams'].split(';')
for p in params:
k, v = p.split(':',1)
self.otherparams[k] = v
и тогда любое представление, определенное с помощью этого Mixin, будет автоматически доступно для использования self.otyherparams
class SomeView( OtherparamsMixin, CBVclass):
а для часто используемых классов CBV можно определить базовые классы, включающие миксин:
class SiteFormView( OtherparamsMixin, FormView):
pass
class SiteListView( OtherparamsMixin, ListView):
pass
и т.д., а затем использовать их вместо стандартных CBV.
Для перенаправления может быть полезно также ввести метод для преобразования диктовского представления набора otherparams обратно в текстовую строку:
@staticmethod
def otherparam_str( otherparams):
p = []
for k,v in otherparams.items():
if ";" in v:
raise ValueError( f'Semicolon not permitted in an otherparams value, but found "{k}" with value "{v}" )
p.append( f"{k}:{v}" )
return p.join(";")