├── .gitignore ├── LICENSE.txt ├── README.rst ├── django_render_partial ├── __init__.py ├── templatetags │ ├── __init__.py │ └── render_partial.py └── tests │ ├── __init__.py │ └── test_templatetags.py ├── requirements.txt ├── setup.py ├── test_project ├── manage.py ├── partial_test │ ├── __init__.py │ ├── models.py │ └── views.py ├── templates │ ├── partial_test.html │ └── partial_view.html └── test_project │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | dist/ 3 | build/ 4 | *.egg-info 5 | *.pyc 6 | *.pyo 7 | *.sqlite3 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, utapyngo 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Installation ============ 1. ``pip install django_render_partial`` 2. Add ``'django_render_partial'`` to ``INSTALLED_APPS`` 3. Ensure that ``'django.template.context_processors.request'`` is in ``TEMPLATES['OPTIONS']['context_processors']`` Usage ===== Write a template for partial view, e.g. ``partial_view.html``::

{{ arg1 }} + {{ arg2 }} = {{ result }}

Write a partial view:: def partial_view(request, *args, **kwargs): result = kwargs['arg1'] + kwargs['arg2'] kwargs['result'] = result return render(request, 'partial_view.html', kwargs) if you are using function-based views, or:: class PartialView(TemplateView): template_name = 'partial_view.html' def get_context_data(self, **kwargs): result = kwargs['arg1'] + kwargs['arg2'] kwargs['result'] = result return super(PartialView, self).get_context_data(**kwargs) if you are using class-based views. Add it to ``urls.py``:: url(r'^partial-view/(?P\w+)/(?P\w+)/$', partial_view, name='partial_view'), or:: url(r'^partial-view/(?P\w+)/(?P\w+)/$', PartialView.as_view(), name='partial_view'), In your template:: {% load render_partial %} {% with some_var=2 %} {% render_partial 'partial_view' arg1=40 arg2=some_var %} {% endwith %} The ``render_partial`` tag would be rendered to::

40 + 2 = 42

