如何使我的 urlpatterns 更智能,以避免模板中的繁琐代码?

How do I make my urlpatterns smarter, to avoid cumbersome code in my templates?

现在我已经定义了我的 URL,它们可以包含类别的不同组合(始终打开),attribute_slugs (1 and/or 2) 和 brand_slug,全部使用下面定义的 url 模式。

在我的 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=属性值 %}。换句话说:只设置了第一个attribute_slug。

另一方面,如果页面已经有一个属性,则检查新属性和旧属性的顺序以确定哪个先出现,然后将新属性添加到 URL,像这样:{% url 'products:product_categories' slug=category.slug attribute_slug=ATTRIBUTEVALUE attribute_slug2=属性值2 %}.

现在,当我这样解释时,这似乎比较简单。但是有很多不同的情况使这变得更加麻烦,例如还要考虑 brand_slug,并考虑到我也有用于从 URL.[= 中删除特定属性或品牌的功能。 11=]

最后,我的问题是; 是否有更好的方法 url模板 URL 标签中的模式和动态参数?

我自己的一个想法是在 URL 模板标签中传递任意数量的参数(就像经典 pythonic 中的 **args 或 **kwargs)。这看起来类似于:{% 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;... 这样的东西被解析为其他参数。

为了最大限度地减少此后的代码重复,可以为所有 class-based 将要使用此技术的视图编写通用代码 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):

并且对于 commonly-used CBV 类 可以定义基础 类 包括 mixin:

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(";")