Sök…


Anpassade filter

Filters låter dig använda en funktion på en variabel. Denna funktion kan ta 0 eller 1 argument. Här är syntaxen:

{{ variable|filter_name }} 
{{ variable|filter_name:argument }}

Filter kan kedjas så det är helt giltigt:

{{ variable|filter_name:argument|another_filter }}

Om översatt till python skulle ovanstående rad ge något liknande:

print(another_filter(filter_name(variable, argument)))

I det här exemplet kommer vi att skriva ett anpassat verbose_name som gäller en modell (instans eller klass) eller en QuerySet. Det kommer att returnera det verbosa namnet på en modell eller dess ordlista flertal om argumentet är satt till True .

@register.filter
def verbose_name(model, plural=False):
    """Return the verbose name of a model.
    `model` can be either:
      - a Model class
      - a Model instance
      - a QuerySet
      - any object refering to a model through a `model` attribute.

    Usage:
      - Get the verbose name of an object
          {{ object|verbose_name }}
      - Get the plural verbose name of an object from a QuerySet
          {{ objects_list|verbose_name:True }}
    """
    if not hasattr(model, '_meta'):
        # handle the case of a QuerySet (among others)
        model = model.model
    opts = model._meta
    if plural:
        return opts.verbose_name_plural
    else:
        return opts.verbose_name

Enkla taggar

Det enklaste sättet att definiera en anpassad malltagg är att använda en simple_tag . Dessa är mycket enkla att installera. Funktionsnamnet kommer att vara etikettnamnet (även om du kan åsidosätta det), och argument kommer att vara symboler ("ord" separerade av mellanslag, förutom mellanrum som är inneslutna mellan citat). Det stöder till och med sökordsargument.

Här är en värdelös tagg som illustrerar vårt exempel:

{% useless 3 foo 'hello world' foo=True bar=baz.hello|capfirst %}

Låt foo och baz vara baz som följande:

{'foo': "HELLO", 'baz': {'hello': "world"}}

Säg att vi vill att den här mycket värdelösa taggen ska visas så här:

HELLO;hello world;bar:World;foo:True<br/>
HELLO;hello world;bar:World;foo:True<br/>
HELLO;hello world;bar:World;foo:True<br/>

Typ av argumentkoncentration upprepas 3 gånger (3 är det första argumentet).

Så här kan taggimplementeringen se ut:

from django.utils.html import format_html_join

@register.simple_tag
def useless(repeat, *args, **kwargs):
    output = ';'.join(args + ['{}:{}'.format(*item) for item in kwargs.items()])
    outputs = [output] * repeat
    return format_html_join('\n', '{}<br/>', ((e,) for e in outputs))

format_html_join tillåter att markera <br/> som säker HTML, men inte innehållet i outputs .

Avancerade anpassade taggar med Node

Ibland är det du vill göra för komplicerat för ett filter eller en simple_tag . Gör detta måste du skapa en sammanställningsfunktion och en återgivare.

I det här exemplet kommer vi att skapa en verbose_name med följande syntax:

Exempel Beskrivning
{% verbose_name obj %} Modellt namn på en modell
{% verbose_name obj 'status' %} Språknamnet på fältet "status"
{% verbose_name obj plural %} Ordbok med flertal av en modell
{% verbose_name obj plural capfirst %} Stora bokstavliga namn flertal av en modell
{% verbose_name obj 'foo' capfirst %} Stora bokstäver på ett fält
{% verbose_name obj field_name %} Satt namn på ett fält från en variabel
{% verbose_name obj 'foo'|add:'_bar' %} Språkigt namn på ett fält "foo_bar"

Anledningen till att vi inte kan göra detta med en enkel tagg är att plural och capfirst varken är variabler eller strängar, de är "nyckelord". Vi kan uppenbarligen besluta att överföra dem som strängar 'plural' och 'capfirst' , men det kan komma i konflikt med fält med dessa namn. Skulle {% verbose_name obj 'plural' %} betyda "verbose name plural of obj " eller "verbose name of obj.plural "?

Låt oss först skapa kompilationsfunktionen:

@register.tag(name='verbose_name')
def do_verbose_name(parser, token):
    """
    - parser: the Parser object. We will use it to parse tokens into
              nodes such as variables, strings, ...
    - token: the Token object. We will use it to iterate each token
             of the template tag.
    """
    # Split tokens within spaces (except spaces inside quotes)
    tokens = token.split_contents()
    tag_name = tokens[0]
    try:
        # each token is a string so we need to parse it to get the actual
        # variable instead of the variable name as a string.
        model = parser.compile_filter(tokens[1])
    except IndexError:
        raise TemplateSyntaxError(
            "'{}' tag requires at least 1 argument.".format(tag_name))

    field_name = None
    flags = {
        'plural': False,
        'capfirst': False,
    }

    bits = tokens[2:]
    for bit in bits:
        if bit in flags.keys():
            # here we don't need `parser.compile_filter` because we expect
            # 'plural' and 'capfirst' flags to be actual strings.
            if flags[bit]:
                raise TemplateSyntaxError(
                    "'{}' tag only accept one occurrence of '{}' flag".format(
                        tag_name, bit)
                )
            flags[bit] = True
            continue
        if field_name:
            raise TemplateSyntaxError((
                "'{}' tag only accept one field name at most. {} is the second "
                "field name encountered."
            ).format(tag_name, bit)
        field_name = parser.compile_filter(bit)

    # VerboseNameNode is our renderer which code is given right below
    return VerboseNameNode(model, field_name, **flags)

Och nu återgivaren:

class VerboseNameNode(Node):

    def __init__(self, model, field_name=None, **flags):
        self.model = model
        self.field_name = field_name
        self.plural = flags.get('plural', False)
        self.capfirst = flags.get('capfirst', False)

    def get_field_verbose_name(self):
        if self.plural:
            raise ValueError("Plural is not supported for fields verbose name.")
        return self.model._meta.get_field(self.field_name).verbose_name

    def get_model_verbose_name(self):
       if self.plural:
           return self.model._meta.verbose_name_plural
       else:
           return self.model._meta.verbose_name

    def render(self, context):
        """This is the main function, it will be called to render the tag.
        As you can see it takes context, but we don't need it here.
        For instance, an advanced version of this template tag could look for an
        `object` or `object_list` in the context if `self.model` is not provided.
        """
        if self.field_name:
            verbose_name = self.get_field_verbose_name()
        else:
            verbose_name = self.get_model_verbose_name()
        if self.capfirst:
            verbose_name = verbose_name.capitalize()
        return verbose_name


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow