如何在Django中列出urlpatterns(端点)?

231

我该如何查看“reverse”正在查找的当前urlpatterns?

我在视图中调用带有参数的反向函数,但是它却不能正常工作。有没有办法检查哪些内容存在以及为什么我的模式不起作用?


https://dev59.com/MnI-5IYBdhLWcg3wfoa1 - pmav99
2
打开DEBUG模式并查看调试输出中的URL列表? - boatcoder
21个回答

7
def get_resolved_urls(url_patterns):
    url_patterns_resolved = []
    for entry in url_patterns:
        if hasattr(entry, 'url_patterns'):
            url_patterns_resolved += get_resolved_urls(
                entry.url_patterns)
        else:
            url_patterns_resolved.append(entry)
    return url_patterns_resolved

在Python manage.py shell中
import urls
get_resolved_urls(urls.urlpatterns)

6

Django 2.0的极简解决方案

比如,如果你正在寻找已安装应用程序的第一个应用程序中的网址,则可以像这样访问它:

from django.urls import get_resolver
from pprint import pprint

pprint(
    get_resolver().url_patterns[0].url_patterns
)


如果您从django.core.urlresolvers导入get_resolver,它也适用于Django 1.*。感谢Marcio! - Fernando Costa Bertoldi

4
我已经扩展了Seti的命令,以显示命名空间、所有URL部分、自动调整列宽,并按(命名空间、名称)排序:
https://gist.github.com/andreif/263a3fa6e7c425297ffee09c25f66b20
import sys
from django.core.management import BaseCommand
from django.conf.urls import RegexURLPattern, RegexURLResolver
from django.core import urlresolvers


def collect_urls(urls=None, namespace=None, prefix=None):
    if urls is None:
        urls = urlresolvers.get_resolver()
    _collected = []
    prefix = prefix or []
    for x in urls.url_patterns:
        if isinstance(x, RegexURLResolver):
            _collected += collect_urls(x, namespace=x.namespace or namespace,
                                       prefix=prefix + [x.regex.pattern])
        elif isinstance(x, RegexURLPattern):
            _collected.append({'namespace': namespace or '',
                               'name': x.name or '',
                               'pattern': prefix + [x.regex.pattern],
                               'lookup_str': x.lookup_str,
                               'default_args': dict(x.default_args)})
        else:
            raise NotImplementedError(repr(x))
    return _collected


def show_urls():
    all_urls = collect_urls()
    all_urls.sort(key=lambda x: (x['namespace'], x['name']))

    max_lengths = {}
    for u in all_urls:
        for k in ['pattern', 'default_args']:
            u[k] = str(u[k])
        for k, v in list(u.items())[:-1]:
            # Skip app_list due to length (contains all app names)
            if (u['namespace'], u['name'], k) == \
                    ('admin', 'app_list', 'pattern'):
                continue
            max_lengths[k] = max(len(v), max_lengths.get(k, 0))

    for u in all_urls:
        sys.stdout.write(' | '.join(
            ('{:%d}' % max_lengths.get(k, len(v))).format(v)
            for k, v in u.items()) + '\n')


class Command(BaseCommand):
    def handle(self, *args, **kwargs):
        show_urls()

注意:Python 3.6中,列顺序被保留,而在旧版本中需要使用OrderedDict

更新:现在有一个包含OrderedDict的新版本存在于django-s软件包中:https://github.com/5monkeys/django-bananas/blob/master/bananas/management/commands/show_urls.py


1
从django.conf.urls导入RegexURLPattern,RegexURLResolver在django> 2.0中不再有效。 但是我适应了要点,现在运行良好,谢谢。 - cwhisperer
最近我自己遇到了这个问题并更新了Gist。需要使用早期版本才能在Django <2.0上运行。 - Andrei

4

Django 1.8,Python 2.7+ 只需在Shell中运行以下命令:Python manage.py shell并执行以下代码。

