之前專案都是使用 cookiecutter + cookiecutter-django 來產生的,前一陣子在它產生出來的設定裡看到 SITE_ID 以及註解,就發現了 Django site framework 。
Django site framework 裡的 site ,就有點像是 virtual host 裡的 host,簡單的說,可以做到以下事情:
- Virtual host:Django 可以有能力去判斷網址裡的網域,來決定如何處理。
- 不同網站入口,但共用資料庫
啟用方法
基本上 site framework 已經內建在 Django 裡了,所以只要啟用就可以
- 在 settings 的 INSTALLED_APPS 加入 “django.contrib.sites”
- 增加 SITE_ID (選填,可以加這個設定,也可以不加)
執行 migrate python manage.py migrate
接著利用 admin 後台或者是 shell 去新增 site。
功能
有提供以下功能
- 物件 (model) 跟指定的 site 關連
- View 可以依據 site 來做出不同的處理跟回應
- 多個 site,但只使用指定的 site
- 自動依照網址內的網域來判斷,並將 site 放到 request 裡
物件 (model) 跟指定的 site 關連
site framework 本身有建立 model ,也就是說有建立資料表格,所以其實是可以讓物件模型跟 site 做關連,那麼之後在處理時,就可以依據這個關係,只顯示出跟指定 site 相關連的物件。
# 引用自 django 文件 from django.contrib.sites.models import Site from django.db import models class Article(models.Model): headline = models.CharField(max_length=200) # ... sites = models.ManyToManyField(Site)
上面的程式碼,就是表示 Article 跟 Site 做多對多的關聯。之後在 View 裡就可以用下面的程式碼撈出跟指定站點相關連的 Article
# 引用自 Django 文件 from django.contrib.sites.shortcuts import get_current_site def article_detail(request, article_id): try: a = Article.objects.get(id=article_id, sites__id=get_current_site(request).id) except Article.DoesNotExist: raise Http404("站點裡沒有文章") # ...
View 可以依據 site 來做出不同的處理跟回應
其實上面的程式碼已經就是了,這邊再舉個例子
# 引用自 Django 文件 from django.contrib.sites.shortcuts import get_current_site def my_view(request): current_site = get_current_site(request) if current_site.domain == 'foo.com': # Do something pass else: # Do something else. pass
這邊使用 site framework 提供的 shortcut – get_current_site 來取得目前的 site,然後用 if – else 來做判斷,執行不同的邏輯
多個 site,但只使用指定的 site
那程式裡已經有多個 site,但是想拆分出來,只服務單一個 site 時,可以在 settings 裡指定 SITE_ID,程式裡可以直接引用,例如
from django.conf import settings def my_view(request): if settings.SITE_ID == 3: # Do something. pass else: # Do something else. pass
這個蠻適合應用在 docker 上,我只要打包好 docker image,之後就可以再利用指定環境變數的方式去讓這個 container 能處理指定的 site
自動依照網址內的網域來判斷,並將 site 放到 request 裡
要完成這個功能,需要在 settings 裡的 MIDDLEWARE 裡,加入 “django.contrib.sites.middleware.CurrentSiteMiddleware”
加入以後,View 裡的 request 就會多出一個 site 的屬性,那在 View 裡,就可以直接使用 request.site
Site framework 內部的運作
依據上面的說明,除了資料庫以外,get_current_site 好像…挺重要的,下面就來繼續挖掘。
我們先從 middleware 開始,CurrentSiteMiddleware 裡面蠻簡單的,只有 override process_request(),裡面只有一行:request.site = get_current_site(request)
!! 又是 get_current_site()
那 get_current_site() 又做了什麼事情?get_current_site() 只判斷 django.contrib.sites 有沒有在 settings INSTALLED_APPS 裡,有的話,就使用 Site.objects.get_current() 來取得目前的 site;沒有的話,改用 RequestSite 類別來判斷,RequestSite 只是一個封裝,封裝出類似 Site 的物件,讓你後續存取可以跟原來的 site 物件一樣。
if apps.is_installed('django.contrib.sites'): from .models import Site return Site.objects.get_current(request) else: from .requests import RequestSite return RequestSite(request)
先看 Site.objecs.get_current() ,這函式的實作是在 django/contrib/sites/models.py 裡。裡面也很簡單,先去看 settings 裡有沒有 SITE_ID,有的話,就用 SITE_ID 去查詢資料表格,取出對應的 site;沒有 SITE_ID 或者是 SITE_ID 被判定為 False (‘’, 0 都算是 False),就改使用 _get_site_by_request() 從 request 去做判讀。
_get_site_by_request() 使用 request.get_host() 取出 host,然後解析出 domain/port,再使用 domain 去查詢資料表格,找到對應的 site。
總結
以上,就是 site framework ,為了驗證我對 site framework 的認知,製作了一個小的展示專案,放在 github 上:https://github.com/elleryq/site_framework_demo
沒有留言:
張貼留言