{ "info": { "author": "Outernet Project", "author_email": "branko@brankovukelic.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 2 - Pre-Alpha", "Environment :: Web Environment", "Framework :: Flask", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License" ], "description": "=========\nNDB utils\n=========\n\nSet of utilities for working with Google AppEngine Datastore.\n\nThe pckage currently has a few mixins for GAE NDB models, and a few custom\nproperties.\n\nNDB utils require FormEncode if you are using custom properties.\n\nInstallation\n============\n\nNDB utils can be installed from PyPI using easy_install::\n\n easy_install ndb-utils\n\nor with pip::\n\n pip install ndb-utils\n\nModel mixins\n============\n\nNDB utils comes with a few mixins for making common modelling tasks easier.\n\nndb_utils.models.TimestampedMixin\n---------------------------------\n\nThis mixin adds two properties to your models: ``created`` (creation\ntimestamp), and ``updated`` (update timestamp). These are both \n``ndb.DateTimeProperty`` with ``auto_now_add`` and ``auto_now`` respectively.\n\nndb_utils.models.RandomMixin\n----------------------------\n\nMixin provides means for retrieving a random entity. Since retrieving all\nentites in order to pick a random item from the list would be too CPU\nintensive, this model mixin adds a ``random_id`` property, which contains a\nrandomly generated integer which is used to look up 'random' entities. The\nrandom number is assigned in a ``_pre_put_hook``.\n\nDuring querying, it samples from the database a subset of 10 entities whose\n``random_id`` is larger than another randomly generated integer and chooses a \nrandom entity from the sample.\n\nThis is obviously not a sure-fire way to retrieve a random entity. The random\nnumber generated during lookup may be larger than any of the random numbers in\nthe database, so another query may be needed.\n\nThe mixin adds one classmethod used to retrieve a random entity:\n``RandomMixin.random()``. Calling this classmethod will retreive one random\nentity.\n\nThere is also a utility method ``RandomMixin.generate_random()`` which\ngenerates a random integer.\n\nndb_utils.models.UniqueByAncestryMixin\n--------------------------------------\n\nThis mixin provides a method for checking uniqueness for a specified ancestry\nchain. This is best illustrated by example. ::\n\n >>> from google.appengine.ext import ndb\n >>> from ndb_utils.models import UniqueByAncestryMixin\n >>> class Foo(ndb.Model):\n ... prop = ndb.StringProperty()\n >>> class Bar(ndb.Model):\n ... prop = ndb.StringProperty()\n >>> class Baz(UniqueByAncestryMixin, ndb.Model):\n ... ancestry_path = ['Foo', 'Bar']\n ... prop = ndb.StringProperty()\n >>> foo = Foo(id='foo')\n >>> bar = Bar(id='bar', parent=foo.key)\n >>> baz = Baz(id='baz', parent=bar.key)\n >>> ndb.put_multi([foo, bar, baz])\n >>> Baz.is_unique('foo', 'bar', 'baz')\n False\n\nTo break down the above, we define a model which specifies the ancestry path.\nThe ``ancestry_path`` property is optional, and should be a list of ancestor\nkinds. The model's own kind is implied, and will be appended to the ancestry\npath automatically.\n\nThe ``UniqueByAncestryMixin.is_unique()`` classmethod takes any number of\npositional arguments, which are interpolated into the ancestry path to build a\npair of kind-id pairs used to build a key. In the example above, we are passing\nin (in order), the ``Foo``'s id, then ``Bar``'s id, and finally the ``Baz``'s\nid, in order to test for uniqueness of the resulting key ``[('Foo', 'foo'),\n('Bar', 'bar'), ('Baz', 'baz')]``.\n\nThe method returns a boolean that is ``True`` if the resulting key is not in\nuse.\n\nNote that this mixin is only useful if the id's of all ancestors, as well as of\nthe entity itself are known in advance. If your model uses an integer id\nprovided by the datastore, you cannot use this mixin. (See the\n``UniquePropertyMixin`` for an alternative solution.)\n\nUsually, you want to check for uniqueness when creating a new entity. When\ndoing so, note that no ancestor query is performed, and thus the\n``is_unique()`` method cannot be used within transactions.\n\nIf you prefer to raise an exception when there is a clash, there is an\nexception class provided by NDB utils. You can raise this exception manually\nusing the ``UniquePropertyMixin.DuplicateError`` class, or\n``ndb_utils.exceptions.DuplicateError``, or by calling the\n``duplicate_error()`` classmethod passing it the same argument you passed to\n``is_unique()``. The last method is only a cosmetic thing. It provides a\nstandard error message and nothing more.\n\nndb_utils.models.UniquePropertyMixin\n------------------------------------\n\nThis mixin provides methods for checking uniqueness of a set of properties\nacross the datastore for a particular model. The properties that are to be\nchecked are declared using the ``unique_properties`` class property.\n\nLet's take a look at an example::\n\n >>> from google.appengine.ext import ndb\n >>> from ndb_utils.models import UniquePropertyMixin\n >>> class Foo(UniquePropertyMixin, ndb.Model):\n ... unique_properties = ['prop']\n ... prop = ndb.StringProperty()\n >>> foo = Foo(prop='foo')\n >>> foo.put()\n >>> Foo.is_unique(prop='foo')\n False\n >>> Foo.is_unique(prop='bar')\n True\n\nUnlike the ``UniqueByAncestryMixin``, the ``is_unique()`` method takes a set of\nkeyword arguments matching the property-value pairs. Only the arguments whose\nnames match the properties listed in the ``unique_properties`` list will be\nused. The method performs a ``count()`` query internally to test for existence\nof any entities matching the specified properties, and returns ``True`` if\nthere are none.\n\nThe current implementation allows a bit more flexibility than useful. There are\nno checks to catch the situations where properties listed in\n``unique_properties`` list are proper properties (you will get an\n``AttributeError`` when you call ``is_unique`` with wrong properties listed),\nand you are not required to test all listed properties either when calling\n``is_unique()``. It's up to the developer to make sure uniqueness tests are\nsuccessful.\n\nAlso note that the query performed in ``is_unique()`` method is not an ancestor\nquery, so this method cannot be used inside transactions.\n\nndb_utils.models.OwnershipMixin\n-------------------------------\n\n``OwnershipMixin`` is used to assign owners to entities. The ownership is\nestablished through a ``KeyProperty`` named ``owner``. The kind of the owner\nentity should be called 'User', and owner is required.\n\nThe mixin provides two methods. One is the ``assign_owner()`` method, which\ntakes either an owner entity or its key and assigns the key to the ``owner``\nproperty. The other method is ``is_owner()`` which takes an owner entity or its\nkey and tests if the entity is owned by the entity.\n\nThe mixin also provides a classmethod, ``get_by_owner()`` which takes either an\nowner entity or its key and returns a query object filtered by owner.\n\nndb_utils.models.ValidatingMixin\n--------------------------------\n\nThis mixin provides methods for validating model instances on ``put()`` or\nmanually. The API for this mixin is still being worked out, so consider it\nstrictly experimental.\n\nValidation uses FormEncode_ under the hood, so you will need to be(come)\nfamiliar with `its API`_.\n\nThe model should have a validation schema, which is a simple dictionary mapping\nproperty names to validators. At the moment, we are not using the FormEncode's\n``Schema`` class, but expect the dictionary schema to be replaced with\nFormEncode schema in future.\n\nHere is a simple example with an email field::\n\n >>> from google.appengine.ext import ndb\n >>> from formencode.validators import Email\n >>> from ndb_utils.models import ValidatingMixin\n >>> class Foo(ValidatingMixin, ndb.Model):\n >>> validate_schema = {'prop': Email()}\n >>> prop = ndb.StringProperty()\n >>> f1 = Foo(prop='invalid_email')\n >>> f1.put()\n Traceback (most recent call last):\n ...\n ValidationError: ...\n >>> f2 = Foo(prop='good@email.com')\n >>> f1.put()\n\nThe ``ValidationError`` exception can be accessed as a property on the model::\n\n >>> try:\n ... f1.put()\n ... except Foo.ValidationError:\n ... print 'Not a valid email'\n\nInternally, when ``put()`` is called, the ``clean()`` instance method is called\nin the ``_pre_put()`` hook. This method goes over all keys in the schema, and\ncalls the validator's ``to_python()`` method on the value of the property. If\nthe validator raises ``formencode.Invalid`` exception, it remembers the error\nand continues. When all validation schema keys are processed, it raises the\n``ValidationError`` exception if there had been any errors.\n\nRepeated properties are currently not supported. This is planned for future\nreleases. Meanwhile, you can create a custom validator to validate repeated\nproperties.\n\nIf you prefer to always validate manually, you can set the ``validate_on_put``\nclass property to ``False`` and call the ``clean()`` method manually.\n\nThe ``clean()`` method returns cleaned data, instead of assigning them to\nproperties, so you will need to call ``populate()`` on the instance to assign\nthe new values. For instance::\n\n >>> try:\n ... f1.populate(**f1.clean())\n ... except Foo.ValidationError:\n ... print 'Not a valid email'\n\nProperty classes\n================\n\nThe NDB utils provide a few property classes that provide features not\navailable in the NDB API by default.\n\nndb_utils.properties.SlugProperty\n---------------------------------\n\nThis is a ``StringProperty`` that validates strings that shoudl be in slug format\n(only containing letters A to Z, digits, underscores and dashes).\n\nndb_utils.properties.EmailProperty\n----------------------------------\n\nThis is ``StringProperty`` that validates email addresses.\n\nndb_utils.properties.DecimalProperty\n------------------------------------\n\nThis property stores decimals (python's ``decimal.Decimal`` type). The values\nare internally stored as integers and querying with comparison operators such\nas ``>=`` or ``<`` is supported. The floating point precision can be specified,\nwhich is used when converting between decimals and integers. This value is\nspecified using ``float_prec`` argument and is 2 by default.\n\n\n.. _FormEncode: http://www.formencode.org/en/latest/\n.. _its API: http://www.formencode.org/en/latest/Validator.html", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/Outernet-Project/ndb-utils", "keywords": null, "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "ndb-utils", "package_url": "https://pypi.org/project/ndb-utils/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/ndb-utils/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/Outernet-Project/ndb-utils" }, "release_url": "https://pypi.org/project/ndb-utils/0.1a1/", "requires_dist": null, "requires_python": null, "summary": "Utilities for working with GAE NDB models", "version": "0.1a1" }, "last_serial": 1033819, "releases": { "0.1a1": [ { "comment_text": "", "digests": { "md5": "e698004ab6cb958ad110a211caf23649", "sha256": "df14e18b84c22fd57d99b1ef52edd9584b15b30f7efb8f4497c701a594922121" }, "downloads": -1, "filename": "ndb-utils-0.1a1.tar.gz", "has_sig": false, "md5_digest": "e698004ab6cb958ad110a211caf23649", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7439, "upload_time": "2014-03-18T15:48:36", "url": "https://files.pythonhosted.org/packages/15/eb/c0a9d5d2f6a72f9560cdcc1f626b38d4a2ba89d3b611dd58d9caad232d51/ndb-utils-0.1a1.tar.gz" }, { "comment_text": "", "digests": { "md5": "84b636f0dee18d144a4c348a2db6e6f9", "sha256": "56399261401c190b66191889c970da8270a218f3496aeafe886338d1d88c57d5" }, "downloads": -1, "filename": "ndb-utils-0.1a1.zip", "has_sig": false, "md5_digest": "84b636f0dee18d144a4c348a2db6e6f9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11707, "upload_time": "2014-03-18T15:48:33", "url": "https://files.pythonhosted.org/packages/0c/73/9add30f19b4bb5e10e75eff326f3585a5a078ca90d4aee7d9f7edc70e13f/ndb-utils-0.1a1.zip" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "e698004ab6cb958ad110a211caf23649", "sha256": "df14e18b84c22fd57d99b1ef52edd9584b15b30f7efb8f4497c701a594922121" }, "downloads": -1, "filename": "ndb-utils-0.1a1.tar.gz", "has_sig": false, "md5_digest": "e698004ab6cb958ad110a211caf23649", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7439, "upload_time": "2014-03-18T15:48:36", "url": "https://files.pythonhosted.org/packages/15/eb/c0a9d5d2f6a72f9560cdcc1f626b38d4a2ba89d3b611dd58d9caad232d51/ndb-utils-0.1a1.tar.gz" }, { "comment_text": "", "digests": { "md5": "84b636f0dee18d144a4c348a2db6e6f9", "sha256": "56399261401c190b66191889c970da8270a218f3496aeafe886338d1d88c57d5" }, "downloads": -1, "filename": "ndb-utils-0.1a1.zip", "has_sig": false, "md5_digest": "84b636f0dee18d144a4c348a2db6e6f9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11707, "upload_time": "2014-03-18T15:48:33", "url": "https://files.pythonhosted.org/packages/0c/73/9add30f19b4bb5e10e75eff326f3585a5a078ca90d4aee7d9f7edc70e13f/ndb-utils-0.1a1.zip" } ] }