{ "info": { "author": "Charlie DeTar", "author_email": "cfd@media.mit.edu", "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", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "django-conceptq\n===============\n\n``conceptq`` is a small, simple utility for pullng portable, composable\n\"concepts\" out of Django ORM queries, using Q objects. \n\nthe problem\n-----------\n\nThe problem: say you have a bit of logic that applies to a model -- for example, a pizza topping is \n\"savory\" if it's salty or spicy, but not sweet. The models:\n\n::\n\n class ToppingManager(models.Manager)\n def savory(self):\n return self.filter(Q(savory=True) | Q(salty=True), sweet=False)\n\n class Topping(models.Model):\n savory = models.BooleanField()\n salty = models.BooleanField()\n sweet = models.BooleanField()\n\n objects = ToppingManager()\n\n class Pizza(models.Model):\n toppings = models.ManyToManyField(Topping)\n\nThis works great, until you want to carry that concept over to \"Pizza\" classes.\nHow do you say that a pizza is savory? It's savory if its toppings are. But\nwhen our logic for the toppings is bound up in the Topping manager, we end up\nhaving to duplicate it in the Pizza manager too:\n\n::\n\n class PizzaManager(models.Manager):\n def savory(self):\n # Oh noes! Duplication city!\n return self.filter(Q(topping__savory=True) | Q(topping__salty=True),\n topping__sweet=False)\n\nWith each step down the related manager chain, we're duplicating the query\nlogic yet again. Not very DRY, bug magnet, etc.\n\nconceptq\n--------\n\n``conceptq`` is primarily a single decorator, ``@concept``, that you can apply to \na manager method. It expects the method to return a \n`Q object `_,\nrather than a queryset; but it wraps the Q object in a ``filter`` call so the \nmanager methods still chain like normal. In addition, it provides a ``via`` method \nto prefix all the calls for you:\n\n::\n\n class ToppingManager(models.Manager):\n @concept\n def savory(self):\n return Q(Q(savory=True) | Q(salty=True), sweet=False)\n\nThis can then be used this way:\n\n::\n\n Topping.objects.savory() # --> returns queryset\n Topping.objects.savory().q # --> returns the Q object \n Topping.objects.savory().via(\"prefix\") # --> returns Q object with prefixed fields\n\nNow we can have fun across relations:\n\n::\n\n Pizza.objects.filter(Topping.objects.savory().via(\"toppings\"))\n # filters with Q(Q(toppings__savory=True) | Q(toppings__salty=True), toppings__sweet=False)\n Customer.objects.filter(Topping.objects.savory().via(\"favorite_pizza__toppings\"))\n # filters with Q(Q(favorite_pizza__toppings__savory=True) | Q(favorite_pizza__toppings__salty=True),\n # Q(favorite_pizza__toppings__sweet=False))\n\nAnd we can use the manager class as a place to build a library of higher level\nconcepts that can be composed together easily:\n\n::\n\n class ToppingManager(models.Manager):\n @concept\n def savory(self):\n return Q(Q(savory=True) | Q(salty=True), sweet=False)\n\n @concept\n def cajun(self):\n return Q(savory=True, burnt=True)\n\n @concept\n def high_calorie(self):\n return Q(calories__gte=300)\n\n @concept\n def diet_cajun(self):\n return ((self.savory().q | self.cajun().q) & ~self.high_calorie().q)\n\n\n >>> diet_cajun_pizzas = Pizza.objects.filter(Topping.objects.diet_cajun().via(\"toppings\"))\n # filters with: \n # Q(\n # Q(Q(toppings__savory=True) | Q(toppings__salty=True), Q(toppings__sweet=False)) |\n # Q(toppings__savory=True, toppings__burnt=True)\n # ) & ~Q(toppings__calories__gte=300)\n # \n\nOh, and it works with F expressions too.\n\nFor more, see the included ``testproject`` and the only 40 lines of source code.\n\nlicense\n-------\n\nCopyright (c) 2013, Charlie DeTar\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met: \n\n1. Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer. \n2. Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution. \n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nThe views and conclusions contained in the software and documentation are those\nof the authors and should not be interpreted as representing official policies, \neither expressed or implied, of the FreeBSD Project.", "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/yourcelf/conceptq", "keywords": null, "license": "UNKNOWN", "maintainer": null, "maintainer_email": null, "name": "django-conceptq", "package_url": "https://pypi.org/project/django-conceptq/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/django-conceptq/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/yourcelf/conceptq" }, "release_url": "https://pypi.org/project/django-conceptq/0.1.0/", "requires_dist": null, "requires_python": null, "summary": "Tiny query wrapper for composable, cross-relation complex queries.", "version": "0.1.0" }, "last_serial": 568397, "releases": { "0.1.0": [] }, "urls": [] }