{ "info": { "author": "Gavin McQuillan", "author_email": "gavin@urbanairship.com", "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-finial\n=============\n\n[![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/)\n\nHierarchical template overriding on a per request basis.\n\n**Deprecation Notice:** This project is no longer actively maintained, and will not be\nupdated for future Django versions. You may be interested in similar projects such as\n[django-waffle][] or [gargoyle][].\n\n[django-waffle]: https://waffle.readthedocs.io/en/latest/\n[gargoyle]: https://gargoyle.readthedocs.io/en/latest/\n\n\nDefinition\n----------\nfin\u00b7i\u00b7al \n`/fin\u0113\u0259l/`\n*Noun*\n\n![Alt text](finial.png \"Example of a finial in architecture.\")\n\n1. A distinctive ornament at the apex of a roof, pinnacle, canopy, or\nsimilar structure.\n2. An ornament at the top, end, or corner of an object such as a post,\npiece of furniture, etc.\n\nYou might see the relation given that this project's goal is to create a\nsparse branch of templates for each override, thus \"decorating\" the\n\"top\" of your existing templates hierarchy.\n\nWhat's it for?\n--------------\n\nAny circumstance in which you wish to do A/B testing, or do dark-launches, or even\ninclude user-specified theming (although, it's a bit heavy for general purpose use),\nyou can use django-finial to do that for you. \n\nIt's especially nice when you're not able or not willing to have multiple versions\nof your software stack deployed in order to get A/B. This allows you to do all of your\nA/B testing on the same branch/checkout.\n\n\nHow it works\n------------\n\nPrincipally, Finial works at the middleware layer of the request/response cycle.\nIf request.user is logged in and said user has overrides defined, finial will rearrange \nthe order of ```STATICFILES_DIRS```, and ```TEMPLATE_DIRS``` such that their resources\nwill be loaded before the 'default' ones. \n\nThere are other features which enable you to override URL paths to views, template_tags to\nspecify URLS for certain assets in your templates, and helpers that will build the URLConf\nsettings necessary to host static assets locally for all of your overrides.\n\nHow to install it\n-----------------\n\nInstallation is easy to get started, but can be quite customized.\n\nFor basic use:\n\n* Install the package ```(virtualenv)# pip install django-finial```\n* Add ```finial``` to your list of INSTALLED_APPS in ```settings.py```\n* Add ```finial.middleware.TemplateOverrideMiddleware``` to your project's middleware tuple\n(someplace after Session and Authentication)\n* Add ```finial.context_processors.asset_url``` to your ```TEMPLATE_CONTEXT_PROCESSORS``` tuple.\n* Run ```python manage.py migrate``` to pick up migrations and table creations\n\nThis allows you to override template loading. But it only gets you so far -- you may need\nto have an override-prefix for finial to use to find your tempalte directories.\n\nPut this in your ```settings.py``` to have finial look in ```/path/to/django/overrides/_templates/```\n\n```python\nFINIAL_TEMPLATE_DIR_PREFIX = '/overrides' # This is the directory prefix from your PROJECT_PATH\n```\nSee ```example_settings.py``` for other common settings\n\n\nGetting Started\n---------------\n\nThere are three different systems at work in Finial: \n\n 1. Template Overrides -- short circuit template loader to load your overridden template first.\n 2. URLConf Overrides -- create a request.urlconf which puts the override url patterns first.\n 3. Staticfiles Overrides -- short circuit staticfiles loaders to load contents from override dirs.\n\n**Template Overrides**\n\nThese are the most straight forward of the three mechanisms. Basically, it takes advantage of the fact that django\nwill return the first template whose name matches the one requested by a view's response constructor. It does this\nby shuffling the order of the ```TEMPLATE_DIRS``` in settings on a per request basis in Finial's ``middleware``. Finial will\nlook for users who have overrides enabled; when it finds them it takes all of the overrides for the given user and \nrearranges the directory structure for ```TEMPLATE_DIRS``` in override priority order.\n\nPriority, in our case, assumes that lower is more important. So an override with a priority of 1 should always win out.\n\n**URLConf Overrides**\n\nThese are a little more complex, but you can opt to use these when you need to have fundamental changes to view logic\nfor a given url endpoint. It allows you to (again on a per request basis) shuffle the ordering of urlpatterns so \nan overridden view can be used in place of the default view for a given url pattern. \n\nWe do this using the django machinery which checks each request object for a ```request.urlconf``` attribute. If it finds\nthis, then it ignores the root URLConf.\n\nFinial knows to setup this request.urlconf by looking at your override urlconf module (has to be its own module) path in\n```settings.py```. We look for a dictionary of override names to urlconf import strings like the following:\n\n```python\n\nFINIAL_URL_OVERRIDES = {\n 'my_override': 'my_project.my_override_finial_patterns'\n}\n```\n\n**Staticfiles Overrides**\n\nOddly, this is the most complex of the three situations. While traditional HTTP servers were designed only with static\ncontents in mind -- django's highly dynamic nature puts it at odds with static media.\n\nWe have two areas in which we have to do pretty radically different things. In development, we need django so serve\nour content. In production we generally have static media hosted by a different domain entirely (S3, CDNs, etc.). To\naddress these differences we have a couple of helper methods to sort things out:\n\n**Local Development with Staticfiles Overrides**\n\nIn this situation people often setup custom django url endpoints to serve the static media from a checkout of their\nproject locally.\n\n```python\nif settings.DEBUG:\n urlpatterns += patterns('',\n url(r'^static/(?P.*)$', 'django.views.static.serve', {\n 'document_root': settings.MEDIA_ROOT}),\n )\n```\n\nSo, to get do the same for all of the overrides we're testing locally, we need a separate url endpoint for each one:\n\n```python\n\nif settings.DEBUG:\n # Remember to do this BEFORE regular staticfiles serving.\n urlpatterns = finial_shortcuts.create_local_override_urls(urlpatterns)\n urlpatterns += patterns('',\n url(r'^static/(?P.*)$', 'django.views.static.serve', {\n 'document_root': settings.MEDIA_ROOT}),\n )\n```\nYou'll notice that a lot of whether you're developing locally or working in production is determined by the ```settings.DEBUG``` flag.\nIf you use some other variable for this, make sure that it mimics DEBUG.\n\nHowever, there's still one more thing you must do to get your static media to load properly for local development.\nMake sure to prepend your ```settings.STATICFILES_FINDERS``` with ```finial.finders.FinialFileSystemFinder```.\n\nYour ```local_settings.py``` should look something like this for staticfiles configurations:\n\n```python\n\nDEBUG=True\nPROJECT_PATH = '/path/to/django/root/'\nFINIAL_URL_VERSION_PREFIX = 'deploy5-'\nFINIAL_STATIC_URL_PREFIX = 'https://s3.amazonaws.com/com.finial.media'\nFINIAL_TEMPLATE_DIR_PREFIX = '/overrides'\nSTATICFILES_FINDERS = (\n 'finial.finders.FinialFileSystemFinder',\n 'django.contrib.staticfiles.finders.FileSystemFinder',\n 'django.contrib.staticfiles.finders.AppDirectoriesFinder',\n)\n```\n\n_Note_: If we want to not require all of our overrides directories to live in our project root, we need to specify\na ```FINIAL_TEMPLATE_DIR_PREFIX``` which is applied after the ```PROJECT_PATH``` variable. The 'root' override directory\nfrom the example above would be ```/path/to/django/root/overrides/```.\n\n\n**Production with Staticfiles Overrides**\n\nAgain, we know we're in production based on the ```settings.DEBUG``` value being ```False```. In that case instead of using local paths,\nwe construct a URL using ```settings.FINIAL_STATIC_URL_PREFIX```, the override name, and optionally a deploy version (\nspecified with ```settings.FINIAL_URL_VERSION_PREFIX```\n\nThe URLs for these assets are determined by the template context processor that we installed in our ```settings.TEMPLATE_CONTEXT_PROCESSORS```\nback in the begining.\n\n\n**Directory Structure**\n\nSince templates and staticfiles are so totally different, we keep them in separate directory structures.\n\n```\n~/path/to/django/overrides/$ tree\n.\n\u251c\u2500\u2500 test_or_staticfiles\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 images\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 configure-image.png\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 docs-image.png\n\u2514\u2500\u2500 test_or_template\n \u251c\u2500\u2500 apps\n \u2502\u00a0\u00a0 \u2514\u2500\u2500 view.html\n \u2514\u2500\u2500 base.html\n\n4 directories, 4 files\n```\n\nOr, alternatively, you can actually put your static media in a 'chroot' inside the test of your static media\nby defining the ``FINIAL_URL_PREFIX``:\n\nIn settings.py:\n```python\nFINIAL_STATIC_URL = 'static/'\nFINIAL_URL_PREFIX = 'overrides/'\n```\nThen you could have a directory structure like so:\n```\n~/path/to/django/static$ tree\nstatic/\n\u2514\u2500\u2500 overrides\n \u2514\u2500\u2500 test_or\n \u2514\u2500\u2500 apps\n \u251c\u2500\u2500 configure-image.png\n \u2514\u2500\u2500 docs-image.png\n\n3 directories, 2 files\n```\n\nThis is particularly nice because it enables us to differentiate between overrides on the same CDN.\nWe don't need to deploy to separate S3 buckets for each override.\n\nCreating/Assigning Overrides\n----------------------------\n**Simple Override Creation**\nThe easy, straightforward way is to simply enter form fields using the Admin interface.\nThis works great for adding singular individuals, or for getting a test override setup in\nyour development environment.\n\n\nBasically, each row in the overrides table defines four things:\n\n- Who\n- Name of the overrides (used in things like urlconf keynames, etc.)\n- Directory path of the overrides (may be used in conjunction with `FINIAL_TEMPLATE_DIR_PREFIX`).\n- Priority (how should this override be rated vs. others?)\n\n**Programmatic Override Creation**\n\nThis is mostly filesystem stuff and some configs. If you've completed the settings assignments above, then\nwe're one step closer.\n\nTaking our ``FINIAL_TEMPLATE_DIR_PREFIX`` into account, we can create a structure like this:\n\n```bash\nmdkir $PROJECT_ROOT/overrides/test_override_template\n```\n\nNow you can copy the specific templates over that you'd like to change. These should be picked up before \n\"regular\" templates for those users who have the override.\n\n**Assigning the Override to a User**\n\nInside your ``manage.py shell``:\n\n```python\n\n>>> from finial import models\n>>> from django.contrib.auth.models import User\n>>> me = User.objects.get(username='gavin') # whichever username here.\n>>> my_override = models.UserTemplateOverride()\n>>> my_override.user = me\n>>> my_override.priority = 10 # In case there are more overrides later\n>>> # These two are always the same in our system; we may do away with one someday...\n>>> my_override.override_name = 'test_override'\n>>> my_override.override_dir = 'test_override'\n>>> my_override.save()\n```\n\nNow login as this user, ``gavin`` in this case, and see if the templates loaded for this user are different\nas you expect.\n\n\nProducing Results: Context Processors\n-------------------------------------\n\nThere are two primary ways of surfacing the differences within template data to users. Both of these make use of\nDjango's Context Processors. As such, you'll need to make sure that views you're attempting to override\nprovide the template with a ``RequestContext`` context type (the default for ```http.render()`` now).\n\n\n**Changing Your Media URLs with asset_url**\n\nIf you just need to modify the ``MEDIA_URL`` or ``STATIC_URL``, then you'll want to use the ``asset_url``.\n\nThe ``asset_url`` assumes you have a request context (because there's data we tack onto the request object\nabout which override we're selecting). You'll use this method in situations in which the static media\nlinked to in the template are different than they are for the rest of the site. It works in two parts;\nfirst, you'll need to define which override is \"active\" for a given view using a decorator; \nsecond, you'll need to make sure that the ``asset_url`` context processor is setup (only needs to happen once).\nUsually, this is necessary when you're doing ``URLConf`` overrides.\n\nIn Settings.py:\n```python\n\nfrom django.conf.global_settings import TEMPLATE_CONTEXT_PROCESSORS\n\nTEMPLATE_CONTEXT_PROCESSORS += (\n 'finial.context_processors.asset_url'\n)\n```\n\nIn your view:\n```python\n\nfrom finial.decorators import active_override\n\n@active_override('test_or')\ndef override_view(request, *args, **kwargs):\n # Sometimes it's easiest just to proxy back to the original view.\n return original_view(request, *args, **kwargs)\n\n```\n\nNow the template returned by ``original_view`` will automatically have their\n``MEDIA_URL``, and ``STATIC_URL`` converted to the appropriate URL for \nlocal development, or for production (as defined by ``settings.DEBUG``).\n\n\n**Informing Javascript of Overrides with override_names**\n\nSometimes your Javascript code will be pretty divorced from your Django deployment. In those cases,\nyou cannot rely on Finial to do the right thing. Instead finial comes with the ability to just inform\nJavascript code of which overrides are present for a given user. For this we use the ``override_names``\ncontext processor.\n\nConsider the following template code (note: this is mean to always be rendered for a site, not just in an override).\n\n```html\n{% if FINIAL_POINTS %}\n
\n{% endif %}\n```\n\nThis way, javascript gets a string which it can parse using a JSON parser to get a list of override_names (in priority order),\nand can make the appropriate choices with which functions to include/run, or templates to render.\n", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://github.com/urbanairship/django-finial", "keywords": "", "license": "BSD", "maintainer": "", "maintainer_email": "", "name": "django-finial", "package_url": "https://pypi.org/project/django-finial/", "platform": "", "project_url": "https://pypi.org/project/django-finial/", "project_urls": { "Homepage": "http://github.com/urbanairship/django-finial" }, "release_url": "https://pypi.org/project/django-finial/0.7.2/", "requires_dist": null, "requires_python": "", "summary": "Template, Static, and URL Overrides per User.", "version": "0.7.2" }, "last_serial": 2380832, "releases": { "0.3.3": [ { "comment_text": "", "digests": { "md5": "06cf5e7d0305e7c495a93fb28e5d7b77", "sha256": "199bf650a672f9def71f9534327b36b66b9311c211f77820e00df44cb18a535b" }, "downloads": -1, "filename": "django-finial-0.3.3.tar.gz", "has_sig": false, "md5_digest": "06cf5e7d0305e7c495a93fb28e5d7b77", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18474, "upload_time": "2013-06-21T19:22:02", "url": "https://files.pythonhosted.org/packages/0a/ba/95cabd650c4341e9fd04b2da8ec9a3558c0e5d7039e6ff7b9b88846dde79/django-finial-0.3.3.tar.gz" } ], "0.3.4": [ { "comment_text": "", "digests": { "md5": "217d4c001dd3b320e1d1ce0cb1ff76a5", "sha256": "90e3e4fb404979fbe3275b5bdc33a4e3f5cd2a542da4963d661466a18f0fba26" }, "downloads": -1, "filename": "django-finial-0.3.4.tar.gz", "has_sig": false, "md5_digest": "217d4c001dd3b320e1d1ce0cb1ff76a5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18591, "upload_time": "2013-07-13T20:18:50", "url": "https://files.pythonhosted.org/packages/5b/6b/5923190f5b54f03556cb5612007968c2227da3eb4723ad771aba7abedc82/django-finial-0.3.4.tar.gz" } ], "0.7.2": [ { "comment_text": "", "digests": { "md5": "af6f7c9d5e33ef9a7f51254af82c6303", "sha256": "ddb372ad88aedadba2cf844db86236c1d78d48db2f22840114baefb28c14d46c" }, "downloads": -1, "filename": "django-finial-0.7.2.tar.gz", "has_sig": false, "md5_digest": "af6f7c9d5e33ef9a7f51254af82c6303", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22572, "upload_time": "2016-10-04T22:02:32", "url": "https://files.pythonhosted.org/packages/4d/ba/edca3f8391e47b302d56d9c78040f7e17cef9cceb948a378b780b45fd5f9/django-finial-0.7.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "af6f7c9d5e33ef9a7f51254af82c6303", "sha256": "ddb372ad88aedadba2cf844db86236c1d78d48db2f22840114baefb28c14d46c" }, "downloads": -1, "filename": "django-finial-0.7.2.tar.gz", "has_sig": false, "md5_digest": "af6f7c9d5e33ef9a7f51254af82c6303", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 22572, "upload_time": "2016-10-04T22:02:32", "url": "https://files.pythonhosted.org/packages/4d/ba/edca3f8391e47b302d56d9c78040f7e17cef9cceb948a378b780b45fd5f9/django-finial-0.7.2.tar.gz" } ] }