Django学习14-数据库相关1

Django数据库支持

Django支持连接多种数据库,除了默认的SQLite外,还包括MySQL、PostgreSQL和Oracle这些关系型数据库。
##注意事项

持久链接

setting中设置CONN_MAX_AGE参数可以设置数据库连接保持的时间(秒),默认为0:每次请求(request)后关闭与数据库的链接;设置为None时:无限制的持续连接。

连接管理

Django在首次进行数据库查询时会打开与数据库的连接。它保持此连接打开并在后续请求中重用它。 Django一旦超过CONN_MAX_AGE定义的最时间或者不再可用,就会关闭连接。
在每个请求开始时,如果连接达到其最大时间,Django就会关闭连接。如果要设置数据库在一段时间后终止空闲连接,则应将CONN_MAX_AGE设置为一个较低的值,以便Django不会尝试使用已被数据库服务器终止的连接。 (此问题可能只会影响非常低流量的网站。)
在每个请求结束时,Django会关闭那些连接时间达到max_age或者处于不可恢复的错误状态的连接。如果在处理请求时发生任何数据库错误,Django会检查连接是否仍然有效,如果没有则关闭它。因此,数据库错误最多只影响一个请求;如果连接变得不可用,则下一个请求将获得新连接。

警告

由于每个线程都维护自己的连接,因此数据库必须支持至少与工作线程一样多的连接。
有时候,大多数视图都不会访问数据库,例如,因为它是外部系统的数据库,或者使用了缓存机制。这种情况下,需要将CONN_MAX_AGE设置为低值甚至为0,因为维护不太可能被重用的连接没有意义。这将有助于保持同时与数据库进行连接的数量。
开发服务器为它处理的每个请求创建一个新线程,会影响持久连接。在开发过程中不要启用它们。
当Django建立与数据库的连接时,它会根据所使用的后端设置适当的参数。如果启用持久连接,则不会再对每个请求重复此设置。如果修改连接的隔离级别或时区等参数,则应在每个请求结束时恢复Django的默认设置,在每个请求开始时强制使用适当的值,或者禁用持久连接。

编码

Django会默认所有的数据库都使用utf-8编码,如果使用了其他编码方式可能会导致意料之外的错误。

Django多数据库联用

大多数情况下,Django只需要使用一个数据库。但也可以指定同时使用多个数据库。

连接MySQL数据库

先试使用MySQL数据库代替默认的SQLite数据库,确保安装了MySQL。

  • 下载pymysql库:pip install pymsql
  • 在管理目录下的__init__.py中添加:
import pymysql
pymysql.install_as_MySQLdb()

让Django将pymysql当做MySQLdb使用

  • 修改setting下的DATABASE设置,填写MySQL的用户、密码和要使用的数据库,使用非root用户还需指明hostport
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'ex1',
        'USER': password.dbuser,
        'PASSWORD': password.mysql_passwd,
        'HOST':'localhost',
        'PORT':'3306',
    },
    # 'default': {
    #     'ENGINE': 'django.db.backends.sqlite3',
    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    # },
  • 执行migrate命令
(venv) ulysses@ulysses:~/PycharmProjects/django_ulysses$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, djcelery, learning_logs, sessions, sites, users
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK

MySQL数据库中就会有数据表。
在这里插入图片描述

使用PostgreSQL

需要安装psycopg2:pip install psycopg2pip install psycopg2-binary。不建议几个不同的数据库一起使用。

DATABASES = {
    # 'default': {
    #     'ENGINE': 'django.db.backends.sqlite3',
    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    # },
    'default':{

    },
    'learning_logs':{
        'ENGINE':'django.db.backends.mysql',
        'NAME': 'learning_logs',
        'PASSWORD':password.mysql_passwd,
        'USER':password.dbuser,
        'HOST':'localhost',
        'PORT':'3306',
    },
    'users':{
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'users',
        'PASSWORD': password.postgres_passwd,
        'USER': password.dbuser
        'HOST': 'localhost',
        'PORT': '5432',
    }
}
DATABASE_ROUTERS = []

指定数据库进行操作

可以在QuerySet“链”中的任何位置为QuerySet选择数据库。只需在QuerySet上调用using()以获取另一个使用指定数据库的QuerySet。

# 查询
Topic.objects.using('learning_logs').all() 
UserProfile.objects.using('users').all()
 
# 保存 或 删除
profile.save(using='users')
profile.delete(using='users')

可以设置数据库自动路由,这样就不需要使用using

数据库自动路由

