How to document Django URLs with Sphinx? - django

I'm generating a new Django project, with several apps. The main goal for this project is to create a REST API. Right now, I've Sphinx working, creating documentation of all my project with
sphinx-quickstart
and
sphinx-apidoc -o doc/packages .
All works well, except for URLs. I wanted to have my URLs documented, as a nice API, fully integrated with the rest of Sphinx documentation.
Is it possible?

It is. This isn't directly documenting the URLs, but I connected the URLs to view documentation in this way:
Make a Sphinx extension that looks something like:
from django.core.urlresolvers import get_resolver
def setup(app):
app.connect('autodoc-process-docstring', process_django_view)
def process_django_view(app, what, name, obj, options, lines):
if what=='function':
res = get_resolver()
if res.reverse_dict.has_key(obj):
url_struct = res.reverse_dict[obj]
lines[:0] = [
"| URL structure: %s\n" % url_struct[0][0][0]
]
Then you need to add it to extensions in your conf.py and also load in the Django environment like explained here: How to build sphinx documentation for django project
There's some other stuff like the regex in url_struct and the parameters that you can include. My own also searches multiple URL resolvers that correspond to different domains.

Related

Django 2.2.3 How get all urls patterns and all views name

I code app with Django == 2.2.3
By now i want to get all urls_patterns of all my project and associated view name.
How can i get something like this:
urls_patterns_view_names_list = [
("view_name", "view_name: url_pattern"),
...,
...,
...,
]
I have seen several topics to the question of url patterns but none correspond and do what I want. For the most part the answers are very old and obsessive for my case. That's why I reformulate my question: I want to have the urls patterns and for each url associated the name of his view: How can I do that with Django 2.2.3 on python 3.7?
You can achieve this with django-extensions:
pip install django-extensions
Then in settings.py:
INSTALLED_APPS = [
...
'django_extensions',
...
]
And finally in your command line:
./manage.py show_urls
This will result in exactly what you want. From the package docs:
show_urls: Produce a tab-separated list of (url_pattern, view_function, name) tuples for a project.
It's not directly trivial, but I've written a module, https://github.com/valohai/django-urr, that has utilities for working and traversing Django URL resolvers.
If you end up using it, you can just use
for entry in django_urr.extract_urls():
print(vars(entry))
– see here for all of the contents of entrys.

Integrate Django Oauth Toolkit urls correctly

