diff --git a/.travis.yml b/.travis.yml index 6d56a03..04376f9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,9 @@ install: before_script: - bash ./travis_postgis_setup.sh -script: python example/manage.py test conduit +script: + - python example/manage.py test conduit + - python example/manage.py test --settings='geoexample.settings' matrix: exclude: diff --git a/conduit/api/base.py b/conduit/api/base.py index fbf8abd..d910b4b 100644 --- a/conduit/api/base.py +++ b/conduit/api/base.py @@ -4,6 +4,8 @@ from django.core.urlresolvers import reverse, NoReverseMatch from django.db.models.fields import FieldDoesNotExist from django.db import models +from django.contrib.gis.db import models as geomodels +from django.contrib.gis.geos import fromstr from django.conf.urls import url from decimal import Decimal from dateutil import parser @@ -465,6 +467,9 @@ def _from_basic_type(self, field, data): if isinstance(field, models.DecimalField): return Decimal(data) + if isinstance(field, geomodels.GeometryField): + return fromstr(data) + if isinstance(field, models.ForeignKey): return data @@ -836,6 +841,9 @@ def _to_basic_type(self, obj, field): if isinstance(field, models.DecimalField): return field.value_to_string(obj) + if isinstance(field, geomodels.GeometryField): + return field.value_to_string(obj) + if isinstance(field, models.ForeignKey): return field.value_from_object(obj) diff --git a/conduit/test/non_geo_testrunner.py b/conduit/test/non_geo_testrunner.py deleted file mode 100644 index 198af1a..0000000 --- a/conduit/test/non_geo_testrunner.py +++ /dev/null @@ -1,103 +0,0 @@ -try: - from django.test.runner import DiscoverRunner as BaseRunner -except ImportError: - # Django < 1.6 fallback - from django.test.simple import DjangoTestSuiteRunner as BaseRunner -from django.conf import settings -from django.utils.unittest import TestSuite -from django.test.runner import dependency_ordered - -class NonGeoTestRunner( BaseRunner ): - """ - The default test runner for Django - will attempt to create all databases in settings.DATABASES. - For local development and quick "spot-checks" with the unit tests - the installation, dependencies, permissions, and time required to set up Postgresql/PostGIS might be annoying. - We can use this test runner to avoid setting up the PostGIS database and running related tests: - `python example/manage.py test --testrunner='conduit.test.non_geo_testrunner.NonGeoTestRunner'` - """ - - def build_suite(self, *args, **kwargs): - suite = super(NonGeoTestRunner, self).build_suite(*args, **kwargs) - filtered_suite = TestSuite() - filtered_suite.addTests( [ test for test in suite if test.__class__.__name__ != 'GeoMethodTestCase' ] ) - return filtered_suite - - def setup_databases(self, **kwargs): - return setup_databases(self.verbosity, self.interactive, **kwargs) - - -def setup_databases(verbosity, interactive, **kwargs): - """ - modified version of the same function located in - site-packages/django/test/runner.py. - this version will not add a database connection - to 'test_databases' for creation if it finds - the ENGINE setting for postgis - """ - from django.db import connections, DEFAULT_DB_ALIAS - - # First pass -- work out which databases actually need to be created, - # and which ones are test mirrors or duplicate entries in DATABASES - mirrored_aliases = {} - test_databases = {} - dependencies = {} - default_sig = connections[DEFAULT_DB_ALIAS].creation.test_db_signature() - for alias in connections: - connection = connections[alias] - # - # DIFF - # - if connection.settings_dict['ENGINE'] == 'django.contrib.gis.db.backends.postgis': - continue - # - # END DIFF - # - if connection.settings_dict['TEST_MIRROR']: - # If the database is marked as a test mirror, save - # the alias. - mirrored_aliases[alias] = ( - connection.settings_dict['TEST_MIRROR']) - else: - # Store a tuple with DB parameters that uniquely identify it. - # If we have two aliases with the same values for that tuple, - # we only need to create the test database once. - item = test_databases.setdefault( - connection.creation.test_db_signature(), - (connection.settings_dict['NAME'], set()) - ) - item[1].add(alias) - - if 'TEST_DEPENDENCIES' in connection.settings_dict: - dependencies[alias] = ( - connection.settings_dict['TEST_DEPENDENCIES']) - else: - if alias != DEFAULT_DB_ALIAS and connection.creation.test_db_signature() != default_sig: - dependencies[alias] = connection.settings_dict.get( - 'TEST_DEPENDENCIES', [DEFAULT_DB_ALIAS]) - - # Second pass -- actually create the databases. - old_names = [] - mirrors = [] - - for signature, (db_name, aliases) in dependency_ordered( - test_databases.items(), dependencies): - test_db_name = None - # Actually create the database for the first connection - for alias in aliases: - connection = connections[alias] - if test_db_name is None: - test_db_name = connection.creation.create_test_db( - verbosity, autoclobber=not interactive) - destroy = True - else: - connection.settings_dict['NAME'] = test_db_name - destroy = False - old_names.append((connection, db_name, destroy)) - - for alias, mirror_alias in mirrored_aliases.items(): - mirrors.append((alias, connections[alias].settings_dict['NAME'])) - connections[alias].settings_dict['NAME'] = ( - connections[mirror_alias].settings_dict['NAME']) - - return old_names, mirrors diff --git a/example/geoexample/test/test_geo_modelresource_methods_and_filters.py b/conduit/test/test_geo_modelresource_methods_and_filters.py similarity index 100% rename from example/geoexample/test/test_geo_modelresource_methods_and_filters.py rename to conduit/test/test_geo_modelresource_methods_and_filters.py diff --git a/conduit/test/testrunners.py b/conduit/test/testrunners.py new file mode 100644 index 0000000..cefa88a --- /dev/null +++ b/conduit/test/testrunners.py @@ -0,0 +1,34 @@ +try: + from django.test.runner import DiscoverRunner as BaseRunner +except ImportError: + # Django < 1.6 fallback + from django.test.simple import DjangoTestSuiteRunner as BaseRunner +from django.utils.unittest import TestSuite + +class GeoTestRunner( BaseRunner ): + """ + only run geo related tests. To run then use the --settings flags: + `python example/manage.py test --settings='geoexample.settings'` + """ + + def build_suite(self, *args, **kwargs): + suite = super(GeoTestRunner, self).build_suite(*args, **kwargs) + filtered_suite = TestSuite() + filtered_suite.addTests( [ test for test in suite if test.__class__.__name__ == 'GeoMethodTestCase' ] ) + return filtered_suite + + +class StandardTestRunner( BaseRunner ): + """ + for local development and quick "spot-checks" with the unit tests + the installation, dependencies, permissions, and time required to set up Postgresql/PostGIS might be annoying. + this test runner excludes geo related tests. it is the default TestRunner. + """ + + def build_suite(self, *args, **kwargs): + suite = super(StandardTestRunner, self).build_suite(*args, **kwargs) + filtered_suite = TestSuite() + filtered_suite.addTests( [ test for test in suite if test.__class__.__name__ != 'GeoMethodTestCase' ] ) + return filtered_suite + + diff --git a/example/example/geo_db_router.py b/example/example/geo_db_router.py deleted file mode 100644 index 09e21d0..0000000 --- a/example/example/geo_db_router.py +++ /dev/null @@ -1,26 +0,0 @@ -class GeoDbRouter(object): - """ - A router to control all database operations on geomodels in the application. - """ - def db_for_read(self, model, **hints): - if model._meta.app_label == 'geoexample': - return 'geodefault' - return None - - def db_for_write(self, model, **hints): - if model._meta.app_label == 'geoexample': - return 'geodefault' - return None - - def allow_relation(self, obj1, obj2, **hints): - if obj1._meta.app_label == 'geoexample' or \ - obj2._meta.app_label == 'geoexample': - return True - return None - - def allow_syncdb(self, db, model): - if db == 'geodefault': - return model._meta.app_label == 'geoexample' - elif model._meta.app_label == 'geoexample': - return False - return None diff --git a/example/example/settings.py b/example/example/settings.py index f274842..98ddb49 100644 --- a/example/example/settings.py +++ b/example/example/settings.py @@ -133,6 +133,8 @@ 'example', ) +TEST_RUNNER = 'conduit.test.testrunners.StandardTestRunner' + # A sample logging configuration. The only tangible logging # performed by this configuration is to send an email to # the site admins on every HTTP 500 error when DEBUG=False. diff --git a/example/geoexample/api/__init__.py b/example/geoexample/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/example/geoexample/views.py b/example/geoexample/api/views.py similarity index 91% rename from example/geoexample/views.py rename to example/geoexample/api/views.py index ba29666..a0e0da2 100644 --- a/example/geoexample/views.py +++ b/example/geoexample/api/views.py @@ -1,14 +1,7 @@ -## api/views.py from conduit.api import ModelResource from conduit.api.fields import ForeignKeyField, ManyToManyField from geoexample.models import GeoBar, GeoBaz, GeoFoo - -# -# -# resources based on GeoManager(s) -# -# class GeoBarResource(ModelResource): class Meta(ModelResource.Meta): model = GeoBar diff --git a/example/geoexample/settings.py b/example/geoexample/settings.py index d5b4e66..7c259d7 100644 --- a/example/geoexample/settings.py +++ b/example/geoexample/settings.py @@ -15,18 +15,35 @@ MANAGERS = ADMINS +# +# NOTE: USER and PASSWORD below are defaulted to what Travis CI expects. +# To run local tests you will need to: +# +# 0. create a spatially enabled database called 'geoexample' +# ( see django-conduit/travis_postgis_setup.sh for examples ) +# +# 1. set your pg_hba.conf postgres user METHOD to 'trust' ( which some people might not like ): +# local all postgres trust +# +# 2. set your pg_hba.conf postgres user METHOD to 'md5' and change the PASSWORD below to match your postgres user +# local all postgres md5 +# +# 3. remember to restart postgresql server with new configs +# sudo /etc/init.d/postgresql restart +# +# DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3' , # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': 'example.db', # Or path to database file if using sqlite3. - # The following settings are not used with sqlite3: - 'USER': '', - 'PASSWORD': '', - 'HOST': '', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. - 'PORT': '', # Set to empty string for default. - } -} + 'default' : { + 'ENGINE': 'django.contrib.gis.db.backends.postgis' , + 'NAME': 'geoexample', + 'USER': 'postgres', + 'PASSWORD': '', + 'HOST': '', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. + 'PORT': '', # Set to empty string for default. + } , + +} # Hosts/domain names that are valid for this site; required if DEBUG is False # See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts @@ -109,10 +126,10 @@ # 'django.middleware.clickjacking.XFrameOptionsMiddleware', ) -ROOT_URLCONF = 'example.urls' +ROOT_URLCONF = 'geoexample.urls' # Python dotted path to the WSGI application used by Django's runserver. -WSGI_APPLICATION = 'example.wsgi.application' +WSGI_APPLICATION = 'geoexample.wsgi.application' TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". @@ -130,42 +147,14 @@ 'django.contrib.admin', 'conduit', # 'api', + 'geoexample', ) -## GEO Specific Settings -DATABASES['geodefault'] = { - 'ENGINE': 'django.contrib.gis.db.backends.postgis' , - 'NAME': 'geoexample', - # - # NOTE: USER and PASSWORD below are defaulted to what Travis CI expects. - # You can skip setting up this database and running these tests by using the --testrunner flag: - # `python example/manage.py test --testrunner='conduit.test.non_geo_testrunner.NonGeoTestRunner'` - # - # To run local tests you will need to: - # - # 0. create a spatially enabled database called 'geoexample' - # ( see django-conduit/travis_postgis_setup.sh for examples ) - # - # 1. set your pg_hba.conf postgres user METHOD to 'trust' ( which some people might not like ): - # local all postgres trust - # - # 2. set your pg_hba.conf postgres user METHOD to 'md5' and change the PASSWORD below to match your postgres user - # local all postgres md5 - # - # 3. remember to restart postgresql server with new configs - # sudo /etc/init.d/postgresql restart - # - # - 'USER': 'postgres', - 'PASSWORD': '', - 'HOST': '', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. - 'PORT': '', # Set to empty string for default. -} -INSTALLED_APPS += 'geoexample' -DATABASE_ROUTERS = ['example.geo_db_router.GeoDbRouter'] POSTGIS_VERSION = (2, 1) +TEST_RUNNER = 'conduit.test.testrunners.GeoTestRunner' + # A sample logging configuration. The only tangible logging # performed by this configuration is to send an email to # the site admins on every HTTP 500 error when DEBUG=False. diff --git a/example/geoexample/urls.py b/example/geoexample/urls.py index d44edae..57a79e3 100644 --- a/example/geoexample/urls.py +++ b/example/geoexample/urls.py @@ -1,7 +1,7 @@ ## api/urls.py from django.conf.urls import patterns, include, url from conduit.api import Api -from api.views import ( +from geoexample.api.views import ( GeoBarResource, GeoBazResource, GeoFooResource @@ -16,5 +16,5 @@ api.register(GeoFooResource()) urlpatterns = patterns('', - url(r'^', include(api.urls)) + url(r'^api/', include(api.urls)) ) diff --git a/example/geoexample/wsgi.py b/example/geoexample/wsgi.py new file mode 100644 index 0000000..c218c3d --- /dev/null +++ b/example/geoexample/wsgi.py @@ -0,0 +1,32 @@ +""" +WSGI config for example project. + +This module contains the WSGI application used by Django's development server +and any production WSGI deployments. It should expose a module-level variable +named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover +this application via the ``WSGI_APPLICATION`` setting. + +Usually you will have the standard Django WSGI application here, but it also +might make sense to replace the whole Django WSGI application with a custom one +that later delegates to the Django one. For example, you could introduce WSGI +middleware here, or combine a Django application with an application of another +framework. + +""" +import os + +# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks +# if running multiple sites in the same mod_wsgi process. To fix this, use +# mod_wsgi daemon mode with each site in its own daemon process, or use +# os.environ["DJANGO_SETTINGS_MODULE"] = "example.settings" +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "geoexample.settings") + +# This application object is used by any WSGI server configured to use this +# file. This includes Django's development server, if the WSGI_APPLICATION +# setting points here. +from django.core.wsgi import get_wsgi_application +application = get_wsgi_application() + +# Apply WSGI middleware here. +# from helloworld.wsgi import HelloWorldApplication +# application = HelloWorldApplication(application)