自定义 Django SlugField 验证会破坏 URL 匹配

Posted

技术标签:

【中文标题】自定义 Django SlugField 验证会破坏 URL 匹配【英文标题】:Custom Django SlugField Validation Breaks URL Matching 【发布时间】:2021-03-03 23:36:18 【问题描述】:

我为我的 URL 中的 slug 编写了一个自定义字段验证器(一个模型接受 slug 中的句点,SLUG_REGEX = '[-a-zA-Z0-9_.]+$'):

def validate_my_slug(value):

    my_slug_re = re.compile(settings.SLUG_REGEX)

    if not my_slug_re.match(value):
        raise ValidationError(
            _('Slugs must consist of letters, numbers, '
              'underscores, hyphens, or periods.'),
            params='value': value,
        )

models.py中的字段:

slug = models.CharField(max_length=64, null=True, blank=True, 
                        validators=[validate_my_slug])

这在 Django 管理中保存模型时有效。但是在我的 URLConf 中,这不再有效(它在更改 slug 验证之前确实有效):

path('product-release/<slug:slug>', ProductReleaseDetail.as_view(), 
     name='product-detail')

    我找到了使用 re_path 的解决方案,但我不确定这是正确/最佳的方法:

     re_path('product-release/(?P<slug>[-a-zA-Z0-9_.]+)$', 
             ProductReleaseDetail.as_view(), 
             name='product-release-detail')
    

    现在这不再有效(出现在上面 urls.py 中的 re_path 模式之前):

     path('product-releases', ProductReleaseList.as_view(), 
          name='product-release-list')
    

除了与产品发布列表匹配的以外,一切正常。当我更改 slug 验证器时,URL http://localhost:8080/product-releases 停止工作,出现错误:

NoReverseMatch at /product-releases
Reverse for 'product-release-detail' with no arguments not found.
1 pattern(s) tried: ['product-release/(?P<slug>[-a-zA-Z0-9_.]+$)']

但这似乎是错误的,因为我什至没有尝试联系product-release-detail;我正在尝试联系product-release-list。我一定是在这里遗漏了一些简单的东西,但现在我的视野蒙上了一层阴影。

views.py

. . .

class ProductReleaseList(ListView):
    model = ProductRelease


class ProductReleaseDetail(DetailView):
    model = ProductRelease

. . .

urlpatterns

. . .

# Product Releases
path('product-releases', ProductReleaseList.as_view(), name='product-release-list'),
re_path('product-release/(?P<slug>[-a-zA-Z0-9_.]+$)', ProductReleaseDetail.as_view(), name='product-release-detail'),
# Release Notes
path('release-notes', ReleaseNoteList.as_view(), name='release-note-list'),
re_path('release-note/(?P<slug>[-a-zA-Z0-9_.]+$)', ReleaseNoteDetail.as_view(), name='release-note-detail'),

. . .

productrelease_list.html

. . .

<h4><a href="% url 'product-release-detail' %"> p </a></h4>

. . .

【问题讨论】:

1) 看起来不错,但 2) 没有意义。请通过复制发布完整的 urlpatterns。您可能在某处忘记了逗号或括号。 好的,但在 IDE 中没有警告或错误,并且站点的其余部分和代码正常工作。我会尽快更新。 现在说得通了。 url 与列表视图正确匹配,但在您使用的模板中 % url % 现在失败了。 我已经用更多细节更新了这个问题。我仍然不确定为什么 product-releases 没有得到匹配,或者更确切地说,为什么在这里使用 slug 正则表达式。这只是列表的常规通用视图。 【参考方案1】:

在您的模板中,您使用的内容类似于:

% url "product-release-detail" varname %

然而,这个变量显然可以为空(或者由于拼写错误甚至总是为空)。使用/&lt;slug:slug&gt;,这将通过(我不同意顺便说一句,但这不是重点)。使用更严格和更好的re_path,现在(正确)会产生错误,因为/product-release/ 不是有效的网址:slug 至少应该有一个字符。

所以根本原因是以太,变量命名不正确,或者你的产品没有 slug(你不应该有)。

地址 cmets

    使用# # 在模板中注释掉。确保您没有使用 &lt;!-- --&gt;,因为它仍然会执行代码。 目前% url "product-release-detail" % 是无效的:它需要一个参数,即一个slug。如果您想链接到列表视图,请使用% url "product-release-list" %,因为它可以不带参数地调用。 我不知道 p 是什么,但我假设是产品对象。在这种情况下,您应该使用%url "product-release-detail" p.name_of_slug_field %

【讨论】:

我很抱歉。我正在处理一些我不愿意分享的代码,所以我试图删除不需要的部分,以使其更清楚地解决这个问题。我想我实际上掩盖了这个问题......你是对的,但我已经测试过了(例如,在我的模板中注释掉了% url product-release-detail %。如果你不介意,你能看看我的编辑吗?我已经说得更清楚了(我希望如此)。并且我确保所有产品版本和所有发行说明在数据库中都有 slug。 谢谢!这按预期工作。使用url 模板标签的一个基本部分完全让我无法理解。我陷入了其他细节的泥潭(比如“列表”没有出现)。您甚至在您的第一个回复中提到它需要一个论点,而我忽略了它。抱歉...再次感谢,您对我们的帮助最大。

以上是关于自定义 Django SlugField 验证会破坏 URL 匹配的主要内容,如果未能解决你的问题,请参考以下文章

为啥我需要在 Django 中使用 SlugField?

为啥在 Django 中使用 SlugField()?

django models.SlugField 怎么使用

如何在 Django 通用列表视图类中使用 slugfield 创建链接?

Django 中的 Slugfield URL 实现

Django 表单验证之自定义表单验证器