2010-07-23 15 views
18

estoy tratando de ejecutarElija la base de datos de prueba?

./manage.py test 

Pero me dice

Tienes un error al crear la base de datos de prueba: se deniega el permiso para crear la base de datos

Obviamente, no tiene permiso para crear la base de datos, pero estoy en un servidor compartido, por lo que no hay mucho que pueda hacer al respecto. Puedo crear una nueva base de datos a través del panel de control, pero no creo que haya ninguna manera en que pueda dejar que Django lo haga automáticamente.

Entonces, ¿no puedo crear la base de datos de prueba manualmente y decirle a Django que la vacíe siempre, en lugar de volver a crear todo?

+1

Su archivo de configuración tiene un nombre de base de datos y un nombre de usuario y contraseña para esa base de datos. ¿Son correctos el nombre de usuario y la contraseña? ¿Realmente puedes usarlos para conectarte? ¿Puedes usar ese nombre de usuario y contraseña para hacer una CREATE DATABASE en Postgres? –

+0

@S.Lott: Sí, el nombre de usuario y la contraseña son correctos. Puedo usarlos para conectar y recuperar filas. 'syncdb' funciona bien; puede crear tablas también Pero no, estoy bastante seguro de que el usuario * no puede * crear bases de datos en postgres: ese es el problema, y ​​es por eso que quiero crearlo manualmente. – mpen

+0

Acabo de encontrar una solución en los foros de webfaction: http://forum.webfaction.com/viewtopic.php?pid=16919 pero dejaré esta pregunta abierta por un momento para ver si alguien puede sugerir una solución menos hacky para Django 1.2. – mpen

Respuesta

13

Tuve un problema similar. Pero quería que Django omitiera la creación de una base de datos de prueba para una de mis instancias (no es un espejo difícil). Siguiendo la sugerencia de Mark, he creado un corredor de prueba de encargo, de la siguiente manera

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 

Entonces creé una entrada dict extra en una de mis bases de datos de entradas dentro settings.py, 'BYPASS_CREATION':'yes',

Por último, he configurado un nuevo TestRunner con

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

¿Qué pasa con el método teardown_databases (https://docs.djangoproject.com/en/dev/topics/testing/advanced/#django.test.runner.DiscoverRunner.teardown_databases)? Si no desea crear una base de datos, ¿este método garantiza que las bases de datos omitidas no se descartarán? ¿Es mejor agregar el método: def teardown_databases (self, old_config, ** kwargs): pass_or_whatever_logic_is_suitable –

+1

Tema viejo, pero para cualquiera que busque una respuesta ... inspirado por esta publicación, aquí hay un corredor de prueba para django 1.8 que agrega un configuración, lo que le permite especificar el DB para usar para la prueba. https://djangosnippets.org/snippets/10544/ – powderflask

0

Modificar los métodos siguientes en 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"] 

parece que funciona ... sólo tiene que añadir los ajustes adicionales a su settings.py si los necesitas.

+0

pg_catalog.pg_tables muestra tablas – rfusca

+0

Si no tiene consultas personalizadas, no será una mala idea ejecutar las pruebas en una base de datos SQLite. Eso * podría * darle un trabajo (limitado). –

+0

@Manoj: pensé en eso, pero no es una muy buena prueba. PostgreSQL y SQLite son un poco diferentes en la forma en que manejan secuencias/restricciones/fk ... es muy posible que yo pueda tener un error w/postgre pero mis pruebas están pasando porque estoy usando sqlite. ¡No está bien! – mpen

2

Puede usar django-nose como su TEST_RUNNER. Una vez instalado, si pasa la siguiente variable de entorno, no eliminará y volverá a crear la base de datos (créelo manualmente usted mismo primero).

REUSE_DB=1 ./manage.py test 

También puede añadir lo siguiente a settings.py por lo que no tiene que escribir REUSE_DB = 1 cada vez que se desea ejecutar pruebas:

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

Nota: Esto también deje todas sus tablas en las bases de datos, lo que significa que la configuración de prueba será un poco más rápida, pero tendrá que actualizar manualmente las tablas (o eliminar y volver a crear la base de datos usted mismo) cuando cambie sus modelos.

8

Sugiero usar sqlite3 para fines de prueba mientras sigo usando mysql/postgres/etc para la producción.

Esto se puede lograr mediante la colocación de esta en el archivo de configuración:

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

ver Running django tests with sqlite

un archivo de base de datos SQLite temporal será creado en su Django principal del proyecto, que va a tener acceso de escritura. La otra ventaja es que sqlite3 es mucho más rápido para probar. Sin embargo, puede encontrarse con problemas si está utilizando cualquier sql en bruto específico de mysql/postgres (que debe evitar de todos modos).

+7

Crea mucho más rápido, pero realmente se topa con problemas como sin errores cuando los datos exceden max_length. Eso me pica muy a menudo. Todas las pruebas pasarían pero volviendo a postgresql fracasaría miserablemente. También sugeriría usar ''default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ': memory:'}' – Danosaure

+1

¡Pero sqlite3 y postgres actúan de manera muy diferente! Verá errores solo de producción que no puede probar durante el desarrollo. –

2

mi variante de base de datos de reutilización:

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

he añadido esto a los comentarios anteriores pero quedó un poco perdido - los recientes cambios en webfaction hacen esto mucho más fácil. Ahora puede crear new private database instances.

Siga las instrucciones allí, y al crear un nuevo usuario, asegúrese de darles permiso para ALTER USER new_username CREATEDB;.

Probablemente también deba cambiar las configuraciones cron predeterminadas para que no intenten comprobar si esta base de datos está activa y las ejecuciones con tanta frecuencia.

+0

También agregar las líneas que permiten la autenticación de usuario para la base de datos de prueba en pg_hba.conf también podría ayudar - 'local test_projdb new_username md5' y luego reiniciar postgres podría ayudar con los errores de permiso en postgres. –

1

El siguiente es un corredor de suite de prueba django para crear una base de datos usando Webfaction XML-RPC API. Tenga en cuenta que la configuración de la base de datos con la API puede demorar hasta un minuto y que la secuencia de comandos puede parecer bloqueada momentáneamente, solo espere un momento.

NOTA: existe un riesgo de seguridad de tener una contraseña para el panel de control en el servidor webfaction, porque alguien que infringe su servidor web SSH podría hacerse cargo de su cuenta Webfaction. Si eso es una preocupación, configure USE_SESSKEY en True y use la secuencia de comandos fabric debajo de esta secuencia de comandos para pasar una identificación de sesión al servidor. La clave de sesión expires in 1 hour de la última llamada API.

test_runner.py del archivo: en el servidor, debe configurar la prueba ./manage.py utilizar 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 Archivo: esta es una envoltura delgada de API Webfaction, se necesita se pueden importar por tanto test_runner.py (en el servidor remoto) y el fabfile.py (en la máquina local)

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 archivo: Una secuencia de comandos muestra de tela para generar la clave de sesión, es necesario sólo si USE_SESSKEY = Verdadero

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

La respuesta aceptada no funcionó para mí. Está tan desactualizado que no se ejecutó en mi base de código heredada con djano 1.5.

Escribí un blogpost entirely describing how I solved this issue creando un corredor de prueba alternativo y cambiando las configuraciones de django para proporcionar todas las configuraciones requeridas y para usar un nuevo corredor de prueba.

0

Solución simple: cambie TEST_DATABASE_PREFIX en django/db/backends/base/creation.py como desee.

Cuestiones relacionadas