Coder Social home page Coder Social logo

django-sniplates's Introduction

django-sniplates

Template snippet libraries for Django

Read the documentation at Read The Docs

django-sniplates's People

Contributors

adamchainz avatar flupzor avatar funkybob avatar kezabelle avatar mattmcc avatar schinckel avatar sergei-maertens avatar trauty-is-me 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-sniplates's Issues

pypi has a version 0.5.0?

Doing a pip install django-sniplates ends up presenting me with 0.5.0, with a date of 2016-08-16, yet here we see releases showing Jan 25, 2016 with 0.4.1. Looks like 0.5.0 is the most current but isn't tagged here.

nested_widget enhancement (with code)

Submitting patched version of sniplates.py (0.7.0) for your consideration for future releases.

Intent: to overcome 2 issues with 'nested_widget':

  1. The name was not properly communicating its semantics/capabilities, and
  2. It was not supporting injection of named blocks, only named variables. Named variables can prove awkward for injecting significant chunks of content.

Effects of modification:

  1. Preserve backward compatibility
  2. Introduce a new tag 'widblock'/'endwidblock', which is synonymous to 'nested_widget'/'endnested' but more concise, and uses the same NestedWidget class as nested_widget
  3. Enhance NestedWidget to support context injection via named blocks inside a widblock (and nested_widget) body.

How this is done, in NestedWidget.render(), is:

  1. pick out the block objects from the nodelist, render and save them separately
  2. inject these block objects into the context under named keys, in the same way as the body content in the node gets injected under the key "content"
  3. render the tag body into context key 'content' the same way as before, but without the nested blocks

Example usage:

{% widblock 'myapp:foo' %}
{% block introText %} ...add styled intro text, with links... {% endblock %}
{% block bodyGraph %} ...include a graph template... {% endblock %}
... optional body of this widget block, to be injected as variable 'content' ...
{% endwidblock %}

Thank you for your effort in developing, sharing and maintaining sniplates. I look forward to making heavy use of it in my current and future projects.

Cheers
David
(aum108 on freenode)

sniplatesCustom.py.txt

RFC: Arbitrary variables as nested content

Motivation

I have the following widgets (simplified for brevity):

{% block _label %}
  <label for="{{ id_for_label }}">{{ label }}</label>
{% endblock %}

{% block CharField %}
  {% reuse _label %}
  <input id="{{ id_for_label }}" type="text">
{% endblock %}

For a particular use case, I want to generate the following:

<label for="id_my_field">Customized <a href="/somewhere">HTML</a> label</label>
<input id="id_my_field" type="text">

Where /somewhere is a result of reverse('some_view').

The hacky solution is set the label of the form field to the HTML and mark it safe. However, due to circular imports and mark_safe not playing along with reverse_lazy this isn't feasible, and besides, HTML belongs in the templates.

Solution

Sniplates has nested_widget, but it forces the variable name to be content, not label, and doesn't have the automatic attribute extraction of the form_field.

Mild option

{% form_field my_field nested_content_for='label' %}
  Customized {% url 'some_view' %} label
{% end_form_field %}

Upon seeing nested_content_for, form_field parses the inside content and assigns the result to label in the context. widget works the same. nested_widget is deprecated as it is equivalent to {% widget nested_content_for='content' %}.

If nested_content_for is not specified as an argument for form_field or widget, they behave like now.

Crazy option

{% form_field my_field nested %}
{% attribute 'label' %}
  Customized {% url 'some_view' %} label
{% attribute 'something_else' %}
  Multiple parameters can be specified!
{% end_form_field %}

Upon seeing nested as an attribute, form_field (and widget) look for {% attribute ... %} sections and add their result to the context. The advantage over the first option is the ability to specify multiple inline arguments.


I'm not particularly attached to the wording, etc. If you're okay with either option, I can write up a PR.

{% form_field %} crashes on an empty ModelMultipleChoiceField

Just ran into this bug (and I probably know how to fix it as well):

Given a form:

class FreeformExportForm(forms.Form):
    templates = forms.ModelMultipleChoiceField(
        queryset=Template.objects.freeform(),
        initial=Template.objects.freeform(),
        widget=forms.CheckboxSelectMultiple
    )

where the queryset is empty (there are no Template objects), doing

{% form_field form.templates %}

in the template crashes.

The violating line is https://github.com/funkybob/django-sniplates/blob/master/sniplates/templatetags/sniplates.py#L445:

@register.simple_tag(takes_context=True)
def form_field(context, field, widget=None, **kwargs):
    if not field:
        raise template.TemplateSyntaxError('form_field requires a value field as first argument')

In this case, bool(field) is equal to False, while it is still a BoundField instance.

