Django URL 相對路徑的設法

看似很單純的事, 結果做起來有夠麻煩, 不知大家是怎麼做的? 網路上的範例幾乎都是將 project 放到根路徑, 即 http://.../。但若想在一台機器上放多個 project 的話, 就得額外處理。可能的做法有:
  • 註冊多個 domain name, 配合 apache 的 virtualhost 將不同 domain name 對到不同的 project。如此一來全部 project 都能用根路徑。
  • 將 project 放到子路徑: http://.../foo/, http://.../bar/。
我不想申請 domain name, 所以就選第二個方案。需要更改的東西有:
  • settings.py
  • urls.py
  • myapp/views.py
  • template/*.html
  • apache conf
做法如下。

settings.py

# 不同 project 設不同的 site_root, 像是 '/foo/' 或 '/bar/'
SITE_ROOT = '/'
MEDIA_URL = SITE_ROOT + 'media/'
ADMIN_MEDIA_PREFIX = SITE_ROOT + 'admin_media/'
# auth 要用到的 URL 設定
LOGIN_URL = SITE_ROOT + 'login/'
LOGIN_REDIRECT_URL = SITE_ROOT
在 views 裡記得用 RequestContext, 這樣 template 裡才能直接取用 {{ MEDIA_URL }}。

urls.py

# 路徑都改用 url 設名字, 之後才能在 template 中用 url tag 指回 view。
url(r'^$', views.index, name='index'), 
url(r'^login/$', 'django.contrib.auth.views.login', name='login'),
url(r'^logout/$', 'django.contrib.auth.views.logout_then_login', name='logout'),
...
# 順便提一下, 跑在 production server 上時會直接用 apache 傳靜態檔案, 
# 但開發時是用 Django 的 web server, 所以要要自己處理靜態檔案
if settings.DEBUG:
    # Define url routes for static files in development mode.
    urlpatterns += patterns('django.views.static',
                            (r'^media/(?P<path>.*)$',
                             'serve', {
                                 'document_root': settings.MEDIA_ROOT,
                                 'show_indexes': True}),)

myapp/views.py

一般情況直接 call views 中的 function 名稱, 但用 HttpResponseRedirect 時就得用 SITE_ROOT, 比方像這樣:
@login_required()
def some_page(request, project_id):
    if not request.user.is_staff:
        return HttpResponseRedirect(settings.SITE_ROOT)
    ...

template/*.html

url tag, 比方說:
<a href="{% url index %}">Homepage</a>

apache conf

我設在 /etc/apache2/conf.d/MY_DJANGO_CONF.conf。大致設法和 Django 用 mod_wsgi 差不多, 只是改一下路徑:
# Alias other operations to wsgi script.
WSGIScriptAlias /foo FILE_PATH_TO_WSGI_SCRIPT
WSGIScriptAlias /bar FILE_PATH_TO_WSGI_SCRIPT

<Location "/foo/media">
    SetHandler None
</Location>

<Location "/bar/media">
    SetHandler None
</Location>

Alias /foo/media FILE_PATH_TO_MEDIA
Alias /bar/media FILE_PATH_TO_MEDIA

# 若用 virtualenv 的話就指到 virtualenv 內的 site-packages
Alias /foo/admin_media /usr/lib/python2.5/site-packages/Django-1.1.1-py2.5.egg/django/contrib/admin/media
Alias /bar/admin_media /usr/lib/python2.5/site-packages/Django-1.1.1-py2.5.egg/django/contrib/admin/media

2010-05-06 更新

今天發現要在 JavaScript 內用 AJAX 連線, 於是得在 JavaScript 裡取得 settings.SITE_ROOT, 作法如下:
  • 在 settings.py 裡註冊一個 context processor:
    TEMPLATE_CONTEXT_PROCESSORS = (
        'django.core.context_processors.auth', # 頭四個是預設的, 覆寫 TEMPLATE_CONTEXT_PROCESSORS 時記得加進去
        'django.core.context_processors.debug',
        'django.core.context_processors.i18n',
        'django.core.context_processors.media',
        'mysite.myapp.views.add_constants',  # 自己用的
    )
  • 在 views.py 裡寫 add_constants:
    def add_constants(request):                                            return {
            'SITE_ROOT': settings.SITE_ROOT,
        }
    
    這兩步的目的是讓每個 view 都傳 SITE_ROOT 到 RequestContext 裡。
  • 在 template 裡置入 SITE_ROOT 的值, 寫到 base.html 裡:
    <script language="javascript" type="text/javascript">
        my_app.site_root = '{{ SITE_ROOT }}'; // my_app 是 global object, 作為我的 js code 的 root object
    </script>
    
    如此一來, 就能用 my_app.site_root 取得值。
真不是普通的麻煩啊。

留言

這個網誌中的熱門文章

(C/C++ ) 如何在 Linux 上使用自行編譯的第三方函式庫

熟悉系統工具好處多多

virtualbox 使用 USB 裝置