【问题标题】:How do I make my urlpatterns smarter, to avoid cumbersome code in my templates?如何使我的 urlpatterns 更智能,以避免模板中出现繁琐的代码?
【发布时间】:2022-01-20 22:36:48
【问题描述】:

现在我已经定义了我的 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 中删除特定属性或品牌的功能。

最后,我的问题是; 模板URL标签中的urlpatterns和动态参数有更好的处理方式吗?

我自己的一个想法是在 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"),
    ]

【问题讨论】:

  • 这些实体是否必须在 URL 中,或者您可以使用 request.GET 来收集查询字符串 http:/.../vk/thing?attr=attrval&amp;brand=somebrand&amp;category=one&amp;... 重定向时,您必须对查询样式进行 urlencode 并将其附加到反向结果中。
  • 它们必须在 URL 中,这就是为什么我没有“仅仅”使用带有查询参数的经典过滤器 :-)
  • 如果您可以识别适当的分隔符,您可以将所有内容打包到一个参数中,并使用字符串方法拆分和连接轻松“解码”/“编码”。例如。 http:/.../vk/attr:attrval;brand:somebrand;category:one;... 有用吗?
  • 我对您的环境没有足够的了解。但是,如果您使用 CBV,您可能会实现站点范围的插件或自定义的基本视图,它将具有特定名称的任何关键字参数解码为(例如)self.params(某种 dict)并提供重新编码的方法(可能修改)其内容(用于重定向等),从而最大限度地减少编码和代码重复。
  • 我会解析出任何保证存在的东西,然后将其他所有东西收集在一个 中。然后,将代码注入我的 CBV(设置方法子类?)以创建 self.params(总是?或仅当 kwarg “otherparams”存在时?)。你可能会启动 self.params=self.kwargs.copy() 然后用你解码的 otherparams 更新它,并且永远不要在你的后续代码中引用 self.kwargs。再说一次,我对您的环境没有任何了解。

标签: django url django-models django-views django-templates


【解决方案1】:

按照建议编写上述讨论。

无论出于何种原因,都排除了将参数作为带有查询字符串的 GET 参数传递的情况。它们必须在 URL 中。

我的建议是只解析 URLconfig 中的常量参数。对于其他人,通过对它们可以拥有和不可以拥有的值施加语法限制,将它们汇总为单个字符串参数。 http://.../attr:attrval;brand:somebrand;category:one;... 之类的东西被解析为其他参数。

为了最大限度地减少代码重复,可以为所有将使用此技术的基于类的视图编写一个通用代码 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 类,可以定义包括 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(";")

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-19
    • 2011-02-11
    • 2016-04-14
    • 1970-01-01
    • 2015-01-02
    • 1970-01-01
    • 2023-03-11
    相关资源
    最近更新 更多