Expand url django patterns - python

i have standard django 1.4 url patterns:
urlpatterns = patterns('',
url('^',include('events.urls')),
url(r'^$', home, {'template_name':'index.html','mod':None}, name='home'),
url(r'^contact$',contact, {'template_name':'index.html',
'mod':'contacto'},name='contact'),
url('^task/(?P<task_id>[\w+-]+)',celery_tasks,name='tasks'),
)
I want to build my sitemap.xml leaving out some urls, for example that /task url should not appear(it makes no sense for the web spiders).
My strategy is passing all the url patterns to my Sitemap class, like this
from sitemaps import EventsSitemap, StaticSitemap
sitemaps = {
'Events': CandidateSiteMap,
'static': StaticSitemap(urlpatterns),
}
As you can see i´m passing the patterns to the class, so i can later filter the urls like this
class StaticSitemap(Sitemap):
def __init__(self, patterns):
self.patterns = patterns
self._items = {}
self._initialize()
def _initialize(self):
do_not_show = ['tasks']
for p in self.patterns:
# no dynamic urls in this class (we handle those separately)
if not p.regex.groups:
if getattr(p,'name',False) and p.name not in do_not_show:
self._items[p.name] = self._get_modification_date(p)
So i keep this list of do_not_show url names and thats how i filter out urls, so far so good, the problem is with included urls such as:
url('^',include('events.urls')),
I can't just iterate on self.patterns and get the included urls, i have to expand them first, thats my question, how can i do that?
How can i get a flat list of urls as if there were no included ones, all were on a single urls module.
Any recommendations to filter out urls in the sitemaps.xml would be most appreciated.

Ok i have to answer my own question because i solved it, what i did was a little function to expand the patterns like this
def expand_patterns(patterns):
new_patterns = []
def recursive_expand(patterns):
for p in patterns:
if getattr(p,'url_patterns',False):
recursive_expand(p.url_patterns)
else:
new_patterns.append(p)
recursive_expand(patterns)
return new_patterns
This will flatten out the urlpatterns into a single list.
So now i can use self.patterns to filter out anything in my Sitemap class :)

Related

Passing variables in django browser string

I am doing a search on the page with the ability to filter. It is necessary to pass the selected fields to form the correct queryset. What is the best way to do this? I am creating str variable in urls. But what if you need to pass 10 or more filter conditions? how to organize dynamically passed variables?
urls
from django.urls import path
from .views import *
urlpatterns = [
path('', OrdersHomeView.as_view(), name='orders_home'),
path('filter/<str:tag>', OrdersFilterView.as_view(), name='orders_filter'),
]
I understand what to do through ?var=&var2=, as in php, but I can't figure out how? it is possible to just process the string str, but maybe there is some django magic?
Make the filter view to work with URL GET params:
page = request.GET.get('page', 0)
page_size = request.GET.get('page_size', 100)
Then construct your URLs to send the filters filter/tag/?page=1&page_size=20

URL pattern in django to match limited set of words

I have a URL pattern in Django where there is a variable (called name) that should only take one of a select list of words. Something like this:
path("profile/<marta|jose|felipe|manuela:name>/", views.profile),
So basically only these paths are valid:
profile/marta/
profile/jose/
profile/felipe/
profile/manuela/
How exactly can I configure this in the Django url patterns? The above syntax tries to illustrate the idea but doesn't work. I've seen this ten year old question but that uses the previous format in the url patterns file so I imagine it should be done differently now...?
Why not put them in view?
from django.http import Http404
def profiles(request, name):
if not name in ['marta', 'jose', 'felipe', 'manuela']:
raise Http404('You shall not pass!')
Or you can simply use re_path:
urlpatterns = [
re_path(r'^profile/(?P<name>marta|jose|felipe|manuela)/$', views.index, name='index'),
]

Django- Reversing custom Admin URLs