I followed the instructions from the official django-oauth toolkit doc (https://django-oauth-toolkit.readthedocs.io/en/latest/tutorial/tutorial_02.html) and included all oauth-toolkit urls manually to prevent users from creating applications.
In my root urls.py i added the following line to the url patterns, as showed in the tutorial:
url(r'^o/', include(oauth2_endpoint_views, namespace="oauth2_provider")),
I used the include from django.conf.url package.
Now when i start the server, it says
"Specifying a namespace in include() without providing an app_name is not supported. Set the app_name attribute in the included module, or pass a 2-tuple containing the list of patterns and app_name instead."
when i add a name attribute to the url-command like this:
url(r'^o/', include(oauth2_endpoint_views, namespace="oauth2_provider"), name="oauth2_provider"),
the server starts up, but when i try to visit "localhost/o/applications" it says "NoReverseMatch: 'oauth2_provider' is not a registered namespace"
What am i doing wrong?
Try
url(r'^o/', include(('oauth2_provider.urls', 'oauth2_provider_app', ), namespace='oauth2_provider'), ),
Refer oauth2_provider

Django syndication framework: prevent appending SITE_ID to the links

According to the documentation here: https://djangobook.com/syndication-feed-framework/
If link doesn’t return the domain, the syndication framework will
insert the domain of the current site, according to your SITE_ID
setting
However, I'm trying to generate a feed of magnet: links. The framework doesn't recognize this and attempts to append the SITE_ID, such that the links end up like this (on localhost):
<link>http://localhost:8000magnet:?xt=...</link>
Is there a way to bypass this?
Here's a way to do it with monkey patching, much cleaner.
I like to create a separate folder "django_patches" for these kinds of things:
myproject/django_patches/__init__.py
from django.contrib.syndication import views
from django.contrib.syndication.views import add_domain
def add_domain_if_we_should(domain, url, secure=False):
if url.startswith('magnet:'):
return url
else:
return add_domain(domain, url, secure=False)
views.add_domain = add_domain_if_we_should
Next, add it to your INSTALLED_APPS so that you can patch the function.
settings.py
INSTALLED_APPS = [
'django_overrides',
...
]
This is a bit gnarly, but here's a potential solution if you don't want to give up on the Django framework:
The problem is that the method add_domain is buried deep in a huge method within syndication framework, and I don't see a clean way to override it. Since this method is used for both the feed URL and the feed items, a monkey patch of add_domain would need to consider this.
Django source:
https://github.com/django/django/blob/master/django/contrib/syndication/views.py#L178
Steps:
1: Subclass the Feed class you're using and do a copy-paste override of the huge method get_feed
2: Modify the line:
link = add_domain(
current_site.domain,
self._get_dynamic_attr('item_link', item),
request.is_secure(),
)
To something like:
link = self._get_dynamic_attr('item_link', item)
I did end up digging through the syndication source code and finding no easy way to override it and did some hacky monkey patching. (Unfortunately I did it before I saw the answers posted here, all of which I assume will work about as well as this one)
Here's how I did it:
def item_link(self, item):
# adding http:// means the internal get_feed won't modify it
return "http://"+item.magnet_link
def get_feed(self, obj, request):
# hacky way to bypass the domain handling
feed = super().get_feed(obj, request)
for item in feed.items:
# strip that http:// we added above
item['link'] = item['link'][7:]
return feed
For future readers, this was as of Django 2.0.1. Hopefully in a future patch they allow support for protocols like magnet.

Not able to pass API urls to the REST API app

I have my Django project split into three apps:
frontend: You guessed it, the pure frontend.
shared_stuff: This is where I've put my models, because I feel they might be shared between apps later on.
rest_api: The, well, REST API.
All three apps are also registered in settings.py.
Now my problem is that the urls meant for rest_api app are also being serviced by the frontend app. Here's what my main urls.py looks like:
urlpatterns = [
url(r'^api/v1', include('rest_api.urls')),
url(r'^', include('frontend.urls')),
]
Am I doing something wrong? Please feel free to ask for more info!
Try this: url(r'^/api/v1', include('rest_api.urls')). Just add a front slash to the pattern.
When you visit http://host/api/v1/xxx, django will use /api/v1/xxx to match the patterns. But ^api/v1 fails the match because of the missing front slash.

Decoupling django apps 2 - how to get object information from a slug in the URL

I am trying to de-couple two apps:
Locations - app containing details about some location (town, country, place etc)
Directory - app containing details of places of interest (shop, railway station, pub, etc) - all categorised.
Both locations.Location and directory.Item contain lat/lng coords and I can find items within a certain distance of a specific lat/lng coord.
I'd like to use the following URL structure:
/locations/<location_slug>/directory/<category_slug>/
But I don't want to make my directory app reliant on my location app.
How can I translate this url to use a view like this in my directory app?
items_near(lat, lng, distance, category):
A work around would be to create a new view somewhere that translates this - but where should I put that? if it goes in the Directory app, then I've coupled that with my Location app, and vice versa.
Would a good idea be to place this workaround code inside my project URLs file? Thus keeping clear of both apps? Any issues with doing it like this?
For your urlpattern to work, the view function invoked has to be aware of both Locations and Directories. The short answer is you can put this view function anywhere you want - it's just a python function. But there might be a few logical places for it, outside of your Directory or Location app, that make sense.
First off, I would not put that view code in in your top-level urls.py, as that file is intended for URLconf related code.
A few options of where to put your view:
Create a new view function in a file that lives outside of any particular app. <project_root>/views.py is one possibility. There is nothing wrong with this view calling the item_near(..) view from the Directory app.
# in myproject/urls.py
urlpatterns = (
...
(r'/locations/(?P<location_slug>[\w\-]+)/directory/(?P<category_slug>[\w\-]+)/',
'myproject.views.items_near_from_slug')
)
# in myproject/views.py
from directory.views import items_near
def items_near_from_slug(request, location_slug, category_slug):
location = get_object_or_404(Location, slug=location_slug)
distance = 2 # not sure where this comes from
# And then just invoke the view from your Directory app
return items_near(request, location.lat, location.lng, distance, category_slug)
Create a new app and put the code there, in <my_new_app>/views.py. There is no requirement that a Django app need to have a models.py, urls.py, etc. Just make sure to include the __init__.py if you want Django to load the app properly (if, for instance, you want Django to automatically find templatetags or app specific templates).
Personally, I would go with option 1 only if the project is relatively simple, and <project_root>/views.py is not in danger of becoming cluttered with views for everything. I would go with option 2 otherwise, especially if you anticipate having other code that needs to be aware of both Locations and Directories. With option 2, you can collect the relevant urlpatterns in their own app-specific urls.py as well.
From the django docs here if you're using django >=1.1 then you can pass any captured info into the included application. So splitting across a few files:
# in locations.urls.py
urlpatterns = ('',
(r'location/(?P<location_slug>.*?)/', include('directory.urls')),
#other stuff
)
# in directory.urls.py
urlpatterns = ('',
(r'directory/(?P<directory_slug>.*?)/', 'directory.views.someview'),
#other stuff
)
# in directory.views.py
def someview(request, location_slug = None, directory_slug = None):
#do stuff
Hope that helps. If you're in django < 1.1 I have no idea.
Irrespective of how much ever "re-usable" you make your app, inevitably there is a need for site-specific code.
I think it is logical to create a "site-specific" application that uses the views of the reusable and decoupled apps.