Parcourir la source

Merge branch 'pluggable-urls'

Baptiste Jonglez il y a 8 ans
Parent
commit
b05e274424
7 fichiers modifiés avec 113 ajouts et 8 suppressions
  1. 31 6
      EXTENDING.md
  2. 44 0
      coin/apps.py
  3. 15 2
      coin/urls.py
  4. 7 0
      coin/utils.py
  5. 1 0
      requirements.txt
  6. 1 0
      vpn/__init__.py
  7. 14 0
      vpn/apps.py

+ 31 - 6
EXTENDING.md

@@ -135,12 +135,37 @@ Here is an example URL pattern to be used in your `urls.py`:
 
 
     url(r'^(?P<id>\d+)$', VPNView.as_view(template_name="vpn/vpn.html"), name="details")
     url(r'^(?P<id>\d+)$', VPNView.as_view(template_name="vpn/vpn.html"), name="details")
 
 
-Note that this pattern **must** be called "details".  The global `urls.py`
-should contain a pattern of the form:
+Note that this pattern **must** be called "details".
+Of course, you can add as many additional views as you want.
 
 
-    url(r'^vpn/', include('coin.vpn.urls', namespace='vpn'))
+URLs
+----
 
 
-where the value of "namespace" is the URL namespace defined in your
-original model (see above).
+App views URLs are pluggable, you only have to tell your app to declare its
+URLs. Then its URLs will be available under `<app_name>/<view_name>` (as long s
+your app is listed in `INSTALLED_APPS`).
 
 
-Of course, you can add as many additional views as you want.
+To do so :
+
+1. Create a `<app_name>/apps.py` like (important part is inheriting
+   `coin.apps.AppURLs`) :
+
+    from django.apps import AppConfig
+    import coin.apps
+
+    class MyAppConfig(AppConfig, coin.apps.AppURLs):
+        name = 'myapp'
+        verbose_name = "Fruity app !"
+
+2. Edit a `<app_dir>/__init__.py` :
+
+    default_app_config = 'coin.myapp.apps.MyAppConfig
+
+
+Optionaly, you can customize which URLs are plugged and to which prefix via the
+`exported_urlpatterns` var on your config class as a list of
+`<prefix>,<urlpatterns>` :
+
+        class MyAppConfig(AppConfig, coin.apps.AppURLS):
+            name = 'my_app'
+            exported_urlpatterns = [('coolapp', 'my_app.cool_urls')]

+ 44 - 0
coin/apps.py

@@ -0,0 +1,44 @@
+from os.path import basename
+
+import six
+from django.apps import apps
+
+from .utils import rstrip_str
+
+
+class AppURLsMeta(type):
+    def __init__(cls, name, bases, data):
+        if len(bases) > 1: # execute only on leaf class
+            exported_urlpatterns = data.pop('exported_urlpatterns', None)
+
+            if exported_urlpatterns:
+                cls.exported_urlpatterns = exported_urlpatterns
+            else:
+                # Default : sets
+                #   exported_urlpatterns = [(<app_name>, <app_url_module>)]
+                current_path = '.' + rstrip_str(rstrip_str(basename(__file__), '.pyc'), '.py')
+                url_module = rstrip_str(cls.__module__, current_path) + '.urls'
+                cls.exported_urlpatterns = [(data['name'], url_module)]
+
+            cls.urlprefix = data.pop('urlprefix', None)
+
+
+class AppURLs(six.with_metaclass(AppURLsMeta)):
+    """ App Mixxin to allow an application to expose pluggable urls
+
+    That's to say, URLs which will be added automatically to the projet
+    urlpatterns.
+
+    You can just make your app inherit from AppURLs, yous app urls.py will be
+    picked and wired on project urlpatterns, using the app name as prefix.
+
+    You can also customize which urlpatterns your app exposes by setting the
+    `exported_urlpattens` on your AppConfig class as list of `<prefix>,<urlpatterns>`
+
+    E.g:
+
+        class MyAppConfig(AppConfig, coin.apps.AppURLS):
+            name = 'my_app'
+            exported_urlpatterns = [('my_app', 'myapp.cool_urls')]
+    """
+    pass

+ 15 - 2
coin/urls.py