from django.conf.urls import RegexURLPattern, RegexURLResolver
from django.core import urlresolvers
urls = urlresolvers.get_resolver(None)

def if_none(value):
    if value:
        return value
    return ''

def print_urls(urls, parent_pattern=None):
    for url in urls.url_patterns:
        if isinstance(url, RegexURLResolver):
            print_urls(url, if_none(parent_pattern) + url.regex.pattern)
        elif isinstance(url, RegexURLPattern):
            print(if_none(parent_pattern) + url.regex.pattern)

print_urls(urls)

1
请问您能否提供更多关于您回答的细节? - toshiro92
1
我已经编辑了我的答案,你需要在你的shell中运行它。 - Aditya Saini

2
import subprocces

res = subprocess.run(
    'python manage.py show_urls',
    capture_output=True,
    shell=True,
)
url_list = [
    line.split('\t')[0]
    for line in res.stdout.decode().split('\n')
]

1
你能简单解释一下吗? - Faisal Nazik
1
subprocess.run()允许您从Python运行命令行可执行文件。您可以传递show_urls参数到manage.py,捕获输出,必须使用shell选项(默认情况下-据我所知),并将其保存到res变量中。然后,url_list是一个列表...它使用“生成器”结构来生成每个列表项。假设res输出带有\t字符的编码URL,我们取一个列表项并将其拆分,然后取余数(第一个元素),即[item for line in ...]...需要迭代所有URL并读取它们。 - Shmack

2

Django >= 2.0 列表解决方案

源自 @CesarCanassa

from django.conf import settings
from django.urls import URLPattern, URLResolver

URLCONF = __import__(settings.ROOT_URLCONF, {}, {}, [''])

def list_urls(patterns, path=None):
    """ recursive """
    if not path:
        path = []
    result = []
    for pattern in patterns:
        if isinstance(pattern, URLPattern):
            result.append(''.join(path) + str(pattern.pattern))
        elif isinstance(pattern, URLResolver):
            result += list_urls(pattern.url_patterns, path + [str(pattern.pattern)])
    return result

为了获取URLPattern名变量(这将给出您从路径中反转的名称,例如name:name): 请将result.append(''.join(path)+ str(pattern.pattern))替换为result.append(str(pattern.name)) - JessicaRyan

1

这是对 @Cesar Canassa 的生成器魔法的又一次适应。您可以将其添加到 yourapp/management/commands/dumpurls.py 目录中,以便作为 management.py 的子命令使用。

注意:我添加了一行代码,以确保它仅过滤yourapp的URL。如果需要其他URL,请相应地更新或删除该行。

作为management.py的子命令

部署路径:yourapp/management/commands/dumpurls.py

from django.core.management.base import BaseCommand, CommandError
from django.conf import settings
from django.urls import URLPattern, URLResolver

def list_urls(lis, acc=None):

    if acc is None:
        acc = []
    if not lis:
        return
    l = lis[0]
    if isinstance(l, URLPattern):
        yield acc + [str(l.pattern),l.name]
    elif isinstance(l, URLResolver):
        yield from list_urls(l.url_patterns, acc + [str(l.pattern)])
    yield from list_urls(lis[1:], acc)

class Command(BaseCommand):
    help = 'List all URLs from the urlconf'

    def handle(self, *args, **options):

        urlconf = __import__(settings.ROOT_URLCONF, {}, {}, [''])

        records, glen, nlen = [], 0, 0

        for p in list_urls(urlconf.urlpatterns):
            record = [''.join(p[:2]), p[2]]

            # Update me, or add an argument
            if record[0].startswith('yourapp'):

                clen = len(record[0])
                if clen > glen: glen = clen

                clen = len(record[1])
                if clen > nlen: nlen = clen
                
                records.append(record)


        self.stdout.write('{:-<{width}}'.format('',width=glen+nlen))
        self.stdout.write('{:<{glen}}Name'.format('Path',glen=glen+4))
        self.stdout.write('{:-<{width}}'.format('',width=glen+nlen))
        for record in records:
            self.stdout.write('{path:<{glen}}{name}'.format(path=record[0],
                name=record[1],
                glen=glen+4))
        self.stdout.write('{:-<{width}}'.format('',width=glen+nlen))

