Neither the docs not the IRC chennel helped much, so I had to hack my way through Django's guts using my non-existent Python skills to create the following generic manager method that finds stuff according to multiple related stuff. The official docs only show how to get e.g. a publication through an article it published, but not how to get a publication that published two articles (or three, or… n). I use it to find a ship involving some characters.
class FooManager(models.Manager):
def get_by_m2m(self, m2m_field_name, related_obj_ids, specified_only = False):
"""
Returns QuerySet with objects that are connected to
all specified "related_obj_ids" through "m2m_field_name".
Limits result to only those objects that are linked _only_
to the specified related objects upon request.
"""
assert len(related_obj_ids) > 0
meta = self.model._meta
m2m = [x for x in meta.many_to_many if x.name == m2m_field_name]
if len(m2m) != 1:
raise 'ManyToMany Field "%s" not found in model "%s.%s"!' % (m2m_field_name, meta.app_label, meta.object_name)
m2m = m2m[0]
pk_column = meta.pk.column
m2m_table = m2m.m2m_db_table()
source_col = m2m.m2m_column_name()
target_col = m2m.m2m_reverse_name()
tables = []
params = []
where = ['"%s"."%s" = t1."%s"' % (meta.db_table, pk_column, source_col)]
n = len(related_obj_ids)
i = 1
for id in related_obj_ids:
if type(id) != type(0): id = id.id
params.append(id)
tables.append('"%s" t%d' % (m2m_table, i))
where.append('t%d."%s" = %%d' % (i, target_col))
if i != n:
where.append('t%d."%s" = t%d."%s"' % (i, source_col, i + 1, source_col))
i = i + 1
if specified_only:
where.append(
'%%d = (SELECT COUNT(*) FROM "%s" WHERE "%s" = "%s"."%s")' % (m2m_table, source_col, meta.db_table, pk_column))
params.append(n)
return self.extra(tables=tables, where=where, params=params)
No comments:
Post a Comment