Выбрать тестовую базу данных?
Я пытаюсь бежать
./manage.py test
Но это говорит мне
Очевидно, что у него нет разрешения на создание базы данных, но я нахожусь на общем сервере, поэтому я мало что могу с этим поделать. Я могу создать новую базу данных через Панель управления, но я не думаю, что есть какой-либо способ, которым я могу позволить Django сделать это автоматически.Получена ошибка при создании тестовой базы данных: отказано в разрешении на создание базы данных
Итак, не могу ли я создать тестовую базу данных вручную и вместо того, чтобы сказать Джанго смывать его каждый раз, вместо того, чтобы воссоздавать все это?
10 ответов:
У меня была похожая проблема. Но я хотел, чтобы 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
Затем я создал дополнительную запись dict в одной из моих записей базы данных внутри settings.py,
'BYPASS_CREATION':'yes',
Наконец, я настроил новый Тестраннер с
TEST_RUNNER = 'auth.data.runner.ByPassableDBDjangoTestSuiteRunner'
Я бы предложил использовать sqlite3 для тестирования, продолжая использовать mysql / postgres/etc для производства.
Это можно сделать, поместив это в файл настроек:
if 'test' in sys.argv: DATABASES['default'] = {'ENGINE': 'django.db.backends.sqlite3'}
Смотрите запуск тестов django с помощью sqlite
Временный файл базы данных sqlite будет создан в вашем домашнем проекте django, к которому у вас будет доступ на запись. Другое преимущество заключается в том, что sqlite3 гораздо быстрее тестируется. Однако вы можете столкнуться с проблемами, если используете какие-либо mysql / postgres специфический raw sql (которого вы должны стараться избегать в любом случае).
Я думаю, что лучшим решением может быть определение вашего собственного тестового бегуна.
Я добавил Это в комментарии выше, но это было потеряно-последние изменения в webfaction делают это намного проще. Теперь вы можете создаватьновые частные экземпляры баз данных .
Следуйте инструкциям там, и при создании нового пользователя обязательно дайте ему разрешение на
ALTER USER new_username CREATEDB;
.Вероятно, вам также следует изменить настройки cron по умолчанию, чтобы они не пытались проверить, работает ли эта база данных так же часто.
Вы можете использовать django-nose в качестве TEST_RUNNER. После установки, если вы передадите следующую переменную среды, она не будет удалять и повторно создавать базу данных (сначала создайте ее вручную самостоятельно).
REUSE_DB=1 ./manage.py test
Вы также можете добавить следующее: settings.py таким образом, вам не нужно писать REUSE_DB=1 каждый раз, когда вы хотите запустить тесты:
os.environ['REUSE_DB'] = "1"
Примечание: это также оставит все ваши таблицы в базах данных, что означает, что установка теста будет немного быстрее, но вы вам придется вручную обновить таблицы (или удалить и заново создать базу данных самостоятельно) при изменении ваших моделей.
Мой вариант повторного использования базы данных:
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)
Ниже приведен набор тестов django runner для создания базы данных с использованиемWebfaction XML-RPC API . Обратите внимание, что настройка базы данных с помощью API может занять до минуты, и сценарий может показаться застрявшим на мгновение, просто подождите некоторое время.
Примечание: существует риск безопасности, связанный с наличием пароля панели управления на сервере webfaction, поскольку кто-то, взломав ваш SSH-сервер, может завладеть вашей учетной записью Webfaction. Если это вызывает беспокойство, установите USE_SESSKEY в True и используйте сценарий структуры ниже этого сценария, чтобы передать идентификатор сеанса серверу. Ключ сеанса истекает через 1 час от последнего вызова API.
Файл test_runner.py: в сервере, который нужно настроить ./manage.py тест на использование 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)
Файл 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
Файл fabfile.py: а пример сценария структуры для генерации ключа сеанса, необходимого только в том случае, если USE_SESSKEY=True
from fabric.api import * from fabric.operations import run, put from webfaction import Webfaction import io env.hosts = ["webfactionusername@webfactionusername.webfactional.com"] 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)
Принятый ответ не сработал для меня. Он настолько устарел, что не работал на моей устаревшей кодовой базе с djano 1.5.
Я написал пост в блоге , полностью описывающий, как я решил эту проблему, создав альтернативный тестовый раннер и изменив настройки django, чтобы обеспечить всю необходимую конфигурацию и использовать новый тестовый раннер.
Измените следующие методы в
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"]
Кажется, работает... просто добавьте дополнительные настройки в свой
settings.py
, если они вам нужны.