سؤال

I am writing a small django app to handle images. I would like to make this as DRY as possible, so this is my goal:

The user uploads a file, and inputs some fields like caption, alt text, title, etc..

On the template, I would like to be able to do something like:

model.image.render_tag

which would output something like:

<img src='path/image.jpg' title='title' alt_text='alt_text..' />

The problem is that that line should be on a template, so that if the developer needs to add some things or remove others, they can just edit the template, and the rendering will change in all of the rendered images.

One could also add a second template and render that one.

Now, there are a couple of methods to achieve this:

1)

Just include a template which renders a namespaced variable, and force the developer to 'change' the image variable name everytime, the template would be something like:

<img src="{{ namespaced_image.raw.path }}" alt_text="{{ namespaced_image.alt_text }}"... />

In this case, the developer should rename the image to its namespaced version before including the path.

Something like:

{% with namespaced_image=image %}
    {% include images/template.html %}
{% end with %}

2)

Add a template tag to render the image, based on a raw string that the user can edit in the settings, something like

settings.py

namespaced_image_tag="<img src={path} ... />"

and my template tag would do something like

@register
def rendered_tag(image)
    return namespaced_image_tag.format(path=image.raw.path, ...)

So the developer could just add the custom tags file and use

{% load images_filters %}

{{ image|rendered_tag }}

3)

A mixture of the above:

There would be a default template.html containing the tag, and a template tag that would render the template by giving it the namespaced image as context.

@register
def rendered_tag(image, template="default_template.html")
    return render_template(template, namespaced_image=image)

So the developer could do something like

{% load images_filters %}
{{ image|rendered_tag }}
{{ image|rendered_tag:"custom_template_name" }}

I don't know however, how rendering several templates per request would affect performances.

Also, I don't really like option #1 because it's not usable and readable.

Option #2 is bad because I cannot use different "templates", and cannot extend others. I also don't like template tags very much, especially because the user is forced to import the file in every used template.

Option #3 is the best in terms of usability, but I feel like it would affect a lot performances. Any tips?

هل كانت مفيدة؟

المحلول

In my opinion, and I think many share this point of view with me, a model shouldn't be responsible for its representation in HTML, so having model.image.render_tag seems wrong to me.

The only parts in an app that know how to represent anything in HTML should be the views and their templates.

You can have a helper function that generates the HTML for an image, and use it in your views, you can also have a simple_tag that refers to that function so that you can use it in your templates.

If the performance of calling it in your templates is an issue, consider using a better performance template engine - you can refer to this SO answer for a benchmark of template engines -, or you can call that helper function in your view and pass the result(s) to your template via the context.

Update

I'll demonstrate what I meant above by an example.

# inside html_utils.py

def image_to_html(image):
    return '<img src="%(path)s" title="%(title)s" alt="%(alt)s" />' % {
        'path': image.raw.path,
        'title': image.title,
        'alt': image.alt_text
    }

# inside html_template_utils.py

import html_utils
from django import template

register = template.Library()

@register.simple_tag
def image_tag(image):
    return html_utils.image_to_html(image)

{# inside images_template.html #}

{% load html_template_utils %}

<div class="images-container">
{% for image in images %}
    {% image_tag image %}
{% endfor %}
</div>

The code above uses the image_to_html helper function from the template by using the image_tag simple_tag we created. Another possible way would be by calling image_to_html in the view and passing the results to the template:

# inside views.py

import html_utils

def view_images(request):
    # ...

    images_html = [html_utils.image_to_html(image) for image in images]

    # ...

    return render_to_response('images_template.html', {
        'images_html': images_html
    }, context_instance=RequestContext(request))

{# inside images_template.html #}

<div class="images-container">
{% for image_html in images_html %}
    {{ image_html }}
{% endfor %}
</div>
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top