I have a table that relates parents to children, it has the following data:
+-----+-----+-----+--------+
| pid | rel | cid | relcat |
+-----+-----+-----+--------+
| 13 | F | 216 | 1 |
| 13 | F | 229 | 1 |
| 13 | f | 328 | 2 |
| 13 | F | 508 | 1 |
| 13 | F | 599 | 1 |
| 13 | f | 702 | 2 |
| 560 | M | 229 | 1 |
| 560 | m | 702 | 2 |
+-----+-----+-----+--------+
I can find brothers of 229 by joining npr table to itself with SQL:
SELECT npr_a.cid,
CASE (SUM(IF(npr_a.relcat=1 AND npr_b.relcat=1,1,0))) WHEN 2 THEN '~FB~' WHEN 1 THEN '~HB~' ELSE '~Foster~' END AS BrotherType,
abs(person_details.isalive) as isalive
FROM person_details,
npr npr_a,
npr npr_b
WHERE ( npr_b.cid = 229) AND
( npr_a.pid = npr_b.pid ) AND
( npr_a.cid <> 229) AND
( npr_b.relcat <> 3 ) AND
( npr_a.relcat <> 3 ) AND
( person_details.id = npr_a.cid )
GROUP BY npr_a.cid;
to get:
+-----+-------------+---------+
| cid | BrotherType | isalive |
+-----+-------------+---------+
| 216 | ~HB~ | 1 |
| 328 | ~Foster~ | 0 |
| 508 | ~HB~ | 0 |
| 599 | ~HB~ | 0 |
| 702 | ~Foster~ | 1 |
+-----+-------------+---------+
I tried many ways to get it using Django queryset but all failed to get the correct results. the best thing I was able to get is:
idp = Npr.objects.filter(cid=229).values_list('pid', flat=True)
idc = Npr.objects.filter(pid__in=idp).exclude(cid=229)
but that solution was not able to generate the BrotherType field.
My models:
class PersonDetails(models.Model):
id = models.AutoField(db_column='ID', primary_key=True)
name= models.CharField(db_column='Name', max_length=20, blank=True, null=True)
isalive = models.BooleanField(db_column='isAlive')
class Meta:
managed = False
db_table = 'person_details'
class Npr(models.Model):
rid = models.AutoField(db_column='rid', primary_key=True)
pid = models.ForeignKey(PersonDetails, on_delete=models.CASCADE, db_column='PID')
rel = models.CharField(max_length=1)
cid = models.ForeignKey(PersonDetails, on_delete=models.CASCADE, db_column='CID')
relcat = models.PositiveIntegerField()
class Meta:
managed = False
db_table = 'npr'
unique_together = (('pid', 'cid'),)
using Django Version: 3.0.4 Python version: 3.7.3 Database: 10.3.22-MariaDB-0+deb10u1-log
Any suggestion to build the required queryset?
luckily I found a solution to my problem.
I created a new model pointing to the same database table
class nNpr(models.Model):
rid = models.AutoField(db_column='rid', primary_key=True)
pid = models.ForeignKey(Npr, on_delete=models.CASCADE, db_column='PID', to_field='pid', related_name='rel_pid')
rel = models.CharField(max_length=1)
cid = models.PositiveSmallIntegerField(db_column='CID')
relcat = models.PositiveIntegerField()
class Meta:
managed = False
db_table = 'npr'
unique_together = (('pid', 'cid'),)
and modified the original model to suppress warning that Django generates when you join a foreign key field to a non unique field, the modified model is:
class Npr(models.Model):
rid = models.AutoField(db_column='rid', primary_key=True)
pid = models.OneToOneField(PersonDetails, on_delete=models.CASCADE, , unique=True, db_column='PID', related_name='rel_pid')
rel = models.CharField(max_length=1)
cid = models.ForeignKey(PersonDetails, on_delete=models.CASCADE, db_column='CID', related_name='rel_cid')
relcat = models.PositiveIntegerField()
class Meta:
managed = False
db_table = 'npr'
unique_together = (('pid', 'cid'),)
the last thing is the query set:
brothers = nNpr.objects.select_related('pid').filter(cid = 229).exclude(Q(relcat = 3)\
| Q(pid__cid = 229) | Q(pid__relcat = 3 )).values('pid__cid', 'pid__cid__name',\
'pid__cid__isalive').annotate(cprel=Sum('relcat'), pcrel=Sum('pid__relcat'))
and the resulting SQL is:
SELECT T2.`CID`, `person_details`.`Name`, `person_details`.`isAlive`, SUM(`npr`.`relcat`) AS `cprel`\
, SUM(T2.`relcat`) AS `pcrel` FROM `npr` INNER JOIN `npr` T2 ON (`npr`.`PID` = T2.`PID`) INNER\
JOIN `person_details` ON (T2.`CID` = `person_details`.`ID`) WHERE (`npr`.`CID` = 229 AND NOT \
((`npr`.`relcat` = 3 OR T2.`CID` = 198 OR T2.`relcat` = 229))) GROUP BY T2.`CID`,\
`person_details`.`Name`, `person_details`.`isAlive` ORDER BY NULL
I found another better solution that doesn't need to create a new model pointing to the same database table. The solution was cascading the required relations recursively on one model:
brothers = Npr.objects.filter(pid__pid__cid = 229, relcat__lt = 3,
pid__pid__relcat__lt = 3).values('cid', 'cid__name')
I have three models with a simple relation as below:
models.py
class Person(models.Model):
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
class PersonSession(models.Model):
start_time = models.DateTimeField(auto_now_add=True)
end_time = models.DateTimeField(null=True,
blank=True)
person = models.ForeignKey(Person, related_name='sessions')
class Billing(models.Model):
DEBT = 'DE'
BALANCED = 'BA'
CREDIT = 'CR'
session = models.OneToOneField(PersonSession,
blank=False,
null=False,
related_name='billing')
STATUS = ((BALANCED, 'Balanced'),
(DEBT, 'Debt'),
(CREDIT, 'Credit'))
status = models.CharField(max_length=2,
choices=STATUS,
blank=False,
default=BALANCED
)
views.py
class PersonFilter(django_filters.FilterSet):
start_time = django_filters.DateFromToRangeFilter(name='sessions__start_time',
distinct=True)
billing_status = django_filters.ChoiceFilter(name='sessions__billing__status',
choices=Billing.STATUS,
distinct=True)
class Meta:
model = Person
fields = ('first_name', 'last_name')
class PersonList(generics.ListCreateAPIView):
queryset = Person.objects.all()
serializer_class = PersonSerializer
filter_backends = (django_filters.rest_framework.DjangoFilterBackend)
filter_class = PersonFilter
I want to get billings from person endpoint which have DE status in billing and are between a period of time:
api/persons?start_time_0=2018-03-20&start_time_1=2018-03-23&billing_status=DE
But the result is not what I were looking for, this returns all persons has a session in that period and has a billing with the DE status, whether that billing is on the period or not.
In other words, it seems use or operation between two filter fields, I think this post is related to this issue but currently I could not find a way to get the result I want. I am using djang 1.10.3.
Edit
I try to write an example to show what I need and what I get from django filter. If I get persons using below query in the example, I got just two person:
select *
from
test_filter_person join test_filter_personsession on test_filter_person.id=test_filter_personsession.person_id join test_filter_billing on test_filter_personsession.id=test_filter_billing.session_id
where
start_time > '2000-02-01' and start_time < '2000-03-01' and status='DE';
Which gets me just person 1 and 2. But if I get somethings expected similar from url I would get all of persons, the similar url (at least one which I expected to be the same) is as below:
http://address/persons?start_time_0=2000-02-01&start_time_1=2000-03-01&billing_status=DE
Edit2
This is the data that my queries in the example are upon and using them you can see what must returns in queries that I mentioned above:
id | first_name | last_name | id | start_time | end_time | person_id | id | status | session_id
----+------------+-----------+----+---------------------------+---------------------------+-----------+----+--------+------------
0 | person | 0 | 0 | 2000-01-01 16:32:00+03:30 | 2000-01-01 17:32:00+03:30 | 0 | 0 | DE | 0
0 | person | 0 | 1 | 2000-02-01 16:32:00+03:30 | 2000-02-01 17:32:00+03:30 | 0 | 1 | BA | 1
0 | person | 0 | 2 | 2000-03-01 16:32:00+03:30 | 2000-03-01 17:32:00+03:30 | 0 | 2 | DE | 2
1 | person | 1 | 3 | 2000-01-01 16:32:00+03:30 | 2000-01-01 17:32:00+03:30 | 1 | 3 | BA | 3
1 | person | 1 | 4 | 2000-02-01 16:32:00+03:30 | 2000-02-01 17:32:00+03:30 | 1 | 4 | DE | 4
1 | person | 1 | 5 | 2000-03-01 16:32:00+03:30 | 2000-03-01 17:32:00+03:30 | 1 | 5 | DE | 5
2 | person | 2 | 6 | 2000-01-01 16:32:00+03:30 | 2000-01-01 17:32:00+03:30 | 2 | 6 | DE | 6
2 | person | 2 | 7 | 2000-02-01 16:32:00+03:30 | 2000-02-01 17:32:00+03:30 | 2 | 7 | DE | 7
2 | person | 2 | 8 | 2000-03-01 16:32:00+03:30 | 2000-03-01 17:32:00+03:30 | 2 | 8 | BA | 8
Edit3
I try using prefetch_related to join tables and get results as I expected because I thought that extra join causes this problem but this did not work and I still get the same result and this had not any effects.
Edit4
This issue has the same problem.
I don't have a solution yet; but I thought a concise summary of the problem will set more and better minds than mine at work!
From what I understand; your core issue is a result of two pre-conditions:
The fact that you have two discrete filters defined on a related model; resulting in filter spanning-multi-valued-relationships
The way FilterSet implements filtering
Let us look at these in more detail:
filter spanning-multi-valued-relationships
This is a great resource to understand issue pre-condition #1 better:
https://docs.djangoproject.com/en/2.0/topics/db/queries/#spanning-multi-valued-relationships
Essentially, the start_time filter adds a .filter(sessions__start_time=value) to your Queryset, and the billing_status filter adds a .filter(sessions_billing_status=value) to the filter. This results in the "spanning-multi-valued-relationships" issue described above, meaning it will do an OR between these filters instead of an AND as you require it to.
This got me thinking, why don't we see the same issue in the start_time filter; but the trick here is that it is defined as a DateFromToRangeFilter; it internally uses a single filter query with the __range= construct. If instead it did sessions__start_time__gt= and sessions__start_time__lt=, we would have the same issue here.
The way FilterSet implements filtering
Talk is cheap; show me the code
#property
def qs(self):
if not hasattr(self, '_qs'):
if not self.is_bound:
self._qs = self.queryset.all()
return self._qs
if not self.form.is_valid():
if self.strict == STRICTNESS.RAISE_VALIDATION_ERROR:
raise forms.ValidationError(self.form.errors)
elif self.strict == STRICTNESS.RETURN_NO_RESULTS:
self._qs = self.queryset.none()
return self._qs
# else STRICTNESS.IGNORE... ignoring
# start with all the results and filter from there
qs = self.queryset.all()
for name, filter_ in six.iteritems(self.filters):
value = self.form.cleaned_data.get(name)
if value is not None: # valid & clean data
qs = filter_.filter(qs, value)
self._qs = qs
return self._qs
As you can see, the qs property is resolved by iterating over a list of Filter objects, passing the initial qs through each of them successively and returning the result. See qs = filter_.filter(qs, value)
Each Filter object here defines a specific def filter operation, that basically takes teh Queryset and then adds a successive .filter to it.
Here's an example from the BaseFilter class
def filter(self, qs, value):
if isinstance(value, Lookup):
lookup = six.text_type(value.lookup_type)
value = value.value
else:
lookup = self.lookup_expr
if value in EMPTY_VALUES:
return qs
if self.distinct:
qs = qs.distinct()
qs = self.get_method(qs)(**{'%s__%s' % (self.name, lookup): value})
return qs
The line of code that matters is: qs = self.get_method(qs)(**{'%s__%s' % (self.name, lookup): value})
So the two pre-conditions create the perfect storm for this issue.
This worked for me:
class FooFilterSet(FilterSet):
def filter_queryset(self, queryset):
"""
Overrides the basic methtod, so that instead of iterating over tthe queryset with multiple `.filter()`
calls, one for each filter, it accumulates the lookup expressions and applies them all in a single
`.filter()` call - to filter with an explicit "AND" in many to many relationships.
"""
filter_kwargs = {}
for name, value in self.form.cleaned_data.items():
if value not in EMPTY_VALUES:
lookup = '%s__%s' % (self.filters[name].field_name, self.filters[name].lookup_expr)
filter_kwargs.update({lookup:value})
queryset = queryset.filter(**filter_kwargs)
assert isinstance(queryset, models.QuerySet), \
"Expected '%s.%s' to return a QuerySet, but got a %s instead." \
% (type(self).__name__, name, type(queryset).__name__)
return queryset
Overriding the filter_queryset method so that it accumulates the expressions and applies them in a single .filter() call
I was following this articles to have 2 columns per 1 field , so my custom field code is something like this :
class GeopositionField(models.Field):
description = "A geoposition (latitude and longitude)"
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 42
super(GeopositionField, self).__init__(*args, **kwargs)
def contribute_to_class(self, cls, name):
self.name = name
position_longitude = DecimalField(decimal_places=6,max_digits=9,default=0,blank=True)
cls.add_to_class("position_longitude",position_longitude)
position_latitude = DecimalField(decimal_places=6,max_digits=8,default=0,blank=True)
cls.add_to_class("position_latitude",position_latitude)
setattr(cls,"position_longitude",position_longitude)
setattr(cls,"position_latitude",position_latitude)
And my model class is Request :
class Request(models.Model):
person = models.ForeignKey(Person)
position = GeopositionField(null = False,default = 0)
( I'm modifying django-geoposition ) Until now I have only position_latitude and position_longitude in my table (and not only "position" like it was originally)
Before
+--------------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+---------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| person_id | int(11) | NO | MUL | NULL | |
| creation_date | datetime | NO | | NULL | |
| position | varchar(50) | NO | | NULL | |
+--------------------+---------------+------+-----+---------+----------------+
After
+--------------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+---------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| person_id | int(11) | NO | MUL | NULL | |
| creation_date | datetime | NO | | NULL | |
| position_longitude | decimal(9,6) | NO | | NULL | |
| position_latitude | decimal(8,6) | NO | | NULL | |
+--------------------+---------------+------+-----+---------+----------------+
That's good, but the problem comes in django admin, and also in the shell, because if I create a "Request" object and then I try to print "position" attribute, I got a error that says the "position" attribute doesn't exists :
>>> from main.models import Request
>>> x = Request()
>>> x.position
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Request' object has no attribute 'position'
>>>
It works if I set position attribute in __init__ method of Request class, but that's not the idea.
So, back to the real problem, when I try to show it in admin panel, if I call explicitly to "position" field django throws an error, curiously it works with "position_latitude" and "position_longitude"
class RequestAdminForm(forms.ModelForm):
class Meta:
model = Request
# fields = ['position_latitude','position_longitude] <-- this works !
fields = ['position'] # <-- this returns error = Unknown field(s) (position) specified for Request
Is there a way to show "position_latitude" and "position_longitude" when RequestAdminForm only have "position" in fields list? That's what I want to achieve. Why happen the "undefined-attribute" problem ?
How can I see the current urlpatterns that "reverse" is looking in?
I'm calling reverse in a view with an argument that I think should work, but doesn't. Any way I can check what's there and why my pattern isn't?
If you want a list of all the urls in your project, first you need to install django-extensions
You can simply install using command.
pip install django-extensions
For more information related to package goto django-extensions
After that, add django_extensions in INSTALLED_APPS in your settings.py file like this:
INSTALLED_APPS = (
...
'django_extensions',
...
)
urls.py example:
from django.urls import path, include
from . import views
from . import health_views
urlpatterns = [
path('get_url_info', views.get_url_func),
path('health', health_views.service_health_check),
path('service-session/status', views.service_session_status)
]
And then, run any of the command in your terminal
python manage.py show_urls
or
./manage.py show_urls
Sample output example based on config urls.py:
/get_url_info django_app.views.get_url_func
/health django_app.health_views.service_health_check
/service-session/status django_app.views.service_session_status
For more information you can check the documentation.
Try this:
from django.urls import get_resolver
get_resolver().reverse_dict.keys()
Or if you're still on Django 1.*:
from django.core.urlresolvers import get_resolver
get_resolver(None).reverse_dict.keys()
Django >= 2.0 solution
I tested the other answers in this post and they were either not working with Django 2.X, incomplete or too complex. Therefore, here is my take on this:
from django.conf import settings
from django.urls import URLPattern, URLResolver
urlconf = __import__(settings.ROOT_URLCONF, {}, {}, [''])
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)]
elif isinstance(l, URLResolver):
yield from list_urls(l.url_patterns, acc + [str(l.pattern)])
yield from list_urls(lis[1:], acc)
for p in list_urls(urlconf.urlpatterns):
print(''.join(p))
This code prints all URLs, unlike some other solutions it will print the full path and not only the last node. e.g.:
admin/
admin/login/
admin/logout/
admin/password_change/
admin/password_change/done/
admin/jsi18n/
admin/r/<int:content_type_id>/<path:object_id>/
admin/auth/group/
admin/auth/group/add/
admin/auth/group/autocomplete/
admin/auth/group/<path:object_id>/history/
admin/auth/group/<path:object_id>/delete/
admin/auth/group/<path:object_id>/change/
admin/auth/group/<path:object_id>/
admin/auth/user/<id>/password/
admin/auth/user/
... etc, etc
Django 1.11, Python 2.7.6
cd to_your_django_project
python manage.py shell
Then paste following code.
from django.conf.urls import RegexURLPattern, RegexURLResolver
from django.core import urlresolvers
urls = urlresolvers.get_resolver()
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)
Sample output:
^django-admin/^$
^django-admin/^login/$
^django-admin/^logout/$
^django-admin/^password_change/$
^django-admin/^password_change/done/$
^django-admin/^jsi18n/$
^django-admin/^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$
^django-admin/^wagtailimages/image/^$
^django-admin/^wagtailimages/image/^add/$
^django-admin/^wagtailimages/image/^(.+)/history/$
^django-admin/^wagtailimages/image/^(.+)/delete/$
^django-admin/^wagtailimages/image/^(.+)/change/$
^django-admin/^wagtailimages/image/^(.+)/$
...
In Django 3.0, it's as easy as:
from django.urls import get_resolver
print(get_resolver().url_patterns)
Prints:
[<URLPattern '' [name='home']>, <URLPattern '/testing' [name='another_url']>]
Here is a quick and dirty hack to just get the information you need without needing to modify any of your settings.
$ pip install django-extensions
$ python manage.py shell -c 'from django.core.management import call_command; from django_extensions.management.commands.show_urls import Command; call_command(Command())'
This is piggy backing off #robert's answer. While correct, I didn't want to have django-extensions as a dependency even if it was for just a second.
I am using the next command:
(Python3 + Django 1.10)
from django.core.management import BaseCommand
from django.conf.urls import RegexURLPattern, RegexURLResolver
from django.core import urlresolvers
class Command(BaseCommand):
def add_arguments(self, parser):
pass
def handle(self, *args, **kwargs):
urls = urlresolvers.get_resolver()
all_urls = list()
def func_for_sorting(i):
if i.name is None:
i.name = ''
return i.name
def show_urls(urls):
for url in urls.url_patterns:
if isinstance(url, RegexURLResolver):
show_urls(url)
elif isinstance(url, RegexURLPattern):
all_urls.append(url)
show_urls(urls)
all_urls.sort(key=func_for_sorting, reverse=False)
print('-' * 100)
for url in all_urls:
print('| {0.regex.pattern:20} | {0.name:20} | {0.lookup_str:20} | {0.default_args} |'.format(url))
print('-' * 100)
Usage:
./manage.py showurls
Sample output:
----------------------------------------------------------------------------------------------------
| ^(.+)/$ | | django.views.generic.base.RedirectView | {} |
| ^(.+)/$ | | django.views.generic.base.RedirectView | {} |
| ^(.+)/$ | | django.views.generic.base.RedirectView | {} |
| ^(.+)/$ | | django.views.generic.base.RedirectView | {} |
| ^(.+)/$ | | django.views.generic.base.RedirectView | {} |
| ^(.+)/$ | | django.views.generic.base.RedirectView | {} |
| ^static\/(?P<path>.*)$ | | django.contrib.staticfiles.views.serve | {} |
| ^media\/(?P<path>.*)$ | | django.views.static.serve | {'document_root': '/home/wlysenko/.virtualenvs/programmerHelper/project/media'} |
| ^(?P<app_label>polls|snippets|questions)/$ | app_list | apps.core.admin.AdminSite.app_index | {} |
| ^(?P<app_label>activity|articles|badges|books|comments|flavours|forum|marks|newsletters|notifications|opinions|polls|questions|replies|snippets|solutions|tags|testing|users|utilities|visits)/reports/$ | app_reports | apps.core.admin.AdminSite.reports_view | {} |
| ^(?P<app_label>activity|articles|badges|books|comments|flavours|forum|marks|newsletters|notifications|opinions|polls|questions|replies|snippets|solutions|tags|testing|users|utilities|visits)/statistics/$ | app_statistics | apps.core.admin.AdminSite.statistics_view | {} |
| articles/(?P<slug>[-\w]+)/$ | article | apps.articles.views.ArticleDetailView | {} |
| book/(?P<slug>[-_\w]+)/$ | book | apps.books.views.BookDetailView | {} |
| category/(?P<slug>[-_\w]+)/$ | category | apps.utilities.views.CategoryDetailView | {} |
| create/$ | create | apps.users.views.UserDetailView | {} |
| delete/$ | delete | apps.users.views.UserDetailView | {} |
| detail/(?P<email>\w+#[-_\w]+.\w+)/$ | detail | apps.users.views.UserDetailView | {} |
| snippet/(?P<slug>[-_\w]+)/$ | detail | apps.snippets.views.SnippetDetailView | {} |
| (?P<contenttype_model_pk>\d+)/(?P<pks_separated_commas>[-,\w]*)/$ | export | apps.export_import_models.views.ExportTemplateView | {} |
| download_preview/$ | export_preview_download | apps.export_import_models.views.ExportPreviewDownloadView | {} |
| ^$ | import | apps.export_import_models.views.ImportTemplateView | {} |
| result/$ | import_result | apps.export_import_models.views.ImportResultTemplateView | {} |
| ^$ | index | django.contrib.admin.sites.AdminSite.index | {} |
| ^$ | index | apps.core.views.IndexView | {} |
| ^jsi18n/$ | javascript-catalog | django.views.i18n.javascript_catalog | {'packages': ('your.app.package',)} |
| ^jsi18n/$ | jsi18n | django.contrib.admin.sites.AdminSite.i18n_javascript | {} |
| level/(?P<slug>[-_\w]+)/$ | level | apps.users.views.UserDetailView | {} |
| ^login/$ | login | django.contrib.admin.sites.AdminSite.login | {} |
| ^logout/$ | logout | django.contrib.admin.sites.AdminSite.logout | {} |
| newsletter/(?P<slug>[_\w]+)/$ | newsletter | apps.newsletters.views.NewsletterDetailView | {} |
| newsletters/$ | newsletters | apps.newsletters.views.NewslettersListView | {} |
| notification/(?P<account_email>[-\w]+#[-\w]+.\w+)/$ | notification | apps.notifications.views.NotificationDetailView | {} |
| ^password_change/$ | password_change | django.contrib.admin.sites.AdminSite.password_change | {} |
| ^password_change/done/$ | password_change_done | django.contrib.admin.sites.AdminSite.password_change_done | {} |
| ^image/(?P<height>\d+)x(?P<width>\d+)/$ | placeholder | apps.core.views.PlaceholderView | {} |
| poll/(?P<pk>\w{8}-\w{4}-\w{4}-\w{4}-\w{12})/(?P<slug>[-\w]+)/$ | poll | apps.polls.views.PollDetailView | {} |
| ^add/$ | polls_choice_add | django.contrib.admin.options.ModelAdmin.add_view | {} |
| ^(.+)/change/$ | polls_choice_change | django.contrib.admin.options.ModelAdmin.change_view | {} |
| ^$ | polls_choice_changelist | django.contrib.admin.options.ModelAdmin.changelist_view | {} |
| ^(.+)/delete/$ | polls_choice_delete | django.contrib.admin.options.ModelAdmin.delete_view | {} |
| ^(.+)/history/$ | polls_choice_history | django.contrib.admin.options.ModelAdmin.history_view | {} |
| ^add/$ | polls_poll_add | django.contrib.admin.options.ModelAdmin.add_view | {} |
| ^(.+)/change/$ | polls_poll_change | django.contrib.admin.options.ModelAdmin.change_view | {} |
| ^$ | polls_poll_changelist | django.contrib.admin.options.ModelAdmin.changelist_view | {} |
| ^(.+)/delete/$ | polls_poll_delete | django.contrib.admin.options.ModelAdmin.delete_view | {} |
| ^(.+)/history/$ | polls_poll_history | django.contrib.admin.options.ModelAdmin.history_view | {} |
| ^$ | polls_vote_changelist | django.contrib.admin.options.ModelAdmin.changelist_view | {} |
| publisher/(?P<slug>[-_\w]+)/$ | publisher | apps.books.views.PublisherDetailView | {} |
| question/(?P<slug>[-_\w]+)/$ | question | apps.questions.views.QuestionDetailView | {} |
| ^add/$ | questions_answer_add | django.contrib.admin.options.ModelAdmin.add_view | {} |
| ^(.+)/change/$ | questions_answer_change | django.contrib.admin.options.ModelAdmin.change_view | {} |
| ^$ | questions_answer_changelist | django.contrib.admin.options.ModelAdmin.changelist_view | {} |
| ^(.+)/delete/$ | questions_answer_delete | django.contrib.admin.options.ModelAdmin.delete_view | {} |
| ^(.+)/history/$ | questions_answer_history | django.contrib.admin.options.ModelAdmin.history_view | {} |
| ^add/$ | questions_question_add | django.contrib.admin.options.ModelAdmin.add_view | {} |
| ^(.+)/change/$ | questions_question_change | django.contrib.admin.options.ModelAdmin.change_view | {} |
| ^$ | questions_question_changelist | django.contrib.admin.options.ModelAdmin.changelist_view | {} |
| ^(.+)/delete/$ | questions_question_delete | django.contrib.admin.options.ModelAdmin.delete_view | {} |
| ^(.+)/history/$ | questions_question_history | django.contrib.admin.options.ModelAdmin.history_view | {} |
| ^setlang/$ | set_language | django.views.i18n.set_language | {} |
| ^add/$ | snippets_snippet_add | django.contrib.admin.options.ModelAdmin.add_view | {} |
| ^(.+)/change/$ | snippets_snippet_change | django.contrib.admin.options.ModelAdmin.change_view | {} |
| ^$ | snippets_snippet_changelist | django.contrib.admin.options.ModelAdmin.changelist_view | {} |
| ^(.+)/delete/$ | snippets_snippet_delete | django.contrib.admin.options.ModelAdmin.delete_view | {} |
| ^(.+)/history/$ | snippets_snippet_history | django.contrib.admin.options.ModelAdmin.history_view | {} |
| solution/(?P<pk>\w{8}-\w{4}-\w{4}-\w{4}-\w{12})/(?P<slug>[-_\w]+)/$ | solution | apps.solutions.views.SolutionDetailView | {} |
| suit/(?P<slug>[-\w]+)/$ | suit | apps.testing.views.SuitDetailView | {} |
| tag/(?P<name>[-_\w]+)/$ | tag | apps.tags.views.TagDetailView | {} |
| theme/(?P<slug>[-_\w]+)/$ | theme | apps.forum.views.SectionDetailView | {} |
| topic/(?P<slug>[-_\w]+)/$ | topic | apps.forum.views.TopicDetailView | {} |
| update/$ | update | apps.users.views.UserDetailView | {} |
| ^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$ | view_on_site | django.contrib.contenttypes.views.shortcut | {} |
| writer/(?P<slug>[-_\w]+)/$ | writer | apps.books.views.WriterDetailView | {} |
----------------------------------------------------------------------------------------------------
There is a recipe on activestate
import urls
def show_urls(urllist, depth=0):
for entry in urllist:
print(" " * depth, entry.regex.pattern)
if hasattr(entry, 'url_patterns'):
show_urls(entry.url_patterns, depth + 1)
show_urls(urls.url_patterns)
There's a plugin I use: https://github.com/django-extensions/django-extensions, it has a show_urls command that could help.
Simply type in a url you know does not exist and the server will return an error message with a list of url patterns.
For example, if you're running a site at http://localhost:8000/something
Type in
http://localhost:8000/something/blahNonsense, and your server will return the url search list and display it in the browser
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
In python manage.py shell
import urls
get_resolved_urls(urls.urlpatterns)
Minimalist solution for django 2.0
For instance, if you're looking for an url that's on the first app of installed_apps, you can access it like that:
from django.urls import get_resolver
from pprint import pprint
pprint(
get_resolver().url_patterns[0].url_patterns
)
Django 1.8, Python 2.7+
Just run these commands in your Shell. Python manage.py shell and execute the following code.
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)
I have extended Seti's command to show namespace, all url parts, auto-adjust column widths, sorted by (namespace,name):
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()
Note: column order is kept in Python 3.6 and one would need to use OrderedDict in older versions.
Update: A new version with OrderedDict now lives in django-🍌s package: https://github.com/5monkeys/django-bananas/blob/master/bananas/management/commands/show_urls.py
Django >= 2.0 List Solution
adopted from #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
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
Here your_main_app is the app name where your settings.py file is placed
Yet another adaption of #Cesar Canassa 's generator magic. This can be added to the yourapp/management/commands/dumpurls.py director of your app so that it'll be accessible as a subcommand in management.py.
note: I added a line to make sure it filters for only yourapp. Update or remove it accordingly if additional URLs are desired.
As a management.py Subcommand
Deploy Path: 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))
Sample Output
(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
You can create a dynamic import to gather all URL Patterns from each application in your project with a simple method like so:
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()
I recently used something like this to create a template tag to show active navigation links.
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')
]
In case you are using DRF, you can print all the URL patterns for a particular router by printing the urlpatterns from router.get_urls() (within your Django app's urls.py file).
Open your apps urls.py and add the print statement to the bottom of the file, so the whole file might look like this:
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())
The patterns are then printed like this:
[<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']>,
...
]