阅读(1220) (0)

Django4.0 多数据库-手动选择数据库

2022-03-15 11:26:26 更新

Django也提供允许在代码中完全控制数据库的API。手动指定数据库分配将优先于路由分配的数据库。

手动为查询集选择数据库

你可以在查询集链的任一点为查询集选择数据库。调用查询集上的 ​using()​ 就可以获取使用指定数据库的其他查询集。
using()​ 使用单一参数:你打算进行查询的数据库别名。比如:

>>> # This will run on the 'default' database.
>>> Author.objects.all()

>>> # So will this.
>>> Author.objects.using('default').all()

>>> # This will run on the 'other' database.
>>> Author.objects.using('other').all()

为保存选择数据库

使用 ​using ​关键字来 ​Model.save()​ 到指定的数据保存的数据库。
比如,要保存对象到 ​legacy_users数据库,你应该这样写:

>>> my_object.save(using='legacy_users')

如果你没有指定 ​using​,​save()​ 方法将保存到路由的默认数据库分配。

将对象从一个数据库移动到另一个

如果已经保存实例到数据库,它可能使用 ​save(using=...)​ 作为迁移实例到新数据库的方法。然而,如果没有使用适合的步骤,这可能会产生意想不到的结果。
考虑下面的例子:

>>> p = Person(name='Fred')
>>> p.save(using='first')  # (statement 1)
>>> p.save(using='second') # (statement 2)

在语句1,新的 ​Person ​对象保存在 ​first ​数据库。这一次,​p ​没有主键,因此 Django 发出了一个SQL ​INSERT ​语句。这会创建主键,并且 Django 分配那个主键到 ​p​。
在语句2中进行保存时,​p ​也有主键值,Django 将试图在新的数据库上使用主键。如果主键值未在 ​second ​数据库中使用,那么将不会有任何问题——对象将被拷贝到新数据库。
然而,如果 ​p ​的主键已经在 ​second ​数据库上使用,那么当保存 ​p ​的时候, ​second ​数据库中存在的对象将被覆盖。
可以通过两种方式避免这种情况。首先,可以清理实例主键。如果对象没有主键,那么 Django 将它作为新对象来处理,避免在 ​second ​数据库上造成任何数据丢失:

>>> p = Person(name='Fred')
>>> p.save(using='first')
>>> p.pk = None # Clear the primary key.
>>> p.save(using='second') # Write a completely new object.

第二个办法就是使用 ​force_insert ​选项来 ​save()​ ,确保 Django 执行了 SQL ​INSERT ​:

>>> p = Person(name='Fred')
>>> p.save(using='first')
>>> p.save(using='second', force_insert=True)

这将确保 ​Fred ​在两个数据库上拥有同一个主键。当试着在 ​second ​上保存时,如果主键已经保存,那么将会引发一个错误。

选择要删除的数据库

默认情况下,用来删除现有对象的调用将在用于首先检索对象的同一数据库上执行:

>>> u = User.objects.using('legacy_users').get(username='fred')
>>> u.delete() # will delete from the `legacy_users` database

指定将要删除模型的数据库,传递 ​using ​关键字参数到 ​Model.delete()​ 方法。这个参数的工作方式与用关键字参数 ​save()​ 是一样的。
例如,如果你正在从 ​legacy_users ​迁移用户到 ​new_users ​数据库,你可以使用这些命令:

>>> user_obj.save(using='new_users')
>>> user_obj.delete(using='legacy_users')

使用多个数据库管理器

在管理器上使用 ​db_manager()​ 方法来让管理员访问非默认数据库。
比如,假设有一个自定义管理器方法来触发数据库——​User.objects.create_user()​。因为 ​create_user()​ 是一个管理器方法,不是 ​QuerySet ​方法,你不能操作 ​User.objects.using('new_users').create_user()​ 。(​create_user()​ 方法只适用 ​User.objects​ ,即管理器,而不是来自管理器上的 ​QuerySet ​。)解决方案是使用 ​db_manager()​ ,像这样:

User.objects.db_manager('new_users').create_user(...)

db_manager()​ 返回绑定到指定数据库的管理器副本。

将 get_queryset() 和多个数据库使用

如果在管理器上覆盖了 ​get_queryset()​ ,请确保在父类上调用这个方法使用 ​super()​ 或者在管理器(包含使用的数据库的名字)上适当处理 ​_db​ 属性。
比如,如果你想从 ​get_queryset​ 方法返回自定义的 ​QuerySet ​类,你可以这样做:

class MyManager(models.Manager):
    def get_queryset(self):
        qs = CustomQuerySet(self.model)
        if self._db is not None:
            qs = qs.using(self._db)
        return qs