让django像Asp.Net Mvc一样自动匹配Controller和Action
Asp.Net Mvc 中,我们可以通过配置如下路由,允许从Url中匹配每段路径到Area, Controller和Action中,在尝试使用了django之后,发现django的路由系统更加灵活,允许通过正则匹配任意Url到任意View,但如果希望通过在Url路径中指定要访问的app, view,就比较麻烦,我下面的尝试,通过匹配Url,使用python自省(反射)查找特定View方法并返回该方法执行结果,建立了一个简单的python路由规则。
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); }
代码列表-1 Asp.Net Mvc路由配置
为了不影响现有逻辑,我在原django项目中新建了一个routed app,在该app中配置路由,下面是django项目结构
DjangoApp |-urls.py |-settings.py |-manage.py |-views.py |-routed |-urls.py |-models.py |-tests.py
代码列表-2 django项目结构
首先需要改动的是项目的根urls.py,凡是所有以"routed"开头的url,都交给routedapp下的urls.py来处理
(r'^routed/', include('DjangoApp.routed.urls')),
代码列表-3 项目根urls.py的更改
然后,在routed app的urls.py中,添加对url按照路由进行解析的规则
from django.conf.urls.defaults import * from DjangoApp.routed.viewstart import start urlpatterns = patterns('', (r'^(?P<controller>\w+)?/(?P<action>\w+)?/(?P<parameter>.+)?$', start), )
代码列表-4 routed app中urls.py的更改
在这个规则中,将所有类似"/routed/controllerabc/actionxyz/parm123"的url都指向viewstart.py中的start方法处理
import inspect from util.response import * from util.common import * def start(request,controller,action,parameter): #initialize controller, execute action with parameter prefix = 'DjangoApp.routed.controllers.' namespace = prefix + controller __import__(namespace) module = common.importmodule(namespace) methods = [k for k,v in inspect.getmembers(module) if k == action] if len(methods) <= 0: return response.http404() return getattr(module,methods[0])(request)
代码列表-5 viewstart.py
在viewstart.py中,根据匹配得到的Controller查找python模块,然后过滤用inspect.getmembers得到的方法,找到对应Action并执行之,python中的反射非常易用,内置方法__import__可动态引入需要的模块,globals方法可以返回传入的字符串代表的对象,getattr方法的返回值直接加上括号即可执行。代码中,util.common中封装了由字符串反射得到对象实例的方法,util.response封装了各种http response的方法,参考代码列表-6、代码列表-7。
import os import sys class common: @classmethod def importmodule(self,namespace): components = namespace.split('.') if len(components) == 1: return globals()[namespace] module = __import__(components[0]) for compent in components[1:]: module = getattr(module, compent) return module
代码列表-6 common.py
from django.template import Context from django.template.loader import get_template from django.shortcuts import render_to_response from django.http import HttpResponse from django.core import serializers class response: @classmethod def json(self,object): json = serializers.serialize("json", object) return HttpResponse(json,'application/json') @classmethod def xml(self,object): xml = serializers.serialize("xml", object) return HttpResponse(xml,'application/xml') @classmethod def falt(self,errormessage=''): return self.json({"errorcode":"500","message":errormessage if errormessage else ''}) @classmethod def view(self,view,view_model=None): view_path = view+'.view' return render_to_response(view_path,view_model) @classmethod def text(self,text): return HttpResponse(text) @classmethod def http404(self): return self.view('http404')
代码列表-7 response.py
最后,我们只要在routed app下创建controllers文件夹,然后创建controller即可通过controller名称对应的Url访问,如创建book.py
from util.response import * from routed.models import Book def list(request): books = Book.objects.all() return response.json(books)
代码列表-8 book.py
输入book controller中的list action对应的url http://localhost/routed/book/list 试试看吧。