Django DRY Feeds

时间:2010-05-06 21:29:34

标签: django django-templates dry feeds django-rss

我正在使用Django Feeds Framework,它非常好用,非常直观且易于使用。但是,我认为在HTML中创建供稿链接时存在问题。

例如:

<link rel="alternate" type="application/rss+xml" title="{{ feed_title }}" href="{{ url_of_feed }}" />

可以轻松找到链接的HREF属性,只需使用reverse()

即可

但是,TITLE属性怎么样?模板引擎应该在哪里寻找?更重要的是,如果Feed以dinamically方式构建并且标题取决于参数(例如this)会怎么样?

我无法想出一个“似乎”对我来说干的解决方案......我能想到的就是使用上下文处理器或模板标签,但是当上下文处理器/模板标签必须使用时,它会变得混乱找到构造Feed类的参数,写下来我意识到我甚至不知道如何在视图中自己创建Feed实例。

如果我将所有这些逻辑放在视图中,那么它不仅仅是一个视图。此外,TITLE的值将位于Feed中的视图中。

2 个答案:

答案 0 :(得分:0)

只是猜测(因为我还没有在我的django应用中使用Feed),但您可以使用Feed对象为Feed添加特殊的template_context,并在base.html中使用它。

答案 1 :(得分:0)

我对此解决方案并不完全满意,它可能会使用Request破坏Feed并依赖于魔术方法。在那里:

#coding:utf-8
# Author: Armando Pérez Marqués <mandx@rbol.org>
# Purpose: Django TemplateTag to output feed links in templates in a DRY way
# Created: 05/07/2010

import re

from django import template
from django.conf import settings
from django.contrib.syndication.views import Feed
from django.core.urlresolvers import reverse, resolve, NoReverseMatch
from django.template import Node
from django.template import TemplateSyntaxError
from django.utils.encoding import smart_str
from django.utils.html import escape as html_escape
from django.utils.safestring import mark_safe

register = template.Library()

kwarg_re = re.compile(r"(?:(\w+)=)?(.+)")

class FeedInfoNode(Node):
    def __init__(self, view_name, args, kwargs, asvar):
        self.view_name = view_name
        self.args = args
        self.kwargs = kwargs
        self.asvar = asvar

    def render(self, context):
        args = [arg.resolve(context) for arg in self.args]
        kwargs = dict([(smart_str(k,'ascii'), v.resolve(context))
                       for k, v in self.kwargs.items()])

        # Try to look up the URL twice: once given the view name, and again
        # relative to what we guess is the "main" app. If they both fail,
        # re-raise the NoReverseMatch unless we're using the
        # {% feed_info ... as var %} construct in which cause return nothing.
        url = ''
        try:
            url = reverse(self.view_name, args=args, kwargs=kwargs, current_app=context.current_app)
        except NoReverseMatch, e:
            if settings.SETTINGS_MODULE:
                project_name = settings.SETTINGS_MODULE.split('.')[0]
                try:
                    url = reverse(project_name + '.' + self.view_name,
                              args=args, kwargs=kwargs, current_app=context.current_app)
                except NoReverseMatch:
                    if self.asvar is None:
                        # Re-raise the original exception, not the one with
                        # the path relative to the project. This makes a
                        # better error message.
                        raise e
            else:
                if self.asvar is None:
                    raise e

        if 'request' in context:
            request = context['request']
        else:
            request = None

        feed_instance, feed_args, feed_kwargs = resolve(url)
        if not isinstance(feed_instance, Feed):
            raise NoReverseMatch, \
                  'feed_info can only reverse class-based feeds'

        feed_obj = feed_instance.get_object(request, *feed_args, **feed_kwargs)

        feed_data = {
            'url': url,
            'obj': feed_instance,
            'args': feed_args,
            'kwargs': feed_kwargs,
            #'title': html_escape(feed_instance.__get_dynamic_attr('title', obj)),
            'title': html_escape(
                feed_instance._Feed__get_dynamic_attr('title', feed_obj)
                ),
            'type': feed_instance.feed_type.mime_type,
        }

        if self.asvar:
            context[self.asvar] = feed_data
            return ''
        else:
            return mark_safe(
                '<link rel="alternate" type="%(type)s" title="%(title)s" href="%(url)s" />' \
                % feed_data
            )

def feed_info(parser, token):
    """
    Returns an mapping containing populated info about the reversed feed
    Works exactly as the url tag, but the mapping is not returned, instead
    a variable is always set  in the context.

    This is a way to define links that aren't tied to a particular URL
    configuration::

        {% feed_info path.to.some_feed_view_class arg1 arg2 as feed_info_var %}

        or

        {% feed_info path.to.some_feed_view_class name1=value1 name2=value2 as feed_info_var %}
    """

    bits = token.split_contents()
    if len(bits) < 2:
        raise TemplateSyntaxError("'%s' takes at least one argument"
                                  " (path to a feed view)" % bits[0])
    viewname = bits[1]
    args = []
    kwargs = {}
    asvar = None
    bits = bits[2:]
    if len(bits) >= 2 and bits[-2] == 'as':
        asvar = bits[-1]
        bits = bits[:-2]

    # Backwards compatibility: check for the old comma separated format
    # {% url urlname arg1,arg2 %}
    # Initial check - that the first space separated bit has a comma in it
    if bits and ',' in bits[0]:
        check_old_format = True
        # In order to *really* be old format, there must be a comma
        # in *every* space separated bit, except the last.
        for bit in bits[1:-1]:
            if ',' not in bit:
                # No comma in this bit. Either the comma we found
                # in bit 1 was a false positive (e.g., comma in a string),
                # or there is a syntax problem with missing commas
                check_old_format = False
                break
    else:
        # No comma found - must be new format.
        check_old_format = False

    if check_old_format:
        # Confirm that this is old format by trying to parse the first
        # argument. An exception will be raised if the comma is
        # unexpected (i.e. outside of a static string).
        match = kwarg_re.match(bits[0])
        if match:
            value = match.groups()[1]
            try:
                parser.compile_filter(value)
            except TemplateSyntaxError:
                bits = ''.join(bits).split(',')

    # Now all the bits are parsed into new format,
    # process them as template vars
    if len(bits):
        for bit in bits:
            match = kwarg_re.match(bit)
            if not match:
                raise TemplateSyntaxError("Malformed arguments to url tag")
            name, value = match.groups()
            if name:
                kwargs[name] = parser.compile_filter(value)
            else:
                args.append(parser.compile_filter(value))

    return FeedInfoNode(viewname, args, kwargs, asvar)

feed_info = register.tag(feed_info)

我从{% url %}模板代码的代码开始,然后在获取Feed的URL后,使用resolve()获取Feed子类实例,然后获取所需的属性。< / p>

<强>注意事项

  • 需要Django 1.2 Class Feeds,不知道如何使用旧的feed方式完成此操作。
  • 如果Feed类使用请求对象,则必须配置request context processor,因为如果上下文中不存在None,则会传递{{1}}。
  • Feed .__ get_dynamic_attr()有一个奇怪的地方。 Feed子类实例没有此方法;相反,它出现另一个名称。不知道如何在运行时找出名字......