postgresql 选择测试数据库?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3320900/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-20 00:13:06  来源:igfitidea点击:

Choose test database?

djangopostgresqldjango-testingwebfaction

提问by mpen

I'm trying to run

我正在努力奔跑

./manage.py test

But it tells me

但它告诉我

Got an error creating the test database: permission denied to create database

创建测试数据库时出错:创建数据库的权限被拒绝

Obviously it doesn't have permission to create the database, but I'm on a shared server, so there's not much I can do about that. I can create a new database through the control panel but I don't think there's any way I can let Django do it automatically.

显然它没有创建数据库的权限,但我在共享服务器上,所以我无能为力。我可以通过控制面板创建一个新数据库,但我认为没有任何方法可以让 Django 自动完成。

So, can't I create the test database manually and instead tell Django to flush it every time, rather than recreating the whole thing?

那么,我不能手动创建测试数据库,而是告诉 Django 每次刷新它,而不是重新创建整个事情吗?

采纳答案by Thiago Ganzarolli

I had a similar issue. But I wanted Django to just bypass the creation of a test database for one of my instances (it is not a mirror tough). Following Mark's suggestion, I created a custom test runner, as follows

我有一个类似的问题。但我希望 Django 绕过为我的一个实例创建测试数据库(这不是一个镜像困​​难)。按照马克的建议,我创建了一个自定义的测试运行器,如下

from django.test.simple import DjangoTestSuiteRunner


class ByPassableDBDjangoTestSuiteRunner(DjangoTestSuiteRunner):

    def setup_databases(self, **kwargs):
        from django.db import connections
        old_names = []
        mirrors = []

        for alias in connections:
            connection = connections[alias]
            # If the database is a test mirror, redirect its connection
            # instead of creating a test database.
            if connection.settings_dict['TEST_MIRROR']:
                mirrors.append((alias, connection))
                mirror_alias = connection.settings_dict['TEST_MIRROR']
                connections._connections[alias] = connections[mirror_alias]
            elif connection.settings_dict.get('BYPASS_CREATION','no') == 'no':
                old_names.append((connection, connection.settings_dict['NAME']))
                connection.creation.create_test_db(self.verbosity, autoclobber=not self.interactive)
        return old_names, mirrors

Then I created an extra dict entry in one of my databases entries inside settings.py, 'BYPASS_CREATION':'yes',

然后我在 settings.py 中的一个数据库条目中创建了一个额外的 dict 条目, 'BYPASS_CREATION':'yes',

Finally, I configured a new TestRunner with

最后,我配置了一个新的 TestRunner

TEST_RUNNER = 'auth.data.runner.ByPassableDBDjangoTestSuiteRunner'

回答by Michael Bylstra

I would suggest using sqlite3 for testing purposes while keeping on using mysql/postgres/etc for production.

我建议使用 sqlite3 进行测试,同时继续使用 mysql/postgres/etc 进行生产。

This can be achieved by placing this in your settings file:

这可以通过将它放在您的设置文件中来实现:

if 'test' in sys.argv:
    DATABASES['default'] = {'ENGINE': 'django.db.backends.sqlite3'}

see Running django tests with sqlite

请参阅使用 sqlite 运行 django 测试

a temporary sqlite database file will be created in your django project home which you will have write access to. The other advantage is that sqlite3 is much faster for testing. You may however run in to problems if you are using any mysql/postgres specific raw sql (which you should try to avoid anyway).

将在您的 django 项目主页中创建一个临时的 sqlite 数据库文件,您将拥有写入权限。另一个优点是 sqlite3 的测试速度要快得多。但是,如果您使用任何 mysql/postgres 特定的原始 sql(无论如何您都应该尽量避免),您可能会遇到问题。

回答by mpen

I think a better solution might be to define your own test runner.

我认为更好的解决方案可能是定义您自己的测试运行程序

回答by shapiromatron

I added this to the comments above but it got kind of lost - recent changes to webfaction make this MUCH easier. You can now create new private database instances.

我将此添加到上面的评论中,但它有点丢失了 - 最近对 webfaction 的更改使这变得更加容易。您现在可以创建新的私有数据库实例

