如何在 Django 中使用反向工作来制作 *** 样式的 URL,以允许 slug 更改?
Posted
技术标签:
【中文标题】如何在 Django 中使用反向工作来制作 *** 样式的 URL,以允许 slug 更改?【英文标题】:How can I make ***-style URLs in Django with a working reverse, that allow the slug to change? 【发布时间】:2015-09-09 07:41:11 【问题描述】:如何制作一个与 *** 类似的 URL 方案?
这与this one 不是同一个问题,尽管它是相似的。不同之处在于我想要一个实现 *** 的所有聪明才智的 URL 方案,并允许 reverse
ing 生成完全 slugged 的 URL。
具体来说,要模仿的 *** 行为:
-
仅按 id 查找,slug 仅用于 SEO/可读性目的
在不正确的 slug 或没有给出 slug 时转发到更正的 slug。例如如果我有一个名称为My Object Name
的对象123
:
/123/ redirects to /123/my-object-name/
/123/something/ redirects to /123/my-object-name/
如果我将对象名称更改为 My New Object Name
,则重定向目标 slug 会相应更改(这是 *** 的行为,如果您编辑问题的标题),例如:
/123/my-object-name/ redirects to /123/my-new-object-name/
反向工作,使% url 'my_view' 123 %
返回/123/my-object-name/
,编辑对象名后返回/123/my-new-object-name/
我用models.py
破解了一些东西:
class MyModel(models.Model):
name = models.CharField(max_length=70)
def my_slugged_url(self):
slug = slugify(self.name)
if slug:
return reverse('my_view', args=[self.id]) + slug + "/"
return reverse('my_view', args=[self.id])
...和urls.py
模式:
url(r'^(\d+)/\S+/$', 'my_view')
url(r'^(\d+)/$', 'my_view'),
...还有一个views.py
:
def my_view(request, id):
obj = get_object_or_404(MyModel, pk=id)
if request.path != obj.my_slugged_url():
return redirect(obj.my_slugged_url())
...但这感觉不对,这意味着当我执行reverse
或% url 'my_view' 123 %
时,它会返回一个类似/123/
的URL,然后必须重定向到/123/my-object-name
。
我怎样才能像 *** 一样进行这项工作?
【问题讨论】:
也许给这两种模式不同的名字? 问题是,带有 slug 的模式 (url(r'^(\d+)/\S+/$', 'my_view')
) 怎么知道要返回什么 slug?
@awidgery 这个问题可能会有所帮助 - Django optional URL patterns。
【参考方案1】:
鉴于您反复出现的评论 - “...模式如何知道要返回哪个 slug”,您似乎无法理解它的工作原理。我会试着为你分解这个过程。
首先,您将编写两个指向一个视图的 url 模式。记得给两种模式不同的name
。
# urls.py
...
url(r'^(?P<object_id>\d+)/$', 'my_view', name='my-view-no-slug'),
url(r'^(?P<object_id>\d+)/(?P<slug>\S+)/$', 'my_view', name='my-view-slug'),
...
现在,这就是有趣的地方:
-
每当向
/123/
发出请求时,它都会匹配第一个url 模式。
当向/123/my-object-name/
发出请求时,它将匹配第二个模式。
当使用错误的 slug 发出请求时,例如 - /123/some-wrong-slug/
,它也会匹配第二个模式。不用担心,您会在视图中检查错误的 slug。
但是所有三个请求都将由一个视图处理。
其次,在模型中定义一个名为 slug
的 property
。您将使用它来生成和访问对象的 slug。
# models.py
class MyModel(...):
...
@property
def slug(self):
return slugify(self.name)
def get_absolute_url(self):
return reverse('my-view-slug', args=[self.id, self.slug])
最后,处理请求的视图应该如下所示:
# views.py
def my_view(request, object_id, slug=None):
# first get the object
my_object = get_object_or_404(MyModel, id=object_id)
# Now we will check if the slug in url is same
# as my_object's slug or not
if slug != my_object.slug:
# either slug is wrong or None
return redirect(my_object.get_absolute_url())
# this is processed if slugs match
# so do whatever you want
return render(request, 'my-template.html', 'my_object': my_object)
我希望这可以清楚地说明如何实现 ***-like url 行为。
【讨论】:
【参考方案2】:# views
def detail(request, object_id, slug):
obj = get_object_or_404(MyModel, pk=object_id)
if obj.slug != slug:
canonical = obj.get_absolute_url()
return redirect(canonical)
context = "obj":obj
return render(request, "myapp/detail.html", context)
# urls
from myapp.views import detail
urlpatterns = ('',
#...
url(r'^(?P<object_id>\d+)/(<?P<slug>\S+)/$', detail, name="detail")
url(r'^(\d+)/$', lambda request, pk: detail(request, pk, None), name="redirect-to-detail"),
# ...
)
# models
class MyModel(models.Model):
def get_absolute_url(self):
return reverse(
"detail",
kwargs=dict(object_id=self.id, slug=self.slug)
)
@property
def slug(self):
return slugify(self.title)
【讨论】:
不需要lambda
,只需在视图函数中使用slug=None
作为默认值。
您还缺少patterns()
。我会推荐一个没有前缀的普通列表,因为 patterns
已被弃用。
我不确定这将如何与 % url 'detail' 123 %'? From my tests it returns
/2/x/` 一起使用,因为它不知道 slug 是什么
使用% url 'detail' obj.id obj.slug %
或简单的 obj.get_absolute_url
。
@knbk :是的,从技术上讲,在视图中使用 slug
的默认值更简单。我想说的是,使用 lambda 会使意图更加明确,但 YMMV以上是关于如何在 Django 中使用反向工作来制作 *** 样式的 URL,以允许 slug 更改?的主要内容,如果未能解决你的问题,请参考以下文章
如何正确使用 Django 反向 FK 查找在 CBV 中显示子模型的实例