Django/Python - 通过多对多关系中的公共集对对象进行分组

Bri*_*unt 7 python puzzle django algorithm

这是一个部分算法逻辑问题(如何做),部分实现问题(如何做到最好!).我正在和Django合作,所以我想我会与之分享.

在Python中,值得一提的是,问题与how-do-i-use-pythons-itertoolsgroupby有些相关.

假设您有两个Django Model派生类:

from django.db import models

class Car(models.Model):
    mods = models.ManyToManyField(Representative)
Run Code Online (Sandbox Code Playgroud)

from django.db import models

class Mods(models.Model):
   ...
Run Code Online (Sandbox Code Playgroud)

如何获得汽车列表,按汽车分组,使用一组共同的Mod?

即我想得到一个类似的:

Cars_by_common_mods = [ 
  { mods: { 'a' }, cars: { 'W1', 'W2' } },
  { mods: { 'a', 'b' }, cars: { 'X1', 'X2', 'X3' }, },
  { mods: { 'b' }, cars: { 'Y1', 'Y2' } },
  { mods: { 'a', 'b', 'c' }, cars: { 'Z1' } },
]
Run Code Online (Sandbox Code Playgroud)

我一直在想类似的东西:

def cars_by_common_mods():
  cars = Cars.objects.all()

  mod_list = []      

  for car in cars:
    mod_list.append( { 'car': car, 'mods': list(car.mods.all()) } 

  ret = []

  for key, mods_group in groupby(list(mods), lambda x: set(x.mods)):
    ret.append(mods_group)

  return ret
Run Code Online (Sandbox Code Playgroud)

但是,这不起作用,因为(可能还有其他原因)groupby似乎没有按照mods集合进行分组.我想mod_list必须排序才能与groupby一起使用.总而言之,我相信那里有一些简单而优雅的东西,既有启发性又有启发性.

干杯谢谢!

Jon*_*nan 4

您是否尝试过先对列表进行排序?尽管有大量数据库命中,但您提出的算法应该可以工作。

import itertools

cars = [
    {'car': 'X2', 'mods': [1,2]},
    {'car': 'Y2', 'mods': [2]},
    {'car': 'W2', 'mods': [1]},
    {'car': 'X1', 'mods': [1,2]},
    {'car': 'W1', 'mods': [1]},
    {'car': 'Y1', 'mods': [2]},
    {'car': 'Z1', 'mods': [1,2,3]},
    {'car': 'X3', 'mods': [1,2]},
]

cars.sort(key=lambda car: car['mods'])

cars_by_common_mods = {}
for k, g in itertools.groupby(cars, lambda car: car['mods']):
    cars_by_common_mods[frozenset(k)] = [car['car'] for car in g]

print cars_by_common_mods
Run Code Online (Sandbox Code Playgroud)

现在,关于这些查询:

import collections
import itertools
from operator import itemgetter

from django.db import connection

cursor = connection.cursor()
cursor.execute('SELECT car_id, mod_id FROM someapp_car_mod ORDER BY 1, 2')
cars = collections.defaultdict(list)
for row in cursor.fetchall():
    cars[row[0]].append(row[1])

# Here's one I prepared earlier, which emulates the sample data we've been working
# with so far, but using the car id instead of the previous string.
cars = {
    1: [1,2],
    2: [2],
    3: [1],
    4: [1,2],
    5: [1],
    6: [2],
    7: [1,2,3],
    8: [1,2],
}

sorted_cars = sorted(cars.iteritems(), key=itemgetter(1))
cars_by_common_mods = []
for k, g in itertools.groupby(sorted_cars, key=itemgetter(1)):
    cars_by_common_mods.append({'mods': k, 'cars': map(itemgetter(0), g)})

print cars_by_common_mods

# Which, for the sample data gives me (reformatted by hand for clarity)
[{'cars': [3, 5],    'mods': [1]},
 {'cars': [1, 4, 8], 'mods': [1, 2]},
 {'cars': [7],       'mods': [1, 2, 3]},
 {'cars': [2, 6],    'mods': [2]}]
Run Code Online (Sandbox Code Playgroud)

现在您已经获得了汽车 ID 和改装 ID 的列表,如果您需要使用完整的对象,您可以对每个对象进行一次查询,以获得每个型号的完整列表,并为这些对象创建一个查找dict,键为他们的 ID——那么,我相信,鲍勃就是众所周知的你父亲的兄弟。