Django要求保留DATABASE内的default条目,但可以没有字典表参数。为此,必须为所有应用程序的模型设置DATABASE_ROUTERS,包括正在使用的任何贡献和第三方应用程序中的模型,以便不会将任何查询连接到默认数据库。这里将user和learning_logs两个应用下的数据分别存储在2个数据库中。DATABASE_APP_MAPPING是项目应用与数据库的映射。auth下模型(User,Group,Permission)依赖于ContentType,因此2这必须在同一个数据库。 admin依赖于auth,因此其模型必须与auth位于同一个数据库中。在给定合适的路由器的情况下,contenttypes.ContentTypesessions.Sessionsites.Site中的每一个都可以存储在任何数据库中,但这并没有意义。

# multi-database
# 'path.to.Router'
DATABASE_ROUTERS = ['django_ulysses.database_router.DatabaseAppRouter']
DATABASE_APP_MAPPING = {
    # 'app_name' : 'database_name'
    'learning_logs': 'learning_logs',
    'users': 'users',
    'auth': 'users',
    'admin': 'users',
    'contenttypes': 'users',
    'sessions': 'users',
}

同时使用多数据库的最简单方法就是设置数据库路由方案。通过路由来包证每一条查询都会找到正确的数据库。Django有默认的路由方案,无需执行任何操作,可以自定义一个路由方案实现自己的数据库分配。
数据库路由是一个类,它需要提供这几个方法:db_for_read指定读取的数据库; db_for_write指定写入的数据库;allow_relation确定两个对象之间是否应该允许关系; allow_migrate是否允许迁移操作。
创建一个 database_router.py 存放数据库路由方案

from django.conf import settings

DATABASE_MAPPING = settings.DATABASE_APP_MAPPING
class DatabaseAppRouter(object):
    """
    A router to control all database operations on models for different
    databases
    In case an app is not set in settings.DATABASE_APPS_MAPPING, the router
    will fallback to the `default` database.

    Settings example:

    DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'}
    """

    def db_for_read(self, model, **hints):
        """"Point all read operations to the specific database."""
        if model._meta.app_label in DATABASE_MAPPING:
            return DATABASE_MAPPING[model._meta.app_label]
        return None

    def db_for_write(self, model, **hints):
        """Point all write operations to the specific database."""
        if model._meta.app_label in DATABASE_MAPPING:
            return DATABASE_MAPPING[model._meta.app_label]
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """Allow any relation between apps that use the same database."""
        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
        return None


    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """Make sure that apps only appear in the related database."""
        print(db, app_label, model_name, hints)
        if db in DATABASE_MAPPING.values():
            return DATABASE_MAPPING.get(app_label) == db
        elif app_label in DATABASE_MAPPING:
            return False
        return None

**hints提示信息,可用于确定哪个数据库应接收给定请求。目前,唯一提供的提示是instance,一个与正在进行的读或写操作相关的对象实例。路由器检查是否存在实例提示,并确定是否应使用该提示来更改路由行为。
在models.py文件中的模型类中添加:

    class Meta:
        app_label = "users"

多个数据库联用时数据导入导出

需要使用--database=[dbname]settings.DATABASES中的数据库名)指定选用的数据库,否则会使用default

  • 数据迁移、创建数据库:
python manage.py migrate [appname] --database=[dbname]
  • 数据导出:
python manage.py dumpdata app1 --database=db1 > app1_fixture.json
python manage.py dumpdata app2 --database=db2 > app2_fixture.json
python manage.py dumpdata auth > auth_fixture.json
  • 数据导入:
python manage.py loaddata app1_fixture.json --database=db1
python manage.py loaddata app2_fixture.json --database=db2

多数据库联用的缺陷

局限性主要体现在跨数据库关系上。
Django目前不提供跨越对多个数据库外键多对多关系的任何支持。如果使用路由器将模型分到到不同的数据库,则由这些模型定义的任何外键和多对多关系必须位于单个数据库的内部,这是出于完整性的考虑。
如果将Postgres,Oracle或MySQL与InnoDB一起使用,则会在数据库完整性级别强制执行 - 数据库级别的键约束会阻止创建无法验证的关系。但是,可以将SQLite或MySQL与MyISAM表一起使用。
如上面的learning_logs和users2个数据库,在迁移learning_logs就会出现问题,找不到auth_user,因为这个表它在另一个数据库中。

django.db.utils.InternalError: (1824, "Failed to open the referenced table 'auth_user'")

那么在allow_relation添加 允许两个应用的关联。

            elif db_obj1 in ('auth', 'learning_logs') \
                    and db_obj2 in ('auth', 'learning_logs'):
                return True

但迁移时,还是有问题

django.db.utils.InternalError: (1824, "Failed to open the referenced table 'auth_user'")

仍然找不到外键所指的数据表。

关于外键的看法

