{"id":164,"date":"2015-05-26T23:50:59","date_gmt":"2015-05-26T15:50:59","guid":{"rendered":"http:\/\/vinta.ws\/code\/?p=164"},"modified":"2026-03-17T00:17:43","modified_gmt":"2026-03-16T16:17:43","slug":"database-routers-in-django","status":"publish","type":"post","link":"https:\/\/vinta.ws\/code\/database-routers-in-django.html","title":{"rendered":"Database Routers in Django"},"content":{"rendered":"<p>\u628a\u90e8\u5206 models \/ tables \u7368\u7acb\u5230\u4e00\u53f0\u8cc7\u6599\u5eab<\/p>\n<h2>Database Router<\/h2>\n<p>\u6c92\u8fa6\u6cd5\u5728 Model class \u88e1\u6307\u5b9a\u9019\u500b model \u53ea\u80fd\u7528\u67d0\u500b database<br \/>\n\u800c\u662f\u8981\u7528 database router<br \/>\n\u5c31\u662f\u5224\u65b7 <code>model._meta.app_label == &#039;xxx&#039;<\/code> \u7684\u6642\u5019\u6307\u5b9a\u4f7f\u7528\u67d0\u4e00\u500b database<br \/>\ndatabase \u662f\u6307\u5b9a\u7fa9\u5728 <code>settings.DATABASES<\/code> \u7684\u90a3\u4e9b<\/p>\n<p>\u4e0d\u904e django \u4e0d\u652f\u63f4\u8de8 database \u7684 model relation<br \/>\n\u4f60\u4e0d\u80fd\u7528 foreign key \u6216 m2m \u6307\u5411\u53e6\u4e00\u500b database \u88e1\u7684 model<br \/>\n\u4f46\u662f\u5176\u5be6\u4f60\u76f4\u63a5\u7528 user_id, song_id \u4e4b\u985e\u7684 int \u6b04\u4f4d\u4f86\u8a18\u9304\u5c31\u597d\u4e86<\/p>\n<p>in settings.py<\/p>\n<pre class=\"line-numbers\"><code class=\"language-py\">DATABASES = {\n    'default': {\n        'ENGINE': 'django.db.backends.mysql',\n        'NAME': 'default',\n        'USER': 'whatever',\n        'PASSWORD': 'whatever',\n        'HOST': '',\n        'PORT': '',\n    },\n    'warehouse': {\n        'ENGINE': 'django.db.backends.mysql',\n        'NAME': 'warehouse',\n        'USER': 'whatever',\n        'PASSWORD': 'whatever',\n        'HOST': '',\n        'PORT': '',\n    },\n}\n\nDATABASE_ROUTERS = [\n    'app.db_routers.SVDatabaseRouter',\n]<\/code><\/pre>\n<p>in app\/db_routers.py<\/p>\n<pre class=\"line-numbers\"><code class=\"language-py\">from oauth2_provider.models import AccessToken as OauthAccessToken\nfrom oauth2_provider.oauth2_backends import get_oauthlib_core\nfrom rest_framework2.authentication import BaseAuthentication\n\nclass SVOauthAuthentication(BaseAuthentication):\n    www_authenticate_realm = 'api'\n\n    def authenticate(self, request):\n        oauthlib_core = get_oauthlib_core()\n        valid, result = oauthlib_core.verify_request(request, scopes=[])\n\n        if valid:\n            return (result.user, result.access_token)\n        else:\n            access_token = request.GET.get('access_token', None)\n            if access_token:\n                try:\n                    access_token_obj = OauthAccessToken.objects.get(token=access_token)\n                except OauthAccessToken.DoesNotExist:\n                    pass\n                else:\n                    return (access_token_obj.user, access_token_obj)\n\n        return None\n\n    def authenticate_header(self, request):\n        return 'Bearer realm=\"{0}\"'.format(self.www_authenticate_realm)<\/code><\/pre>\n<p>ref:<br \/>\n<a href=\"https:\/\/docs.djangoproject.com\/en\/dev\/topics\/db\/multi-db\/\">https:\/\/docs.djangoproject.com\/en\/dev\/topics\/db\/multi-db\/<\/a><\/p>\n<h2>Models<\/h2>\n<p>\u628a\u6240\u6709\u9700\u8981\u653e\u5230\u53e6\u4e00\u53f0 db \u7684 models \u90fd\u653e\u5728\u540c\u4e00\u500b app \u4e0b<br \/>\n\u65b9\u4fbf\u7ba1\u7406<\/p>\n<p>in warehouse\/models.py<\/p>\n<pre class=\"line-numbers\"><code class=\"language-py\">class PlayRecord(SVWarehouseDBMixin, models.Model):\n    song_id = models.IntegerField()\n    user_id = models.IntegerField(null=True, blank=True)\n    is_full = models.BooleanField(default=False)\n    ip_address = models.IPAddressField()\n    location = models.CharField(max_length=2)\n    created_at = models.DateTimeField()<\/code><\/pre>\n<p>\u7e7c\u627f SVWarehouseDBMixin \u9019\u500b mixin \u7684 class \u6703\u88ab\u653e\u5230 warehouse \u9019\u53f0\u8cc7\u6599\u5eab\uff01<br \/>\n\u6240\u4ee5 migrate \u7684\u6642\u5019\u8981\u6ce8\u610f\uff0c\u8a18\u5f97\u5728 migration \u6a94\u6848\u88e1\u52a0\u4e0a\uff1a<\/p>\n<pre class=\"line-numbers\"><code class=\"language-py\">from south.db import dbs\nwarehouse_db = dbs['warehouse']<\/code><\/pre>\n<p>\u9019\u6a23 south \u624d\u6703\u5728 warehouse \u9019\u53f0\u8cc7\u6599\u5eab\u4e0a\u5efa\u7acb table<br \/>\n\u4e0d\u7136\u5c31\u662f\u4f60\u81ea\u5df1\u624b\u52d5\u53bb CREATE TABLE<\/p>\n<h2>Migration<\/h2>\n<p>\u5982\u679c\u4f60\u8981\u628a\u820a\u6709\u7684 app \u7684 models \u642c\u5230\u53e6\u4e00\u53f0\u8cc7\u6599\u5eab<br \/>\n\u4f46\u662f models \u4e0d\u52d5\uff08\u9084\u662f\u653e\u5728\u672c\u4f86\u7684 app \u5e95\u4e0b\uff09<br \/>\n\u4f60\u53ef\u80fd\u6703\u9700\u8981 reset \u6574\u500b migration \u7d00\u9304<br \/>\n\u5f9e\u982d\u958b\u59cb\u5efa\u7acb\u4e00\u500b\u65b0\u7684 migration<br \/>\n\u56e0\u70ba schema \u6703\u932f\u4e82<br \/>\n\u6240\u4ee5\u9084\u662f\u5efa\u8b70\u65b0\u958b\u4e00\u500b app \u4f86\u653e\u90a3\u4e9b\u8981\u642c\u5230\u53e6\u4e00\u53f0\u8cc7\u6599\u5eab\u7684 models<br \/>\n\u9019\u6a23 database router \u548c migration \u90fd\u6703\u6bd4\u8f03\u55ae\u7d14<\/p>\n<p>in app\/migrations\/0001_initial.py<\/p>\n<pre class=\"line-numbers\"><code class=\"language-bash\">$ pip install south==1.0.2\n\n# syncdb \u9ed8\u8a8d\u53ea\u6703\u4f5c\u7528\u5230 default \u8cc7\u6599\u5eab\uff0c\u4f60\u8981\u660e\u78ba\u6307\u5b9a\u8981\u7528\u54ea\u500b database \u624d\u884c\n$ .\/manage.py syncdb --noinput\n$ .\/manage.py syncdb --noinput --database=warehouse\n\n# migrate \u537b\u53ef\u4ee5\u4f5c\u7528\u5230\u5176\u4ed6\u8cc7\u6599\u5eab\n# \u56e0\u70ba migrate \u54ea\u500b\u8cc7\u6599\u5eab\u662f migration file \u88e1\u7684 <code>db<\/code> \u53c3\u6578\u5728\u6c7a\u5b9a\u7684\n$ .\/manage.py migrate<\/code><\/pre>\n<p>ref:<br \/>\n<a href=\"http:\/\/stackoverflow.com\/questions\/7029228\/is-using-multiple-databases-and-south-together-possible\">http:\/\/stackoverflow.com\/questions\/7029228\/is-using-multiple-databases-and-south-together-possible<\/a><\/p>\n<h2>Unit Tests<\/h2>\n<p>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.<\/p>\n<p>\u5728\u4f7f\u7528 <code>--keepdb<\/code> \u7684\u60c5\u6cc1\u4e0b\uff0c\u5982\u679c\u4f60\u7684\u6e2c\u8a66\u57f7\u884c\u5230\u4e00\u534a\u5c31\u56e0\u70ba\u932f\u8aa4\u800c\u4e2d\u65b7\u4e86\uff0c\u53ef\u80fd\u6703\u767c\u751f\u8cc7\u6599\u5eab\u88e1\u6709\u8cc7\u6599\u9084\u6c92\u88ab flush \u7684\u554f\u984c\uff0c\u5c0e\u81f4\u4e0b\u6b21\u57f7\u884c\u6e2c\u8a66\u6642\u5931\u6557\u3002\u4e0d\u904e\u5982\u679c\u4f60\u6c92\u6709\u7528 <code>--keepdb<\/code> \u7684\u8a71\uff0c\u56e0\u70ba\u6bcf\u6b21\u90fd\u6703\u91cd\u5efa\u8cc7\u6599\u5eab\uff0c\u6240\u4ee5\u4e0d\u6703\u6709\u9019\u500b\u554f\u984c\u3002<\/p>\n<pre class=\"line-numbers\"><code class=\"language-py\">from django.test import TestCase\n\nclass YourBTestCase(TestCase):\n    multi_db = True\n\n    def setUp(self):\n        do_shit()<\/code><\/pre>\n<p>ref:<br \/>\n<a href=\"https:\/\/docs.djangoproject.com\/en\/dev\/topics\/testing\/tools\/\">https:\/\/docs.djangoproject.com\/en\/dev\/topics\/testing\/tools\/<\/a><br \/>\n<a href=\"https:\/\/docs.djangoproject.com\/en\/dev\/topics\/testing\/advanced\/#topics-testing-advanced-multidb\">https:\/\/docs.djangoproject.com\/en\/dev\/topics\/testing\/advanced\/#topics-testing-advanced-multidb<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u628a\u90e8\u5206 models \/ tables \u7368\u7acb\u5230\u4e00\u53f0\u8cc7\u6599\u5eab<\/p>\n","protected":false},"author":1,"featured_media":331,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22,4,116],"tags":[59,13,95,2],"class_list":["post-164","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-about-database","category-about-python","category-about-web-development","tag-database-migrations","tag-django","tag-django-models","tag-python"],"_links":{"self":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts\/164","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/comments?post=164"}],"version-history":[{"count":0,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts\/164\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/media\/331"}],"wp:attachment":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/media?parent=164"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/categories?post=164"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/tags?post=164"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}