把部分 models / tables 獨立到一台資料庫
Database Router
沒辦法在 Model class 裡指定這個 model 只能用某個 database
而是要用 database router
就是判斷 model._meta.app_label == 'xxx'
的時候指定使用某一個 database
database 是指定義在 settings.DATABASES
的那些
不過 django 不支援跨 database 的 model relation
你不能用 foreign key 或 m2m 指向另一個 database 裡的 model
但是其實你直接用 user_id, song_id 之類的 int 欄位來記錄就好了
in settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'default',
'USER': 'whatever',
'PASSWORD': 'whatever',
'HOST': '',
'PORT': '',
},
'warehouse': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'warehouse',
'USER': 'whatever',
'PASSWORD': 'whatever',
'HOST': '',
'PORT': '',
},
}
DATABASE_ROUTERS = [
'app.db_routers.SVDatabaseRouter',
]
in app/db_routers.py
from oauth2_provider.models import AccessToken as OauthAccessToken
from oauth2_provider.oauth2_backends import get_oauthlib_core
from rest_framework2.authentication import BaseAuthentication
class SVOauthAuthentication(BaseAuthentication):
www_authenticate_realm = 'api'
def authenticate(self, request):
oauthlib_core = get_oauthlib_core()
valid, result = oauthlib_core.verify_request(request, scopes=[])
if valid:
return (result.user, result.access_token)
else:
access_token = request.GET.get('access_token', None)
if access_token:
try:
access_token_obj = OauthAccessToken.objects.get(token=access_token)
except OauthAccessToken.DoesNotExist:
pass
else:
return (access_token_obj.user, access_token_obj)
return None
def authenticate_header(self, request):
return 'Bearer realm="{0}"'.format(self.www_authenticate_realm)
ref:
https://docs.djangoproject.com/en/dev/topics/db/multi-db/
Models
把所有需要放到另一台 db 的 models 都放在同一個 app 下
方便管理
in warehouse/models.py
class PlayRecord(SVWarehouseDBMixin, models.Model):
song_id = models.IntegerField()
user_id = models.IntegerField(null=True, blank=True)
is_full = models.BooleanField(default=False)
ip_address = models.IPAddressField()
location = models.CharField(max_length=2)
created_at = models.DateTimeField()
繼承 SVWarehouseDBMixin 這個 minxin 的 class 會被放到 warehouse 這台資料庫!
所以 migrate 的時候要注意,記得在 migration 檔案裡加上:
from south.db import dbs
warehouse_db = dbs['warehouse']
這樣 south 才會在 warehouse 這台資料庫上建立 table
不然就是你自己手動去 CREATE TABLE
Migration
如果你要把舊有的 app 的 models 搬到另一台資料庫
但是 models 不動(還是放在本來的 app 底下)
你可能會需要 reset 整個 migration 紀錄
從頭開始建立一個新的 migration
因為 schema 會錯亂
所以還是建議新開一個 app 來放那些要搬到另一台資料庫的 models
這樣 database router 和 migration 都會比較單純
in app/migrations/0001_initial.py
$ pip install south==1.0.2
# syncdb 默認只會作用到 default 資料庫,你要明確指定要用哪個 database 才行
$ ./manage.py syncdb --noinput
$ ./manage.py syncdb --noinput --database=warehouse
# migrate 卻可以作用到其他資料庫
# 因為 migrate 哪個資料庫是 migration file 裡的 `db` 參數在決定的
$ ./manage.py migrate
ref:
http://stackoverflow.com/questions/7029228/is-using-multiple-databases-and-south-together-possible
Unit Tests
Django only flushes the default database at the start of each test run. If your setup contains multiple databases, and you have a test that requires every database to be clean, you can use the multi_db attribute on the test suite to request a full flush.
在使用 --keepdb
的情況下,如果你的測試執行到一半就因為錯誤而中斷了,可能會發生資料庫裡有資料還沒被 flush 的問題,導致下次執行測試時失敗。不過如果你沒有用 --keepdb
的話,因為每次都會重建資料庫,所以不會有這個問題。
from django.test import TestCase
class YourBTestCase(TestCase):
multi_db = True
def setUp(self):
do_shit()
ref:
https://docs.djangoproject.com/en/dev/topics/testing/tools/
https://docs.djangoproject.com/en/dev/topics/testing/advanced/#topics-testing-advanced-multidb