2010-07-23 6 views
18

Я пытаюсь запуститьВыберите тестовую базу данных?

./manage.py test 

Но он говорит мне

Есть ошибка при создании тестовой базы данных: разрешение отказано создать базу данных

Очевидно, что это не имеет разрешение на создание базы данных, но я нахожусь на общем сервере, поэтому я ничего не могу с этим поделать. Я могу создать новую базу данных через панель управления, но я не думаю, что я могу позволить Django сделать это автоматически.

Итак, не могу ли я создать тестовую базу данных вручную и вместо этого рассказывать Django о том, чтобы очищать ее каждый раз, а не воссоздавать все это?

+1

В вашем файле настроек есть имя базы данных и имя пользователя и пароль для этой базы данных. Правильны ли имя пользователя и пароль? Можете ли вы действительно использовать их для подключения? Можете ли вы использовать это имя пользователя и пароль для создания CREATE DATABASE в Postgres? –

+0

@S.Lott: Да, имя пользователя и пароль верны. Я могу использовать их для подключения и получения строк. 'syncdb' отлично работает; он также может создавать таблицы. Но нет, я уверен, что пользователь * не может * создавать базы данных в postgres - вот в чем проблема, и именно поэтому я хочу создать его вручную. – mpen

+0

Просто нашел sol'n на форумах webfaction: http://forum.webfaction.com/viewtopic.php?pid=16919, но я оставлю этот вопрос открытым, чтобы узнать, может ли кто-нибудь предложить менее хакерское решение для Django 1.2. – mpen

ответ

13

У меня была аналогичная проблема. Но я хотел, чтобы 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',

Наконец, я установил новый TestRunner с

TEST_RUNNER = 'auth.data.runner.ByPassableDBDjangoTestSuiteRunner' 
+1

Как насчет метода teardown_databases (https://docs.djangoproject.com/en/dev/topics/testing/advanced/#django.test.runner.DiscoverRunner.teardown_databases)? Если вы не хотите создавать базу данных, этот метод гарантирует, что обходные базы данных не будут удалены? Это лучше добавить метод: def teardown_databases (self, old_config, ** kwargs): pass_or_whatever_logic_is_suitable –

+1

Старый поток, но для тех, кто ищет ответ ... вдохновленный этим сообщением, вот тестовый бегун для django 1.8, который добавляет , позволяя вам указать БД для тестирования. https://djangosnippets.org/snippets/10544/ – powderflask

0

Измените следующие методы в 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, если вам нужно их.

+0

pg_catalog.pg_tables показывает таблицы – rfusca

+0

Если у вас нет пользовательских запросов, будет неплохо выполнять тесты с базой данных SQLite. Это * может * дать вам (ограниченную) работу. –

+0

@Manoj: Я думал об этом, но тогда это не очень хороший тест. PostgreSQL и SQLite немного отличаются в том, как они обрабатывают последовательности/ограничения/fk ... очень возможно, что я мог бы иметь ошибку с postgre, но мои тесты проходят, потому что я использую sqlite. Нехорошо! – mpen

2

Вы можете использовать django-nose как ваш TEST_RUNNER. После установки, если вы передадите следующую переменную среды, она не удалит и не заново создаст базу данных (сначала создайте ее самостоятельно).

REUSE_DB=1 ./manage.py test 

Вы также можете добавить следующие строки в settings.py так что вам не придется писать REUSE_DB = 1 каждый раз, когда вы хотите запустить тесты:

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

Примечание: это будет также оставьте все свои таблицы в базах данных, что означает, что установка теста будет немного быстрее, но вам придется вручную обновлять таблицы (или удалять и воссоздавать базу данных самостоятельно) при изменении своих моделей.

8

Я бы предложил использовать sqlite3 для тестирования, продолжая использовать mysql/postgres/etc для производства.

Это может быть достигнуто путем размещения этого в файле настроек:

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

см Running django tests with sqlite

временный SQLite файл базы данных будет создан в вашем Джанго домашней странице проекта, который вы будете иметь доступ на запись. Другим преимуществом является то, что sqlite3 намного быстрее для тестирования. Тем не менее, вы можете столкнуться с проблемами, если используете какой-либо сырой sql-файл mysql/postgres (который вы все равно должны избегать).

+7

Он работает намного быстрее, но вы действительно сталкиваетесь с такими проблемами, как ошибки, когда данные превышают max_length. Это слишком часто укусит меня. Все тесты пройдут, но, вернувшись к postgresql, он будет терпеть неудачу. Также предложили бы использовать '' default ': {' ENGINE ':' django.db.backends.sqlite3 ',' NAME ':': memory: '} ' – Danosaure

+1

Но sqlite3 и postgres действуют совсем по-другому! Вы увидите ошибки, связанные с производством, которые вы не можете тестировать в процессе разработки. –

2

мой вариант повторного использования базы данных:

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) 
3

Я добавил это к комментариям выше, но это было бы потерял - недавние изменения в webfaction сделать это гораздо проще. Теперь вы можете создать new private database instances.

Следуйте инструкциям, а при создании нового пользователя обязательно дайте им разрешение на ALTER USER new_username CREATEDB;.

Возможно, вы также должны изменить настройки по умолчанию cron, чтобы они не пытались проверить, была ли эта база данных запущена и работает так же часто.

+0

Также добавление строк, позволяющих пользователю auth для тестовой базы данных в pg_hba.conf, также может помочь - 'local test_projdb new_username md5', а затем перезапустить postgres может помочь с ошибками разрешений в postgres. –

1

Ниже приведен пример бегуна набора тестов django для создания базы данных с использованием Webfaction XML-RPC API. Обратите внимание: настройка базы данных с использованием API может занять до минуты, и скрипт может оказаться застрявшим на мгновение, просто подождите немного.

ПРИМЕЧАНИЕ. Существует риск безопасности пароля панели управления на сервере webfaction, поскольку кто-то, кто нарушает работу вашего веб-сервера, может использовать вашу учетную запись Webfaction. Если это вызывает беспокойство, установите USE_SESSKEY в значение Истина и используйте сценарий с файлом ниже этого сценария, чтобы передать идентификатор сеанса на сервер. Ключ сеанса expires in 1 hour из последнего вызова 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 = ["[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) 
1

Принятый ответ не сработал для меня. Это так устарело, что он не запускался на моей старой кодовой базе с помощью jjano 1.5.

Я написал blogpost entirely describing how I solved this issue, создав альтернативный тестовый бегун и изменив настройки django, чтобы предоставить всю необходимую конфигурацию и использовать новый тестовый бегун.

0

Простой способ обхода: изменить TEST_DATABASE_PREFIX в django/db/backends/base/creation.py как вам нравится.

Смежные вопросы