[Python-modules-commits] [django-hstore] 01/07: import django-hstore_1.5~alpha~git20161126+dfsg.orig.tar.gz
Scott Kitterman
kitterman at moszumanska.debian.org
Sat Nov 26 23:16:51 UTC 2016
This is an automated email from the git hooks/post-receive script.
kitterman pushed a commit to branch master
in repository django-hstore.
commit b16ee57cdd3438b3d6f9e8b8302e3f5c1609d543
Author: Scott Kitterman <scott at kitterman.com>
Date: Sat Nov 26 16:39:05 2016 -0500
import django-hstore_1.5~alpha~git20161126+dfsg.orig.tar.gz
---
AUTHORS | 22 +
LICENSE | 42 ++
MANIFEST.in | 3 +
PKG-INFO | 68 ++
README.rst | 42 ++
django_hstore.egg-info/PKG-INFO | 68 ++
django_hstore.egg-info/SOURCES.txt | 48 ++
django_hstore.egg-info/dependency_links.txt | 1 +
django_hstore.egg-info/not-zip-safe | 1 +
django_hstore.egg-info/top_level.txt | 1 +
django_hstore/__init__.py | 17 +
django_hstore/apps.py | 91 +++
django_hstore/compat.py | 14 +
django_hstore/descriptors.py | 55 ++
django_hstore/dict.py | 165 +++++
django_hstore/exceptions.py | 9 +
django_hstore/fields.py | 348 ++++++++++
django_hstore/forms.py | 119 ++++
django_hstore/hstore.py | 7 +
django_hstore/lookups.py | 140 ++++
django_hstore/managers.py | 41 ++
django_hstore/models.py | 2 +
django_hstore/query.py | 251 ++++++++
.../static/admin/js/django_hstore/hstore-widget.js | 200 ++++++
django_hstore/templates/hstore_default_widget.html | 65 ++
.../templates/hstore_grappelli_widget.html | 63 ++
django_hstore/utils.py | 83 +++
django_hstore/virtual.py | 171 +++++
django_hstore/widgets.py | 81 +++
setup.cfg | 19 +
setup.py | 74 +++
tests/__init__.py | 0
tests/django_hstore_tests/__init__.py | 0
tests/django_hstore_tests/admin.py | 34 +
tests/django_hstore_tests/models.py | 238 +++++++
tests/django_hstore_tests/tests/__init__.py | 0
.../tests/test_dictionary_field.py | 701 +++++++++++++++++++++
.../tests/test_gis_integration.py | 73 +++
.../tests/test_not_transactional.py | 28 +
.../tests/test_reference_field.py | 192 ++++++
.../django_hstore_tests/tests/test_schema_mode.py | 459 ++++++++++++++
.../tests/test_serialized_dictionary_field.py | 460 ++++++++++++++
tests/local_settings.py.example | 15 +
tests/local_settings_psycopg.py.example | 15 +
tests/manage.py | 9 +
tests/settings.py | 96 +++
tests/settings_psycopg.py | 101 +++
tests/urls.py | 16 +
48 files changed, 4748 insertions(+)
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..7c924fd
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,22 @@
+Current Mantainers
+------------------
+Federico Capoano https://github.com/nemesisdesign/
+Andrei Antoukh https://github.com/niwibe
+
+
+Original Author
+---------------
+Jordan McCoy https://github.com/jordanm
+
+
+Contributors
+------------
+Jannis Leidel https://github.com/jezdez
+Oliver Yu https://github.com/oliy
+Pavel Chernykh https://github.com/pavel-v-chernykh
+Tippr https://github.com/Tippr
+Donald Stufft https://github.com/dstufft
+Mikko Hellsing https://github.com/aino
+Adieu https://github.com/adieu
+Alexander Dinu https://github.com/aluuu
+Anthony Lukach http://github.com/alukach
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c58c25d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,42 @@
+Copyright (C) 2013-2014 Federico Capoano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+Original Author
+===============
+Copyright (C) 2011 Jordan McCoy
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..216b19b
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,3 @@
+include AUTHORS LICENSE README.rst
+recursive-include tests *
+recursive-exclude * *.pyc *.swp
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..d93276c
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,68 @@
+Metadata-Version: 1.1
+Name: django-hstore
+Version: 1.5.alpha
+Summary: PostgreSQL HStore support for Django
+Home-page: https://github.com/djangonauts/django-hstore
+Author: Djangonauts Organization
+Author-email: django-hstore at googlegroups.com
+License: BSD
+Download-URL: https://github.com/djangonauts/django-hstore/releases
+Description: =============
+ django-hstore
+ =============
+
+ .. image:: https://travis-ci.org/djangonauts/django-hstore.svg
+ :target: https://travis-ci.org/djangonauts/django-hstore
+
+ .. image:: https://coveralls.io/repos/djangonauts/django-hstore/badge.svg
+ :target: https://coveralls.io/r/djangonauts/django-hstore
+
+ .. image:: https://requires.io/github/djangonauts/django-hstore/requirements.svg?branch=master
+ :target: https://requires.io/github/djangonauts/django-hstore/requirements/?branch=master
+ :alt: Requirements Status
+
+ .. image:: https://badge.fury.io/py/django-hstore.svg
+ :target: https://pypi.python.org/pypi/django-hstore
+
+ .. image:: https://img.shields.io/pypi/dm/django-hstore.svg
+ :target: https://pypi.python.org/pypi/django-hstore
+
+ ------------
+
+ You need **dynamic columns** in your tables. What do you do?
+
+ - Create lots of tables to handle it. Nice, now you’ll need more models and lots of additional sqls. Insertion and selection will be slow as hell.
+ - Use a **noSQL** database just for this issue. **Good luck**.
+ - Create a serialized column. Nice, insertion will be fine, and reading data from a record too. But, what if you have a condition in your select that includes serialized data? Yeah, regular expressions.
+
+ ------------
+
+ Documentation_ - `Mailing List`_
+
+ .. _Documentation: http://djangonauts.github.io/django-hstore/
+ .. _`Mailing List`: https://groups.google.com/forum/#!forum/django-hstore
+
+ ------------
+
+ Projects using this package
+ ---------------------------
+
+ - `django-rest-framework-hstore <https://github.com/djangonauts/django-rest-framework-hstore>`__: **django-rest-framework** tools for **django-hstore**
+ - `Nodeshot <https://github.com/ninuxorg/nodeshot>`__: extensible django web application for management of community-led georeferenced data - some features of **django-hstore**, like the ``schema-mode`` have been developed for this project
+
+Keywords: django,hstore,schemaless
+Platform: Platform Indipendent
+Classifier: Development Status :: 4 - Beta
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Classifier: Framework :: Django
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..b8d5cac
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,42 @@
+=============
+django-hstore
+=============
+
+.. image:: https://travis-ci.org/djangonauts/django-hstore.svg
+ :target: https://travis-ci.org/djangonauts/django-hstore
+
+.. image:: https://coveralls.io/repos/djangonauts/django-hstore/badge.svg
+ :target: https://coveralls.io/r/djangonauts/django-hstore
+
+.. image:: https://requires.io/github/djangonauts/django-hstore/requirements.svg?branch=master
+ :target: https://requires.io/github/djangonauts/django-hstore/requirements/?branch=master
+ :alt: Requirements Status
+
+.. image:: https://badge.fury.io/py/django-hstore.svg
+ :target: https://pypi.python.org/pypi/django-hstore
+
+.. image:: https://img.shields.io/pypi/dm/django-hstore.svg
+ :target: https://pypi.python.org/pypi/django-hstore
+
+------------
+
+You need **dynamic columns** in your tables. What do you do?
+
+- Create lots of tables to handle it. Nice, now you’ll need more models and lots of additional sqls. Insertion and selection will be slow as hell.
+- Use a **noSQL** database just for this issue. **Good luck**.
+- Create a serialized column. Nice, insertion will be fine, and reading data from a record too. But, what if you have a condition in your select that includes serialized data? Yeah, regular expressions.
+
+------------
+
+Documentation_ - `Mailing List`_
+
+.. _Documentation: http://djangonauts.github.io/django-hstore/
+.. _`Mailing List`: https://groups.google.com/forum/#!forum/django-hstore
+
+------------
+
+Projects using this package
+---------------------------
+
+- `django-rest-framework-hstore <https://github.com/djangonauts/django-rest-framework-hstore>`__: **django-rest-framework** tools for **django-hstore**
+- `Nodeshot <https://github.com/ninuxorg/nodeshot>`__: extensible django web application for management of community-led georeferenced data - some features of **django-hstore**, like the ``schema-mode`` have been developed for this project
diff --git a/django_hstore.egg-info/PKG-INFO b/django_hstore.egg-info/PKG-INFO
new file mode 100644
index 0000000..d93276c
--- /dev/null
+++ b/django_hstore.egg-info/PKG-INFO
@@ -0,0 +1,68 @@
+Metadata-Version: 1.1
+Name: django-hstore
+Version: 1.5.alpha
+Summary: PostgreSQL HStore support for Django
+Home-page: https://github.com/djangonauts/django-hstore
+Author: Djangonauts Organization
+Author-email: django-hstore at googlegroups.com
+License: BSD
+Download-URL: https://github.com/djangonauts/django-hstore/releases
+Description: =============
+ django-hstore
+ =============
+
+ .. image:: https://travis-ci.org/djangonauts/django-hstore.svg
+ :target: https://travis-ci.org/djangonauts/django-hstore
+
+ .. image:: https://coveralls.io/repos/djangonauts/django-hstore/badge.svg
+ :target: https://coveralls.io/r/djangonauts/django-hstore
+
+ .. image:: https://requires.io/github/djangonauts/django-hstore/requirements.svg?branch=master
+ :target: https://requires.io/github/djangonauts/django-hstore/requirements/?branch=master
+ :alt: Requirements Status
+
+ .. image:: https://badge.fury.io/py/django-hstore.svg
+ :target: https://pypi.python.org/pypi/django-hstore
+
+ .. image:: https://img.shields.io/pypi/dm/django-hstore.svg
+ :target: https://pypi.python.org/pypi/django-hstore
+
+ ------------
+
+ You need **dynamic columns** in your tables. What do you do?
+
+ - Create lots of tables to handle it. Nice, now you’ll need more models and lots of additional sqls. Insertion and selection will be slow as hell.
+ - Use a **noSQL** database just for this issue. **Good luck**.
+ - Create a serialized column. Nice, insertion will be fine, and reading data from a record too. But, what if you have a condition in your select that includes serialized data? Yeah, regular expressions.
+
+ ------------
+
+ Documentation_ - `Mailing List`_
+
+ .. _Documentation: http://djangonauts.github.io/django-hstore/
+ .. _`Mailing List`: https://groups.google.com/forum/#!forum/django-hstore
+
+ ------------
+
+ Projects using this package
+ ---------------------------
+
+ - `django-rest-framework-hstore <https://github.com/djangonauts/django-rest-framework-hstore>`__: **django-rest-framework** tools for **django-hstore**
+ - `Nodeshot <https://github.com/ninuxorg/nodeshot>`__: extensible django web application for management of community-led georeferenced data - some features of **django-hstore**, like the ``schema-mode`` have been developed for this project
+
+Keywords: django,hstore,schemaless
+Platform: Platform Indipendent
+Classifier: Development Status :: 4 - Beta
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
+Classifier: Framework :: Django
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
diff --git a/django_hstore.egg-info/SOURCES.txt b/django_hstore.egg-info/SOURCES.txt
new file mode 100644
index 0000000..4da780b
--- /dev/null
+++ b/django_hstore.egg-info/SOURCES.txt
@@ -0,0 +1,48 @@
+AUTHORS
+LICENSE
+MANIFEST.in
+README.rst
+setup.cfg
+setup.py
+django_hstore/__init__.py
+django_hstore/apps.py
+django_hstore/compat.py
+django_hstore/descriptors.py
+django_hstore/dict.py
+django_hstore/exceptions.py
+django_hstore/fields.py
+django_hstore/forms.py
+django_hstore/hstore.py
+django_hstore/lookups.py
+django_hstore/managers.py
+django_hstore/models.py
+django_hstore/query.py
+django_hstore/utils.py
+django_hstore/virtual.py
+django_hstore/widgets.py
+django_hstore.egg-info/PKG-INFO
+django_hstore.egg-info/SOURCES.txt
+django_hstore.egg-info/dependency_links.txt
+django_hstore.egg-info/not-zip-safe
+django_hstore.egg-info/top_level.txt
+django_hstore/static/admin/js/django_hstore/hstore-widget.js
+django_hstore/static/admin/js/django_hstore/underscore-min.js
+django_hstore/templates/hstore_default_widget.html
+django_hstore/templates/hstore_grappelli_widget.html
+tests/__init__.py
+tests/local_settings.py.example
+tests/local_settings_psycopg.py.example
+tests/manage.py
+tests/settings.py
+tests/settings_psycopg.py
+tests/urls.py
+tests/django_hstore_tests/__init__.py
+tests/django_hstore_tests/admin.py
+tests/django_hstore_tests/models.py
+tests/django_hstore_tests/tests/__init__.py
+tests/django_hstore_tests/tests/test_dictionary_field.py
+tests/django_hstore_tests/tests/test_gis_integration.py
+tests/django_hstore_tests/tests/test_not_transactional.py
+tests/django_hstore_tests/tests/test_reference_field.py
+tests/django_hstore_tests/tests/test_schema_mode.py
+tests/django_hstore_tests/tests/test_serialized_dictionary_field.py
\ No newline at end of file
diff --git a/django_hstore.egg-info/dependency_links.txt b/django_hstore.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/django_hstore.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/django_hstore.egg-info/not-zip-safe b/django_hstore.egg-info/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/django_hstore.egg-info/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/django_hstore.egg-info/top_level.txt b/django_hstore.egg-info/top_level.txt
new file mode 100644
index 0000000..6f8c57e
--- /dev/null
+++ b/django_hstore.egg-info/top_level.txt
@@ -0,0 +1 @@
+django_hstore
diff --git a/django_hstore/__init__.py b/django_hstore/__init__.py
new file mode 100644
index 0000000..b022dee
--- /dev/null
+++ b/django_hstore/__init__.py
@@ -0,0 +1,17 @@
+VERSION = (1, 5, 0, 'alpha')
+__version__ = VERSION
+
+
+def get_version():
+ version = '%s.%s' % (VERSION[0], VERSION[1])
+ if VERSION[2]:
+ version = '%s.%s' % (version, VERSION[2])
+ if VERSION[3:] == ('alpha', 0):
+ version = '%s pre-alpha' % version
+ else:
+ if VERSION[3] != 'final':
+ version = '%s %s' % (version, VERSION[3])
+ return version
+
+
+default_app_config = 'django_hstore.apps.HStoreConfig'
diff --git a/django_hstore/apps.py b/django_hstore/apps.py
new file mode 100644
index 0000000..c7f8da6
--- /dev/null
+++ b/django_hstore/apps.py
@@ -0,0 +1,91 @@
+import sys
+
+import django
+from django.conf import settings
+from django.db.backends.signals import connection_created
+from django.apps import AppConfig
+
+from psycopg2.extras import register_hstore
+
+
+HSTORE_REGISTER_GLOBALLY = getattr(settings, "DJANGO_HSTORE_ADAPTER_REGISTRATION", "global") == "global"
+CONNECTION_CREATED_SIGNAL_WEAKREF = bool(getattr(settings, "DJANGO_HSTORE_ADAPTER_SIGNAL_WEAKREF", False))
+
+# This allows users that introduce hstore into an existing
+# environment to disable global registration of the hstore adapter
+# in order to avoid unpredictable behavior when having hstore installed individually
+# on each database instead of on having it installed on template1.
+
+# Check if GEODJANGO is being used
+GEODJANGO_INSTALLED = False
+
+for database in settings.DATABASES.values():
+ if 'postgis' in database.get('ENGINE'):
+ GEODJANGO_INSTALLED = True
+ break
+
+
+class ConnectionCreateHandler(object):
+ """
+ Generic connection handlers manager.
+ Executes attached functions when connection is created.
+ With possibility of attaching single execution methods.
+ """
+ generic_handlers = []
+ unique_handlers = []
+
+ def __call__(self, sender, connection, **kwargs):
+ handlers = set()
+
+ if len(self.unique_handlers) > 0:
+ handlers.update(self.unique_handlers)
+ self.unique_handlers = []
+
+ handlers.update(self.generic_handlers)
+
+ # List comprehension is used instead of for statement
+ # only for performance.
+ return [x(connection) for x in handlers]
+
+ def attach_handler(self, func, vendor=None, unique=False):
+ if unique:
+ self.unique_handlers.append(func)
+ else:
+ self.generic_handlers.append(func)
+
+connection_handler = ConnectionCreateHandler()
+
+
+def register_hstore_handler(connection, **kwargs):
+ # do not register hstore if DB is not postgres
+ # do not register if HAS_HSTORE flag is set to false
+ if connection.vendor != 'postgresql' or \
+ connection.settings_dict.get('HAS_HSTORE', True) is False:
+ return
+ # if the ``NAME`` of the database in the connection settings is ``None``
+ # defer hstore registration by setting up a new unique handler
+ if connection.settings_dict['NAME'] is None:
+ connection_handler.attach_handler(register_hstore_handler,
+ vendor="postgresql",
+ unique=HSTORE_REGISTER_GLOBALLY)
+ return
+
+ if sys.version_info[0] < 3:
+ register_hstore(connection.connection, globally=HSTORE_REGISTER_GLOBALLY, unicode=True)
+ else:
+ register_hstore(connection.connection, globally=HSTORE_REGISTER_GLOBALLY)
+
+
+connection_handler.attach_handler(register_hstore_handler,
+ vendor="postgresql",
+ unique=HSTORE_REGISTER_GLOBALLY)
+
+
+class HStoreConfig(AppConfig):
+ name = 'django_hstore'
+ verbose = 'Django HStore'
+
+ def ready(self):
+ connection_created.connect(connection_handler,
+ weak=CONNECTION_CREATED_SIGNAL_WEAKREF,
+ dispatch_uid="_connection_create_handler")
diff --git a/django_hstore/compat.py b/django_hstore/compat.py
new file mode 100644
index 0000000..3b595b3
--- /dev/null
+++ b/django_hstore/compat.py
@@ -0,0 +1,14 @@
+import sys
+
+
+class UnicodeMixin(object):
+ """
+ Mixin class to handle defining the proper __str__/__unicode__
+ methods in Python 2 or 3.
+ """
+ if sys.version_info[0] >= 3: # Python 3
+ def __str__(self):
+ return self.__unicode__()
+ else: # Python 2
+ def __str__(self):
+ return self.__unicode__().encode('utf8')
diff --git a/django_hstore/descriptors.py b/django_hstore/descriptors.py
new file mode 100644
index 0000000..14f6f69
--- /dev/null
+++ b/django_hstore/descriptors.py
@@ -0,0 +1,55 @@
+from .dict import HStoreDict, HStoreReferenceDict
+
+__all__ = [
+ 'HStoreDescriptor',
+ 'HStoreReferenceDescriptor',
+ 'SerializedDictDescriptor'
+]
+
+
+class Creator(object):
+ """
+ A placeholder class that provides a way to set the attribute on the model.
+ """
+ def __init__(self, field):
+ self.field = field
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ return obj.__dict__[self.field.name]
+
+ def __set__(self, obj, value):
+ obj.__dict__[self.field.name] = self.field.to_python(value)
+
+
+class HStoreDescriptor(Creator):
+ _DictClass = HStoreDict
+
+ def __init__(self, *args, **kwargs):
+ self.schema_mode = kwargs.pop('schema_mode', False)
+ super(HStoreDescriptor, self).__init__(*args, **kwargs)
+
+ def __set__(self, obj, value):
+ value = self.field.to_python(value)
+ if isinstance(value, dict):
+ value = self._DictClass(
+ value=value, field=self.field, instance=obj, schema_mode=self.schema_mode
+ )
+ obj.__dict__[self.field.name] = value
+
+
+class SerializedDictDescriptor(Creator):
+ _DictClass = dict
+
+ def __set__(self, obj, value):
+ # Deserialization is only needed when retrieving data from DB
+ # (not when field is being set via assignment (`x.data = {...}`)
+ # or via the `.create()` method.
+ if value and self.field._from_db(obj):
+ value = self.field.to_python(value)
+ obj.__dict__[self.field.name] = value
+
+
+class HStoreReferenceDescriptor(HStoreDescriptor):
+ _DictClass = HStoreReferenceDict
diff --git a/django_hstore/dict.py b/django_hstore/dict.py
new file mode 100644
index 0000000..16efc34
--- /dev/null
+++ b/django_hstore/dict.py
@@ -0,0 +1,165 @@
+import json
+
+from decimal import Decimal
+
+from django.utils import six
+from django.utils.encoding import force_text, force_str
+
+from .compat import UnicodeMixin
+from . import utils, exceptions
+
+
+__all__ = [
+ 'HStoreDict',
+ 'HStoreReferenceDict',
+]
+
+
+class DecimalEncoder(json.JSONEncoder):
+ def default(self, obj):
+ if isinstance(obj, Decimal):
+ return float(obj)
+ return json.JSONEncoder.default(self, obj)
+
+
+class HStoreDict(UnicodeMixin, dict):
+ """
+ A dictionary subclass which implements hstore support.
+ """
+ schema_mode = False # python2.6 compatibility
+
+ def __init__(self, value=None, field=None, instance=None, schema_mode=False, **kwargs):
+ self.schema_mode = schema_mode
+
+ # if passed value is string
+ # ensure is json formatted
+ if isinstance(value, six.string_types):
+ try:
+ value = json.loads(value)
+ except ValueError as e:
+ raise exceptions.HStoreDictException(
+ 'HStoreDict accepts only valid json formatted strings.',
+ json_error_message=force_text(e)
+ )
+ elif value is None:
+ value = {}
+
+ # allow dictionaries only
+ if not isinstance(value, dict):
+ raise exceptions.HStoreDictException(
+ 'HStoreDict accepts only dictionary objects, None and json formatted string representations of json objects'
+ )
+
+ if not self.schema_mode:
+ # ensure values are acceptable
+ for key, val in value.items():
+ value[key] = self.ensure_acceptable_value(val)
+
+ super(HStoreDict, self).__init__(value, **kwargs)
+ self.field = field
+ self.instance = instance
+
+ def __setitem__(self, *args, **kwargs):
+ """
+ perform checks before setting the value of a key
+ """
+ # ensure values are acceptable
+ value = self.ensure_acceptable_value(args[1])
+ # prepare *args
+ args = (args[0], value)
+ super(HStoreDict, self).__setitem__(*args, **kwargs)
+
+ def __getitem__(self, *args, **kwargs):
+ """
+ retrieve value preserving type if in schema mode, string only otherwise
+ """
+ value = super(HStoreDict, self).__getitem__(*args, **kwargs)
+
+ if self.schema_mode:
+ try:
+ return self.instance._hstore_virtual_fields[args[0]].to_python(value)
+ except KeyError:
+ pass
+
+ return value
+
+ def get(self, *args):
+ key = args[0]
+ try:
+ return self.__getitem__(key)
+ except KeyError:
+ if len(args) > 1:
+ return args[1] # return default value
+ else:
+ return None
+
+ # This method is used both for python3 and python2
+ # thanks to UnicodeMixin
+ def __unicode__(self):
+ return force_text(json.dumps(self))
+
+ def __getstate__(self):
+ return self.__dict__
+
+ def __copy__(self):
+ return self.__class__(self, self.field)
+
+ def update(self, *args, **kwargs):
+ for key, value in dict(*args, **kwargs).items():
+ self[key] = value
+
+ def ensure_acceptable_value(self, value):
+ """
+ if schema_mode disabled (default behaviour):
+ - ensure booleans, integers, floats, Decimals, lists and dicts are
+ converted to string
+ - convert True and False objects to "true" and "false" so they can be
+ decoded back with the json library if needed
+ - convert lists and dictionaries to json formatted strings
+ - leave alone all other objects because they might be representation of django models
+ else:
+ - encode utf8 strings in python2
+ - convert to string
+ """
+ if not self.schema_mode:
+ if isinstance(value, bool):
+ return force_text(value).lower()
+ elif isinstance(value, six.integer_types + (float, Decimal)):
+ return force_text(value)
+ elif isinstance(value, (list, dict)):
+ return force_text(json.dumps(value, cls=DecimalEncoder))
+ else:
+ return value
+ else:
+ # perform string conversion unless is None
+ if value is not None:
+ value = force_str(value)
+ return value
+
+ def remove(self, keys):
+ """
+ Removes the specified keys from this dictionary.
+ """
+ queryset = self.instance._base_manager.get_query_set()
+ queryset.filter(pk=self.instance.pk).hremove(self.field.name, keys)
+
+
+class HStoreReferenceDict(HStoreDict):
+ """
+ A dictionary which adds support to storing references to models
+ """
+ def __getitem__(self, *args, **kwargs):
+ value = super(self.__class__, self).__getitem__(*args, **kwargs)
+ # if value is a string it needs to be converted to model instance
+ if isinstance(value, six.string_types):
+ reference = utils.acquire_reference(value)
+ self.__setitem__(args[0], reference)
+ return reference
+ # otherwise just return the relation
+ return value
+
+ def get(self, key, default=None):
+ try:
+ return self.__getitem__(key)
+ except KeyError:
+ return default
diff --git a/django_hstore/exceptions.py b/django_hstore/exceptions.py
new file mode 100644
index 0000000..2c4eea1
--- /dev/null
+++ b/django_hstore/exceptions.py
@@ -0,0 +1,9 @@
+from __future__ import unicode_literals, absolute_import
+
+
+class HStoreDictException(Exception):
+ json_error_message = None
+
+ def __init__(self, *args, **kwargs):
+ self.json_error_message = kwargs.pop('json_error_message', None)
+ super(HStoreDictException, self).__init__(*args, **kwargs)
diff --git a/django_hstore/fields.py b/django_hstore/fields.py
new file mode 100644
index 0000000..d4fd146
--- /dev/null
+++ b/django_hstore/fields.py
@@ -0,0 +1,348 @@
+from __future__ import absolute_import, unicode_literals
+
+import datetime
+import json
+
+import django
+from django.db import models
+from django.utils import six
+from django.utils.translation import ugettext_lazy as _
+
+from . import forms, utils
+from .descriptors import HStoreDescriptor, HStoreReferenceDescriptor, SerializedDictDescriptor
+from .dict import HStoreDict, HStoreReferenceDict
+from .virtual import create_hstore_virtual_field
+
+
+class HStoreField(models.Field):
+ """ HStore Base Field """
+
+ def __init_dict(self, value):
+ """
+ initializes HStoreDict
+ """
+ return HStoreDict(value, self)
+
+ def validate(self, value, *args):
+ super(HStoreField, self).validate(value, *args)
+ forms.validate_hstore(value, is_serialized=hasattr(self, 'serializer'))
+
+ def contribute_to_class(self, cls, name):
+ super(HStoreField, self).contribute_to_class(cls, name)
+ setattr(cls, self.name, HStoreDescriptor(self))
+
+ def get_default(self):
+ """
+ Returns the default value for this field.
+ """
+ # if default defined
+ if self.has_default():
+ # if default is callable
+ if callable(self.default):
+ return self.__init_dict(self.default())
+ # if it's a dict
+ elif isinstance(self.default, dict):
+ return self.__init_dict(self.default)
+ # else just return it
+ return self.default
+ # default to empty dict
+ return self.__init_dict({})
+
+ def get_prep_value(self, value):
+ if isinstance(value, dict) and not isinstance(value, HStoreDict):
+ return self.__init_dict(value)
+ else:
+ return value
+
+ def get_db_prep_value(self, value, connection, prepared=False):
+ if not prepared:
+ value = self.get_prep_value(value)
+ return value
+
+ def value_to_string(self, obj):
+ return self._get_val_from_obj(obj)
+
+ def db_type(self, connection=None):
+ return 'hstore'
+
+ def south_field_triple(self): # pragma no cover
+ from south.modelsinspector import introspector
+ name = '%s.%s' % (self.__class__.__module__, self.__class__.__name__)
+ args, kwargs = introspector(self)
+ return name, args, kwargs
+
+
+if django.VERSION >= (1, 7):
+ from .lookups import (HStoreGreaterThan, HStoreGreaterThanOrEqual, HStoreLessThan,
+ HStoreLessThanOrEqual, HStoreContains, HStoreIContains, HStoreIsNull)
+
+ HStoreField.register_lookup(HStoreGreaterThan)
+ HStoreField.register_lookup(HStoreGreaterThanOrEqual)
+ HStoreField.register_lookup(HStoreLessThan)
+ HStoreField.register_lookup(HStoreLessThanOrEqual)
+ HStoreField.register_lookup(HStoreContains)
+ HStoreField.register_lookup(HStoreIContains)
+ HStoreField.register_lookup(HStoreIsNull)
+
+
+class DictionaryField(HStoreField):
+ description = _("A python dictionary in a postgresql hstore field.")
+
+ def __init__(self, *args, **kwargs):
+ self.schema = kwargs.pop('schema', None)
+ self.schema_mode = False
+ # if schema parameter is supplied the behaviour is slightly different
+ if self.schema is not None:
+ self._validate_schema(self.schema)
+ self.schema_mode = True
+ # DictionaryField with schema is not editable via admin
+ kwargs['editable'] = False
+ # null defaults to True to facilitate migrations
+ kwargs['null'] = kwargs.get('null', True)
+
+ super(DictionaryField, self).__init__(*args, **kwargs)
+
+ def __init_dict(self, value):
+ """
+ init HStoreDict
+ pass schema_mode=True if in "schema" mode
+ """
+ return HStoreDict(value, self, schema_mode=self.schema_mode)
+
+ def contribute_to_class(self, cls, name):
+ super(DictionaryField, self).contribute_to_class(cls, name)
+ setattr(cls, self.name, HStoreDescriptor(self, schema_mode=self.schema_mode))
+
+ if self.schema:
+ self._create_hstore_virtual_fields(cls, name)
+
+ def formfield(self, **kwargs):
+ kwargs['form_class'] = forms.DictionaryField
+ return super(DictionaryField, self).formfield(**kwargs)
+
+ def _value_to_python(self, value):
+ return value
+
+ def south_field_triple(self): # pragma no cover
+ name, args, kwargs = super(DictionaryField, self).south_field_triple()
+ # if schema mode replace the default value {} with None as {} would break south
+ if self.schema_mode:
+ kwargs['default'] = None
+ return name, args, kwargs
+
+ def _validate_schema(self, schema):
+ if not isinstance(schema, list):
+ raise ValueError('schema parameter must be a list')
+
+ if len(schema) == 0:
+ raise ValueError('schema parameter cannot be an empty list')
+
+ for field in schema:
+ if not isinstance(field, dict):
+ raise ValueError('schema parameter must contain dicts representing fields, read the docs to see the format')
+ if 'name' not in field:
+ raise ValueError('schema element %s is missing the name key' % field)
+ if 'class' not in field:
+ raise ValueError('schema element %s is missing the class key' % field)
+
+ def _create_hstore_virtual_fields(self, cls, hstore_field_name):
+ """
+ this methods creates all the virtual fields automatically by reading the schema attribute
+ """
+ if not self.schema and self.schema_mode is False:
+ return
+ # add hstore_virtual_fields attribute to class
+ if not hasattr(cls, '_hstore_virtual_fields'):
+ cls._hstore_virtual_fields = {}
+ # loop over all fields defined in schema
+ for field in self.schema:
+ # initialize the virtual field by specifying the class, the kwargs and the hstore field name
+ virtual_field = create_hstore_virtual_field(field['class'],
+ field.get('kwargs', {}),
+ hstore_field_name)
+ # this will call the contribute_to_class method in virtual.HStoreVirtualMixin
+ cls.add_to_class(field['name'], virtual_field)
+ # add this field to hstore_virtual_fields dict
+ cls._hstore_virtual_fields[field['name']] = virtual_field
+
+ def reload_schema(self, schema):
+ """
+ Reload schema arbitrarily at run-time
+ """
+ if schema:
+ self._validate_schema(schema)
+ self.schema = schema
+ self.schema_mode = True
+ self.editable = False
+ else:
+ self.schema = None
+ self.schema_mode = False
+ self.editable = True
+ # remove any existing virtual field
+ self._remove_hstore_virtual_fields()
+ # set new descriptor on model class
+ setattr(self.model, self.name, HStoreDescriptor(self, schema_mode=self.schema_mode))
+ # create virtual fields
+ self._create_hstore_virtual_fields(self.model, self.name)
+
+ def _remove_hstore_virtual_fields(self):
+ """ remove hstore virtual fields from class """
+ cls = self.model
+ # remove all hstore virtual fields related attributes
+ if hasattr(cls, '_hstore_virtual_fields'):
+ # remove attributes from class
+ for field_name in cls._hstore_virtual_fields.keys():
... 4084 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-hstore.git
More information about the Python-modules-commits
mailing list