Follow the instructions there, and when creating a new user make sure to give them the permission to ALTER USER new_username CREATEDB;.

按照那里的说明进行操作,并在创建新用户时确保授予他们ALTER USER new_username CREATEDB;.

You probably also should change the default cron settings so they don't try to check if this database is up and runnings as frequently.

您可能还应该更改默认的 cron 设置,这样他们就不会尝试检查此数据库是否已启动并经常运行。

回答by mhost

You could use django-noseas your TEST_RUNNER. Once installed, if you pass the following environment variable, it will not delete and re-create the database (create it manually yourself first).

您可以使用django-nose作为 TEST_RUNNER。安装后,如果传入以下环境变量,不会删除并重新创建数据库(先自己手动创建)。

REUSE_DB=1 ./manage.py test

You can also add the following to settings.py so you don't have to write REUSE_DB=1 every time you want to run tests:

您还可以将以下内容添加到 settings.py 中,这样您就不必每次要运行测试时都编写 REUSE_DB=1 :

os.environ['REUSE_DB'] = "1"

Note:this will also leave all your tables in the databases which means test setup will be a little quicker, but you will have to manually update the tables(or delete and re-create the database yourself) when you change your models.

注意:这也会将您的所有表保留在数据库中,这意味着测试设置会更快一些,但是当您更改模型时,您必须手动更新表(或自己删除并重新创建数据库)。

回答by lemanyk

my variant to reusing database:

我重用数据库的变体:

from django.test.simple import DjangoTestSuiteRunner
from django.core.management import call_command


class TestRunner(DjangoTestSuiteRunner):
    def setup_databases(self, **kwargs):
        from django.db import connections

        settings = connections['default'].settings_dict
        settings['NAME'] = settings['TEST_NAME']
        settings['USER'] = settings['TEST_USER']
        settings['PASSWORD'] = settings['TEST_PASSWD']

        call_command('syncdb', verbosity=1, interactive=False, load_initial_data=False)

    def teardown_databases(self, old_config, **kwargs):
        from django.db import connection

        cursor = connection.cursor()
        cursor.execute('show tables;')
        parts = ('DROP TABLE IF EXISTS %s;' % table for (table,) in cursor.fetchall())
        sql = 'SET FOREIGN_KEY_CHECKS = 0;\n' + '\n'.join(parts) + 'SET FOREIGN_KEY_CHECKS = 1;\n'
        connection.cursor().execute(sql)

回答by Lie Ryan

The following is a django test suite runner to create database using Webfaction XML-RPC API. Note, setting up the database using the API may take up to a minute, and the script may appear to be stuck momentarily, just wait for a little while.

以下是一个 django 测试套件运行器,用于使用Webfaction XML-RPC API创建数据库。请注意,使用 API 设置数据库可能需要长达一分钟的时间,并且脚本可能会暂时卡住,请稍等片刻。

NOTE: there is a security risk of having control panel password in the webfaction server, because someone breaching into your web server SSH could take over your Webfaction account. If that is a concern, set USE_SESSKEY to True and use the fabric script below this script to pass a session id to the server. The session key expires in 1 hourfrom the last API call.

注意:在网络服务器中使用控制面板密码存在安全风险,因为有人侵入您的网络服务器 SSH 可能会接管您的网络系统帐户。如果这是一个问题,请将 USE_SESSKEY 设置为 True 并使用此脚本下方的结构脚本将会话 ID 传递给服务器。会话密钥上次 API 调用后 1 小时后到期

File test_runner.py: in the server, you need to configure ./manage.py test to use WebfactionTestRunner

文件 test_runner.py:在服务器端,需要配置 ./manage.py test 才能使用 WebfactionTestRunner

"""
This test runner uses Webfaction XML-RPC API to create and destroy database
"""

# you can put your control panel username and password here.
# NOTE: there is a security risk of having control panel password in
# the webfaction server, because someone breaching into your web server
# SSH could take over your Webfaction account. If that is a concern,
# set USE_SESSKEY to True and use the fabric script below this script to
# generate a session.

