Package: django-url-framework
The django-url-framework will help you get your django applications done faster.It automatically detects urls in a django application, similar to the way Ruby on Rails does it with the Controller-Action-View implementation.Controllers are created in each django application with a predefined file naming scheme (foo_controller.py) and extendingActionController. TheActionControllercontains methods often used in a web context, and does common request-related processing.Each application can have multiple controllers thus allowing for smaller classes in a larger application.Each function not starting with an underscore becomes it's own action. By simply returning a dictionary from the action, it will be rendered with the template named using thecontroller/action.htmlnaming scheme.Each action and controller can override certain global settings such as using a custom template name or giving the action (or controller) a custom name.InstallFrom pypi:pip install django-url-frameworkAlternatively just check out the source here and runpython setup.py installorpip install .Add to your projectsettings.pyINSTALLED_APPS=(...,'django_url_framework',...)urls.pyimportdjango_url_frameworkfromdjango.confimportsettingsfromdjango.conf.urlsimportpatterns,includedjango_url_framework.site.autodiscover(new_inflection_library=True)urlpatterns=patterns('',(r'^',include(django_url_framework.site.urls)),)ExampleFolder structureproject/
  app/
      cart_controller.py
      id_controller.py
      templates/
           cart/
              add.html
              index.html
              remove.html
           id_manager/
              bar.htmlcart_controller.pyfromdjango_url_framework.controllerimportActionControllerclassCartController(ActionController):defedit(self,request,id=None):return{}defremove(self,request,id):return{}defindex(self,request):return{}id_controller.pyfromdjango_url_framework.controllerimportActionControllerclassIDManagerController(ActionController):defindex(self,request,object_id=None):return{}defbar(self,request):return{}defbar__delete(self,request):return{}ResultThe following URLs will be created:/cart/ <- will go to *index action*