I'm using an App that configures some Admin URLs as:
#admin.py....
def get_urls(self):
urls = super(FormAdmin, self).get_urls()
extra_urls = [
url("^(?P<form_id>\d+)/entries/$",
self.admin_site.admin_view(self.entries_view),
name="form_entries"),
#......
]
return extra_urls + urls
I'm having trouble using template tags to get a URL corresponding to that, in one of my templates. I'm trying stuff like:
4-Entries
(Where forms is the App's label). I keep running into the No Reverse Match type of errors:
NoReverseMatch at /polls/ Reverse for 'forms_form_entries' with
arguments '(4,)' and keyword arguments '{}' not found. 0 pattern(s)
tried: []
What am I missing that makes the tag work correctly?
Try this in html
4-Entries
Your url pattern name form_entries doesn't match forms_form_entries from the URL tag. Django does not automatically prefix the pattern name with <app_name>_ as you appear to be expecting.
Change one of them so that they match.

Django testing, mock valid requests to urls

I have a bunch of urls in my urls.py file that have the login_required decorator
# Index Page
url(r'^$', login_required(views.IndexPage.as_view()), name='index'),
# Schedule urls
url(r'^schedules/$', login_required(views.ScheduleListView.as_view()),
name='schedule-list'),
url(r'^schedule/(?P<pk>[\d]+)/$',
login_required(views.ScheduleDetailView.as_view()),
name='schedule-detail'),
url(r'^schedule-freeze/(?P<pk>[\d]+)/$',
login_required(views.freezeSchedule),
name='schedule-freeze'),
url(r'^schedule-create/$', login_required(views.ScheduleCreate.as_view()),
name='schedule-create'),
url(r'^schedule-delete/(?P<pk>[\d]+)$',
login_required(views.ScheduleDelete.as_view()),
name='schedule-delete'),
url(r'^schedule-update/(?P<pk>[\d]+)/$',
login_required(views.ScheduleUpdate.as_view()),
name='schedule-update'),
url(r'^schedule-generate/(?P<pk>[\d]+)/$',
login_required(views.scheduleGenerate), name='schedule-generate'),
# Client urls
url(r'^clients/$', login_required(views.ClientList.as_view()),
name='client-list'),
url(r'^client/(?P<slug>[\w-]+)/$',
login_required(views.ClientDetail.as_view()), name='client-detail'),
url(r'^client-create/$', login_required(views.ClientCreate.as_view()),
name='client-create'),
url(r'^client-delete/(?P<slug>[\w-]+)/$',
login_required(views.ClientDelete.as_view()), name='client-delete'),
url(r'^client-update/(?P<slug>[\w-]+)/$',
login_required(views.ClientUpdate.as_view()), name='client-update'),
# And so on ....
For every restricted view I'm trying to write a test which ensures unauthorized users are redirected to the login page when trying to access the view. If possible I'd like to be able to achieve this in a single block of code, instead of writing a single test for every single URL.
I've tried something like the following:
list_urls = [e for e in get_resolver(urls).reverse_dict.keys() if isinstance(e, str)]
for url in list_urls:
# Fetches the urlpath e.g. 'client-list'
namedspaced_url = 'reports:' + url
path = reverse(namedspaced_url)
response = self.client.get(path)
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, reverse('login') + '?next=' + path)
list_urls returns a list of all the named urls inside my urls.py file i.e. ['schedule-create', 'server-detail', 'schedule-list', 'schedule-update', 'index', ....]
The Problem
this piece of code: reverse(namedspaced_url)
Where this causes issues is that each url has a different regular expression pattern, i.e. some take slugs some take pk's
so the line path = reverse(namedspaced_url) will work for simple URLs like those which point at ListViews but will fail for more complex URLs, such as those that point at DetailViews which require slug's/pk's, i.e. path = reverse(namedspaces_url, args=[1945])
Is it possible to temporarily override / ignore Django's pattern matching / routing to force a request to go through (regardless of passed args)
Or do I have to manually write a test for each URL with valid kwargs/args to satisfy regex?
Is there another completely different approach I can take to write tests for all my login_required() views?
Update
Using introspection I came up with the following monstrosity to solve my problem
def test_page_redirects_for_unauthorised_users(self):
url_dict = get_resolver(urls).reverse_dict
url_list = [e for e in get_resolver(urls).reverse_dict.keys() if
isinstance(e, str)]
for url in url_list:
patterns = url_dict[url][0][0][1]
matches = [1 if e == 'pk' else "slug" if e == 'slug' else None for
e in patterns]
path = reverse('reports:' + url, args=matches)
response = self.client.get(path)
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, reverse('login') + '?next=' + path)
You're trying to test something very complicated because you've decided to use login_required to decorate the urlconf.
Why not decorate the class instead? That way you can simply test each class to make sure it has the login_required decorator. This eliminates the need for mocking slug and pk regex values.
in the proect_name/project_name/urls.py
urlpatterns = [
url(r'', login_required(include('app_name.urls')),
]
This will apply login_required to all urls in the project_name/app_name/urls.py

How to use one app to satisfy multiple URLs in Django

I'm trying to use one app to satisfy multiple url paths. That is to say, I want the url /blog/ and /job/ to use the same app, but different views. There are a number of ways to do this I'm sure, but none of them seem very clean. Here's what I'm doing right now
# /urls.py
urlpatterns = patterns("",
(r"^(blog|job)/", include("myproject.myapp.urls")),
)
# /myapp/urls.py
urlpatterns = patterns("myproject.myapp.views",
(r"^(?P<id>\d+)/edit/$", "myproject.myapp.views.edit"),
(r"^(?P<id>\d+)/delete/$", "myproject.myapp.views.delete"),
(r"^(?P<id>\d+)/update/$", "myproject.myapp.views.update"),
(r"^insert/$", "myproject.myapp.views.insert"),
)
urlpatterns += patterns("",
(r"^(?P<object_id>\d+)/$", "django.views.generic.list_detail.object_detail", info_dict, "NOIDEA-detail"),
(r"^/$", "django.views.generic.list_detail.object_list", info_dict, "NOIDEA-community"),
)
# /myapp/views.py
def edit(request, type, id):
if (type == "blog"):
editBlog(request, id)
else (type == "job")
editJob(request, id)
def editBlog(request, id):
# some code
def editJob(request, id):
# some code
I've ended up breaking all of this into multiple model and view files to make the code cleaner but the above example doesn't account for things like reverse url lookups which breaks all of my template {% url %} calls.
Originally, I had blogs, jobs, events, contests, etc all living in their own apps, but all of their functionality is so similar, that it didn't make sense to leave it that way, so I attempted to combine them... and this happened. You see those "NOIDEA-detail" and "NOIDEA-community" url names on my generic views? Yeah, I don't know what to use there :-(
You can have more than one modules defining URLs. You can have /blog/ URLs in myapp/urls.py and /job/ URLs in myapp/job_urls.py. Or you can have two modules within a urls subpackage.
Alternatively you can manually prefix your url definitions:
urlpatterns = patterns("myproject.myapp.views",
(r"^jobs/(?P<id>\d+)/edit/$", "myproject.myapp.views.edit"),
(r"^jobs/(?P<id>\d+)/delete/$", "myproject.myapp.views.delete"),
(r"^jobs/(?P<id>\d+)/update/$", "myproject.myapp.views.update"),
(r"^jobs/insert/$", "myproject.myapp.views.insert"),
)
urlpatterns += patterns("",
(r"^blog/(?P<object_id>\d+)/$", "django.views.generic.list_detail.object_detail", info_dict, "NOIDEA-detail"),
(r"^blog/$", "django.views.generic.list_detail.object_list", info_dict, "NOIDEA-community"),
)
And then mount them as:
urlpatterns = patterns("",
(r"", include("myapp.urls")),
)
Personally I would go for more RESTful URL definitions though. Such as blog/(?P<post_id>\d+)/edit/$.
Looks pretty good to me. If you want reverse lookups, just have a different reverse name for each url format, even if they end up pointing to the same view.

Categories