@funkybob suggested change:

@register.simple_tag(takes_context=True)
def form_field(context, field, widget=None, **kwargs):
    if not isinstance(field, BoundField):
        raise template.TemplateSyntaxError('form_field requires a value field as first argument')

Nested widget and {% trans %} template tag.

Template:

{% extends "mail/base.html" %}
{% load i18n reservation_tags sniplates %}

{% block content %}

    {% load_widgets mail='sniplates/mail.html' %}

    {% nested_widget 'mail:p' %}
        {% trans "You can find the tickets and all visit information per group here. These links
are shareable with the companion of each group:" %}
    {% endnested %}

{% endblock %}

sniplates/mail.html:

Mail sections - to be used as nested_widgets

{% block introduction %}
<!-- introduction -->
<br><br>
<h3 style="color: {{ link_color }};font-family: &quot;Arial&quot;, sans-serif;font-weight: normal;padding: 0;margin: 0;text-align: left;line-height: 1.3;word-break: normal;font-size: 20px;">{{ content }}</h3>
{% endblock introduction %}

Generic content
{% block p %}
<!-- content -->
<p style="margin: 0;margin-bottom: 10px;color: {{ color }};font-family: &quot;Arial&quot;, sans-serif;font-weight: normal;padding: 0;text-align: left;line-height: 1.3;font-size: 17px;">{{ content }}</p>
{% endblock p %}

Expected output:

<!-- content -->
<p style="margin: 0;margin-bottom: 10px;color: #000;font-family: &quot;Arial&quot;, sans-serif;font-weight: normal;padding: 0;text-align: left;line-height: 1.3;font-size: 17px;">
       You can find the tickets and all visit information per group here. These links
are shareable with the companion of each group:
    </p>

Actual output:

<!-- content -->
<p style="margin: 0;margin-bottom: 10px;color: #000;font-family: &quot;Arial&quot;, sans-serif;font-weight: normal;padding: 0;text-align: left;line-height: 1.3;font-size: 17px;">
        {% trans "You can find the tickets and all visit information per group here. These links
are shareable with the companion of each group:" %}
</p>

Release 0.7.0 to PyPI

I saw my Django 3.0 compat PR got merged and some smaller tweaks were done (thanks!) including the version bump to 0.7.0, but this hasn't been published to PyPI yet. Could you find some time to do this?

Alternatively - since I'm already maintainer on this repo, I wouldn't mind taking care of PyPI releases either :-)

Recursive reuse which I think should work, doesn't?

So, I've got a template tag, which I'm using to wrapover load_widgets and reuse ... or at least that's the plan:

def my_data_nodes():
    level3 = {'title': 'test3', 'children':[]}
    level2 = {'title': 'test2', 'children': [level3]}
    level1 = {'title': 'test', 'children': [level2]}
    return [level1]

@register.simple_tag(takes_context=True)
def show_list(context, ...):
    load_widgets(context, list_items="list_items.html", _soft=True)
    with using(context, 'list_items'):
        result = reuse(context, ['list'], nodes=my_data_nodes())
    return result

So that should:

  • get_template('list_items.html')
  • set up the existing context to temporarily have the alias (list_items)
  • render a given block (list)
    This is my boiling it down to its root components, I started out trying various other things, but now I'm just lost.

Given the template list_items.html:

{% load sniplates %}
{% block list %}<ul>{% for node in nodes %}{% reuse "listitem" %}{% endfor %}</ul>{% endblock list %}

{% block listitem %}<li>
{{ node.title }}
{% if node.children %}{% reuse "list" nodes=node.children %}{% endif %}
</li>{% endblock listitem %}

basically I should end up with a standard <ul><li>... list, I think.

But what I'm actually seeing is only 1 level of items -- the call to {% reuse 'list' ...%} inside the listitem block never renders anything.

I think it's something to do with the push/pops on contexts, but I cannot figure out much beyond that.

  • On reuse number 1 (the call in the python template tag), block_context contains:
    • {'listitem': [<Block Node: listitem ...>], 'list': [<Block Node: list. ...>]}
  • On reuse number 2 (the call {% reuse 'listitem' %} within {% block list %}) the block context only contains:
    • {'listitem': [<Block Node: listitem ...>], 'list': []} -- the BlockNode for list is gone.
  • On reuse number 3, which ought to start a new <ul> (the call to {% reuse 'list' %} within the listitem block) both keys are now empty:
    • {'listitem': [], 'list': []}

I have no idea if that's intentional, but it seems like recursion through popping and rendering/reusing blocks ought to be faster than using includes recursively (which did work), so I thought I'd try it.

The output for using includes directly was:

  • test
    • test2
      • test3

The output from the attempt at {% reuse %} was:

  • test

Passing incorrect arguments should not fail silently

bootstrap.html:

{% block label %}
    <span class="label label-{{ label_type|default:'default' }}">
        {{ text }}
    </span>
{% endblock %}

template.html

{% load sniplates %}
{% load_widgets bootstrap="bootstrap.html" %}
{% widget "bootstrap:label" labell_type="error" text="Things go here" %}

In the case above, labell_type is ignored altogether - it would be nice if the template author could be told there is a problem.

Feature/pluggable-explosives: value vs. raw_value in templates

So, first bit of testing this feature branch, and I ran into the following issue:

A lot of the provided templates will have to change value to raw_value, including your test templates I think. Right now, the raw_value None will be output in TextInput as <input type="text" value="None">. Or, the value property should maybe catch the values None, 'False,True`.

My personal preference would be to just use raw_value in the template, but that can lead to inconsistencies. For scalar values it makes sense, for fields with choices not so much, since the choices property returns values that have been force_text'ed, you have to do the comparisons against the force_text'ed raw_value, which is value in the template. So then you will be using raw_value and value in the template, for seemingly the same check - i.e.: is this choice the current/selected value?

Consider Debug toolbar support

Opening for discussion here, as I'm not on IRC right now.
If I recall correctly, @funkybob, you're somehow wizard enough to not be a heavy user of django-debug-toolbar. I'm not, alas, and rely on it for sanity-checking/debugging.
I'd like to explore/propose exposing enough hooks for the debug-toolbar template panel to pick up renderings that come out of sniplates.
Specifically, currently, {% load_widgets %} calls out to resolve_blocks, which loads a template (via get_template) but never renders it, and because the API debug-toolbar expects a template to use (calling ._render on a Template instance) is never touched by sniplates, the template never ends up collected into the djdt panel.
A naive solution to that is:

   ...
   # For Django 1.8 compatibility
    template = getattr(template, 'template', template)
    if settings.DEBUG:
        template_rendered.send(sender=None, template=template, context=context)

This would at least mean that templates loaded turn up in the templates list, which is a useful thing in and of itself, if you know that DTL (and subsequently often 3rd party tag libraries) will often try and continue vs erroring loudly.

To get individual block renderings would be slightly more work (but ultimately would be my preference, so that answering the questions like "what's in this form_field's context right now?" is easier for me), but also probably require deciding on how to handle any explicit djdt support anyway, hence this discussion :)