/cart/(\w+)/
/cart/edit/
/cart/edit/(\w+)/
/cart/remove/(\w+)/
/foo/
/foo/(\w+)/
/foo/bar/
/foo/bar/delete/You can easily access your URLs using django's built-in{% url ... %}tag. Simply call{% url cart_index %}or{% url cart_delete id %}and it will work as you would expect.There is also a helper tag for faster linking within the same controller.{% go_action remove %}will take you to/cart/remove/. To use it,{% load url_framework %}in your templates.The names of the controller files do not affect your URLs, however, the files must have_controller.pysuffix. The URL name of the controller is derived from the class name, minus the Controller part. You can also manually specify controller names using thecontroller_nameattribute on the controller class.Controller namesThe controller name is derived from it's class name, by converting camelcase into underscores.
For instanceFooControlleris simplefoo, whileFooBarControllerbecomesfoo_bar.The latest version uses theinflectionlibrary, however to avoid breaking old code, this is still optional until 2021.The biggest difference is that withinflection,HTTPResponsebecomeshttp_response, while the old name would behttpresponse. I suggest enabling theinflectionlibrary for all new and existing projects. You can manually specify names for controllers whose name change would break your code, or disable the inflection library for those controllers using a flag.You can give the controller a custom name with thecontroller_nameparameter:classController(ActionController):controller_name="foo"Enable or disable the use of the newinflectionlibrary using a flagclassController(ActionController):use_inflection_library=TrueOther useful controller settingsclassBarController(ActionController):# default filename extension for all templatestemplate_extension="pug"# will require every template file to start with this stringtemplate_prefix="foo_"# will not look for templates in subdirectories, but in the root templates/ folderno_subdirectories=False# do not prefix templates with `_` (underscore) when they are called using an AJAX requestno_ajax_prefix=False# Set a prefix for the controller's name, applies even if# you set controller_name (template name is based on controller_name, sans prefix)# NOTE: The urlconf name will not include the prefix, only the actual URL itself# Thus: FooController.list will have the URL /prefixed_foo/list/, but the url name will be# `foo_list`.controller_prefix="prefixed_"# completely override the name of the controllercontroller_name="shopping_cart"# When used with custom urlconf in actions, these arguments will not be passed to the action# example: "/<id:int>/<skip:bool>/" Only `id` will be passed to the `action`, while `skip` will not be.consume_urlconf_keyword_arguments=['skip']# set a prefix for all the URLs in this controller# So, what normally would be `/controller/action/`, becomes `^prefix/controller/action/`urlconf_prefix:list=["^prefix"]# A custom json encoder, subclassing JSONEncoderjson_default_encoder:JSONEncoder=None# use the yaml default flow styleyaml_default_flow_style:bool=True# use the new inflection library to generate controller url# if this is None, will use the global setting, otherwise override this on a per controller basisuse_inflection_library:Union[bool,None]=NoneTemplate filenamesBy default templates are stored in the subdirectory with the controller's name, and the templates are given the same filename as the action name.
If a request is determinned to be AJAX in nature, the template filename is prefixed with an underscore.
Example:classFooController(ActionController):deffoo_action(self,request):return{}File structure:/foo/foo_action.html/foo/_foo_action.html<---forAJAXrequests.You can disable this prefixing on a per action or per controller level.For all actions in a controller:classFooController(ActionController):no_ajax_prefix=TrueFor a single action:fromdjango_url_framework.decorators.action_optionsclassFooController(ActionController):@no_ajax_prefixdeffoo_action(self,request):return{}Action namesclassFooController(ActionController):defaction(self,request):return{}Creates the following URL:/controller/action/Double underscores__in action names are converted to slashes in the urlconf, so:action__namebecomes/action/name/.classController(ActionController):defaction__foo(self,request):return{}Creates the following URL:/controller/action/foo/Decorate to nameYou can also decorate functions to give them different names and prefixes and urls. See decorator package for more details, here is an example:@action_options.name("foo")@action_options.prefix("prefix_")defbar(self,request):return{}will result in:/controller/prefix_foo/The action will now have the template/controller/foo.html. Prefixes do not affect template naming.Action parametersProviding a third parameter to an action will create a URLconf for that parameter, like so:defaction(self,request,object_id):return{}Will allow you to call that action with:/controller/action/(\w+)/ <--- parameter consisting of A-Za-z0-9_If you make the argument optional, an additional URLconf entry is created allowing you to call the action without the third argument.defaction(self,request,object_id=None):return{}Results in:/controller/action/
/controller/action/(\w+)/  <--- optional argument consisting of A-Za-z0-9_Decorate for JSON, YAML or AutomaticYou can decorate any action to have a default renderer.
Instead of usingself._as_jsonas before, you can just put a decorator like so:fromdjango_url_framework.decoratorsimportjson_action@json_action(json_encoder=None)defaction(self,request,year,month):...return{}Other decorators include@yaml_action(default_flow_style:bool)and@auto().
YaML is self-explanatory, however@autois a bit interesting, it will automatically determine the renderer based on theHTTP_ACCEPTheader.Warning- if you expose raw data in your actions, that normally would be massaged inside a Server-Side template, DO NOT USE the@autodecorator as this allows an attacker to download raw data from your server.
However, if your responses are designed for an API, the@autodecorator will enable the API client to request data as it sees fit, for example, it can request a Server-Side rendered HTML, or the same data as JSON or YaML.Here is a list of supported renderers:text/html -TemplateRenderer- renders using the appropriate Django templatetext/plain -TextRenderer- prints text data as is, or prints object types usingpprint.pformatapplication/json -JSONRenderer- renders data as JSONapplication/yaml -YamlRenderer- renders data as YaML@auto()accepts the following parameters:json_encoderyaml_default_flow_style
The work the same as if passed to@json_action()or@yaml_action()Set HTTP Status Codes easilyAny action can return a tuple of two items, the second item should be anintand will become the HTTP status code for your response.@json_action()defupdate(self,request,year,month):...returnFalse,304#not modified@json_action()defcreate(self,request,year,month):...returnTrue,201#createdDecorate for custom parametersYou can also create your own custom parameters by using the@url_parametersdecorator to the function.fromdjango_url_framework.decorators.action_optionsimporturl_paramtersclassController(ActionController):@url_parameters(r'(?P<year>\d{4})/(?P<month>\d\d)')defaction(self,request,year,month):...return{}The above will create the following url patterns:/controller/action/(?P<year>\d{4})/(?P<month>\d\d)Note the lack of trailing slash - you must provide this yourself.Custom url for any actionYou can write your own urlconf for each action, by decorating it with@urlconf.fromdjango_url_framework.decorators.action_optionsimporturlconfclassController(ActionController):@action_options.urlconf([r'^bar/(?P<year>\d{4})/$',r'^bar/(?P<year>\d{4})/(?P<month>\d\d)/$',r'^foo/(?P<year>\d{4})/(?P<month>\d\d)/(?P<day>\d\d)/$'],do_not_autogenerate=True)defaction(self,request,year,month=None,day=None):...return{}The above will create the following url patterns:/controller/bar/(?P<year>\d{4})/
/controller/bar/(?P<year>\d{4})/(?P<month>\d\d)/$
/controller/foo/(?P<year>\d{4})/(?P<month>\d\d)/(?P<day>\d\d)/$Thedo_not_autogenerateargument istrueby default and will prevent any urls for this action
from being autogenerated. Ifdo_not_autogeneratewere to be set to false in the example below,
the following url would also be created:/controller/action/This URL would not actually work since theyearargument is required theactionfunction.Flash messagesThe ActionController also has a_flashinstance variable that allows you to send messages to the user that can survive a redirect. Simply useself._flash.append("Message")self._flash.error("Error message")The flash messages can be either messages or error messages. The flash object is automatically exported into the context and you can use it as such:{%ifflash.has_messages%}{%formessageinflash.get_and_clear%}{%ifmessage.is_error%}<spanclass='icon-error'></span>{%endif%}<pclass="{{message.type}}">{{message}}</p>{%endfor%}{%endif}Before and After each actionYou can override_before_filterand/or_after_filterto perform certain actions and checks before or after an action. Read more inActionControllerdocs.These methods accept the "request" parameter which is an HTTP request object for this request.classAccountController(ActionController):def_before_filter(self,request):campaign_id=request.GET.get("campaign_id")try:self._campaign=Campaign.objects.get(pk=campaign_id)exceptCampaign.DoesNotExist:self._campaign=NoneYou can disable the before and after filters by decorating any action with the@disable_filtersdecorator.Example:fromdjango_url_framework.decorators.action_optionsimportdisable_filters@disable_filtersdefaction(self,request):return{}One of the great features of django url framework is that you can require login for all actions in a controller by simply decorating the before_filter with a decorator to require logging in, see next section!AuthenticationTo require login on an action use the@login_requireddecorator provided by django-url-framework. The decorator also works on_before_filter.fromdjango_url_framework.decoratorsimportlogin_requiredclassAccountController(ActionController):@login_requireddefaction(self,request):return{}If the user isn’t logged in, redirect tosettings.LOGIN_URL, passing the current absolute path in the query string. Example:/accounts/login/?next=/polls/3/.login_required()also takes an optionallogin_urlparameter. Example:fromdjango_url_framework.decoratorsimportlogin_requiredclassAccountController(ActionController):@login_required(login_url="/login/")defaction(self,request):return{}By default, the path that the user should be redirected to upon successful authentication is stored in a query string parameter called "next". If you would prefer to use a different name for this parameter,login_required()takes an optionalredirect_field_nameparameter.Additionally you can use@superuser_required,@permission_required(permission_instance)and@must_be_member_of_group(group_name="some_group").Another example makes it easy to limiting access to a subset of data based on the logged in user for the whole controller.fromdjango_url_framework.decoratorsimportlogin_requiredclassItemController(ActionController):@login_required()def_before_filter(self):self.my_items=Item.objects.filter(user=request.user)self.my_products=Product.objects.filter(item__in=self.my_items)return{"page_title":"Item Page"}defitem(self,request,pk):item=get_object_or_404(self.my_items,pk=pk)return{"item":item}defproduct(self,request,pk):item=get_object_or_404(self.my_products,pk=pk)return{"product":product}Only POST? (or GET or anything...)You can limit what http methods a function can be called with.The example below limits theupdateaction to onlyPOSTandDELETEhttp methods.fromdjango_url_framework.decoratorsimporthttp_methodsclassController(ActionController):@http_methods.POST@http_methods.DELETEdefupdate(self,request):return{}By default all actions can be called with all http methods.Custom template extensionsWhen using jade or something similar you can specify a custom extension for all templates in the controller.classFooController(ActionController):#custom extension for all templates in this controllertemplate_extension="jade"