Note that the ``with`` tag above is not required for ``render_partial`` to work. It is used to show that ``render_partial`` accepts variables. Every argument will be evaluated against context except for the names of any keyword arguments. If you don't want to expose your partial view in ``urls.py``, you can also use fully qualified dot separated view name:: {% render_partial 'partial_test.views.PartialView' arg1=40 arg2=some_var %} {% render_partial 'partial_test.views.partial_view' arg1=40 arg2=some_var %} **IMPORTANT**: the calling template must receive a context variable called ``request`` containing the original ``HttpRequest``. Don't forget to add ``'django.template.context_processors.request'`` to ``TEMPLATES['OPTIONS']['context_processors']``. Adapted from https://djangosnippets.org/snippets/1568/ -------------------------------------------------------------------------------- /django_render_partial/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/utapyngo/django-render-partial/37256e32a365adefddd88a58a809db3743790120/django_render_partial/__init__.py -------------------------------------------------------------------------------- /django_render_partial/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/utapyngo/django-render-partial/37256e32a365adefddd88a58a809db3743790120/django_render_partial/templatetags/__init__.py -------------------------------------------------------------------------------- /django_render_partial/templatetags/render_partial.py: -------------------------------------------------------------------------------- 1 | from django.template import Library, Node, TemplateSyntaxError, Variable 2 | from django.urls import NoReverseMatch, resolve, reverse 3 | from django.urls.utils import get_callable 4 | 5 | register = Library() 6 | 7 | 8 | class ViewNode(Node): 9 | def __init__(self, view_name, args, kwargs): 10 | self.view_name = view_name 11 | self.args = args 12 | self.kwargs = kwargs 13 | 14 | def render(self, context): 15 | if 'request' not in context: 16 | return '' 17 | request = context['request'] 18 | view_name = Variable(self.view_name).resolve(context) 19 | args = [Variable(arg).resolve(context) for arg in self.args] 20 | kwargs = {} 21 | for key, value in self.kwargs.items(): 22 | kwargs[key] = Variable(value).resolve(context) 23 | try: 24 | url = reverse(view_name, args=args, kwargs=kwargs) 25 | url = url.replace('%40', '@') 26 | match = resolve(url) 27 | view = match.func 28 | except NoReverseMatch: 29 | view = get_callable(view_name) 30 | if hasattr(view, 'as_view'): 31 | view = view.as_view() 32 | url = request.path 33 | if callable(view): 34 | old_path = request.path 35 | try: 36 | request.path = url 37 | v = view(request, *args, **kwargs) 38 | try: 39 | content = v.rendered_content 40 | except AttributeError: 41 | content = v.content.decode() 42 | return content 43 | finally: 44 | request.path = old_path 45 | raise ValueError('%r is not callable' % view) 46 | 47 | 48 | @register.tag 49 | def render_partial(parser, token): 50 | """ 51 | Inserts the output of a view, using fully qualified view name, 52 | or view name from urls.py. 53 | 54 | {% render_partial view_name arg[ arg2] k=v [k2=v2...] %} 55 | 56 | IMPORTANT: the calling template must receive a context variable called 57 | 'request' containing the original HttpRequest. This means you should be OK 58 | with permissions and other session state. 59 | 60 | (Note that every argument will be evaluated against context except for the 61 | names of any keyword arguments.) 62 | """ 63 | 64 | args = [] 65 | kwargs = {} 66 | tokens = token.split_contents() 67 | if len(tokens) < 2: 68 | raise TemplateSyntaxError( 69 | '%r tag requires one or more arguments' % 70 | token.contents.split()[0] 71 | ) 72 | tokens.pop(0) # tag name 73 | view_name = tokens.pop(0) 74 | for token in tokens: 75 | equals = token.find('=') 76 | if equals == -1: 77 | args.append(token) 78 | else: 79 | kwargs[str(token[:equals])] = token[equals+1:] 80 | return ViewNode(view_name, args, kwargs) 81 | -------------------------------------------------------------------------------- /django_render_partial/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/utapyngo/django-render-partial/37256e32a365adefddd88a58a809db3743790120/django_render_partial/tests/__init__.py -------------------------------------------------------------------------------- /django_render_partial/tests/test_templatetags.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpRequest, HttpResponse 2 | from django.template import Context, Template 3 | from django.test import TestCase, override_settings 4 | 5 | 6 | def partial_view(request, *args, **kwargs): 7 | content = Template( 8 | "{{ result }}" 9 | ).render(Context(dict( 10 | result=kwargs['x'] * 2 11 | ))) 12 | return HttpResponse(content) 13 | 14 | 15 | class RenderPartialTemplateTagTests(TestCase): 16 | @override_settings(TEMPLATES=[{'BACKEND': 'django.template.backends.django.DjangoTemplates'}]) 17 | def test_render_partial_tag(self): 18 | content = Template( 19 | '{% load render_partial %}' 20 | '{% render_partial "django_render_partial.tests.test_templatetags.partial_view" x=4 %}' 21 | ).render(Context(dict( 22 | request=HttpRequest() 23 | ))) 24 | self.assertEqual('8', content) 25 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from setuptools import setup 4 | 5 | version = '0.4' 6 | 7 | long_description = '' 8 | 9 | if os.path.exists('README.rst'): 10 | long_description = open('README.rst').read() 11 | 12 | setup( 13 | name='django-render-partial', 14 | version=version, 15 | description='Django render_partial tag allows inserting rendered views into templates', 16 | long_description=long_description, 17 | author='utapyngo', 18 | author_email='ut@pyngo.tom.ru', 19 | url='https://github.com/utapyngo/django-render-partial', 20 | packages=['django_render_partial', 'django_render_partial.templatetags'], 21 | keywords=['django', 'render', 'partial', 'view', 'template', 'tag'], 22 | classifiers=[ 23 | 'Development Status :: 4 - Beta', 24 | 'Operating System :: OS Independent', 25 | 'Intended Audience :: Developers', 26 | 'License :: OSI Approved :: BSD License', 27 | 'Programming Language :: Python', 28 | 'Programming Language :: Python :: 3', 29 | 'Topic :: Software Development :: Libraries', 30 | 'Topic :: Utilities', 31 | 'Environment :: Web Environment', 32 | 'Framework :: Django', 33 | ], 34 | ) 35 | -------------------------------------------------------------------------------- /test_project/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /test_project/partial_test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/utapyngo/django-render-partial/37256e32a365adefddd88a58a809db3743790120/test_project/partial_test/__init__.py -------------------------------------------------------------------------------- /test_project/partial_test/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/utapyngo/django-render-partial/37256e32a365adefddd88a58a809db3743790120/test_project/partial_test/models.py -------------------------------------------------------------------------------- /test_project/partial_test/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.views.generic.base import TemplateView 3 | 4 | 5 | def partial_test(request): 6 | return render(request, 'partial_test.html') 7 | 8 | 9 | class PartialView(TemplateView): 10 | template_name = 'partial_view.html' 11 | 12 | def get_context_data(self, **kwargs): 13 | result = kwargs['arg1'] + kwargs['arg2'] 14 | kwargs['result'] = result 15 | return super(PartialView, self).get_context_data(**kwargs) 16 | 17 | 18 | def partial_view(request, *args, **kwargs): 19 | result = kwargs['arg1'] + kwargs['arg2'] 20 | kwargs['result'] = result 21 | return render(request, 'partial_view.html', kwargs) 22 | -------------------------------------------------------------------------------- /test_project/templates/partial_test.html: -------------------------------------------------------------------------------- 1 | {% load render_partial %} 2 | 3 | {% with some_var=2 %} 4 | {% render_partial 'class_based_partial_view' arg1=40 arg2=some_var %} 5 | {% render_partial 'function_based_partial_view' arg1=40 arg2=some_var %} 6 | 7 | {% render_partial 'partial_test.views.PartialView' arg1=40 arg2=some_var %} 8 | {% render_partial 'partial_test.views.partial_view' arg1=40 arg2=some_var %} 9 | {% endwith %} 10 | -------------------------------------------------------------------------------- /test_project/templates/partial_view.html: -------------------------------------------------------------------------------- 1 |

