How to migrate multiple databases in Django? - python

I am doing a project with Django, there are too much data so that I need to form different databases to store them. I made a database router for each app to match the unique database, but when I migrate them, all of the tables were migrate to the same database, here are the codes:
from django.conf import settings
DATABASE_MAPPING = settings.DATABASES_APPS_MAPPING
class DatebaseAppsRouter(object):
"""
该类为连接不同数据库的路由,在setting中通过设置DATABASE_MAPPING来匹配数据库
例:
DATABASE_APPS_MAAPING = {"app1":"db1", "app2":"db2"}
"""
def db_for_read(self, model, **kwargs):
"""将所有读操作指向指定的数据库"""
if model._meta.app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[model._meta.app_label]
return None
def db_for_write(self, model, **kwargs):
"""将所有写操作指向指定的数据库"""
if model._meta.app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[model._meta.app_label]
return None
def allow_relation(self, obj1, obj2, **kwargs):
"""允许使用相同数据库的应用程序之间的任何关系"""
db_obj1 = DATABASE_MAPPING.get(obj1._meta.app_label)
db_obj2 = DATABASE_MAPPING.get(obj2._meta.app_label)
if db_obj1 and db_obj2:
if db_obj1 == db_obj2:
return True
else:
return False
else:
return None
def allow_syncdb(self, db, model):
"""确保这些应用程序只出现在相关的数据库中"""
if db in DATABASE_MAPPING.values():
return DATABASE_MAPPING.get(model._meta.app_label) == db
elif model._meta.app_label in DATABASE_MAPPING:
return False
return None
def allow_migrate(self, db, app_label, model=None, **kwargs):
"""确保身份验证应用程序只出现在login数据库中"""
if db in DATABASE_MAPPING.values():
return DATABASE_MAPPING.get(app_label) == db
elif app_label in DATABASE_MAPPING:
return False
return None
By using the router, I would like to migrate multiple databases, but it doesn't work
System check identified some issues:
WARNINGS:
?: (urls.W001) Your URL pattern '^login/$' uses include with a route ending with a '$'. Remove the dollar from the route to avoid problems including URLs.
?: (urls.W001) Your URL pattern '^main/$' uses include with a route ending with a '$'. Remove the dollar from the route to avoid problems including URLs.
?: (urls.W005) URL namespace 'admin' isn't unique. You may not be able to reverse all URLs in this namespace
Operations to perform:
Apply all migrations: Themepark, admin, auth, contenttypes, ginseng_holding_hotel, holiday_resort, login, nature_scenic_area, sessions, the_scent_spot_trusteeship, tourist_communications, travel_agency
Running migrations:
No migrations to apply.
So how to solve this problem? Thanks!

Replace your app name with 'your_app' and the database you want to run the migration on with 'your_app_respective_db' from the mapping you have. Hope this helps.
python manage.py migrate your_app --database your_app_respective_db

I think the problem is with your urls.py
If you are using django.urls.path() you don't have to add the carrot sign and the dollar sign and do it this way.
urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.login, name='login'),
path('main/', views.main, name='main'),
]
But if you were using django.urls.url() then you should do this
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login/', views.login, name='login'),
url(r'^main/', views.main, name='main'),
]
if you don't mind, can you show us your urls.py?

You have to add your different DB details as per your app in settings.py as following:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': config('DATABASE_NAME'),
'USER': config('DATABASE_USER'),
'PASSWORD': config('DATABASE_PASSWORD'),
'HOST': config('DATABASE_HOST'),
'PORT': config('DATABASE_PORT'),
'OPTIONS': {
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
'charset': 'utf8mb4',
},
},
'app1_name': {
'ENGINE': 'django.db.backends.mysql',
'NAME': config('APP1_DATABASE_NAME'),
'USER': config('DATABASE_USER'),
'PASSWORD': config('DATABASE_PASSWORD'),
'HOST': config('DATABASE_HOST'),
'PORT': config('DATABASE_PORT'),
'OPTIONS': {
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
'charset': 'utf8mb4',
},
},
'app2_name': {
'ENGINE': 'django.db.backends.mysql',
'NAME': config('APP2_DATABASE_NAME'),
'USER': config('DATABASE_USER'),
'PASSWORD': config('DATABASE_PASSWORD'),
'HOST': config('DATABASE_HOST'),
'PORT': config('DATABASE_PORT'),
'OPTIONS': {
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
'charset': 'utf8mb4',
},
}
}
default will be your main app. Also when you do migrations and migrate, you need to mention the database name like following.
python manage.py makemigrations app1
python manage.py migrate app1 --database=app1