使用外键的原因无非是要满足某种范式的要求,本质上是一种约束、一种逻辑。数据库上使用外键目的是维护数据强一致性,后果是数据库同步、迁移、分表、分库时各种约束很麻烦。就像Django支持多数据库存储,却不支持跨数据库的外键。
可以将涉及到外键的逻辑全部在应用代码里实现,将逻辑放在代码里本身就是很合理的,双向的话就使用关联表。对于有打算使用多数据库存储的,一开始就考虑不使用外键。确定是从头到尾只在一个数据库中,就无所谓。

数据导入

Django在进行数据库操作时,要有相对应的数据表。
创建数据表的过程:models中创建模型–>使用makemigrations命令生成迁移文件–>migrate修改数据库表结构。

使用迁移文件提供数据

有时候在对数据库进行处理之前,需要填充一些初始数据。
在learning_logs应用下的models.py 下有一个模型ExampleModel

class ExampleModel(models.Model):
    name = models.CharField(max_length=10)
    content = MDTextField()

对应的数据表:
在这里插入图片描述
使用迁移文件的方式为这个模型提供数据。
运行python manage.py makemigrations --empty learning_logs生成一个空的迁移文件0011_auto_20181104_1624,内容:

# Generated by Django 2.1.2 on 2018-11-04 08:24

from django.db import migrations


class Migration(migrations.Migration):

    dependencies = [
        ('learning_logs', '0010_examplemodel'),
    ]

    operations = [
    ]



修改这个迁移文件,创建一个新的函数,并让RunPython调用它

# Generated by Django 2.1.2 on 2018-11-04 08:24

from django.db import migrations

def init_name(apps, schema_editor):
    # We can't import the ExampleModel model directly as it may be a newer
    # version than this migration expects. We use the historical version.
    ExampleModel = apps.get_model('learning_logs', 'ExampleModel')
    User = apps.get_model('auth', 'User')
    for user in User.objects.all():
        example = ExampleModel()
        example.name = user.username
        example.save()

class Migration(migrations.Migration):

    dependencies = [
        ('learning_logs', '0010_examplemodel'),
    ]

    operations = [
        migrations.RunPython(init_name),
    ]

RunPython需要一个可调用对象作为参数,而这个可调用对象它接受两个参数 :第一个是app注册表,其中包含所有模型的历史版本,用以匹配迁移所在的历史记录;第二个是SchemaEditor,可以用于手动影响数据库架构更改(创建模型、删除模型等)。
init_name这个函数中获取了UserExampleModel2个模型,将auth_user表的所有username数据都导入到learning_logs_examplemodel表的name中。
运行python mange.py migrate执行迁移:
在这里插入图片描述
获取了全部的username信息。详细内容参见,Django的Writing database migrations

用fixture导入数据

可以使用fixture来导入数据,这些数据不会自动导入,除非使用
TransactionTestCase.fixtures。所谓fixture就是一个数据的集合,通过它Django就能得知如何将数据导入数据库中。可以使用python manage.py dumpdata命令来创建一个fixture:

python manage.py dumpdata learning_logs.examplemodel > examplemodel.json

将刚才使用的learning_logs.examplemodel表的数据全部导出到对应的json文件中,不指定具体的模型就会导出所有应用的所有模型。除了使用dumpdata命令,也可以手动编写fixture文件,支持的格式:JSON、XML或YAML。
与dumpdata相对的,可以使用python manage.py loaddata <fixturename> 来将数据添加到数据库,每次执行loaddata时都会重新fixture读取数据,重新加载到数据库。可以在setting中添加FIXTURE_DIRS来指定Django搜寻fixture的路径

FIXTURE_DIRS = [os.path.join(BASE_DIR, 'fixture'), ]

执行从fixture中加载数据。

(venv) ulysses@ulysses:~/PycharmProjects/django_ulysses$ python manage.py loaddata examplemodel
Installed 57 object(s) from 1 fixture(s)

数据库的迁移

Django默认使用SQLite3数据库,当要更换数据库时,需要对整个数据库进行迁移。

python manage.py dumpdata -o=fixture/django_ulysses.json

在另一台上使用migrate命令创建数据库表后,使用loaddata命令

python manage.py loaddata django_ulysses.json

从json文本中导入数据会出现问题,提示导入content_type失败
可以看到django_content_type这张表是与的是用户、用户组相关的
在这里插入图片描述
在运行migrate命令时就会有数据添加到这张表内,除非手动删除后再添加。

使用数据库自带导出导入命令

MySQL数据库的mysqldump会导出带有完整sql命令的文件,导入时可以直接导入。

mysqldump  [数据库] [表] > [导出的数据库文件]
mysql -u用户名    -p密码    <  [要导入的数据库数据]

类似的PostgreSQL的导出导入:

pg_dump [数据库] > [数据文件]
psql -U gpadmin -d your-db -f your-table.sql

猜你喜欢

转载自blog.csdn.net/qq_19268039/article/details/83717966
今日推荐