mirror of
https://github.com/django/django.git
synced 2025-06-05 03:29:12 +00:00
Fixed #28800 -- Added a listurls management command
This commit is contained in:
parent
b9aa3239ab
commit
4dfae3a3c2
1
AUTHORS
1
AUTHORS
@ -1036,6 +1036,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Tyson Clugg <tyson@clugg.net>
|
Tyson Clugg <tyson@clugg.net>
|
||||||
Tyson Tate <tyson@fallingbullets.com>
|
Tyson Tate <tyson@fallingbullets.com>
|
||||||
Unai Zalakain <unai@gisa-elkartea.org>
|
Unai Zalakain <unai@gisa-elkartea.org>
|
||||||
|
Ülgen Sarıkavak <https://github.com/ulgens>
|
||||||
Valentina Mukhamedzhanova <umirra@gmail.com>
|
Valentina Mukhamedzhanova <umirra@gmail.com>
|
||||||
valtron
|
valtron
|
||||||
Vasiliy Stavenko <stavenko@gmail.com>
|
Vasiliy Stavenko <stavenko@gmail.com>
|
||||||
|
275
django/core/management/commands/listurls.py
Normal file
275
django/core/management/commands/listurls.py
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
import json
|
||||||
|
from importlib import import_module
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.admindocs.views import (
|
||||||
|
extract_views_from_urlpatterns,
|
||||||
|
simplify_regex,
|
||||||
|
)
|
||||||
|
from django.core.management import color
|
||||||
|
from django.core.management.base import BaseCommand, CommandError, CommandParser
|
||||||
|
from django.utils import termcolors
|
||||||
|
|
||||||
|
FORMATS = (
|
||||||
|
"aligned",
|
||||||
|
"table",
|
||||||
|
"verbose",
|
||||||
|
"json",
|
||||||
|
"pretty-json",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
COLORLESS_FORMATS = (
|
||||||
|
"json",
|
||||||
|
"pretty-json",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "List URL patterns in the project with optional filtering by prefixes."
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.style.HEADER = termcolors.make_style(opts=("bold",))
|
||||||
|
self.style.ROUTE = termcolors.make_style(opts=("bold",))
|
||||||
|
self.style.VIEW_NAME = termcolors.make_style(fg="yellow", opts=("bold",))
|
||||||
|
self.style.NAME = termcolors.make_style(fg="red", opts=("bold",))
|
||||||
|
|
||||||
|
def add_arguments(self, parser: CommandParser):
|
||||||
|
super().add_arguments(parser)
|
||||||
|
|
||||||
|
# Sorting
|
||||||
|
parser.add_argument(
|
||||||
|
"--unsorted",
|
||||||
|
"-u",
|
||||||
|
action="store_true",
|
||||||
|
dest="unsorted",
|
||||||
|
help="Filter URLs by the given prefix[es]",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Prefix
|
||||||
|
parser.add_argument(
|
||||||
|
"--prefix",
|
||||||
|
"-p",
|
||||||
|
dest="prefixes",
|
||||||
|
help="Only list URLs with these prefixes.",
|
||||||
|
nargs="*",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Format
|
||||||
|
parser.add_argument(
|
||||||
|
"--format",
|
||||||
|
"-f",
|
||||||
|
choices=FORMATS,
|
||||||
|
default="aligned",
|
||||||
|
dest="format",
|
||||||
|
help="Formatting style of the output",
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
# Make sure the prefixes are a list
|
||||||
|
prefixes = options["prefixes"]
|
||||||
|
if prefixes:
|
||||||
|
prefixes = prefixes if isinstance(prefixes, list) else [prefixes]
|
||||||
|
|
||||||
|
url_patterns = get_url_patterns(prefixes=prefixes)
|
||||||
|
|
||||||
|
if not url_patterns:
|
||||||
|
raise CommandError("There are no URL patterns that match given prefixes")
|
||||||
|
|
||||||
|
unsorted = options["unsorted"]
|
||||||
|
no_color = options["no_color"]
|
||||||
|
format = options["format"]
|
||||||
|
|
||||||
|
# Apply sorting
|
||||||
|
if not unsorted:
|
||||||
|
url_patterns.sort()
|
||||||
|
|
||||||
|
# Apply colors
|
||||||
|
if (
|
||||||
|
color.supports_color()
|
||||||
|
and (not no_color)
|
||||||
|
and (format not in COLORLESS_FORMATS)
|
||||||
|
):
|
||||||
|
url_patterns = self.apply_color(url_patterns=url_patterns)
|
||||||
|
|
||||||
|
# Apply formatting
|
||||||
|
url_patterns = self.apply_format(
|
||||||
|
url_patterns=url_patterns,
|
||||||
|
format=format,
|
||||||
|
)
|
||||||
|
|
||||||
|
return url_patterns
|
||||||
|
|
||||||
|
def apply_color(self, url_patterns):
|
||||||
|
colored_url_patterns = []
|
||||||
|
|
||||||
|
for url_pattern in url_patterns:
|
||||||
|
# Route
|
||||||
|
route = url_pattern[0]
|
||||||
|
route = self.style.ROUTE(route)
|
||||||
|
|
||||||
|
# View
|
||||||
|
view = url_pattern[1]
|
||||||
|
module_path, module_name = view.rsplit(".", 1)
|
||||||
|
module_name = self.style.VIEW_NAME(module_name)
|
||||||
|
view = f"{module_path}.{module_name}"
|
||||||
|
|
||||||
|
# Name
|
||||||
|
name = url_pattern[2]
|
||||||
|
if name:
|
||||||
|
namespace, name = name.rsplit(":", 1) if ":" in name else ("", name)
|
||||||
|
name = self.style.NAME(name)
|
||||||
|
name = f"{namespace}:{name}" if namespace else name
|
||||||
|
|
||||||
|
# Append to the list
|
||||||
|
colored_url_patterns.append((route, view, name))
|
||||||
|
|
||||||
|
return colored_url_patterns
|
||||||
|
|
||||||
|
def apply_format(self, url_patterns, format):
|
||||||
|
format_method_name = f"format_{format.replace('-', '_')}"
|
||||||
|
format_method = getattr(self, format_method_name)
|
||||||
|
|
||||||
|
return format_method(url_patterns)
|
||||||
|
|
||||||
|
def format_table(self, url_patterns):
|
||||||
|
formatted_str = StringIO()
|
||||||
|
|
||||||
|
widths = []
|
||||||
|
margin = 2
|
||||||
|
for columns in zip(*url_patterns, strict=False):
|
||||||
|
widths.append(len(max(columns, key=len)) + margin)
|
||||||
|
|
||||||
|
# Headers
|
||||||
|
headers = (
|
||||||
|
self.style.HEADER("Route"),
|
||||||
|
self.style.HEADER("View"),
|
||||||
|
self.style.HEADER("Name"),
|
||||||
|
)
|
||||||
|
|
||||||
|
header_parts = []
|
||||||
|
for width, header in zip(widths, headers, strict=False):
|
||||||
|
header_parts.append(header.ljust(width))
|
||||||
|
|
||||||
|
formatted_str.write(" | ".join(header_parts))
|
||||||
|
formatted_str.write("\n")
|
||||||
|
|
||||||
|
# Header - content seperator
|
||||||
|
formatted_str.write("-+-".join("-" * width for width in widths))
|
||||||
|
formatted_str.write("\n")
|
||||||
|
|
||||||
|
# Rows (content)
|
||||||
|
for row in url_patterns:
|
||||||
|
row_parts = []
|
||||||
|
|
||||||
|
for width, cdata in zip(widths, row, strict=False):
|
||||||
|
row_parts.append(cdata.ljust(width))
|
||||||
|
|
||||||
|
formatted_str.write(" | ".join(row_parts))
|
||||||
|
formatted_str.write("\n")
|
||||||
|
|
||||||
|
return formatted_str.getvalue()
|
||||||
|
|
||||||
|
def format_aligned(self, url_patterns):
|
||||||
|
formatted_str = StringIO()
|
||||||
|
|
||||||
|
widths = []
|
||||||
|
for columns in zip(*url_patterns, strict=False):
|
||||||
|
margin = 2
|
||||||
|
widths.append(len(max(columns, key=len)) + margin)
|
||||||
|
|
||||||
|
for row in url_patterns:
|
||||||
|
for width, cdata in zip(widths, row, strict=False):
|
||||||
|
formatted_str.write(cdata.ljust(width))
|
||||||
|
|
||||||
|
formatted_str.write("\n")
|
||||||
|
|
||||||
|
return formatted_str.getvalue()
|
||||||
|
|
||||||
|
def format_verbose(self, url_patterns):
|
||||||
|
formatted_str = StringIO()
|
||||||
|
|
||||||
|
for route, view, name in url_patterns:
|
||||||
|
route_str = f"{self.style.HEADER('Route:')} {route}"
|
||||||
|
view_str = f"{self.style.HEADER('View:')} {view}"
|
||||||
|
name_str = f"{self.style.HEADER('Name:')} {name}" if name else ""
|
||||||
|
|
||||||
|
seperator = "-" * 20 + "\n"
|
||||||
|
parts = (
|
||||||
|
route_str,
|
||||||
|
view_str,
|
||||||
|
name_str,
|
||||||
|
seperator,
|
||||||
|
)
|
||||||
|
|
||||||
|
formatted_str.write("\n".join(part for part in parts if part))
|
||||||
|
|
||||||
|
return formatted_str.getvalue()
|
||||||
|
|
||||||
|
def format_json(self, url_patterns, pretty=False):
|
||||||
|
indent = 4 if pretty else None
|
||||||
|
|
||||||
|
# Having keys in the resulting JSON makes it more useful
|
||||||
|
url_pattern_dicts = []
|
||||||
|
for route, view, name in url_patterns:
|
||||||
|
url_pattern_dict = {
|
||||||
|
"route": route,
|
||||||
|
"view": view,
|
||||||
|
"name": name,
|
||||||
|
}
|
||||||
|
url_pattern_dicts.append(url_pattern_dict)
|
||||||
|
|
||||||
|
return json.dumps(url_pattern_dicts, indent=indent)
|
||||||
|
|
||||||
|
def format_pretty_json(self, url_patterns):
|
||||||
|
return self.format_json(url_patterns, pretty=True)
|
||||||
|
|
||||||
|
|
||||||
|
def get_url_patterns(prefixes=None):
|
||||||
|
"""
|
||||||
|
Returns a list of URL patterns in the project with given prefixes.
|
||||||
|
|
||||||
|
Each object in the returned list is a tuple[str] with 3 elements:
|
||||||
|
(route, view, name)
|
||||||
|
"""
|
||||||
|
|
||||||
|
url_patterns = []
|
||||||
|
urlconf = import_module(settings.ROOT_URLCONF)
|
||||||
|
|
||||||
|
for view_func, regex, namespace_list, name in extract_views_from_urlpatterns(
|
||||||
|
urlconf.urlpatterns
|
||||||
|
):
|
||||||
|
# Route
|
||||||
|
route = simplify_regex(regex)
|
||||||
|
|
||||||
|
# View
|
||||||
|
view = "{}.{}".format(
|
||||||
|
view_func.__module__,
|
||||||
|
getattr(view_func, "__name__", view_func.__class__.__name__),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Name
|
||||||
|
namespace = ""
|
||||||
|
|
||||||
|
if namespace_list:
|
||||||
|
for part in namespace_list:
|
||||||
|
namespace += part + ":"
|
||||||
|
|
||||||
|
name = namespace + name if name else None
|
||||||
|
name = name or ""
|
||||||
|
|
||||||
|
# Append to the list
|
||||||
|
url_patterns.append((route, view, name))
|
||||||
|
|
||||||
|
# Filter out when prefixes are given but the pattern's route doesn't match
|
||||||
|
if prefixes:
|
||||||
|
url_patterns = [
|
||||||
|
url_pattern
|
||||||
|
for url_pattern in url_patterns
|
||||||
|
if any(url_pattern[0].startswith(prefix) for prefix in prefixes)
|
||||||
|
]
|
||||||
|
|
||||||
|
return url_patterns
|
@ -498,6 +498,18 @@ Only support for PostgreSQL is implemented.
|
|||||||
|
|
||||||
If this option is provided, models are also created for database views.
|
If this option is provided, models are also created for database views.
|
||||||
|
|
||||||
|
``listurls``
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. django-admin:: listurls [url_prefix ...]
|
||||||
|
|
||||||
|
Displays a list of the URLs and their associated short names and views in the
|
||||||
|
project. Optionally restrict to one or more prefixes.
|
||||||
|
|
||||||
|
.. django-admin-option:: --table
|
||||||
|
|
||||||
|
Output the list of URLs as a table, one URL per line.
|
||||||
|
|
||||||
``loaddata``
|
``loaddata``
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -255,6 +255,9 @@ Management Commands
|
|||||||
``Command.autodetector`` attribute for subclasses to override in order to use
|
``Command.autodetector`` attribute for subclasses to override in order to use
|
||||||
a custom autodetector class.
|
a custom autodetector class.
|
||||||
|
|
||||||
|
* Introduce a :djadmin:`listurls` command that lists the URLs in the application,
|
||||||
|
including the view function and name, if present.
|
||||||
|
|
||||||
Migrations
|
Migrations
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
0
tests/admin_scripts/app_with_urls/__init__.py
Normal file
0
tests/admin_scripts/app_with_urls/__init__.py
Normal file
12
tests/admin_scripts/app_with_urls/root_urls.py
Normal file
12
tests/admin_scripts/app_with_urls/root_urls.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from django.urls import include, path
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path(
|
||||||
|
route="nons/",
|
||||||
|
view=include("admin_scripts.app_with_urls.urls_nons"),
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
route="namespaced/",
|
||||||
|
view=include("admin_scripts.app_with_urls.urls_namespaced", namespace="ns"),
|
||||||
|
),
|
||||||
|
]
|
17
tests/admin_scripts/app_with_urls/urls_namespaced.py
Normal file
17
tests/admin_scripts/app_with_urls/urls_namespaced.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
app_name = "app_with_urls"
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path(
|
||||||
|
route="unnamed",
|
||||||
|
view=views.view_func_namespaced_unnamed,
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
route="named",
|
||||||
|
view=views.view_func_namespaced_named,
|
||||||
|
name="named",
|
||||||
|
),
|
||||||
|
]
|
17
tests/admin_scripts/app_with_urls/urls_nons.py
Normal file
17
tests/admin_scripts/app_with_urls/urls_nons.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
app_name = "app_with_urls"
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path(
|
||||||
|
route="unnamed",
|
||||||
|
view=views.view_func_nons_unnamed,
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
route="named",
|
||||||
|
view=views.view_func_nons_named,
|
||||||
|
name="named",
|
||||||
|
),
|
||||||
|
]
|
14
tests/admin_scripts/app_with_urls/views.py
Normal file
14
tests/admin_scripts/app_with_urls/views.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
def view_func_namespaced_unnamed(request):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def view_func_namespaced_named(request):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def view_func_nons_unnamed(request):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def view_func_nons_named(request):
|
||||||
|
pass
|
@ -4,6 +4,7 @@ advertised - especially with regards to the handling of the
|
|||||||
DJANGO_SETTINGS_MODULE and default settings.py files.
|
DJANGO_SETTINGS_MODULE and default settings.py files.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
@ -3148,6 +3149,286 @@ class Dumpdata(AdminScriptTestCase):
|
|||||||
self.assertNoOutput(out)
|
self.assertNoOutput(out)
|
||||||
|
|
||||||
|
|
||||||
|
class Listurls(AdminScriptTestCase):
|
||||||
|
"""
|
||||||
|
Tests for the listurls command.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@override_settings(ROOT_URLCONF="admin_scripts.app_with_urls.root_urls")
|
||||||
|
def test_default(self):
|
||||||
|
self.write_settings(
|
||||||
|
"settings.py",
|
||||||
|
apps=["admin_scripts.app_with_urls"],
|
||||||
|
)
|
||||||
|
|
||||||
|
args = ["listurls"]
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
|
||||||
|
# Check route, view and (if defined) name for each URL
|
||||||
|
self.assertOutput(out, "/namespaced/named")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_namespaced_named",
|
||||||
|
)
|
||||||
|
self.assertOutput(out, "ns:named")
|
||||||
|
|
||||||
|
self.assertOutput(out, "/namespaced/unnamed")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_namespaced_unnamed",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertOutput(out, "/nons/named")
|
||||||
|
self.assertOutput(out, "admin_scripts.app_with_urls.views.view_func_nons_named")
|
||||||
|
self.assertOutput(out, "app_with_urls:named")
|
||||||
|
|
||||||
|
self.assertOutput(out, "/nons/unnamed")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_nons_unnamed",
|
||||||
|
)
|
||||||
|
|
||||||
|
@override_settings(ROOT_URLCONF="admin_scripts.app_with_urls.root_urls")
|
||||||
|
def test_aligned(self):
|
||||||
|
self.write_settings(
|
||||||
|
"settings.py",
|
||||||
|
apps=["admin_scripts.app_with_urls"],
|
||||||
|
)
|
||||||
|
|
||||||
|
args = ["listurls", "-f", "aligned"]
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
|
||||||
|
# Check route, view and (if defined) name for each URL
|
||||||
|
self.assertOutput(out, "/namespaced/named")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_namespaced_named",
|
||||||
|
)
|
||||||
|
self.assertOutput(out, "ns:named")
|
||||||
|
|
||||||
|
self.assertOutput(out, "/namespaced/unnamed")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_namespaced_unnamed",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertOutput(out, "/nons/named")
|
||||||
|
self.assertOutput(out, "admin_scripts.app_with_urls.views.view_func_nons_named")
|
||||||
|
self.assertOutput(out, "app_with_urls:named")
|
||||||
|
|
||||||
|
self.assertOutput(out, "/nons/unnamed")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_nons_unnamed",
|
||||||
|
)
|
||||||
|
|
||||||
|
@override_settings(ROOT_URLCONF="admin_scripts.app_with_urls.root_urls")
|
||||||
|
def test_table(self):
|
||||||
|
self.write_settings(
|
||||||
|
"settings.py",
|
||||||
|
apps=["admin_scripts.app_with_urls"],
|
||||||
|
)
|
||||||
|
|
||||||
|
args = ["listurls", "-f", "table"]
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
|
||||||
|
# Check table headers
|
||||||
|
self.assertOutput(out, "Route")
|
||||||
|
self.assertOutput(out, "View")
|
||||||
|
self.assertOutput(out, "Name")
|
||||||
|
self.assertOutput(out, "---+---")
|
||||||
|
|
||||||
|
# Check route, view and (if defined) name for each URL
|
||||||
|
self.assertOutput(out, "/namespaced/named")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_namespaced_named",
|
||||||
|
)
|
||||||
|
self.assertOutput(out, "ns:named")
|
||||||
|
|
||||||
|
self.assertOutput(out, "/namespaced/unnamed")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_namespaced_unnamed",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertOutput(out, "/nons/named")
|
||||||
|
self.assertOutput(out, "admin_scripts.app_with_urls.views.view_func_nons_named")
|
||||||
|
self.assertOutput(out, "app_with_urls:named")
|
||||||
|
|
||||||
|
self.assertOutput(out, "/nons/unnamed")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_nons_unnamed",
|
||||||
|
)
|
||||||
|
|
||||||
|
@override_settings(ROOT_URLCONF="admin_scripts.app_with_urls.root_urls")
|
||||||
|
def test_verbose(self):
|
||||||
|
self.write_settings(
|
||||||
|
"settings.py",
|
||||||
|
apps=["admin_scripts.app_with_urls"],
|
||||||
|
)
|
||||||
|
|
||||||
|
args = ["listurls", "-f", "verbose"]
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
|
||||||
|
self.assertOutput(out, "Route:")
|
||||||
|
self.assertOutput(out, "View:")
|
||||||
|
self.assertOutput(out, "Name:")
|
||||||
|
self.assertOutput(out, "-" * 20)
|
||||||
|
|
||||||
|
self.assertOutput(out, "/namespaced/named")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_namespaced_named",
|
||||||
|
)
|
||||||
|
self.assertOutput(out, "ns:named")
|
||||||
|
|
||||||
|
self.assertOutput(out, "/namespaced/unnamed")
|
||||||
|
self.assertOutput(
|
||||||
|
out, "admin_scripts.app_with_urls.views.view_func_namespaced_unnamed"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertOutput(out, "/nons/named")
|
||||||
|
self.assertOutput(out, "app_with_urls:named")
|
||||||
|
self.assertOutput(out, "admin_scripts.app_with_urls.views.view_func_nons_named")
|
||||||
|
|
||||||
|
self.assertOutput(out, "/nons/unnamed")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_nons_unnamed",
|
||||||
|
)
|
||||||
|
|
||||||
|
@override_settings(ROOT_URLCONF="admin_scripts.app_with_urls.root_urls")
|
||||||
|
def test_json(self):
|
||||||
|
self.write_settings(
|
||||||
|
"settings.py",
|
||||||
|
apps=["admin_scripts.app_with_urls"],
|
||||||
|
)
|
||||||
|
|
||||||
|
args = ["listurls", "-f", "json"]
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
|
||||||
|
try:
|
||||||
|
json.loads(out)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
self.fail("Output is not valid JSON")
|
||||||
|
|
||||||
|
self.assertOutput(out, '"route": "/namespaced/named"')
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
'"view": "admin_scripts.app_with_urls.views.view_func_namespaced_named"',
|
||||||
|
)
|
||||||
|
self.assertOutput(out, '"name": "ns:named"')
|
||||||
|
|
||||||
|
self.assertOutput(out, '"route": "/namespaced/unnamed"')
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
'"view": "admin_scripts.app_with_urls.views.view_func_namespaced_unnamed"',
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertOutput(out, '"route": "/nons/named"')
|
||||||
|
self.assertOutput(out, "admin_scripts.app_with_urls.views.view_func_nons_named")
|
||||||
|
self.assertOutput(out, "app_with_urls:named")
|
||||||
|
|
||||||
|
self.assertOutput(out, "/nons/unnamed")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_nons_unnamed",
|
||||||
|
)
|
||||||
|
|
||||||
|
@override_settings(ROOT_URLCONF="admin_scripts.app_with_urls.root_urls")
|
||||||
|
def test_pretty_json(self):
|
||||||
|
self.write_settings(
|
||||||
|
"settings.py",
|
||||||
|
apps=["admin_scripts.app_with_urls"],
|
||||||
|
)
|
||||||
|
|
||||||
|
args = ["listurls", "-f", "pretty-json"]
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
|
||||||
|
self.assertOutput(out, "/namespaced/named")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_namespaced_named",
|
||||||
|
)
|
||||||
|
self.assertOutput(out, "ns:named")
|
||||||
|
|
||||||
|
self.assertOutput(out, "/namespaced/unnamed")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_namespaced_unnamed",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertOutput(out, "/nons/named")
|
||||||
|
self.assertOutput(out, "app_with_urls:named")
|
||||||
|
self.assertOutput(out, "admin_scripts.app_with_urls.views.view_func_nons_named")
|
||||||
|
|
||||||
|
self.assertOutput(out, "/nons/unnamed")
|
||||||
|
self.assertOutput(
|
||||||
|
out,
|
||||||
|
"admin_scripts.app_with_urls.views.view_func_nons_unnamed",
|
||||||
|
)
|
||||||
|
|
||||||
|
@override_settings(ROOT_URLCONF="admin_scripts.app_with_urls.root_urls")
|
||||||
|
def test_unsorted(self):
|
||||||
|
self.write_settings(
|
||||||
|
"settings.py",
|
||||||
|
apps=["admin_scripts.app_with_urls"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# JSON format is the easiest to parse and test
|
||||||
|
args = ["listurls", "-f", "json", "--unsorted"]
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
url_patterns = json.loads(out)
|
||||||
|
|
||||||
|
self.assertNotEqual(
|
||||||
|
url_patterns,
|
||||||
|
sorted(url_patterns, key=lambda u: u["route"]),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_no_urls(self):
|
||||||
|
self.write_settings("settings.py")
|
||||||
|
|
||||||
|
args = ["listurls"]
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
|
||||||
|
self.assertOutput(err, "There are no URL patterns that match given prefixes")
|
||||||
|
self.assertNoOutput(out)
|
||||||
|
|
||||||
|
@override_settings(ROOT_URLCONF="admin_scripts.app_with_urls.root_urls")
|
||||||
|
def test_prefixes(self):
|
||||||
|
self.write_settings(
|
||||||
|
"settings.py",
|
||||||
|
apps=["admin_scripts.app_with_urls"],
|
||||||
|
)
|
||||||
|
|
||||||
|
args = ["listurls", "-p", "/namespaced"]
|
||||||
|
out, err = self.run_manage(args)
|
||||||
|
|
||||||
|
self.assertNoOutput(err)
|
||||||
|
|
||||||
|
self.assertOutput(out, "/namespaced/named")
|
||||||
|
self.assertOutput(out, "ns:named")
|
||||||
|
self.assertOutput(out, "/namespaced/unnamed")
|
||||||
|
|
||||||
|
self.assertNotInOutput(out, "/nons/named")
|
||||||
|
self.assertNotInOutput(out, "app_with_urls:named")
|
||||||
|
self.assertNotInOutput(out, "/nons/unnamed")
|
||||||
|
|
||||||
|
|
||||||
class MainModule(AdminScriptTestCase):
|
class MainModule(AdminScriptTestCase):
|
||||||
"""python -m django works like django-admin."""
|
"""python -m django works like django-admin."""
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user