Related

Wrong model._meta.app_label (from other app) in Django database router

i have two different SQL databases and three apps in my django project.
The apps are named PROD, TEST and common.
I am trying to route everything that comes from an url of PROD to models.py from PROD and database1, and everything that comes from an url of TEST to models.py from TEST and database2.
TEST
-- models.py
PROD
-- models.py
common
It worked fine, until i introduced 'common'(after cloning the project to another folder) and i don't know why.
settings.py:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'cfg_db_ic2_net_dev_test',
'USER': '',
'PASSWORD': '',
'HOST': '',
'PORT': '',
},
'proddb': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'cfg_db_ic2_net_dev_prod',
'USER': '',
'PASSWORD': '',
'HOST': '',
'PORT': '',
}
}
DATABASE_ROUTERS = ['PROD.dbRouter.ProdDBRouter']
PROD/dbRouter.py:
def db_for_read(self, model, **hints):
"Point all operations on prod models to 'proddb'"
from django.conf import settings
print(model._meta.app_label)
if model._meta.app_label == 'PROD':
return 'proddb'
return None
...
PROD/views.py:
def hosts(request):
print("I did this")
return render(request=request, template_name="common/hosts.html", context={"hosts": NetDefined.objects.all, "landscape": landscape})
The models.py for each TEST and PROD is pretty identical.
The issue that i have is that the template hosts.html is populated with data from the default data base and not from proddb database.
The reason for this is that the DBRouter doesn't recognize the model's model._meta.app_label, it says it's "TEST".
Even though the url is definitely routed correctly, the "I did this" is printed.
This only occured after reinstalling the project and introducing the third app.
So my question is: why doesn't Django recognize, that the views and models from "PROD" are being used and instead provides the wrong app_label to the router?
Thanks for your help!

How to work with multiple databases in Django [using Database Routers]

