django 中的动态 url 路由

Dynamic url routing in django

我有一个 RoutingUrl 模型,它描述了我站点上使用的所有 urls 以及必须管理 url 和一些其他路由信息的视图(视图模型的外键) . url 的大小在不断增加,应该也支持重定向。型号大致如下:

class RoutingUrl(models.Model):
    url = models.CharField(unique=True, verbose_name='routing url')
    crc_checksum = models.IntegerField(editable=False)
    redirect_to = models.ForeignKey('RoutingUrl', related_name='redirect_from', blank=True, null=True)
    view = models.ForeignKey(View, related_name='routing_urls')

    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

class View(models.Model):
    name = models.CharField()

RoutingUrl 也有一个通用外键,模型包含有关要呈现的页面的信息(必须支持不同的模型,这就是通用外键的原因)。

现在的问题是:如何在django中实现这样的动态路由?我的感觉是我有两个选择:

  1. 我可以创建一个中间件,它将通过实施 process_request 方法(在检查 url 模式之前)负责将请求分派到正确的视图。显然这样的中间件应该位于中间件堆栈的底部,以保持其他中间件的功能。因此,该解决方案将绕过 Django 路由系统。
  2. 另一种选择是添加一个 catch all url 匹配所有内容的模式,然后只编写我自己的 handler/dispatcher 作为视图。该处理程序实现路由过程,因此将调用适当的视图和 return 它的 HttpResponse

你能建议我这两个选项中哪一个最适合实现这样的路由吗?当然,如果有第三种更好的选择,请不要犹豫向我推荐。

稍微调查一下 django 中间件挂钩,很明显 process_request 不是实现路由功能的最佳选择。事实上,从文档中:

It should return either None or an HttpResponse object. If it returns None, Django will continue processing this request, executing any other process_request() middleware, then, process_view() middleware, and finally, the appropriate view. If it returns an HttpResponse object, Django won’t bother calling any other request, view or exception middleware, or the appropriate view; it’ll apply response middleware to that HttpResponse, and return the result.

所以 HttpResponse 会破坏中间件堆栈功能。 process_view 大致相同,这将避免异常中间件的调用。此时采用第二种方案似乎更明智...

django-cms 插件证实了这种直觉,正如您可以从 urlpatterns 定义的 source code 中看到的那样:

from django.conf import settings
from django.conf.urls import url

from cms.apphook_pool import apphook_pool
from cms.appresolver import get_app_patterns
from cms.views import details

# This is a constant, really, but must live here due to import order
SLUG_REGEXP = '[0-9A-Za-z-_.//]+'

if settings.APPEND_SLASH:
    regexp = r'^(?P<slug>%s)/$' % SLUG_REGEXP
else:
    regexp = r'^(?P<slug>%s)$' % SLUG_REGEXP

if apphook_pool.get_apphooks():
    # If there are some application urls, use special resolver,
    # so we will have standard reverse support.
    urlpatterns = get_app_patterns()
else:
    urlpatterns = []

urlpatterns.extend([
    url(regexp, details, name='pages-details-by-slug'),
    url(r'^$', details, {'slug': ''}, name='pages-root'),
])