GithubHelp home page GithubHelp logo

django-cms / django-classy-tags Goto Github PK

View Code? Open in Web Editor NEW
380.0 380.0 45.0 376 KB

Class based template tags for django

Home Page: http://django-classy-tags.rtfd.org

License: Other

Python 96.31% HTML 3.69%
django python templatetags

django-classy-tags's Introduction

django CMS

image

image

image

Code Climate

Open source enterprise content management system based on the Django framework and backed by the non-profit django CMS Association (Sponsor us!).

Contribute to this project and win rewards

Because django CMS is a community-driven project, we welcome everyone to get involved in the project. Become part of a fantastic community and help us make django CMS the best open source CMS in the world.

Attention

Please use the develop-4 branch as the target for pull requests for on-going development.

Security fixes will be backported to older branches by the core team as appropriate.

Features

  • hierarchical pages
  • extensive built-in support for multilingual websites
  • multi-site support
  • draft/publish workflows
  • version control
  • a sophisticated publishing architecture, that's also usable in your own applications
  • frontend content editing
  • a hierarchical content structure for nested plugins
  • an extensible navigation system that your own applications can hook into
  • SEO-friendly URLs
  • designed to integrate thoroughly into other applications

Developing applications that integrate with and take advantage of django CMS features is easy and well-documented.

More information on our website.

Requirements

See the Python/Django requirements for the current release version in our documentation.

See the installation how-to guide for an overview of some other requirements and dependencies of the current release.

Getting started

These tutorials take you step-by-step through some key aspects of django CMS.

Documentation

Our documentation working group maintains documentation for several versions of the project. Key versions are:

  • stable (default), for the current release version
  • latest, representing the latest build of the develop-4 branch

For more information about our branch policy, see Branches.

Our documentation is hosted courtesy of Read the Docs.

The dependencies for the docs are compiled by pip-tools.

Test django CMS in our demo

The demo platform is kindly provided by Divio, platinum member of the django CMS Association.

Try demo with Divio Cloud

Getting Help

Please head over to our Discord Server or Stackoverflow for support.

Professional support

Choose from a list of trusted tech partner of the django CMS Association to get your website project delivered successfully.

Choose a trusted web host for your django CMS project and get your website online today.

The django CMS Association

The django CMS Association is a non-profit organization that was founded in 2020 with the goal to drive the success of django CMS, by increasing customer happiness, market share and open-source contributions. We provide infrastructure and guidance for the django CMS project.

The non-profit django CMS Association is dependent on donations to fulfill its purpose. The best way to donate is to become a member of the association and pay membership fees. The funding will be funneled back into core development and community projects.

Join the django CMS Association.

Credits

django-classy-tags's People

Contributors

anukaal avatar bashu avatar brente avatar dependabot[bot] avatar finalangel avatar fladi avatar fsbraun avatar jayvdb avatar jimux avatar kevinmarsh avatar kiahosseini avatar mariajgrimaldi avatar marksweb avatar narenderrajub avatar nicolairidani avatar ojii avatar pre-commit-ci[bot] avatar thomasgoirand avatar timgraham avatar vad avatar yakky avatar yannik-ammann avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-classy-tags's Issues

Correct way to declare the Argument

Hello @ojii

My problem is what is the correct way to declare the argument
Although the documentation cites various forms of data entry for each available arguments, I could not make it work with any of them.
I tried to look at the tests, but they are very simple and do not say much
At least for me I do not speak English fluently, the documentation was not very useful for this case.
I think the implementation of a real templatetag showing that more complex functional examples would be very useful

Returning to my problem:

I have the following syntax in my template tag:

{% mytag id=10 nofullscreen cssicon="fa fa-stack-exchange" namedview="login" template="mytemplate/mycustom_render_template.html" %}
       This is the internal content, which should be rendered inside the "<div class="widget-body"></div>"
       The internal content can be any possible structures that Django Template Language allows use
         {% block overridable %} 
              {% trans "This block is overridable by django template inheritance" %} 
         {% endblock overridable %}

{% endmytag %}

I have come across, for example with the following errors

TemplateSyntaxError at /
Could not parse the remainder: '=10' from 'id=10'

TemplateSyntaxError at /
Could not parse the remainder: '="fa fa-stack-exchange"' from 'cssicon="fa fa-stack-exchange"'

basically my attempt to use the Arguments (ps. I tested the other Arguments classes, most always falls into the same error)
This is a very simplified version of what I need in templatetag / html

class Mytag(InclusionTag):
    template = "mytag/mytag_default_template.html"
    name = 'mytag'
    options = Options(
        Argument('nofullscreen',
                 default=False,
                 required=False),
        StringArgument('cssicon',
                       resolve=True,
                       required=False,)
        ,
        StringArgument('template',
                       resolve=True,
                       required=False,)
        ,
        StringArgument('namedview',
                       resolve=True,
                       required=False,)
        ,
        IntegerArgument('id',
                        default=10,
                        resolve=False,
                        required=False,)
        ,
        blocks=[('endmytag', 'nodelist')],
    )

mytag/mytag_default_template.html

<div class="widget"
     {% if id %}
        id="{{ id }}"
     {% endif %}
     {% if nofullscreen %}
        data-widget-fullscreen="false"
     {% endif %}
        {% if namedview_after_reverse_function %}
            data-widget-load="{{ namedview_after_reverse_function }}"
        {% endif %}
     >
    <header role="heading">
        <div class="widget-ctrls" role="menu">
            {% if cssicon %}
                <span class="widget-icon"> <i class="{{ cssicon }}"></i> </span>
            {% endif %}
        </div>
    </header>
    <div role="content">
        <div class="widget-body">
                TODO: Internal content
        </div>
    </div>