USE_SESSKEY = True
# CP_USERNAME = 'webfactionusername' # required if and only if USE_SESSKEY is False
# CP_PASSWORD = 'webfactionpassword' # required if and only if USE_SESSKEY is False

import sys
import os
from django.test.simple import DjangoTestSuiteRunner
from django import db
from webfaction import Webfaction

def get_sesskey():
    f = os.path.expanduser("~/sesskey")
    sesskey = open(f).read().strip()
    os.remove(f)
    return sesskey

if USE_SESSKEY:
    wf = Webfaction(get_sesskey())
else:
    wf = Webfaction()
    wf.login(CP_USERNAME, CP_PASSWORD)


def get_db_user_and_type(connection):
    db_types = {
        'django.db.backends.postgresql_psycopg2': 'postgresql',
        'django.db.backends.mysql': 'mysql',
    }
    return (
        connection.settings_dict['USER'],
        db_types[connection.settings_dict['ENGINE']],
    )


def _create_test_db(self, verbosity, autoclobber):
    """
    Internal implementation - creates the test db tables.
    """

    test_database_name = self._get_test_db_name()

    db_user, db_type = get_db_user_and_type(self.connection)

    try:
        wf.create_db(db_user, test_database_name, db_type)
    except Exception as e:
        sys.stderr.write(
            "Got an error creating the test database: %s\n" % e)
        if not autoclobber:
            confirm = raw_input(
                "Type 'yes' if you would like to try deleting the test "
                "database '%s', or 'no' to cancel: " % test_database_name)
        if autoclobber or confirm == 'yes':
            try:
                if verbosity >= 1:
                    print("Destroying old test database '%s'..."
                        % self.connection.alias)
                wf.delete_db(test_database_name, db_type)
                wf.create_db(db_user, test_database_name, db_type)
            except Exception as e:
                sys.stderr.write(
                    "Got an error recreating the test database: %s\n" % e)
                sys.exit(2)
        else:
            print("Tests cancelled.")
            sys.exit(1)

    db.close_connection()
    return test_database_name


def _destroy_test_db(self, test_database_name, verbosity):
    """
    Internal implementation - remove the test db tables.
    """
    db_user, db_type = get_db_user_and_type(self.connection)
    wf.delete_db(test_database_name, db_type)
    self.connection.close()


class WebfactionTestRunner(DjangoTestSuiteRunner):
    def __init__(self, *args, **kwargs):
        # Monkey patch BaseDatabaseCreation with our own version
        from django.db.backends.creation import BaseDatabaseCreation
        BaseDatabaseCreation._create_test_db = _create_test_db
        BaseDatabaseCreation._destroy_test_db = _destroy_test_db

        return super(WebfactionTestRunner, self).__init__(*args, **kwargs)

File webfaction.py: this is a thin wrapper for Webfaction API, it need to be importable by both test_runner.py (in the remote server) and the fabfile.py (in the local machine)

文件 webfaction.py:这是 Webfaction API 的一个瘦包装器,它需要被 test_runner.py(在远程服务器中)和 fabfile.py(在本地机器中)都可以导入

import xmlrpclib

class Webfaction(object):
    def __init__(self, sesskey=None):
        self.connection = xmlrpclib.ServerProxy("https://api.webfaction.com/")
        self.sesskey = sesskey

    def login(self, username, password):
        self.sesskey, _ = self.connection.login(username, password)

    def create_db(self, db_user, db_name, db_type):
        """ Create a database owned by db_user """
        self.connection.create_db(self.sesskey, db_name, db_type, 'unused')

        # deletes the default user created by Webfaction API
        self.connection.make_user_owner_of_db(self.sesskey, db_user, db_name, db_type)
        self.connection.delete_db_user(self.sesskey, db_name, db_type)

    def delete_db(self, db_name, db_type):
        try:
            self.connection.delete_db_user(self.sesskey, db_name, db_type)
        except xmlrpclib.Fault as e:
            print 'ignored error:', e
        try:
            self.connection.delete_db(self.sesskey, db_name, db_type)
        except xmlrpclib.Fault as e:
            print 'ignored error:', e

File fabfile.py: A sample fabric script to generate session key, needed only if USE_SESSKEY=True