Basically:

  • is it something you'd be open to?
  • is settings.DEBUG enough of a discriminator for setting up the signals djdt reads from?

Value of None in formfield incorrect

There's an error in the value checking for form_field. Value is a callable and needs to be called. I'll provide a test and PR with fix later this week.

I'm on mobile now, sorry for the short report - merely a heads up and reminder for me.

Possible to get rid of `display`?

I have this use case: an inline_formset with a ModelForm, these are the models involved:

class Build(models.Model):
    title = models.CharField(max_length=50)

class BuildPhoto(models.Model):
    build = models.ForeignKey(Build)
    photo = models.ForeignKey('albums.Photo')
    url = models.URLField(blank=True)

    def __unicode__(self):
        return self.build.title  # simplified, but traverses the FK


class BuildPhotoForm(forms.ModelForm):
    class Meta:
        model = BuildPhoto
        widgets = {'photo': forms.HiddenInput}

Now, when I make an inlineformset for BuildPhotoForm with BuildPhoto, Django adds the 'id' field in BuildPhotoForm (django.forms.models.BaseModelFormSet.add_fields). This field is a ModelChoiceField and the queryset is not configurable, except if you subclass the BaseFormSet and override add_fields to modify this.

This is a HiddenInput widget by default, just like the 'photo' field. However, for these widgets in sniplates, the 'choices' is evaluated, while for ModelChoiceFields field.choices is a ModelChoiceIterator and thus lazy. This is very costly for the id field in my case, as it traverses the FK, leading to a query bomb in my template. I have no interest in the choices. It seems to me that choices is only evaluated to get the display of the field, so I vote for getting rid of display. It doesn't make a lot of sense in multiple-value fields anyway. Or, if it's needed, make it lazy.

Concrete example: I was getting +3000 queries before subclassing, after 'fixing' it in my subclassed BaseFormSet, I'm only getting 27 queries.

Thoughts?

Reuse swallows empty blocklists silently.

Similar I think to the fix in 53b4205 for form_field, given:
{% reuse '' %} or (how I discovered it) not quoting the blockname {% reuse blockname %} will silently fail because the block_list the function receives is u''

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.