Szukaj…


Niestandardowe filtry

Filtry pozwalają na zastosowanie funkcji do zmiennej. Ta funkcja może przyjmować 0 lub 1 argument. Oto składnia:

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

Filtry można powiązać, więc jest to całkowicie poprawne:

{{ variable|filter_name:argument|another_filter }}

W przypadku przetłumaczenia na python powyższa linia dałaby coś takiego:

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

W tym przykładzie napiszemy niestandardowy filtr verbose_name który dotyczy modelu (instancji lub klasy) lub QuerySet. Zwróci pełną nazwę modelu lub pełną liczbę mnogą, jeśli argument jest ustawiony na 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

Proste tagi

Najprostszym sposobem zdefiniowania niestandardowego znacznika szablonu jest użycie tagu simple_tag . Są bardzo proste w konfiguracji. Nazwa funkcji będzie nazwą znacznika (choć można ją zastąpić), a argumentami będą tokeny („słowa” oddzielone spacjami, z wyjątkiem spacji zawartych w cudzysłowach). Obsługuje nawet argumenty słów kluczowych.

Oto bezużyteczny tag, który zilustruje nasz przykład:

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

Niech foo i baz będą zmiennymi kontekstowymi, takimi jak:

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

Powiedzmy, że chcemy, aby ten bardzo bezużyteczny tag wyświetlał się tak:

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

Rodzaj konkatenacji argumentów powtórzono 3 razy (3 to pierwszy argument).

Oto jak może wyglądać implementacja tagu:

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 pozwala oznaczyć <br/> jako bezpieczny HTML, ale nie zawartość outputs .

Zaawansowane tagi niestandardowe przy użyciu węzła

Czasami to, co chcesz zrobić, jest po prostu zbyt skomplikowane dla filter lub simple_tag . W tym celu musisz utworzyć funkcję kompilacji i moduł renderujący.

W tym przykładzie utworzymy szablon tag verbose_name o następującej składni:

Przykład Opis
{% verbose_name obj %} Pełna nazwa modelu
{% verbose_name obj 'status' %} Pełna nazwa pola „status”
{% verbose_name obj plural %} Pełna nazwa liczby mnogiej modelu
{% verbose_name obj plural capfirst %} Pełna nazwa modelu w liczbie mnogiej
{% verbose_name obj 'foo' capfirst %} Pełna nazwa pola pisana wielką literą
{% verbose_name obj field_name %} Pełna nazwa pola ze zmiennej
{% verbose_name obj 'foo'|add:'_bar' %} Pełna nazwa pola „foo_bar”

Powodem, dla którego nie możemy tego zrobić za pomocą prostego znacznika, jest to, że plural i capfirst nie są ani zmiennymi, ani łańcuchami, są „słowami kluczowymi”. Moglibyśmy oczywiście zdecydować o przekazaniu ich jako ciągów 'plural' i 'capfirst' , ale może to powodować konflikty z polami o tych nazwach. Czy {% verbose_name obj 'plural' %} znaczyłoby „pełna nazwa mnoga obj ” lub „ obj.plural nazwa obj.plural ”?

Najpierw stwórzmy funkcję kompilacji:

@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)

A teraz renderer:

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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow