在Django中同时显示slug和ID在URL中,但只依据ID进行路由的方法

8
我将尽力帮助您进行翻译。以下是需要翻译的内容:

我想要实现的目标是:我的新闻应用程序应该显示一个slug,但只能通过ID查询文章,形式为/news/24/this-is-the-slug

不幸的是,在尝试浏览文章时,我遇到了一个NoReverseMatch: Reverse for 'news_detail' with arguments '('',)' and keyword arguments '{}' not found.的错误。模板中生成的URL看起来是正确的(如上所述,我可以通过Haystack搜索来确认这一点,它提供了正确的URL)。

models.py

class News(models.Model):
    id = models.IntegerField(primary_key=True, editable=False)
    category = models.CharField(max_length=50L)
    title = models.CharField(max_length=200L)
    rss_summary = models.TextField(max_length=2000L)
    body_text = models.TextField(max_length=5000L)
    post_date = models.DateTimeField()
    prettyurl = models.SlugField(max_length=100L)

    class Meta:
        db_table = 'news'

    def __unicode__(self):
        return self.title

    def get_absolute_url(self):
        return urlresolvers.reverse('news_detail', kwargs={'pk': self.id, 'slug': self.prettyurl })

urls.py

urlpatterns = patterns(
    '',
    url(
        r'^$',
        view=views.NewsListView.as_view(),
        name='news_index'),
    url(
        r'^(?P<pk>\d+)/',
        view=views.NewsDetailView.as_view(),
        name='news_detail'),
    url(
        r'^(?P<pk>\d+)/(?P<slug>[-\w]+)/$',
        view=views.NewsDetailView.as_view(),
        name='news_detail'),
    url(
        r'^archive/$',
        view=views.NewsArchiveIndexView.as_view(),
        name="archive_month"),
    [... more unrelated urls ...]

views.py

class NewsDetailView(DetailView):
    #name='news_detail'),
    model = News
    context_object_name = 'news'
    #slug_url_kwarg = 'prettyurl'
    #slug_field = 'prettyurl'
    template_name = 'news/detail.html'

模板

`<p><a href="{% url 'news_detail' news.slug %}">Permalink</a> for this article.`

1
如果您只需要ID而不需要slug,那么将其作为参数传递给视图就没有必要了,对吧? - yuvi
2个回答

4
感谢 @Daniel Roseman 和 @yuvi 的帮助。在你们的帮助下,我通过将URL模式定义为以下内容解决了我的问题:
r'^(?P<pk>\d+)(?:/(?P<slug>[\w\d-]+))?/$',

这允许我获取所有需要的表单:

  • 新闻/nn
  • 新闻/nn/
  • 新闻/nn/文章标题
  • 新闻/nn/文章标题/

在模板中,我使用:

{% url 'news_detail' news.id news.prettyurl %}

在上面的列表中,显示了第四个版本。
再次感谢!

1
你也可以使用 {% url 'news_detail' news.id news.title|slugify %} - JuanPablo
但是使用这个解决方案,您可以通过 /news/24/this-is-the-slug/news/24/dadadsdasdas 获取内容。 - JuanPablo
是的,但那是我的本意。ID 用于查询,slug 只是为了好看。 - weeheavy
@weeheavy,正则表达式中的冒号是什么意思?我也在尝试做同样的事情,但是当我进行单元测试时,我也会得到NoReverseMatch的错误。目前,只有在包括slug时才能正常工作,而不包括slug时则不能正常工作。 - Jonathan
你也可以使用 (?:/(?P<slug>.*))?/$,以允许用户在Slug中输入其他字符,如#@%$、空格等。 - Algorithmatic

1

我不是很确定你为什么要捕获slug。与其在URL模式中有一个命名组,不如只有一个忽略PK后面的所有内容:

r'^(?P<pk>\d+)/.*',

这个方法可以在传递或不传递slug的情况下正常工作,因此您可以消除重复的模式。

然而,你所做的有两个基本问题。首先,尽管你声明只想匹配PK,但你甚至没有将PK传递到URL中,只传递了slug。其次,即使slug为空,错误消息也会显示(args变量只是'')。

您应该传递实际的PK:

{% url 'news_detail' news.pk %}

2
虽然我同意他不需要捕获slug,但是url模式/.*并没有忽略其余部分。实际上相反,它会捕获几乎任何你扔给它的东西,只要在它之前有一个/number/。这种模式通常应该避免使用(我认为是这样),但当需要时,它应该出现在最后一个url模式中,以避免可能出现接收到针对更具体模式的请求的情况(例如,如果不首先出现像`r'^(?P<pk>\d+)/(?P<some_other_num>\d+)/$'这样的模式将被吞噬)。 - yuvi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接