tests - Las pruebas de Django se quejan de que faltan tablas
running tests django (3)
Dado que está usando una base de datos heredada, probablemente no esté agregando el nombre de la aplicación a INSTALLED_APPS. Si una aplicación no está incluida en INSTALLED_APPS, las tablas para los modelos de las aplicaciones no se crearán en syncdb. Esto funciona para usted en producción ya que ya tiene una tabla, pero no en un entorno de prueba.
Puede adoptar cualquiera de los siguientes:
La forma de supermonkeypatch: elimine app_name de la clase Meta del Cliente, coloque el modelo en un archivo models.py dentro de un mcif con el nombre del módulo python, y agregue mcif a INSTALLED_APPS, solo para realizar pruebas
La mejor manera: extienda DjangoTestSuiteRunner y anule setup_test_environment para llamar a super y luego cree su tabla heredada manualmente en la base de datos de prueba.
La mejor manera: ponga su modelo en el módulo de aplicación debidamente nombrado. Elimine app_name del modelo Meta pero agregue managed = False docs . Incluir el nombre de la aplicación en INSTALLED_APPS. Ahora django no creará una tabla para ese modelo. Luego use este buen fragmento de código que la gente del grupo Caktus ha compilado para ejecutar sus pruebas.
¡Aclamaciones!
Editar - Cómo usar el DjangoTestSuiteRunner anulado
Necesitarás al menos Django 1.2 para esto.
Copia el código desde aquí . Ponlo en utils.py dentro de la aplicación mcif.
Agrega / edita lo siguiente en settings.py:
TEST_RUNNER = ''mcif.utils.ManagedModelTestRunner''
Ahora, cuando ejecute las pruebas, todas las tablas no administradas se tratarán como tablas administradas solo durante la duración de la prueba. Así que las tablas se crearán antes de ejecutar las pruebas.
Observe esta parte del código, que es donde ocurre la magia.
self.unmanaged_models = [m for m in get_models() if not m._meta.managed]
for m in self.unmanaged_models:
m._meta.managed = True
2ª Edición: Posibles Gotchas
Asegúrese de lo siguiente:
- El usuario de la base de datos tiene privilegios para crear bases de datos y no solo tablas porque django intentará crear una base de datos de prueba
- Los casos de prueba extienden django.test.TransactionTestCase, ya que tiene un comportamiento transaccional
- Si no se aplica ninguna de las situaciones anteriores, coloque un pdb en el entorno_prueba_testRunner de ManagedModelTest para asegurarse de que se está alcanzando el código. Porque si se alcanza ese código, la tabla debería crearse
Tercera edición: depuración Dentro de mcif.utils.ManagedModelTestRunner, reemplace la función setup_test_environment con lo siguiente y déjeme saber si la salida de su prueba cambia:
def setup_test_environment(self, *args, **kwargs):
print "Loading ManagedModelTestRunner"
from django.db.models.loading import get_models
self.unmanaged_models = [m for m in get_models()
if not m._meta.managed]
for m in self.unmanaged_models:
print "Modifying model %s to be managed for testing" % m
m._meta.managed = True
super(ManagedModelTestRunner, self).setup_test_environment(*args, **kwargs)
Cuando ejecuto mi prueba que trata con mi modelo de Customer
, recibo el siguiente error:
DatabaseError: (1146, "Table ''test_mcif2.customer'' doesn''t exist")
No estoy del todo sorprendido porque tengo mi proyecto Django conectado a una base de datos "heredada". Ya que mis mesas no fueron creadas "a la manera de Django", no es sorprendente que Django no pueda hablar con ellas sin un poco de trabajo. Aquí está mi modelo:
from django.db import models
from django.db import connection, transaction
from mcif.models.mcif_model import McifModel
class Customer(McifModel):
class Meta:
db_table = u''customer''
app_name = ''mcif''
id = models.BigIntegerField(primary_key=True)
customer_number = models.CharField(unique=True, max_length=255)
social_security_number = models.CharField(unique=True, max_length=33)
name = models.CharField(unique=True, max_length=255)
phone = models.CharField(unique=True, max_length=255)
deceased = models.IntegerField(unique=True, null=True, blank=True)
do_not_mail = models.IntegerField(null=True, blank=True)
created_at = models.DateTimeField()
updated_at = models.DateTimeField()
def distinguishing_column_names(self):
return [''name'', ''customer_number'', ''social_security_number'', ''phone'']
¿Alguna idea de por qué exactamente esto no está funcionando?
Edición: Aquí está McifModel
:
from django.db import models
from django.db import connection, transaction
class McifModel(models.Model):
class Meta:
abstract = True
def upsert(self):
cursor = connection.cursor()
cursor.execute(self.upsert_sql())
transaction.commit_unless_managed()
return self
def value_list(self):
return '',''.join(map(lambda column_name: "''{c}''".format(c=getattr(self, column_name)), self.distinguishing_column_names()))
def upsert_sql(self):
column_names = '',''.join(self.distinguishing_column_names())
return "INSERT IGNORE INTO {t} ({c}) VALUES ({v})".format(t=self._meta.db_table, c=column_names, v=self.value_list())
@classmethod
def save_from_row(cls, row):
object = cls()
map(lambda column_name: setattr(object, column_name, row.value(object._meta.db_table, column_name)), object.distinguishing_column_names())
return object.upsert()
Edición: Tomé el consejo de tarequeh y puse el contenido del archivo Caktus en mcif/utils.py
. También configuré TEST_RUNNER = ''mcif.utils.ManagedModelTestRunner''
. Si voy a la consola puedo verificar que el Customer
no está administrado:
>>> [m for m in get_models() if not m._meta.managed]
[<class ''mcif.models.customer.Customer''>]
Sin embargo, mi prueba todavía se queja de que la tabla no existe. ¿Qué me estoy perdiendo?
Aquí está mi settings.py:
# Django settings for mcifdjango project.
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
(''Jason Swett'', ''[email protected]''),
)
MANAGERS = ADMINS
DATABASES = {
''default'': {
''ENGINE'': ''django.db.backends.mysql'', # Add ''postgresql_psycopg2'', ''postgresql'', ''mysql'', ''sqlite3'' or ''oracle''.
''NAME'': ''xxxxx'', # Or path to database file if using sqlite3.
''USER'': ''xxxxx'', # Not used with sqlite3.
''PASSWORD'': ''xxxxx'', # Not used with sqlite3.
''HOST'': '''', # Set to empty string for localhost. Not used with sqlite3.
''PORT'': '''', # Set to empty string for default. Not used with sqlite3.
}
}
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# On Unix systems, a value of None will cause Django to use the same
# timezone as the operating system.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = ''America/Chicago''
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = ''en-us''
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
# If you set this to False, Django will not format dates, numbers and
# calendars according to the current locale
USE_L10N = True
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = ''''
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL = ''''
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = ''/media/''
# Make this unique, and don''t share it with anybody.
SECRET_KEY = ''#7+qm%hqfe+z8ul5@x_i&sqmu!n=4sa0&i0_#)m99*w$fbk3%#''
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
''django.template.loaders.filesystem.Loader'',
''django.template.loaders.app_directories.Loader'',
# ''django.template.loaders.eggs.Loader'',
)
MIDDLEWARE_CLASSES = (
''django.middleware.common.CommonMiddleware'',
''django.contrib.sessions.middleware.SessionMiddleware'',
''django.middleware.csrf.CsrfViewMiddleware'',
''django.contrib.auth.middleware.AuthenticationMiddleware'',
''django.contrib.messages.middleware.MessageMiddleware'',
)
ROOT_URLCONF = ''mcifdjango.urls''
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don''t forget to use absolute paths, not relative paths.
)
INSTALLED_APPS = (
''django.contrib.auth'',
''django.contrib.contenttypes'',
''django.contrib.sessions'',
''django.contrib.sites'',
''django.contrib.messages'',
''django.contrib.admin'',
''django_extensions'',
''mcif'',
# Uncomment the next line to enable the admin:
# ''django.contrib.admin'',
# Uncomment the next line to enable admin documentation:
# ''django.contrib.admindocs'',
)
TEST_RUNNER = ''mcif.utils.ManagedModelTestRunner''
import os
ROOTDIR = os.path.abspath(os.path.dirname(__file__))
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don''t forget to use absolute paths, not relative paths.
ROOTDIR + ''/mcif/templates'',
)
Edición 2:
Aquí está mi clase de Customer
ahora:
from django.db import models
from django.db import connection, transaction
from mcif.models.mcif_model import McifModel
class Customer(McifModel):
class Meta:
db_table = u''customer''
managed = False
id = models.BigIntegerField(primary_key=True)
customer_number = models.CharField(unique=True, max_length=255)
social_security_number = models.CharField(unique=True, max_length=33)
name = models.CharField(unique=True, max_length=255)
phone = models.CharField(unique=True, max_length=255)
deceased = models.IntegerField(unique=True, null=True, blank=True)
do_not_mail = models.IntegerField(null=True, blank=True)
created_at = models.DateTimeField()
updated_at = models.DateTimeField()
def distinguishing_column_names(self):
return [''name'', ''customer_number'', ''social_security_number'', ''phone'']
Esto es lo que obtengo cuando ejecuto la prueba:
$ ./manage.py test mcif.CustomerUpsertTest
Creating test database ''default''...
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_user_permissions
Creating table auth_user_groups
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table django_admin_log
Installing index for auth.Permission model
Installing index for auth.Group_permissions model
Installing index for auth.User_user_permissions model
Installing index for auth.User_groups model
Installing index for auth.Message model
Installing index for admin.LogEntry model
No fixtures found.
E
======================================================================
ERROR: test_upsert (mcif.tests.customer_upsert_test.CustomerUpsertTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/jason/projects/mcifdjango/mcif/tests/customer_upsert_test.py", line 9, in test_upsert
customer.upsert()
File "/home/jason/projects/mcifdjango/mcif/models/mcif_model.py", line 11, in upsert
cursor.execute(self.upsert_sql())
File "/usr/lib/pymodules/python2.6/django/db/backends/mysql/base.py", line 86, in execute
return self.cursor.execute(query, args)
File "/usr/lib/pymodules/python2.6/MySQLdb/cursors.py", line 166, in execute
self.errorhandler(self, exc, value)
File "/usr/lib/pymodules/python2.6/MySQLdb/connections.py", line 35, in defaulterrorhandler
raise errorclass, errorvalue
DatabaseError: (1146, "Table ''test_mcif_django.customer'' doesn''t exist")
----------------------------------------------------------------------
Ran 1 test in 3.724s
FAILED (errors=1)
Destroying test database ''default''...
Las soluciones presentadas por tarequeh funcionaron para mí después de anular DATABASE_ROUTERS.
Estoy utilizando enrutadores para evitar escrituras en la base de datos heredada. Para solucionar esto, creé un archivo test_settings con el siguiente contenido:
from settings import *
DEBUG = True
TEST_RUNNER = ''legacy.utils.ManagedModelTestRunner''
DATABASE_ROUTERS = []
DATABASES = {
''default'': {
''ENGINE'': ''django.db.backends.sqlite3'',
''NAME'': os.path.join(HERE, ''test.db''),
},
}
Luego, cuando se ejecutan pruebas:
python manage.py test [app_name] --settings=test_settings
No hay suficiente información arriba para responder tu primera pregunta. Sin embargo, una vez que haya resuelto el problema, probablemente querrá instalar django-extensions por el siguiente motivo: tiene un comando sqldiff
increíblemente útil que le informará si existe una discrepancia entre la base de datos heredada y su modelo de aplicación.