# coding: utf-8
from __future__ import absolute_import, unicode_literals
from django.db import models
from django.utils.encoding import force_text
from django.utils.html import conditional_escape, mark_safe
from django_tables2.templatetags.django_tables2 import title
from .base import Column, library
[docs]@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_text` on each item.
filter: callable to filter, limit or order the QuerySet, it gets the
`ManyRelatedManager` as first argument and must return.
By default, it returns `all()``
separator: separator string to join the items with. default: ', '
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)
@property
def name(self):
return '{} {}'.format(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: u.name)
'''
def __init__(self, transform=None, filter=None, separator=', ', *args, **kwargs):
if transform is not None:
self.transform = transform
if filter is not None:
self.filter = filter
self.separator = separator
kwargs.setdefault('orderable', False)
super(ManyToManyColumn, self).__init__(*args, **kwargs)
[docs] 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):
# if value is None or not value.exists():
if not value.exists():
return '-'
return mark_safe(
conditional_escape(self.separator).join(
map(conditional_escape, map(self.transform, self.filter(value)))
)
)
@classmethod
def from_field(cls, field):
if isinstance(field, models.ManyToManyField):
return cls(verbose_name=title(field.verbose_name))