{{ arg1 }} + {{ arg2 }} = {{ result }}

2 | -------------------------------------------------------------------------------- /test_project/test_project/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/utapyngo/django-render-partial/37256e32a365adefddd88a58a809db3743790120/test_project/test_project/__init__.py -------------------------------------------------------------------------------- /test_project/test_project/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for test_project project. 3 | 4 | For more information on this file, see 5 | https://docs.djangoproject.com/en/1.6/topics/settings/ 6 | 7 | For the full list of settings and their values, see 8 | https://docs.djangoproject.com/en/1.6/ref/settings/ 9 | """ 10 | 11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 12 | import os 13 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 14 | 15 | 16 | # Quick-start development settings - unsuitable for production 17 | # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/ 18 | 19 | # SECURITY WARNING: keep the secret key used in production secret! 20 | SECRET_KEY = 'j^6w$y2%2wapj-md24of%xmj+i(g4_l&hi)2pce1xm5l3a@zo6' 21 | 22 | # SECURITY WARNING: don't run with debug turned on in production! 23 | DEBUG = True 24 | 25 | ALLOWED_HOSTS = [] 26 | 27 | 28 | # Application definition 29 | 30 | INSTALLED_APPS = ( 31 | 'django.contrib.admin', 32 | 'django.contrib.auth', 33 | 'django.contrib.contenttypes', 34 | 'django.contrib.sessions', 35 | 'django.contrib.messages', 36 | 'django.contrib.staticfiles', 37 | 'django_render_partial', 38 | 'partial_test' 39 | ) 40 | 41 | MIDDLEWARE_CLASSES = ( 42 | 'django.contrib.sessions.middleware.SessionMiddleware', 43 | 'django.middleware.common.CommonMiddleware', 44 | 'django.middleware.csrf.CsrfViewMiddleware', 45 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 46 | 'django.contrib.messages.middleware.MessageMiddleware', 47 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 48 | ) 49 | 50 | TEMPLATES = [ 51 | { 52 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 53 | 'DIRS': ( 54 | os.path.join(BASE_DIR, 'templates'), 55 | ), 56 | 'OPTIONS': { 57 | 'loaders': [ 58 | ('django.template.loaders.cached.Loader', ( 59 | 'django.template.loaders.filesystem.Loader', 60 | 'django.template.loaders.app_directories.Loader', 61 | ),) 62 | ], 63 | 'context_processors': ( 64 | 'django.contrib.auth.context_processors.auth', 65 | 'django.template.context_processors.debug', 66 | 'django.template.context_processors.i18n', 67 | 'django.template.context_processors.media', 68 | 'django.template.context_processors.static', 69 | 'django.template.context_processors.tz', 70 | 'django.contrib.messages.context_processors.messages', 71 | 'django.template.context_processors.request', 72 | ) 73 | } 74 | } 75 | ] 76 | 77 | 78 | ROOT_URLCONF = 'test_project.urls' 79 | 80 | WSGI_APPLICATION = 'test_project.wsgi.application' 81 | 82 | 83 | # Database 84 | # https://docs.djangoproject.com/en/1.6/ref/settings/#databases 85 | 86 | DATABASES = { 87 | 'default': { 88 | 'ENGINE': 'django.db.backends.sqlite3', 89 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 90 | } 91 | } 92 | 93 | # Internationalization 94 | # https://docs.djangoproject.com/en/1.6/topics/i18n/ 95 | 96 | LANGUAGE_CODE = 'en-us' 97 | 98 | TIME_ZONE = 'UTC' 99 | 100 | USE_I18N = True 101 | 102 | USE_L10N = True 103 | 104 | USE_TZ = True 105 | 106 | 107 | # Static files (CSS, JavaScript, Images) 108 | # https://docs.djangoproject.com/en/1.6/howto/static-files/ 109 | 110 | STATIC_URL = '/static/' 111 | -------------------------------------------------------------------------------- /test_project/test_project/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from partial_test.views import partial_test, PartialView, partial_view 4 | 5 | from django.contrib import admin 6 | admin.autodiscover() 7 | 8 | urlpatterns = ( 9 | url(r'^$', partial_test, name='partial_test'), 10 | url(r'^function-based-partial-view/(?P\d+)/(?P\d+)/$', 11 | partial_view, 12 | name='function_based_partial_view'), 13 | url(r'^class-based-partial-view/(?P\d+)/(?P\d+)/$', 14 | PartialView.as_view(), 15 | name='class_based_partial_view'), 16 | url(r'^admin/', admin.site.urls), 17 | ) 18 | -------------------------------------------------------------------------------- /test_project/test_project/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for test_project project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings") 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | lint 4 | py{35,36,37}-django{20,21,-latest} 5 | 6 | [testenv] 7 | deps = 8 | django20: Django>=2.0,<2.1 9 | django21: Django>=2.1,<2.2 10 | django-latest: Django 11 | setenv = 12 | PIP_DISABLE_PIP_VERSION_CHECK = 1 13 | PYTHONPATH = . 14 | commands = 15 | python test_project/manage.py test -v2 16 | 17 | [testenv:lint] 18 | deps = 19 | flake8 20 | commands = 21 | flake8 --max-line-length=100 django_render_partial 22 | --------------------------------------------------------------------------------