{
"info": {
"author": "Jonas und der Wolf GmbH",
"author_email": "opensource@jonasundderwolf.de",
"bugtrack_url": null,
"classifiers": [
"Development Status :: 4 - Beta",
"Environment :: Web Environment",
"Framework :: Django",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Topic :: Software Development :: Libraries :: Python Modules"
],
"description": "===================\nfallback-property\n===================\n\n.. image:: https://img.shields.io/pypi/v/fallback-property.svg\n :target: https://pypi.python.org/pypi/fallback-property\n\n.. image:: https://travis-ci.org/jonasundderwolf/fallback-property.png?branch=master\n :target: http://travis-ci.org/jonasundderwolf/fallback-property\n :alt: Build Status\n\n.. image:: https://coveralls.io/repos/jonasundderwolf/fallback-property/badge.png?branch=master\n :target: https://coveralls.io/r/jonasundderwolf/fallback-property\n :alt: Coverage\n\n.. image:: https://img.shields.io/pypi/pyversions/fallback-property.svg\n\n.. image:: https://img.shields.io/pypi/status/fallback-property.svg\n\n.. image:: https://img.shields.io/pypi/l/fallback-property.svg\n\nRequirements\n============\n\n- Python 3.6\n\n\nWhat is it?\n===========\n\n``fallback_property`` transforms a function into a property and uses the\ndecorated function as fallback if no value was assigned to the property itself.\nA special descriptor (``fallback_property.FallbackDescriptor``)\nis used internally.\n\n\nDjango (or similar frameworks)\n------------------------------\n\n``fallback_property`` is useful if you have a function that aggregates\nvalues from related objects, which could already be fetched using an annotated\nqueryset.\nThe decorator will favor the precalculated value over calling the actual method.\n\nIt is especially helpful, if you optimize your application and want to\nreplace legacy or performance critical properties with precalulated values\nusing ``.annotate()``.\n\n\nHow to use it?\n==============\n\nSimply define a function and use the decorator ``fallback_property`` ::\n\n from fallback_property import fallback_property\n\n class Foo:\n\n @fallback_property()\n def fallback_func(self):\n return 7\n\n\nArguments\n---------\n\nThe ``fallback_property()`` has two optional arguments.\n\n``cached: bool = True``\n If the property is accessed multiple times, call the fallback function only once.\n\n``logging: bool = False``\n Log a warning if there was a fallback to the decorated, original method.\n\n\nUsage Example (Django)\n======================\n\nSuppose we have the following models ::\n\n from django.db import models\n\n\n class Pipeline(model.Model):\n @property\n def total_length(self):\n return sum(self.parts.values_list('length', flat=True))\n\n\n class Parts(models.Model):\n length = models.PositiveIntegerField()\n pipeline = models.ForeignKey(Pipeline, related_name='parts')\n\n\nCalling ``pipline.total_length`` will always trigger another query and is\neven more expensive when dealing with multiple objects. This can be\noptimized by using ``.annotate()`` and ``fallback_property()`` ::\n\n from django.db import models, QuerySet\n from django.db.functions import Coalesce\n from django.db.models import Sum\n from fallback_property import fallback_property\n\n\n class PipelineQuerySet(QuerySet):\n\n def with_total_length(self):\n return self.annotate(\n total_length=Coalesce(\n Sum('parts__length', output_field=models.IntegerField()),\n 0,\n )\n )\n\n\n class Pipeline(model.Model):\n\n @fallback_property(logging=True)\n def total_length(self):\n return sum(self.parts.values_list('length', flat=True))\n\n\nYou can now access the ``total_length`` without triggering another query or\nget a warning, when the fallback function is used ::\n\n pipeline = Pipeline.objects.with_total_length().first()\n print(pipeline.total_length)\n\n\n**Important: The annotated value and the property must have the same name.**\n\n\nRelated objects\n---------------\n\nWhen dealing with related objects in Django be aware that the ORM imposes certain limitations:\n\nIn the following example one might expect to get an instance of ``User``, but instead the\nvalue of the primary key is returned::\n\n from django.db import models, QuerySet\n from django.db.functions import Coalesce\n from django.db.models import F\n from fallback_property import fallback_property\n\n\n class PartQuerySet(QuerySet):\n\n def with_owner(self):\n return self.annotate(\n owner=Coalesce(\n F('_owner'),\n F('pipeline__owner'),\n None,\n )\n )\n\n\n class Pipeline(model.Model):\n owner = models.ForeignKey(User)\n\n\n class Parts(models.Model):\n _owner = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)\n length = models.PositiveIntegerField()\n pipeline = models.ForeignKey(Pipeline, related_name='parts')\n\n objects = PartQuerySet()\n\n @fallback_property()\n def owner(self):\n return self._owner or self.pipline.owner\n\n\n >>> print(Part.objects.with_owner().first().owner)\n >>> 1\n\n\nDevelopment\n===========\n\nThis project is using `poetry `_ to manage all\ndev dependencies.\n\nClone this repository and run ::\n\n poetry develop\n poetry run pip install django\n\nto create a virtual environment with all dependencies.\n\nYou can now run the test suite using ::\n\n poetry run pytest\n\n\nThis repository follows the `angular commit conventions `_.\nYou can register a pre-commit hook to validate your commit messages by using\n`husky `_. The configurations are already in place if\nyou have nodejs installed. Just run ::\n\n npm install\n\n\nand the pre-commit hook will be registered.\n",
"description_content_type": "text/x-rst",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "https://github.com/jonasunnderwolf/fallback-property",
"keywords": "",
"license": "BSD-3-Clause",
"maintainer": "Jonas und der Wolf GmbH",
"maintainer_email": "opensource@jonasundderwolf.de",
"name": "fallback-property",
"package_url": "https://pypi.org/project/fallback-property/",
"platform": "",
"project_url": "https://pypi.org/project/fallback-property/",
"project_urls": {
"Documentation": "https://github.com/jonasundderwolf/fallback-property/blob/master/README.rst",
"Homepage": "https://github.com/jonasunnderwolf/fallback-property",
"Repository": "https://github.com/jonasundderwolf/fallback-property"
},
"release_url": "https://pypi.org/project/fallback-property/0.1.0/",
"requires_dist": null,
"requires_python": ">=3.6",
"summary": "A decorator which prefers a precalculated attribute over calling the decorated method.",
"version": "0.1.0"
},
"last_serial": 4831834,
"releases": {
"0.1.0": [
{
"comment_text": "",
"digests": {
"md5": "d3943ee2c40e479024bba029817f46b8",
"sha256": "28e7c589ac747fbf9a119040064142ee482260b13b058db9b4aae37504bd319a"
},
"downloads": -1,
"filename": "fallback_property-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "d3943ee2c40e479024bba029817f46b8",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 7411,
"upload_time": "2019-02-17T16:21:51",
"url": "https://files.pythonhosted.org/packages/26/f0/bdf42f6b358769d0a3f2650645ddff751e3be1e97d81a71e74cdfcafe956/fallback_property-0.1.0-py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "2dd9e571b4c97a5b1252dbc926900d50",
"sha256": "9ffb4adaf381675fff6d27e405ec32dd20a5b87e596600d168361f5ce9149170"
},
"downloads": -1,
"filename": "fallback-property-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "2dd9e571b4c97a5b1252dbc926900d50",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 5365,
"upload_time": "2019-02-17T16:21:49",
"url": "https://files.pythonhosted.org/packages/51/42/4005452ed897a00607d889099d7dc79bbe0bf26f1cae0481615756d1638b/fallback-property-0.1.0.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "d3943ee2c40e479024bba029817f46b8",
"sha256": "28e7c589ac747fbf9a119040064142ee482260b13b058db9b4aae37504bd319a"
},
"downloads": -1,
"filename": "fallback_property-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "d3943ee2c40e479024bba029817f46b8",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 7411,
"upload_time": "2019-02-17T16:21:51",
"url": "https://files.pythonhosted.org/packages/26/f0/bdf42f6b358769d0a3f2650645ddff751e3be1e97d81a71e74cdfcafe956/fallback_property-0.1.0-py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "2dd9e571b4c97a5b1252dbc926900d50",
"sha256": "9ffb4adaf381675fff6d27e405ec32dd20a5b87e596600d168361f5ce9149170"
},
"downloads": -1,
"filename": "fallback-property-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "2dd9e571b4c97a5b1252dbc926900d50",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 5365,
"upload_time": "2019-02-17T16:21:49",
"url": "https://files.pythonhosted.org/packages/51/42/4005452ed897a00607d889099d7dc79bbe0bf26f1cae0481615756d1638b/fallback-property-0.1.0.tar.gz"
}
]
}