文件 fabfile.py:生成会话密钥的示例结构脚本,仅当 USE_SESSKEY=True 时才需要

from fabric.api import *
from fabric.operations import run, put
from webfaction import Webfaction
import io

env.hosts = ["[email protected]"]
env.password = "webfactionpassword"

def run_test():
    wf = Webfaction()
    wf.login(env.hosts[0].split('@')[0], env.password)
    sesskey_file = '~/sesskey'
    sesskey = wf.sesskey
    try:
        put(io.StringIO(unicode(sesskey)), sesskey_file, mode='0600')
        # put your test code here
        # e.g. run('DJANGO_SETTINGS_MODULE=settings /path/to/virtualenv/python /path/to/manage.py test --testrunner=test_runner.WebfactionTestRunner')
        raise Exception('write your test here')
    finally:
        run("rm -f %s" % sesskey_file)

回答by Justyna 'att' Ilczuk

The accepted answer didn't work for me. It's so outdated, that it didn't run on my legacy codebase with djano 1.5.

接受的答案对我不起作用。它已经过时了,无法在我使用 djano 1.5 的旧代码库上运行。

I wrote a blogpost entirely describing how I solved this issueby creating an alternative test runner and changing django settings to provide all the required config and to use new test runner.

我写了一篇博文,完全描述了我如何通过创建替代测试运行程序并更改 django 设置来提供所有必需的配置并使用新的测试运行程序来解决这个问题

回答by Francesco Frassinelli

Simple workaround: change TEST_DATABASE_PREFIXin django/db/backends/base/creation.pyas you like.

简单的解决方法:变化TEST_DATABASE_PREFIX中的django/db/backends/base/creation.py,只要你喜欢。

回答by mpen

Modify the following methods in django/db/backends/creation.py:

修改中的以下方法django/db/backends/creation.py

def _destroy_test_db(self, test_database_name, verbosity):
    "Internal implementation - remove the test db tables."

    # Remove the test database to clean up after
    # ourselves. Connect to the previous database (not the test database)
    # to do so, because it's not allowed to delete a database while being
    # connected to it.
    self._set_test_dict()
    cursor = self.connection.cursor()
    self.set_autocommit()
    time.sleep(1) # To avoid "database is being accessed by other users" errors.

    cursor.execute("""SELECT table_name FROM information_schema.tables WHERE table_schema='public'""")
    rows = cursor.fetchall()
    for row in rows:
        try:
            print "Dropping table '%s'" % row[0]
            cursor.execute('drop table %s cascade ' % row[0])
        except:
            print "Couldn't drop '%s'" % row[0] 

    #cursor.execute("DROP DATABASE %s" % self.connection.ops.quote_name(test_database_name))
    self.connection.close()

def _create_test_db(self, verbosity, autoclobber):
    "Internal implementation - creates the test db tables."

    suffix = self.sql_table_creation_suffix()

    if self.connection.settings_dict['TEST_NAME']:
        test_database_name = self.connection.settings_dict['TEST_NAME']
    else:
        test_database_name = TEST_DATABASE_PREFIX + self.connection.settings_dict['NAME']

    qn = self.connection.ops.quote_name

    # Create the test database and connect to it. We need to autocommit
    # if the database supports it because PostgreSQL doesn't allow
    # CREATE/DROP DATABASE statements within transactions.
    self._set_test_dict()
    cursor = self.connection.cursor()
    self.set_autocommit()

    return test_database_name

def _set_test_dict(self):
    if "TEST_NAME" in self.connection.settings_dict:
        self.connection.settings_dict["NAME"] = self.connection.settings_dict["TEST_NAME"]
    if "TEST_USER" in self.connection.settings_dict:
        self.connection.settings_dict['USER'] = self.connection.settings_dict["TEST_USER"]
    if "TEST_PASSWORD" in self.connection.settings_dict:
        self.connection.settings_dict['PASSWORD'] = self.connection.settings_dict["TEST_PASSWORD"]

Seems to work... just add the extra settings to your settings.pyif you need 'em.

似乎工作......settings.py如果你需要它们,只需将额外的设置添加到你的。