From 89fb7aa31044bd3d63302b82ab31e893fa0a43a6 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 9 Jun 2010 15:31:19 +0000 Subject: [PATCH] [soc2010/query-refactor] Introced NativeAutoField, also started with some basic MongoDB tests (really just very basic ORM tests), and introduced various APIs into the mongodb backend that were necessary for running unittests. git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/query-refactor@13338 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/mongodb/base.py | 19 +++++++++-- django/contrib/mongodb/creation.py | 1 + django/contrib/mongodb/introspection.py | 6 ++++ django/core/management/commands/flush.py | 9 ++++-- django/db/models/fields/__init__.py | 39 +++++++++++++++-------- tests/regressiontests/mongodb/__init__.py | 0 tests/regressiontests/mongodb/models.py | 10 ++++++ tests/regressiontests/mongodb/tests.py | 11 +++++++ 8 files changed, 75 insertions(+), 20 deletions(-) create mode 100644 django/contrib/mongodb/introspection.py create mode 100644 tests/regressiontests/mongodb/__init__.py create mode 100644 tests/regressiontests/mongodb/models.py create mode 100644 tests/regressiontests/mongodb/tests.py diff --git a/django/contrib/mongodb/base.py b/django/contrib/mongodb/base.py index 825a39787d..e92c6db25f 100644 --- a/django/contrib/mongodb/base.py +++ b/django/contrib/mongodb/base.py @@ -1,8 +1,9 @@ from pymongo import Connection -from django.db.backends import BaseDatabaseWrapper +from django.db.backends import BaseDatabaseWrapper, BaseDatabaseValidation from django.db.backends.signals import connection_created from django.contrib.mongodb.creation import DatabaseCreation +from django.contrib.mongodb.introspection import DatabaseIntrospection from django.utils.importlib import import_module @@ -12,9 +13,11 @@ class DatabaseFeatures(object): class DatabaseOperations(object): compiler_module = "django.contrib.mongodb.compiler" + sql_ddl = False - def __init__(self, *args, **kwargs): + def __init__(self, connection): self._cache = {} + self.connection = connection def max_name_length(self): return 254 @@ -34,13 +37,23 @@ class DatabaseOperations(object): import_module(self.compiler_module), compiler_name ) return self._cache[compiler_name] + + def flush(self, only_django=False): + if only_django: + tables = self.connection.introspection.django_table_names(only_existing=True) + else: + tables = self.connection.introspection.table_names() + for table in tables: + self.connection.db.drop_collection(table) class DatabaseWrapper(BaseDatabaseWrapper): def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.features = DatabaseFeatures() - self.ops = DatabaseOperations() + self.ops = DatabaseOperations(self) self.creation = DatabaseCreation(self) + self.validation = BaseDatabaseValidation(self) + self.introspection = DatabaseIntrospection(self) self._connection = None @property diff --git a/django/contrib/mongodb/creation.py b/django/contrib/mongodb/creation.py index 9102d7f997..ed4a26316b 100644 --- a/django/contrib/mongodb/creation.py +++ b/django/contrib/mongodb/creation.py @@ -11,6 +11,7 @@ class DatabaseCreation(object): else: test_database_name = TEST_DATABASE_PREFIX + self.connection.settings_dict['NAME'] self.connection.settings_dict["NAME"] = test_database_name + self.connection.settings_dict["SUPPORTS_TRANSACTIONS"] = False return test_database_name def destroy_test_db(self, old_database_name, verbosity=1): diff --git a/django/contrib/mongodb/introspection.py b/django/contrib/mongodb/introspection.py new file mode 100644 index 0000000000..eae5da668a --- /dev/null +++ b/django/contrib/mongodb/introspection.py @@ -0,0 +1,6 @@ +from django.db.backends import BaseDatabaseIntrospection + + +class DatabaseIntrospection(BaseDatabaseIntrospection): + def table_names(self): + return self.connection.db.collection_names() diff --git a/django/core/management/commands/flush.py b/django/core/management/commands/flush.py index 6836fe35ca..94c12490ce 100644 --- a/django/core/management/commands/flush.py +++ b/django/core/management/commands/flush.py @@ -35,9 +35,7 @@ class Command(NoArgsCommand): import_module('.management', app_name) except ImportError: pass - - sql_list = sql_flush(self.style, connection, only_django=True) - + if interactive: confirm = raw_input("""You have requested a flush of the database. This will IRREVERSIBLY DESTROY all data currently in the %r database, @@ -49,6 +47,11 @@ Are you sure you want to do this? confirm = 'yes' if confirm == 'yes': + # TODO: HACK, make this more OO. + if not getattr(connection.ops, "sql_ddl", True): + connection.ops.flush(only_django=True) + return + sql_list = sql_flush(self.style, connection, only_django=True) try: cursor = connection.cursor() for sql in sql_list: diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 65b60a0173..0c9b603609 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -447,17 +447,32 @@ class Field(object): "Returns the value of this field in the given model instance." return getattr(obj, self.attname) -class AutoField(Field): +class BaseAutoField(Field): + empty_strings_allowed = False + + def __init__(self, *args, **kwargs): + assert kwargs.get('primary_key'), "%ss must have primary_key=True." % self.__class__.__name__ + kwargs['blank'] = True + super(BaseAutoField, self).__init__(*args, **kwargs) + + def contribute_to_class(self, cls, name): + assert not cls._meta.has_auto_field, "A model can't have more than one AutoField." + super(BaseAutoField, self).contribute_to_class(cls, name) + cls._meta.has_auto_field = True + cls._meta.auto_field = self + + def get_internal_type(self): + return "AutoField" + + def formfield(self, **kwargs): + return None + +class AutoField(BaseAutoField): description = _("Integer") - empty_strings_allowed = False default_error_messages = { 'invalid': _(u'This value must be an integer.'), } - def __init__(self, *args, **kwargs): - assert kwargs.get('primary_key', False) is True, "%ss must have primary_key=True." % self.__class__.__name__ - kwargs['blank'] = True - Field.__init__(self, *args, **kwargs) def to_python(self, value): if value is None: @@ -475,14 +490,10 @@ class AutoField(Field): return None return int(value) - def contribute_to_class(self, cls, name): - assert not cls._meta.has_auto_field, "A model can't have more than one AutoField." - super(AutoField, self).contribute_to_class(cls, name) - cls._meta.has_auto_field = True - cls._meta.auto_field = self - - def formfield(self, **kwargs): - return None +class NativeAutoField(BaseAutoField): + # TODO: eventually delegate validation and other such things to the + # backends. For now it's enough that this class exists. + pass class BooleanField(Field): empty_strings_allowed = False diff --git a/tests/regressiontests/mongodb/__init__.py b/tests/regressiontests/mongodb/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regressiontests/mongodb/models.py b/tests/regressiontests/mongodb/models.py new file mode 100644 index 0000000000..e07e8f191c --- /dev/null +++ b/tests/regressiontests/mongodb/models.py @@ -0,0 +1,10 @@ +from django.db import models + + +class Artist(models.Model): + id = models.NativeAutoField(primary_key=True) + name = models.CharField(max_length=255) + good = models.BooleanField() + + def __unicode__(self): + return self.name diff --git a/tests/regressiontests/mongodb/tests.py b/tests/regressiontests/mongodb/tests.py new file mode 100644 index 0000000000..cbf0dcbcd8 --- /dev/null +++ b/tests/regressiontests/mongodb/tests.py @@ -0,0 +1,11 @@ +from django.test import TestCase + +from models import Artist + + +class MongoTestCase(TestCase): + def test_create(self): + b = Artist.objects.create(name="Bruce Springsteen", good=True) + self.assertTrue(b.pk is not None) + self.assertEqual(b.name, "Bruce Springsteen") + self.assertTrue(b.good)