I need to isolate django`s default (auth, contenttypes, sessions) apps database, I read in documentation [Multiple Databses][1] all is clear and well explained, but not working to me :(
settings.py
DATABASES = {
'default_django': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
},
'default': {
'ENGINE': 'django.contrib.gis.db.backends.postgis',
'OPTIONS': {
'options': '-c search_path=eos,public'
},
'NAME': '****',
'USER': '*****',
'PASSWORD': '****',
'HOST': 'localhost',
'PORT': '5432',
}
}
DATABASE_ROUTES = ['core.dbrouter.AuthRoute', 'core.dbrouter.GisRoute']
and ./core/dbrouter.py
class AuthRouter:
"""
A router to control all database operations on models in the
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
"""
route_app_labels = {
'auth', 'admin', 'contenttypes',
'sessions', 'messages', 'staticfiles'
}
def db_for_read(self, model, **hints):
"""
Attempts to read auth and contenttypes models go to auth_db.
"""
if model._meta.app_label in self.route_app_labels:
return 'default_django'
return None
def db_for_write(self, model, **hints):
"""
Attempts to write auth and contenttypes models go to auth_db.
"""
if model._meta.app_label in self.route_app_labels:
return 'default_django'
return None
def allow_relation(self, obj1, obj2, **hints):
"""
Allow relations if a model in the auth or contenttypes apps is
involved.
"""
if (
obj1._meta.app_label in self.route_app_labels or
obj2._meta.app_label in self.route_app_labels
):
return True
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
Make sure the auth and contenttypes apps only appear in the
'auth_db' database.
"""
if app_label in self.route_app_labels:
return db == 'default_django'
return None
When I do python3 manage.py migrate it is shows
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
No migrations to apply.
Please, could someone explain?(Why?)
[1]: https://docs.djangoproject.com/en/3.1/topics/db/multi-db/
you forgot an R.
DATABASE_ROUTES = ['core.dbrouter.AuthRoute', 'core.dbrouter.GisRoute']
↓
DATABASE_ROUTERS = ['core.dbrouter.AuthRoute', 'core.dbrouter.GisRoute']

Serving separate database per user django

i am creating database according this now: Saperate PostgreSQL db for each client, with automated migrations on creating client on single Django app and on same server
Now I want to serve the db per user. We can return db from db routers only if they exists in the DATABASES varriable in the settings file.
Does anybody know how to serve db which are created on server but not added in settings.py, which will be according to the logged in user.
There will be a main db for user, in which their relations with their db name is saved.
Settings.py
DATABASE_ROUTERS = ['app.router_middleware.DataBaseRouter']
DATABASES = {'default':
{
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'abc',
'USER': 'xyz',
'PASSWORD': '*****',
'HOST': 'localhost',
'PORT': '5432',
}
}
import django
django.setup()
from Client.views import ALL_DB
DATABASES.update(ALL_DB)
Client.views
ALL_DB = { x.db_name: {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': x.db_name,
'USER': 'xyz',
'PASSWORD': '****',
'HOST': 'localhost',
'PORT': '5432',
} for x in Client.objects.all()}
router_middleware.py
db_name = 'default'
class RouterMiddleware(CoreSessionMiddleware):
def process_request(self, request):
if request.user.is_authenticated() :
user = request.user
if ClientEmployee.objects.filter(user=user).exists():
client_employee = ClientEmployee.objects.get(user=user)
db_name = client_employee.client.db_name
class DataBaseRouter(object):
def db_for_read(self, model, **hints):
return db_name
def db_for_write(self, model, **hints):
return db_name

Django saving models in different databases

I am new to Django.
I have two apps created in my project:
python3 manage.py startapp app1
python3 manage.py startapp app1
I am using Mysql as DB and I want that each app should use different schemas.
I try to follow the steps described here:
Sharing (mysql) database between apps Django with Database routers
So in settings.py I defined 2 MySql schemas and keep default schema and
add the add the DATABASE_ROUTERS.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app1',
'app2',
]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
'mydb1':{
'ENGINE': 'django.db.backends.mysql',
'NAME': 'APP1DB',
'USER': 'user1',
'PASSWORD': '****',
'HOST': 'localhost', # Or an IP Address that your DB is hosted on
'PORT': '3306',
},
'mydb2':{
'ENGINE': 'django.db.backends.mysql',
'NAME': 'APP2DB',
'USER': 'user1',
'PASSWORD': '****',
'HOST': 'localhost', # Or an IP Address that your DB is hosted on
'PORT': '3306',
},
}
DATABASE_ROUTERS = ['app1.dbRouter.App1DBRouter', 'app2.dbRouter.App2DBRouter']
Additional files:
app1/models1.py
from django.db import models
# Create your models here.
class Model1(models.Model):
name = models.CharField(max_length=100)
app2/models2.py
from django.db import models
# Create your models here.
class Model2(models.Model):
name = models.CharField(max_length=100)
And files:
app1/dbRouter.py
class App1DBRouter(object):
def db_for_read(self,model, **hints):
if model._meta.app_label == 'app1':
return 'mydb1'
return None
def db_for_write(self,model, **hints):
if model._meta.app_label == 'app1':
return 'mydb1'
return None
def allow_relation(self,obj1, obj2, **hints):
if obj1._meta.app_label == 'app1' and \
obj2._meta.app_label == 'app1':
return True
return None
def allow_syncdb(self,db, model):
if db == 'mydb1':
if model._meta.app_label == 'app1':
return True
elif model._meta.app_label == 'app1':
return False
return None
app2/dbRouter.py:
class App2DBRouter(object):
def db_for_read(self,model, **hints):
if model._meta.app_label == 'app2':
return 'mydb2'
return None
def db_for_write(self,model, **hints):
if model._meta.app_label == 'app2':
return 'mydb2'
return None
def allow_relation(self,obj1, obj2, **hints):
if obj1._meta.app_label == 'app2' and \
obj2._meta.app_label == 'app2':
return True
return None
def allow_syncdb(self,db, model):
if db == 'mydb2':
if model._meta.app_label == 'app2':
return True
elif model._meta.app_label == 'app2':
return False
return None
After this I expect that when I run commands makemigrations and migrate I would get 2 different tables in 2 different schemas?
So:
$ python3 manage.py makemigrations
Migrations for 'app1':
app1/migrations/0001_initial.py
- Create model Model1
Migrations for 'app2':
app2/migrations/0001_initial.py
- Create model Model2
$ python3 manage.py migrate
Operations to perform:
Apply all migrations: admin, app1, app2, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
...
Applying sessions.0001_initial... OK
But there are no tables created except django_migrations in both schemas.
If I use command:
$ python3 manage.py migrate --database=mydb1
then both model tables are created in the APP1DB.
mysql> SHOW TABLES;
+----------------------------+
| Tables_in_APP1DB |
+----------------------------+
| app1_model1 |
| app2_model2 |
| auth_group |
So how to solve this?
According to the documentation database routers may implement the method allow_migrate(db, app_label, model_name=None, **hints) in order to determine whether a certain migration should be performed or not. The methods of custom database routers are always invoked through the default database router django.db.router:
def allow_migrate(self, db, app_label, **hints):
for router in self.routers:
try:
method = router.allow_migrate
except AttributeError:
# If the router doesn't have a method, skip to the next one.
continue
[...]
return True
Because your routers don't define such a method it simply returns True at the end of the method and therefore applies the requested migration to the specified database.
You can achieve the separation of apps across the different databases by defining this method:
class App1DBRouter(object):
[...]
def allow_migrate(self, db, app_label, model_name=None, **hints):
if app_label == 'app1':
return db == 'mydb1'
return None
And similarly for app2.

Simple step-by-step guide to Django multiple databases please

I've been through a few posts and the Django documentation but still can't get multiple databases to work. I'm new to Django and feel quite confused indeed.
In my project, one app should use an existing vo.sqlite3 with lots of data in it. I want to be able to handle this vo.sqlite3 in my vo app, separately from the default database db.sqlite3 (the one that handles users, etc).
Would you anyone be able to point me to a simple step-by-step way to get such setup to work?
Many thanks.
An example, in your settings, set up your databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite1'),
},
'client1': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite2'),
},
}
Then wharever you perform a query, you can select the database.
UserModel.objects.using(db).get(username=username)
db = you database name
You can view the full example here
Not only levi's solution above worked, I even managed to make automated routing work, see my settings.py and routers.py.
settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'db.sqlite3',
},
'vodb': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'vo.sqlite3',
}
}
DATABASE_ROUTERS = ['vo.routers.vo_Router',]
DATABASE_APPS_MAPPING = {'vo': 'vodb'}
routers.py
from django.conf import settings
class vo_Router(object):
def db_for_read(self, model, **hints):
return 'vodb'
def db_for_write(self, model, **hints):
return 'vodb'
def allow_relation(self, obj1, obj2, **hints):
db_list = ('default', 'vodb')
if obj1._state.db in db_list and obj2._state.db in db_list:
return True
return None
def allow_migrate(self, db, model):
return True
def allow_syncdb(self, db, model):
if db == 'vodb':
return model._meta.app_label == 'vo'
elif model._meta.app_label == 'vo':
return False
return None
The piece that was missing was the execution of manage.py syncdb --database=vodb. However, this added a lot of unnecessary Django tables to the app database vo.sqlite3, and even created a new table for the object defined in my models.py.
In my case, I wanted just to link existing tables (with data) in vo.sqlite3 to the objects in models.py. Is there a way to do it Django? Thanks.

Categories

Resources