@@ -1,13 +1,14 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 from __future__ import unicode_literals
 
 
+from django.apps import apps
 from django.conf import settings
 from django.conf import settings
 from django.conf.urls import patterns, include, url
 from django.conf.urls import patterns, include, url
 from django.conf.urls.static import static
 from django.conf.urls.static import static
 from django.contrib.staticfiles.urls import staticfiles_urlpatterns
 from django.contrib.staticfiles.urls import staticfiles_urlpatterns
 
 
 from coin import views
 from coin import views
-
+import coin.apps
 
 
 import autocomplete_light
 import autocomplete_light
 autocomplete_light.autodiscover()
 autocomplete_light.autodiscover()
@@ -17,6 +18,17 @@ admin.autodiscover()
 
 
 from coin.isp_database.views import isp_json
 from coin.isp_database.views import isp_json
 
 
+
+def apps_urlpatterns():
+    """ Yields url lists ready to be appended to urlpatterns list
+    """
+    for app_config in apps.get_app_configs():
+        if isinstance(app_config, coin.apps.AppURLs):
+            for prefix, pats in app_config.exported_urlpatterns:
+                yield url(
+                    r'^{}/'.format(prefix),
+                    include(pats, namespace=prefix))
+
 urlpatterns = patterns(
 urlpatterns = patterns(
     '',
     '',
     url(r'^$', 'coin.members.views.index', name='home'),
     url(r'^$', 'coin.members.views.index', name='home'),
@@ -25,7 +37,6 @@ urlpatterns = patterns(
     url(r'^members/', include('coin.members.urls', namespace='members')),
     url(r'^members/', include('coin.members.urls', namespace='members')),
     url(r'^billing/', include('coin.billing.urls', namespace='billing')),
     url(r'^billing/', include('coin.billing.urls', namespace='billing')),
     url(r'^subscription/', include('coin.offers.urls', namespace='subscription')),
     url(r'^subscription/', include('coin.offers.urls', namespace='subscription')),
-    url(r'^vpn/', include('vpn.urls', namespace='vpn')),
 
 
     url(r'^admin/', include(admin.site.urls)),
     url(r'^admin/', include(admin.site.urls)),
 
 
@@ -40,3 +51,5 @@ urlpatterns = patterns(
 urlpatterns += staticfiles_urlpatterns()
 urlpatterns += staticfiles_urlpatterns()
 urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
 urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
 
 
+# Pluggable apps URLs
+urlpatterns += list(apps_urlpatterns())

+ 7 - 0
coin/utils.py

@@ -30,6 +30,13 @@ re_chat_url = re.compile(r'(?P<protocol>\w+://)(?P<server>[\w\.]+)/(?P<channel>.
 def str_or_none(obj):
 def str_or_none(obj):
     return str(obj) if obj else None
     return str(obj) if obj else None
 
 
+def rstrip_str(s, suffix):
+    """Return a copy of the string [s] with the string [suffix] removed from
+    the end (if [s] ends with [suffix], otherwise return s)."""
+    if s.endswith(suffix):
+        return s[:-len(suffix)]
+    else:
+        return s
 
 
 def ldap_hash(password):
 def ldap_hash(password):
     """Hash a password for use with LDAP.  If the password is already hashed,
     """Hash a password for use with LDAP.  If the password is already hashed,

+ 1 - 0
requirements.txt

@@ -14,3 +14,4 @@ django-localflavor==1.1
 -e git+https://github.com/chrisglass/xhtml2pdf@a5d37ffd0ccb0603bdf668198de0f21766816104#egg=xhtml2pdf-master
 -e git+https://github.com/chrisglass/xhtml2pdf@a5d37ffd0ccb0603bdf668198de0f21766816104#egg=xhtml2pdf-master
 -e git+https://github.com/jlaine/django-ldapdb@1c4f9f29e52176f4367a1dffec2ecd2e123e2e7a#egg=django-ldapdb
 -e git+https://github.com/jlaine/django-ldapdb@1c4f9f29e52176f4367a1dffec2ecd2e123e2e7a#egg=django-ldapdb
 feedparser
 feedparser
+six==1.10.0

+ 1 - 0
vpn/__init__.py

@@ -0,0 +1 @@
+default_app_config = 'vpn.apps.VPNConfig'

+ 14 - 0
vpn/apps.py

@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.apps import AppConfig
+import coin.apps
+
+from . import urls
+
+
+class VPNConfig(AppConfig, coin.apps.AppURLs):
+    name = 'vpn'
+    verbose_name = "Gestion d'accès VPN"
+
+    exported_urlpatterns = [('vpn', urls.urlpatterns)]