Как сделать мои 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(";")
Вернуться на верх