#57 Pluggable app URLs

Fermé
jocelyn veut fusionner 2 commits à partir de jocelyn/jd-pluggable-urls vers FFDN/master
6 fichiers modifiés avec 102 ajouts et 8 suppressions
  1. 31 6
      EXTENDING.md
  2. 41 0
      coin/apps.py
  3. 15 2
      coin/urls.py
  4. 1 0
      coin/vpn/__init__.py
  5. 13 0
      coin/vpn/apps.py
  6. 1 0
      coin/vpn/models.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')]

+ 41 - 0
coin/apps.py

@@ -0,0 +1,41 @@
+import six
+import django
+from django.apps import apps
+from os.path import basename
+
+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 = basename(__file__).rstrip('.py')
+                url_module = cls.__module__.rstrip(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('coin.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())

+ 1 - 0
coin/vpn/__init__.py

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

+ 13 - 0
coin/vpn/apps.py

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

+ 1 - 0
coin/vpn/models.py

@@ -152,6 +152,7 @@ class VPNConfiguration(CoinLdapSyncMixin, Configuration):
 
 
     class Meta:
     class Meta:
         verbose_name = 'VPN'
         verbose_name = 'VPN'
+        db_table = 'vpn_vpnconfiguration'
 
 
 
 
 class LdapVPNConfig(ldapdb.models.Model):
 class LdapVPNConfig(ldapdb.models.Model):