样例输出

(env) django@dev:myproj~> ./manage.py dumpurls
-------------------------------------------------------------------------------------------------------
Path                                                                        Name
-------------------------------------------------------------------------------------------------------
yourapp/^api-key/$                                                          api-key-list
yourapp/^api-key\.(?P<format>[a-z0-9]+)/?$                                  api-key-list
yourapp/^attacks/$                                                          attack-list
yourapp/^attacks\.(?P<format>[a-z0-9]+)/?$                                  attack-list
yourapp/^attack-histories/$                                                 attackhistory-list
yourapp/^attack-histories\.(?P<format>[a-z0-9]+)/?$                         attackhistory-list
yourapp/^files/$                                                            file-list
yourapp/^files\.(?P<format>[a-z0-9]+)/?$                                    file-list
yourapp/^modules/$                                                          module-list
yourapp/^modules\.(?P<format>[a-z0-9]+)/?$                                  module-list

1
你可以使用动态导入的方式,通过一个简单的方法来收集项目中每个应用程序的所有URL模式,例如:

def get_url_patterns():
    import importlib
    from django.apps import apps

    list_of_all_url_patterns = list()
    for name, app in apps.app_configs.items():
        # you have a directory structure where you should be able to build the correct path
        # my example shows that apps.[app_name].urls is where to look
        mod_to_import = f'apps.{name}.urls'
        try:
            urls = getattr(importlib.import_module(mod_to_import), "urlpatterns")
            list_of_all_url_patterns.extend(urls)
        except ImportError as ex:
            # is an app without urls
            pass

    return list_of_all_url_patterns

list_of_all_url_patterns = get_url_patterns()

最近我使用类似的方法创建了一个模板标签,用于显示活动导航链接。


1
from django.urls.resolvers import RegexPattern,RoutePattern
from your_main_app import urls

def get_urls():
    url_list = []
    for url in urls.urlpatterns:
        url_list.append(url.pattern._regex) if isinstance(url.pattern, RegexPattern) else url_list.append(url.pattern._route)

    return url_list

这里的 your_main_app 是指您的 settings.py 文件所在的应用程序名称。

0
如果您正在使用DRF,则可以通过打印router.get_urls()(在Django应用程序的urls.py文件中)中的urlpatterns来打印特定路由器的所有URL模式。
打开您的应用程序的urls.py文件,并将打印语句添加到文件底部,以便整个文件看起来像这样:
import pprint

from django.urls import include, path
from rest_framework import routers

from . import views

router = routers.DefaultRouter()
router.register(r"users", views.UserViewSet, basename="User")
router.register(r"auth", views.AuthenticationView, basename="Auth")
router.register(r"dummy", views.DummyViewSet, basename="Dummy")
router.register("surveys", views.SurveyViewSet, basename="survey")

urlpatterns = [
    path("", include(router.urls)),
]

pprint.pprint(router.get_urls())

然后将模式打印出来,如下所示:

[<URLPattern '^users/$' [name='User-list']>,
 <URLPattern '^users\.(?P<format>[a-z0-9]+)/?$' [name='User-list']>,
 <URLPattern '^users/admins/$' [name='User-admins']>,
 <URLPattern '^users/admins\.(?P<format>[a-z0-9]+)/?$' [name='User-admins']>,
 <URLPattern '^users/current/$' [name='User-current']>,
 <URLPattern '^users/current\.(?P<format>[a-z0-9]+)/?$' [name='User-current']>,
 <URLPattern '^users/(?P<pk>[^/.]+)/$' [name='User-detail']>,
 <URLPattern '^users/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$' [name='User-detail']>,
 <URLPattern '^auth/login/$' [name='Auth-login']>,
...
]

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