【问题标题】:Django DRY FeedsDjango DRY 饲料
【发布时间】:2011-02-16 14:22:49
【问题描述】:

我正在使用 Django Feeds Framework,它非常好用,非常直观且易于使用。但是,我认为在 HTML 中创建指向提要的链接时会出现问题。

例如:

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

链接的HREF属性很容易查到,用reverse()就行了

但是,TITLE 属性呢?模板引擎应该在哪里寻找这个?更重要的是,如果提要是动态构建的,并且标题取决于参数(如this)怎么办?

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

如果我把所有这些逻辑都放在视图中,它就不会只是一个视图。此外,TITLE 的值将在视图和提要中。

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

    • 这个我有想过,但是这个方法还是有问题,比如如何获取当前的分类。上下文处理器无法访问上下文数据,它们只需要请求对象。
    【解决方案2】:

    我对这个解决方案并不完全满意,它可能会破坏使用Request 的提要,并且取决于一种神奇的方法。就这样:

    #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子类实例,然后获取需要的属性。

    注意事项

    • 需要 Django 1.2 类提要,不知道如何使用旧的提要方式。
    • 如果提要类使用请求对象,则必须配置request context processor,因为如果上下文中不存在None,则会传递它。
    • Feed.__get_dynamic_attr() 有点奇怪。 Feed 子类实例没有这个方法;相反,它以另一个名称出现。不知道如何在运行时找出名称...

    【讨论】:

    • 我要这样做,因为它类似于使用{% url %}
    猜你喜欢
    • 2011-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-06
    • 2017-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多