Django:reverse()和get_absolute_url()为同一个对象返回不同的输出?

时间:2017-04-23 18:23:04

标签: python django django-flatpages

与flatpages reverse()一起使用时,get_abolute_url()会返回不同的输出:

>>> about = FlatPage.objects.get(id=2)
>>> 
>>> about
<FlatPage: /about-us/ -- About us page>
>>>
>>> about.url
>>> '/about-us/'
>>>
>>> about.get_absolute_url()
'/about-us/'
>>>
>>>
>>> reverse('django.contrib.flatpages.views.flatpage', args=[about.url])
'/%2Fabout-us/'    ## from where %2F comes from ?
>>>

以下是网站范围urls.py

from django.conf.urls import url, include
from django.contrib import admin
from django.contrib .flatpages import urls as flatpage_urls
# from . import blog

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'', include(flatpage_urls)),
]

虽然,我可以访问http://127.0.0.1:8000/about-us/处的某个页面。 %2F来自哪里?

我原以为两种方法都应该返回相同的输出。这是怎么回事?

更新

以下是flatpages/urls.py

from django.conf.urls import url
from django.contrib.flatpages import views

urlpatterns = [
    url(r'^(?P<url>.*)$', views.flatpage, name='django.contrib.flatpages.views.flatpage'),
]

更新2:

将urls.py更新为:

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    # url(r'', include(flatpage_urls)),
]

urlpatterns += [
    url(r'^(?P<url>.*/)$', views.flatpage),
]

2 个答案:

答案 0 :(得分:0)

在我看来,你试图在另一个URL中插入一个URL,这看起来很奇怪。

从你的代码:

reverse('django.contrib.flatpages.views.flatpage', args=[about.url])

您还澄清了about.url包含/about-us/。此字符串将被引用并插入URL:

http://hostname.example/<here>/

或者:

http://hostname.example/%2Fabout-us%2F/

我不明白为什么你没有看到最后一个%2F。

答案 1 :(得分:0)

django.contrib.flatpages显然与reverse()与args或kwargs不兼容。预期用途是使用其自定义标记:

{% load flatpages %}
{% get_flatpages as flatpages %}
<ul>
    {% for page in flatpages %}
        <li><a href="{{ page.url }}">{{ page.title }}</a></li>
    {% endfor %}
</ul>

来源Flatpages > Getting a list of FlatPage objects in your templates

为什么%2f

FlatPage.get_absolute_url()仅返回self.url字段中的任何内容,在您的示例中,该字段是由斜杠包围的字符串,即&#39; / about-us /&#39;。

def get_absolute_url(self):
    # Handle script prefix manually because we bypass reverse()
    return iri_to_uri(get_script_prefix().rstrip('/') + self.url)

另一方面,reverse()调用_reverse_with_prefix(),其前缀为网址斜杠/,结果为//about-us/。然后,它错误地确定双斜杠表示尝试的模式重写,因此它用URL ASCII代码%2f替换第二个斜杠以中和它。

不幸的是,FlatPage.url的表单验证程序要求违反前导/

def clean_url(self):
    url = self.cleaned_data['url']
    if not url.startswith('/'):
        raise forms.ValidationError(
            ugettext("URL is missing a leading slash."),
            ...

您可以使用不带斜杠的前缀来解决它,例如:

url(r'^pages', include(django.contrib.flatpages.urls))

但这也会匹配pagesabout-us/。如果您使用r'^'完全删除前缀,则_reverse_with_prefix()会在/前加上,以避免相对链接。

您可以像https://docs.djangoproject.com/en/1.11/ref/contrib/flatpages/#using-the-urlconf中的第3个示例那样对网址进行硬编码,但这会破坏在flatpages表中管理网址的目的。

from django.contrib.flatpages import views

urlpatterns += [
    url(r'^about-us/$', views.flatpage, {'url': '/about-us/'}, name='about'),
    url(r'^license/$', views.flatpage, {'url': '/license/'}, name='license'),
]