File: manytomanycolumn.py

package info (click to toggle)
django-tables 2.7.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,752 kB
  • sloc: python: 7,120; makefile: 132; sh: 74
file content (101 lines) | stat: -rw-r--r-- 3,676 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
from django.db import models
from django.utils.encoding import force_str
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe

from .base import Column, LinkTransform, library


@library.register
class ManyToManyColumn(Column):
    """
    Display the list of objects from a `ManyRelatedManager`

    Ordering is disabled for this column.

    Arguments:
        transform: callable to transform each item to text, it gets an item as argument
            and must return a string-like representation of the item.
            By default, it calls `~django.utils.force_str` on each item.
        filter: callable to filter, limit or order the QuerySet, it gets the
            `ManyRelatedManager` as first argument and must return a filtered QuerySet.
            By default, it returns `all()`
        separator: separator string to join the items with. default: ``", "``
        linkify_item: callable, arguments to reverse() or `True` to wrap items in a ``<a>`` tag.
            For a detailed explanation, see ``linkify`` argument to ``Column``.

    For example, when displaying a list of friends with their full name::

        # models.py
        class Person(models.Model):
            first_name = models.CharField(max_length=200)
            last_name = models.CharField(max_length=200)
            friends = models.ManyToManyField(Person)
            is_active = models.BooleanField(default=True)

            @property
            def name(self):
                return f"{self.first_name} {self.last_name}"

        # tables.py
        class PersonTable(tables.Table):
            name = tables.Column(order_by=("last_name", "first_name"))
            friends = tables.ManyToManyColumn(transform=lambda user: user.name)

    If only the active friends should be displayed, you can use the `filter` argument::

        friends = tables.ManyToManyColumn(filter=lambda qs: qs.filter(is_active=True))

    """

    def __init__(
        self, transform=None, filter=None, separator=", ", linkify_item=None, *args, **kwargs
    ):
        kwargs.setdefault("orderable", False)
        super().__init__(*args, **kwargs)

        if transform is not None:
            self.transform = transform
        if filter is not None:
            self.filter = filter
        self.separator = separator

        link_kwargs = None
        if callable(linkify_item):
            link_kwargs = dict(url=linkify_item)
        elif isinstance(linkify_item, (dict, tuple)):
            link_kwargs = dict(reverse_args=linkify_item)
        elif linkify_item is True:
            link_kwargs = dict()

        if link_kwargs is not None:
            self.linkify_item = LinkTransform(attrs=self.attrs.get("a", {}), **link_kwargs)

    def transform(self, obj):
        """
        Transform is applied to each item of the list of objects from the ManyToMany relation.
        """
        return force_str(obj)

    def filter(self, qs):
        """
        Filter is called on the ManyRelatedManager to allow ordering, filtering or limiting
        on the set of related objects.
        """
        return qs.all()

    def render(self, value):
        items = []
        for item in self.filter(value):
            content = conditional_escape(self.transform(item))
            if hasattr(self, "linkify_item"):
                content = self.linkify_item(content=content, record=item)

            items.append(content)

        return mark_safe(conditional_escape(self.separator).join(items))

    @classmethod
    def from_field(cls, field, **kwargs):
        if isinstance(field, models.ManyToManyField):
            return cls(**kwargs)