</div>

The HTML I wish it was generated after I fully implement this templatetag:

<div class="widget" id="10" data-widget-fullscreen="false" data-widget-load="/login/">
    <header role="heading">
        <div class="widget-ctrls" role="menu">
                <span class="widget-icon"> <i class="fa fa-stack-exchange"></i> </span>
        </div>
    </header>
    <div role="content">
        <div class="widget-body">
                       This is the internal content, which should be rendered inside the "<div class="widget-body"></div>"
                       The internal content can be any possible structures that Django Template Language allows use
         {% block overridable %} 
              {% trans "This block is overridable by django template inheritance" %} 
         {% endblock overridable %}

        </div>
    </div>
</div>

0.7.1 introduces error in flattening

Under django 1.8.8 using django cms, my templates are failing with the error dictionary update sequence element #0 has length 0; 2 is required. Problem went away when I reverted to 0.7.0. The failing traceback (below) had calls to the functions modified in 0.7.1 just before it fails.

We've got a complex codebase using django cms, and I'm afraid I don't have time to come up with a standalone proof of this failure, but it's triggering on a view where an InclusionTag includes a template which has more classy-tags.

Traceback:
File "venv/lib/python3.4/site-packages/django/core/handlers/base.py" in get_response
  164.                 response = response.render()
File "venv/lib/python3.4/site-packages/django/template/response.py" in render
  158.             self.content = self.rendered_content
File "venv/lib/python3.4/site-packages/django/template/response.py" in rendered_content
  135.         content = template.render(context, self._request)
File "venv/lib/python3.4/site-packages/django/template/backends/django.py" in render
  74.         return self.template.render(context)
File "venv/lib/python3.4/site-packages/django/template/base.py" in render
  210.                     return self._render(context)
File "venv/lib/python3.4/site-packages/django/test/utils.py" in instrumented_test_render
  96.     return self.nodelist.render(context)
File "venv/lib/python3.4/site-packages/django/template/base.py" in render
  905.                 bit = self.render_node(node, context)
File "venv/lib/python3.4/site-packages/django/template/debug.py" in render_node
  79.             return node.render(context)
File "venv/lib/python3.4/site-packages/django/template/loader_tags.py" in render
  135.         return compiled_parent._render(context)
File "venv/lib/python3.4/site-packages/django/test/utils.py" in instrumented_test_render
  96.     return self.nodelist.render(context)
File "venv/lib/python3.4/site-packages/django/template/base.py" in render
  905.                 bit = self.render_node(node, context)
File "venv/lib/python3.4/site-packages/django/template/debug.py" in render_node
  79.             return node.render(context)
File "venv/lib/python3.4/site-packages/classytags/core.py" in render
  146.         return self.render_tag(context, **kwargs)
File "venv/lib/python3.4/site-packages/sekizai/templatetags/sekizai_tags.py" in render_tag
  83.         rendered_contents = nodelist.render(context)
File "venv/lib/python3.4/site-packages/django/template/base.py" in render
  905.                 bit = self.render_node(node, context)
File "venv/lib/python3.4/site-packages/django/template/debug.py" in render_node
  79.             return node.render(context)
File "venv/lib/python3.4/site-packages/classytags/core.py" in render
  146.         return self.render_tag(context, **kwargs)
File "venv/lib/python3.4/site-packages/cms/templatetags/cms_tags.py" in render_tag
  672.         rendered_contents = nodelist.render(context)
File "venv/lib/python3.4/site-packages/django/template/base.py" in render
  905.                 bit = self.render_node(node, context)
File "venv/lib/python3.4/site-packages/django/template/debug.py" in render_node
  79.             return node.render(context)
File "venv/lib/python3.4/site-packages/django/template/loader_tags.py" in render
  65.                 result = block.nodelist.render(context)
File "venv/lib/python3.4/site-packages/django/template/base.py" in render
  905.                 bit = self.render_node(node, context)
File "venv/lib/python3.4/site-packages/django/template/debug.py" in render_node
  79.             return node.render(context)
File "venv/lib/python3.4/site-packages/django/template/loader_tags.py" in render
  65.                 result = block.nodelist.render(context)
File "venv/lib/python3.4/site-packages/django/template/base.py" in render
  905.                 bit = self.render_node(node, context)
File "venv/lib/python3.4/site-packages/django/template/debug.py" in render_node
  79.             return node.render(context)
File "venv/lib/python3.4/site-packages/classytags/core.py" in render
  146.         return self.render_tag(context, **kwargs)
File "venv/lib/python3.4/site-packages/cms/templatetags/cms_tags.py" in render_tag
  301.             content = get_placeholder_content(context, request, page, name, inherit, nodelist)
File "venv/lib/python3.4/site-packages/cms/templatetags/cms_tags.py" in get_placeholder_content
  233.         content = render_placeholder(placeholder, context, name)
File "venv/lib/python3.4/site-packages/cms/plugin_rendering.py" in render_placeholder
  163.     content.extend(render_plugins(plugins, context, placeholder, processors))
File "venv/lib/python3.4/site-packages/cms/plugin_rendering.py" in render_plugins
  92.         out.append(plugin.render_plugin(context, placeholder, processors=processors))
File "venv/lib/python3.4/site-packages/cms/models/pluginmodel.py" in render_plugin
  186.             return render_plugin(context, instance, placeholder, template, processors, context.current_app)
File "venv/lib/python3.4/site-packages/cms/plugin_rendering.py" in render_plugin
  62.         content = render_to_string(template, context)
File "venv/lib/python3.4/site-packages/django/template/loader.py" in render_to_string
  99.         return template.render(context, request)
File "venv/lib/python3.4/site-packages/django/template/backends/django.py" in render
  74.         return self.template.render(context)
File "venv/lib/python3.4/site-packages/django/template/base.py" in render
  210.                     return self._render(context)
File "venv/lib/python3.4/site-packages/django/test/utils.py" in instrumented_test_render
  96.     return self.nodelist.render(context)
File "venv/lib/python3.4/site-packages/django/template/base.py" in render
  905.                 bit = self.render_node(node, context)
File "venv/lib/python3.4/site-packages/django/template/debug.py" in render_node
  79.             return node.render(context)
File "venv/lib/python3.4/site-packages/classytags/core.py" in render
  146.         return self.render_tag(context, **kwargs)
File "venv/lib/python3.4/site-packages/classytags/helpers.py" in render_tag
  88.                 flatten_context(self.get_context(context, **kwargs))
File "venv/lib/python3.4/site-packages/classytags/utils.py" in flatten_context
  90.         return context.flatten()
File "venv/lib/python3.4/site-packages/django/template/context.py" in flatten
  110.             flat.update(d)

Exception Type: ValueError at /path/
Exception Value: dictionary update sequence element #0 has length 0; 2 is required

Proper exception handling

Django 1.3. In debug mode, I get "technical 500 response" when an exception (e.g., ArgumentRequiredError) is raised. The final error looks like

File "/usr/lib/python2.7/site-packages/django/views/debug.py", line 175, in get_template_exception_info
'message': self.exc_value.args[0],

IndexError: tuple index out of range

This is because Django tries to figure out some info using the 'args' attribute of the exception instance and this attribute hasn't been set properly. Adding 'args' to BaseError class allows to workaround this.

A good and useful app anyway.

3.0.1: 17 failing tests with TemplateSyntaxError

Hi! When trying to rebuild django-classy-tags for the removal of the python setup.py test use, I ran into 17 failing tests, e.g.:

======================================================================
ERROR: test_too_many_arguments (tests.test_core.ClassytagsTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3.10/site-packages/django/template/base.py", line 505, in parse
    compile_func = self.tags[command]
KeyError: 'no_arg'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/build/python-django-classy-tags/src/django-classy-tags-3.0.1/tests/test_core.py", line 836, in test_too_many_arguments
    self.assertRaises(exceptions.TooManyArguments,
  File "/usr/lib/python3.10/unittest/case.py", line 738, in assertRaises
    return context.handle('assertRaises', args, kwargs)
  File "/usr/lib/python3.10/unittest/case.py", line 201, in handle
    callable_obj(*args, **kwargs)
  File "/usr/lib/python3.10/site-packages/django/template/base.py", line 154, in __init__
    self.nodelist = self.compile_nodelist()
  File "/usr/lib/python3.10/site-packages/django/template/base.py", line 200, in compile_nodelist
    return parser.parse()
  File "/usr/lib/python3.10/site-packages/django/template/base.py", line 507, in parse
    self.invalid_block_tag(token, command, parse_until)
  File "/usr/lib/python3.10/site-packages/django/template/base.py", line 568, in invalid_block_tag
    raise self.error(
django.template.exceptions.TemplateSyntaxError: Invalid block tag on line 1: 'no_arg'. Did you forget to register or load this tag?

======================================================================
FAIL: test_naming (tests.test_core.ClassytagsTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/build/python-django-classy-tags/src/django-classy-tags-3.0.1/tests/test_core.py", line 570, in test_naming
    self.assertTrue('my_tag' in lib.tags, msg)
AssertionError: False is not true : 'my_tag' not in dict_keys(['MyTag'])

----------------------------------------------------------------------
Ran 72 tests in 0.009s

python-django-classy-tags-3.0.1-2-x86_64-check.log

I probably should no longer have PyPI Access

Hi, just got an email from PyPI that django-classy-tags is a "critical project". I still have "Owner" permissions on this project, but haven't contributed to this project (or even done any Django) in years. It's probably best if you remove my permissions from PyPI for this package.

The use of setup.py test is deprecated

Hi! The use of python setup.py test is deprecated and should be replaced with a call to the test suite directly:

WARNING: Testing via this command is deprecated and will be removed in a future version. Users looking for a generic test entry point independent of test runner are encouraged to use tox.

version 0.3.2 breaks django-cms 2.1.3 installed with pip

The package is installed as a dependency by installing django-cms 2.1.3.

This version creates an endless recursion.

Caught RuntimeError while rendering: maximum recursion depth exceeded

menus/templates/menu/dummy.html, error at line 1
{% extends template %}
Rolled back to version 0.3.1.

Not sure how this would work, but..

So I'm in the middle of working on a project with several apps, and I'd like to be able to display "remote" content in my views without having to muck about in the details of actually defining what it means to display something "remotely".

(NOTE: The distinction of Local vs. Remote is, in this case, relative to the app in question or not actual, factual, remote content.)


ClsidComponentView.py:

@register.tag
class ClsidComponentView(InclusionTag):
    template = 'ClsidLookupError.html'
    name = 'Avalon.ClsidComponentView'

    options = Options(
        Argument( 'prop_lhs', resolve = False ),
        '=',
        Argument( 'prop_rhs', resolve = False ),
        blocks =[
            BlockDefinition( 'content', \
                    VariableBlockName('endblock %(value)s', 'prop_lhs'), \
                    'endblock')
        ]
    )

    def get_context(self, context):
        return { "Section", "Foo" }

    def render_tag(self, context, prop_lhs, prop_rhs, block_name, content):
        print('Property %s = "%s".' %( prop_lhs, prop_rhs ))

        ## 
        ## This space intentionally left blank.
        ## 

        context.push()
        document = content.render(context)

        context.pop()
        return document

HOWEVER, when I try using this, I get

IndexError at /admin/

pop from empty list

Request URL: http://(Server Name):8000/admin/
Django Version: 1.6.2
Exception Type: IndexError
Exception Value: pop from empty list
Exception Location: /usr/lib/python3.3/site-packages/classytags/parser.py in parse_blocks, line 192
Python Executable: /usr/bin/python
Python Version: 3.3.5
Python Path: ['/home/PSIGLENMERE/rudy.valencia/core/Callaway',
'/usr/lib/python3.3/site-packages/django_divioadmin-0.2.3-py3.3.egg',
'/usr/lib/python33.zip',
'/usr/lib/python3.3',
'/usr/lib/python3.3/plat-linux',
'/usr/lib/python3.3/lib-dynload',
'/usr/lib/python3.3/site-packages',
'/home/PSIGLENMERE/rudy.valencia/core/Callaway']
Server time: Sun, 6 Apr 2014 16:51:15 +0000

What am I doing to trigger this?

Thanks!

  • Robert

InclusionTag may leak context state?

If I have an inclusion tag which does the following:

# name=tag1
def get_context(self, context, *args, **kwargs):
    context.update(test=1)
    return context

and a second template tag:

# name=tag2
def get_context(self, context, *args, **kwargs):
    context.update(test=2)
    return context

Then the state of the context may leak between, and after, the template tags are called, I think, eg:
{% tag1 %} {% tag2 %} {{ test }}
would display 2 for test, while
{% tag2 %} {% tag1 %} {{ test }}
would yield 1.

Overall, I'd actually have expected {{ test }} to be blank, after the fact, as it's not an explicit AsTag, and may thus be clobbering existing values.

Off the top of my head, I think the InclusionTag's render_tag method could be changed to something like:

template = self.get_template(context, **kwargs)
context.push()
data = self.get_context(context, **kwargs)
context.pop()
output = render_to_string(template, data)

(this can't be done inside the subclass's get_context, because it would pop off whatever has just been set onto it)

Edit:

I'm not sure what Django's own inclusion tags do in this scenario, as I've not used anything but classytags in years, so I suppose this may be mirroring their implementation ...

InclusionTag can't define the template to be used based on an argument passed to the templatetag

The InclusionTag's __init__ checks if self.template is None, which I think beats the purpose of having a get_template() method to be able to select the template based on some argument passed to the tag.

This does not work:

class IncludeExample(InclusionTag):
    options = Options(
        Argument('template_name'),
    )

    def get_template(self, context, template_name):
        return template_name

But this does work, though it's useless to define template = ''

class IncludeExample(InclusionTag):
    template = ''
    options = Options(
        Argument('template_name'),
    )

    def get_template(self, context, template_name):
        return template_name

Argument not resolved fully

Say I got a nested argument, i.e. Model.objects.all

{% tag Model.objects.all %}

With tag having a Argument('queryset',),

Instead of getting the queryset, I get an empty string. But when I put Model.objects.all() into the context without a chain of . it gets the queryset.

invalid version number

This line https://github.com/ojii/django-classy-tags/blob/a33e0ddf09f2d86c5de87d27ac171a068e18c7d5/classytags/tests.py#L14

is not compatible with latest django from master, and version number based on git changeset.

ValueError: invalid version number '1.5.dev20120927201904'

Django changeset:

commit 1df58968a4b2247aff91db40f1325f079ba3cdce
Author: Preston Holmes [email protected]
Date: Thu Sep 27 13:19:04 2012 -0700

Added a note regarding interaction between GitHub and Trac Plugin

Request Context in Inclusion Tag?

Maybe I am misunderstanding something, but I can't see a way to render a template with a RequestContext as opposed to a normal Context when using InclusionTag? In a normal inclusion tag I can pass context_class=RequestContext. Is there a way to do this?

AsTag doesn't seem to work (correctly) inside a with block

I have a reasonable straightforward AsTag:

# -*- coding: utf-8 -*-
from classytags.core import Options
from classytags.arguments import Argument, MultiKeywordArgument
from classytags.helpers import AsTag
from django import template
from unidecode import unidecode

register = template.Library()

class DataField(AsTag):
    name = 'data_field'

    options = Options(
        Argument('object'),
        Argument('field', resolve=False),
        MultiKeywordArgument('fkwargs', required=False),
        'as',
        Argument('varname', resolve=False, required=False),
    )

    def get_value(self, context, object, field, fkwargs):
        """
        Fetches a field from the provided ``object`` by interpolating (using
        'format()') the given ``field`` and ``fkwargs``.

        Example:
            {% data_field object '{loc}_color_{option}' loc='outer' option='b' as field %}

        Sets a context variable ``field`` with the contents of
        ``object.outer_color_b``. Clearly this is more useful when the format
        parameters: ``loc`` and ``option`` are themselves context variables,
        rather than string constants. =)

        Can also be used in a degenerate form without any placeholders or
        fkwargs to obtain an un-interpolated field from the object.
        """

        try:
            if fkwargs and '{' in field and '}' in field:
                field = field.format(**fkwargs)
        except:
            return None
        print(object, field, '=>', getattr(object, field, None))
        return getattr(object, field, None)

register.tag(DataField)

Works great! (ClassyTags is awesome!)

However, when I use the "as" variant, inside an existing {% with ... %}... {% endwith %} block, it works fine, but the resulting new context variable 'varname' is forgotten once the with-block is closed. Is this to be expected? This is not the behavior I get from, say, the {% cycle 'row1' 'row2' as rowcolors %} built-in templatetag.

Example:

{% with a=1 %}{# it doesn't matter what we use here #}
<p>{% data_field object 'color_{num}' num=7 %}</p><!-- works great! -->
{% data_field object 'color_{num}' num=7 as color_7 %}
<p>{{ color_7 }}</p><!-- works great! -->
{% endwith %}
<p>{{ color_7 }}</p><!-- Huh? This is empty, color_7 no longer exists -->

Am I doing it wrong?

Allow Options to be added together.

Given a list of Options:

OPTS = Options(
    Argument(...), Argument(...),
)

It would be nice to use composability to build up option lists, somewhat similarly to how Django management commands can (though they have the luxury of just being tuples of options, I think?)

OPTS = Options(
    Argument(...), Argument(...),
)
OPTS2 = OPTS + Options(
    StringArgument(...), Flag(...),
)

This would require implementing the __add__ method to Options, and presumably explicitly tethering to only other Options instances, lest someone try passing in a tuple, or some other iterable that isn't appropriate.

Currently, doing so yields:

>>> from classytags.core import Options
>>> Options() + Options()
TypeError: unsupported operand type(s) for +: 'Options' and 'Options'

Tag.__repr__: attribute __name__ does not exist

There's something wrong with the Tag.repr method:

def __repr__(self):
    '<Tag: %s>' % self.__name__

It's not returning a value, and when dumping the context in django-cms 1.2RC1 I get this error:

TemplateSyntaxError: Caught AttributeError while rendering: 'RenderPlugin' object has no attribute '__name__'

I tried replacing the code with this:

def __repr__(self):
    return '<Tag: %s>' % self.name

and it works (at least with django-cms).

license, docs and tests missing from pypi sdist

Hi! I'm packaging python-django-classy-tags for Arch Linux.
When packaging we usually run tests and add (certain) licenses to the package. Unfortunately the sdist tarballs on pypi.org don't contain either.

Given, that also fixes for python3.8 and django 3.0 have made their way into this repository since version 0.9.0, I guess it would be a great time for a new release. Thank you!

Updated django-cms from 2.3 to 2.3.1 suddenly gets traceback with classytag

Hi,

I just did pip install -U django-cms to update it from 2.3 to 2.3.1, then when I try to runserver on my dev machine, I get the following traceback, please help me figure out what's wrong here:

Traceback (most recent call last):
  File "C:\dev\virtualenvs\djangocmsenv\vbsite\manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "c:\python27\lib\site-packages\django\core\management\__init__.py", line
443, in execute_from_command_line
    utility.execute()
  File "c:\python27\lib\site-packages\django\core\management\__init__.py", line
382, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "c:\python27\lib\site-packages\django\core\management\base.py", line 196, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "c:\python27\lib\site-packages\django\core\management\base.py", line 217, in execute
    translation.activate('en-us')
  File "c:\python27\lib\site-packages\django\utils\translation\__init__.py", line 105, in activate
    return _trans.activate(language)
  File "c:\python27\lib\site-packages\django\utils\translation\trans_real.py", line 194, in activate
    _active.value = translation(language)
  File "c:\python27\lib\site-packages\django\utils\translation\trans_real.py", line 183, in translation
    default_translation = _fetch(settings.LANGUAGE_CODE)
  File "c:\python27\lib\site-packages\django\utils\translation\trans_real.py", line 160, in _fetch
    app = import_module(appname)
  File "c:\python27\lib\site-packages\django\utils\importlib.py", line 35, in import_module
    __import__(name)
  File "C:\dev\virtualenvs\djangocmsenv\Lib\site-packages\cms\__init__.py", line 9, in <module>
    patch_settings()
  File "C:\dev\virtualenvs\djangocmsenv\Lib\site-packages\cms\conf\__init__.py", line 39, in patch_settings
    post_patch_check()
  File "C:\dev\virtualenvs\djangocmsenv\Lib\site-packages\cms\conf\patch.py", line 56, in post_patch_check
    if not validate_template(template[0], ['js', 'css']):
  File "C:\dev\virtualenvs\djangocmsenv\Lib\site-packages\sekizai\helpers.py", line 97, in validate_template
    found = get_namespaces(template)
  File "C:\dev\virtualenvs\djangocmsenv\Lib\site-packages\sekizai\helpers.py", line 87, in get_namespaces
    compiled_template = get_template(template)
  File "c:\python27\lib\site-packages\django\template\loader.py", line 145, in get_template
    template, origin = find_template(template_name)
  File "c:\python27\lib\site-packages\django\template\loader.py", line 134, in find_template
    source, display_name = loader(name, dirs)
  File "c:\python27\lib\site-packages\django\template\loader.py", line 42, in __call__
    return self.load_template(template_name, template_dirs)
  File "c:\python27\lib\site-packages\django\template\loader.py", line 48, in load_template
    template = get_template_from_string(source, origin, template_name)
  File "c:\python27\lib\site-packages\django\template\loader.py", line 156, in get_template_from_string
    return Template(source, origin, name)
  File "c:\python27\lib\site-packages\django\template\base.py", line 125, in __init__
    self.nodelist = compile_string(template_string, origin)
  File "c:\python27\lib\site-packages\django\template\base.py", line 153, in compile_string
    return parser.parse()
  File "c:\python27\lib\site-packages\django\template\base.py", line 267, in parse
    compiled_result = compile_func(self, token)
  File "c:\python27\lib\site-packages\django\template\loader_tags.py", line 214, in do_extends
    nodelist = parser.parse()
  File "c:\python27\lib\site-packages\django\template\base.py", line 267, in parse
    compiled_result = compile_func(self, token)
  File "C:\dev\virtualenvs\djangocmsenv\Lib\site-packages\classytags\core.py", line 88, in __init__
    self.kwargs, self.blocks = self.options.parse(parser, tokens)
  File "C:\dev\virtualenvs\djangocmsenv\Lib\site-packages\classytags\core.py", line 55, in parse
    return argument_parser.parse(parser, tokens)
  File "C:\dev\virtualenvs\djangocmsenv\Lib\site-packages\classytags\parser.py", line 35, in parse
    self.parse_blocks()
  File "C:\dev\virtualenvs\djangocmsenv\Lib\site-packages\cms\templatetags\cms_tags.py", line 156, in parse_blocks
    return super(PlaceholderParser, self).parse_blocks()
  File "C:\dev\virtualenvs\djangocmsenv\Lib\site-packages\classytags\parser.py", line 169, in parse_blocks
    empty_block = blocks.pop(0)
IndexError: pop from empty list

Document AsTag implementation details

It is assumed in the AsTag implementation that the last argument of the tag is the variable name to save the result. But that assumption is not documented and leads to incorrect tag implementations.

For example, django-taggit-templatetags2 project implements its tags as a subclasses of the AsTag but they accept additional arguments after the as varname variable, and this leads to bugs that are very hard to understand and debug.

Perhaps, it should be explicitly noted in the documentation that the last argument of the AsTag is always assumed to be a context variable name.

Allow Options instances to display a __repr__

Given a tuple of Arguments:

OPTS = (Argument('...'), Argument('...'), Argument('...'))

One can easily see a vague idea of what's contained:

>>> print(OPTS)  # calls __repr__
(<Argument: ...>, <Argument: ...>)

However, if those same arguments are marshalled into an Options object:

options = Options(*OPTS)

Then the ability to inspect the options list easily is lost:

>>> print(options)
<classytags.core.Options object at 0xSomeMemoryOffset>

Realistically, it's a little thing, but it means documenting options via Sphinx doesn't work, which is a shame for internally documenting projects, which is how I came to find this.

`Tag` breaking in Django 4.1 when registering without the `name` kwarg

I've just run into an issue upgrading to Django 4.1, when using classytags.core.Tag Django is complaining that my tag hasn't been registered. I've tracked it down to this bit of the code:

class TagMeta(type):
"""
Metaclass for the Tag class that set's the name attribute onto the class
and a _decorated_function pseudo-function which is used by Django's
template system to get the tag name.
"""
def __new__(cls, name, bases, attrs):
parents = [base for base in bases if isinstance(base, TagMeta)]
if not parents:
return super().__new__(cls, name, bases, attrs)
tag_name = str(attrs.get('name', get_default_name(name)))
def fake_func():
pass # pragma: no cover
fake_func.__name__ = tag_name
attrs['_decorated_function'] = fake_func

Where it's making some assumptions about how Django's template system gets the name attribute from _decorated_function, however this changed under the hood in this commit in Django django/django@3d7ac64#diff-4ea91e90ebf87795fbf00d2f3bd8bb6333ef908db1adc0bc2b19f6dd5b71490f django/django#15291

2022-09-08_12-34

Example

from django import template
from classytags.core import Tag

register = template.Library()

@register.tag
class MyCustomTag(Tag):
  pass

@register.tag(name="foo_bar")
class MyCustomTagWithName(Tag):
  pass

print(register.tags)

In Django 4.0 (and presumably earlier) this prints

{'my_custom_tag': <class '__main__.MyCustomTag'>, 'foo_bar': <class '__main__.MyCustomTagWithName'>}

and in Django 4.1 this prints

{'MyCustomTag': <class '__main__.MyCustomTag'>, 'foo_bar': <class '__main__.MyCustomTagWithName'>}

add KeywordFlag class

in case, that there is a need to add multiple flags, and those flags not always will be used in full, it will be a good idea to add KeywordFlag, so there will be a possibility to add few flags and use any subset of them.

KeywordArgument is not working correctly

It might be that i misunderstand the intended behaviour but when using a KeywordArgument the resulting value is actually a dict with the key: value again.

In other words:
KeywordArgument('foo') and passing foo=bar results in kwargs['foo']['foo'] = 'bar'

allow use MultiValueArgument and MultiKeywordArgument together without word_breakpoint

Hi! Thank's for great library!

I want use MultiValueArgument and MultiKeywordArgument together like this:

class MyTag(Tag):
    name = 'my_tag'
    options = Options(
        MultiValueArgument('args'),
        MultiKeywordArgument('kwargs'),
        blocks=[('end_my_tag', 'nodelist')],
    )

    def render_tag(self, context, args, kwargs, nodelist):
        # some logic ...
        return some_result

# in template
{% my_tag var_1 var_2 key_1=v_1 key=v_2 %}

But in case I get error with parsing of arguments: MultiValueArgument takes all 'var_1 var_2 key_1=v_1 key=v_2' and can not parse 'key_1=v_1':

TemplateSyntaxError at /.../
Could not parse the remainder: '=v_1' from 'key_1=v_1'

To decide this bug it need use 'word_breakpoint' like this:

class MyTag(Tag):
    name = 'my_tag'
    options = Options(
        MultiValueArgument('args'),
        'word_breakpoint',
        MultiKeywordArgument('kwargs'),
        blocks=[('end_my_tag', 'nodelist')],
    )

    def render_tag(self, context, args, kwargs, nodelist):
        # some logic ...
        return some_result

# in template
{% my_tag var_1 var_2 word_breakpoint key_1=v_1 key=v_2 %}

But this solution need to use 'word_breakpoint'.

Problems on rendering for dojo

Hi, i am trying to substitute the dafault templatetag engine from django with classytags for variables passing.
I am doing the same thing with django engine and with classytags, the output is different, i do not know why.
So in classic django templatetag i have this:

@register.tag(name="contentpane")
def do_content_box(parser, token):
    nodelist = parser.parse(('endcontentpane'))
    parser.delete_first_token()
    return FormatContentPaneNode(nodelist)

class FormatContentPaneNode(template.Node):
    def __init__(self, nodelist):
        self.nodelist = nodelist


    def render(self, context):
        content = self.nodelist.render(context)

        return """<div  dojoType="dijit.layout.ContentPane">
  %(content)s
</div>""" % { 'content':content}

This renders as :

<div widgetid="dijit_layout_ContentPane_1" id="dijit_layout_ContentPane_1" role="group" title="" class="dijitContentPane" dojotype="dijit.layout.ContentPane">
  <p>content<p>         
</div>

But if i use the classytags way:

from classytags.core import  Options,Tag
from classytags.arguments import Argument
from classytags.helpers import InclusionTag, AsTag
from django import template
from dojango.util.config import Config
from dojango.util import dojo_collector




register = template.Library()
"""Add required modules """
dojo_collector.add_module("dijit.layout.ContentPane")
dojo_collector.add_module("dojo.parser")

class Contentpane(Tag):
        name="contentpane"
        options = Options(
        Argument('variable', required=False),
        'as',
        Argument('varname', required=False ),
        blocks=[('endcontentpane', 'nodelist')],
    )

        def render_tag(self, context, variable, varname, nodelist=None):
            content = nodelist.render(context)
            if varname:
                return """<div dojoType="dijit.layout.Contentpane" %(varname)s="%(variable)s"> 
  %(content)s
</div>""" % { 'content':content,
             'variable': variable,
             'varname': varname }
            else: return "<div dojoType=\"dijit.layout.Contentpane\"> %(content)s </div>" % { 'content': content }
contentpane=register.tag(Contentpane) 

it renders:

<div dojotype="dijit.layout.Contentpane"> 
  <p>content<p>
                     </div>

so no div id nothing that is added on render.
For testing you can use this template:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title></title><style type="text/css">
                @import "http://ajax.googleapis.com/ajax/libs/dojo/1.8.1/dojo/resources/dojo.css";
                @import "http://ajax.googleapis.com/ajax/libs/dojo/1.8.1/dojox/widget/Portlet/Portlet.css";
                @import "http://ajax.googleapis.com/ajax/libs/dojo/1.8.1/dijit/themes/claro/claro.css";
            </style><link rel="stylesheet" href="/static/bootstrap/bootstrap.css" type="text/css"><script type="text/javascript">


                var djConfig = {
                    'isDebug':true,
                    'parseOnLoad':true,
                    'baseUrl':"/dojango/dojo-media/1.8.1"

                        ,'dojoBlankHtmlUrl':"/dojango/dojo-media/1.8.1/dojango/resources/blank.html"
                        ,'dojoIframeHistoryUrl':"/dojango/dojo-media/1.8.1/dojango/resources/blank.html"


                        ,'paths':{'dojo': 'http://ajax.googleapis.com/ajax/libs/dojo/1.8.1/dojo', 'dijit': 'http://ajax.googleapis.com/ajax/libs/dojo/1.8.1/dijit', 'dojox': 'http://ajax.googleapis.com/ajax/libs/dojo/1.8.1/dojox'}

                };

        </script><script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/dojo/1.8.1/dojo/dojo.js"></script><script type="text/javascript" src="/dojango/dojo-media/1.8.1/dojango/dojango.js"></script></head>


    <body class="claro">

            <script>
            require([
                    "dijit/layout/ContentPane",
                    "dojo/parser"
 ], 
        function(ready){
    ready(function(){
    });
});
                    </script>

<div style="height: 1024px;width: 1600px">
                    <div dojoType="dijit.layout.Contentpane"> 
                    <p>Content</p>
                     </div>
</div>
    </body>

</html>

If you can find out whi or how i can make this work , i would be grateful.

Thanks,
Claudiu

test_integer_variable fails on Django 4.0

py39-dj40 run-test: commands[2] | coverage run setup.py test
running test
WARNING: Testing via this command is deprecated and will be removed in a future version. Users looking for a generic test entry point independent of test runner are encouraged to use tox.
running egg_info
writing django_classy_tags.egg-info/PKG-INFO
writing dependency_links to django_classy_tags.egg-info/dependency_links.txt
writing requirements to django_classy_tags.egg-info/requires.txt
writing top-level names to django_classy_tags.egg-info/top_level.txt
reading manifest file 'django_classy_tags.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
warning: no previously-included files matching '*.py[co]' found under directory '*'
adding license file 'LICENSE'
writing manifest file 'django_classy_tags.egg-info/SOURCES.txt'
running build_ext
Found 72 test(s).
System check identified no issues (0 silenced).
........./Users/jayvdb/django/django-classy-tags/classytags/values.py:36: TemplateSyntaxWarning: 'four' is not a valid choice. Valid choices: ['one', 'two', 'three'].
  warnings.warn(message, TemplateSyntaxWarning)
/Users/jayvdb/django/django-classy-tags/classytags/values.py:36: TemplateSyntaxWarning: 4 is not a valid choice. Valid choices: [1, 2, 3].
  warnings.warn(message, TemplateSyntaxWarning)
.................../Users/jayvdb/django/django-classy-tags/classytags/values.py:36: TemplateSyntaxWarning: 'one' could not be converted to Integer
  warnings.warn(message, TemplateSyntaxWarning)
E...........................................
======================================================================
ERROR: test_integer_variable (tests.test_core.ClassytagsTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/jayvdb/django/django-classy-tags/tests/test_core.py", line 788, in test_integer_variable
    self.assertEqual(tpl.render(context), '1')
  File "/Users/jayvdb/django/django-classy-tags/.tox/py39-dj40/lib/python3.9/site-packages/django/template/base.py", line 176, in render
    return self._render(context)
  File "/Users/jayvdb/django/django-classy-tags/.tox/py39-dj40/lib/python3.9/site-packages/django/test/utils.py", line 101, in instrumented_test_render
    return self.nodelist.render(context)
  File "/Users/jayvdb/django/django-classy-tags/.tox/py39-dj40/lib/python3.9/site-packages/django/template/base.py", line 977, in render
    return SafeString(''.join([
TypeError: sequence item 0: expected str instance, int found

----------------------------------------------------------------------
Ran 72 tests in 0.044s

FAILED (errors=1)

Custom default values as input from templates

Template label.html:

<span{%if class %} class="{{ class }}"{% endif %}>Label: {{ text }}</span>

What I currently use:

@register.simple_tag(takes_context=True)
def label(context, label=None, **kwargs):
    label = label or context.get('label', {})
    label_context = {
        'class': label.get('class'),
        'text': label.get('text', 'Default'),
    }
    label_context.update(kwargs)
    label_template = 'atoms/label.html'
    return render_to_string(label_template, context=label_context)

All of these inputs work:

{% label %}
=> <span>Label: Default</span>
{% label text="Dummy" %}
=> <span>Label: Dummy</span>
{% label text=name class="warning" %} // name="foo"
=> <span class="warning">Label: foo</span>
{% label text=name|default:'Anonymous'%} // name=None
=> <span>Label: Anonymous</span>

Features:

  • Passing unlimited context variables to the tag.
  • Using arbitrary django filters on input (example given: default value)

How can I get that with classy-tags?

I tried using this inclusin tag:

class Label(InclusionTag):
    template = 'atoms/label.html'
    options = Options(
        KeywordArgument('text'),
        KeywordArgument('class', required=False),
    )
    def get_context(self, context, **kwargs):
        return context.update(**kwargs)
register.tag(Label)

However that does not work yet.

With {% label text='foo' %}, kwargs becomes:
{'class': {}, 'text': {'text': django.utils.safestring.SafeText('foo')}}

Note: I do not want to force any order of arguments.

Any ideas?

Distribute newest version on PyPi for Django 3 compatibilty

Hi,
current PyPi version 0.9.0 is a bit behind the source here on Github.

Thus the PyPi package is not Django 3 compliant as it uses the deprecated import

# core.py
from django.utils import six

which has been already fixed in the sources.

Suggestion: make a fresh PyPi distibution.

problem with rendering a tag with csrf_token

hi,

I'm trying to write my first template tag using classy tags

I would like to render a form using a template like:

{% mailchimp_subscribe_form 12345 %}

my default template code is pretty simple:

{% load i18n %}
{% load url from future %}
<form action="{% url 'mailchimp.views.subscribe' %}" method="post">{% csrf_token %}
{{ form.as_p }}
<button type="submit">{%trans "Submit" %}</button>
</form>

my tag definition

class Subscribe(InclusionTag):
    name = 'mailchimp_subscribe_form'
    template = 'mailchimp/subscribe.html'
    options = Options(
        IntegerArgument('list_id'),
        StringArgument('template', default='mailchimp/subscribe.html', required=False),
    )

    def get_context(self, context, list_id, template):
        subscribeForm = SubscribeForm(initial={'list_id': list_id})
        return context.update({
            'form': subscribeForm,
            'template': template,
        })

my django app is set up properly in the sense that I can render the above template from a view and the csrf token is injected as expected, on the other hand when I would like to render the template using the template tag above, no csrf token is available in the rendered code

how can I add get the csrf token into my rendered tag?

ps: sorry, for posting here a support question, but could not find anything in the docs and with google

Django>=2 support

Hi guys
Does this package works with Django>=2?
If so documentation and setup.py would be updated ;D

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.