Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/347.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何聚合某些字段中具有相同值的多个对象?_Python_Django - Fatal编程技术网

Python 如何聚合某些字段中具有相同值的多个对象?

Python 如何聚合某些字段中具有相同值的多个对象?,python,django,Python,Django,我有这个模型: class Connection(models.Model): CONNECTION_CHOICES = [ ('REST-API', 'REST-API'), ('SSH', 'SSH'), ('SFTP', 'SFTP'), ('SOAP-API', 'SOAP-API'), ] endpoint = models.CharField(max_length=240, blank=True,

我有这个模型:

class Connection(models.Model):
    CONNECTION_CHOICES = [
        ('REST-API', 'REST-API'),
        ('SSH', 'SSH'),
        ('SFTP', 'SFTP'),
        ('SOAP-API', 'SOAP-API'),
    ]
    endpoint = models.CharField(max_length=240, blank=True, null=True)
    port = models.IntegerField(blank=True, null=True)
    connection_type = models.CharField(max_length=240, choices=CONNECTION_CHOICES)
    source_tool = models.ForeignKey(Tool, on_delete=models.CASCADE, related_name='source-tool+')
    target_tool = models.ForeignKey(Tool, on_delete=models.CASCADE, related_name='target-tool+')


    def __str__(self):
        return self.source_tool.name + " to " + self.target_tool.name 
    

    def get_absolute_url(self):
        return reverse('tools:connection-detail', kwargs={'pk': self.pk})
在视图中,我尝试组合对象,其中源工具和目标工具是相同的,但连接类型不同

目前,我有这样的看法:

def api_map_view(request):
    json = {}
    nodes = []
    links = []
    connections = Connection.objects.all()

    for connection in connections:
        if {'name': connection.source_tool.name, 'id': connection.source_tool.id} not in nodes:
            nodes.append({'name': connection.source_tool.name, 'id': connection.source_tool.id})
        if {'name': connection.target_tool.name, 'id': connection.target_tool.id} not in nodes:
            nodes.append({'name': connection.target_tool.name, 'id': connection.target_tool.id})
        if {'source': connection.source_tool.id, 'target': connection.target_tool.id} in links:
            links.replace({'source': connection.source_tool.id, 'target': connection.target_tool.id, 'type': links['type'] + '/' + connection_type})
        else:
            links.append({'source': connection.source_tool.id, 'target': connection.target_tool.id, 'type': connection.connection_type})
    json['nodes'] = nodes
    json['links'] = links
    print(json)
    return JsonResponse(data=json)
这个返回,例如

{
   'nodes':
           [
               {'name': 'Ansible', 'id': 1 },
               {'name': 'Terraform', 'id': 2},
               {'name': 'Foreman', 'id': 3}
           ],
   'links':
           [
               {'source': 1, 'target': 2, 'type': 'SSH'},
               {'source': 2, 'target': 3, 'type': 'REST-API'}
               {'source': 1, 'target': 2, 'type': 'REST-API'}
           ]
}
我的用例是,我想修改连接,我不会为同一个连接获得两个不同的列表条目,只是类型不同。我希望实现以下目标,而不是上面的JSON:

{
   'nodes':
           [
               {'name': 'Ansible', 'id': 1 },
               {'name': 'Terraform', 'id': 2},
               {'name': 'Foreman', 'id': 3}
           ],
   'links':
           [
               {'source': 1, 'target': 2, 'type': 'SSH/REST-API'},
               {'source': 2, 'target': 3, 'type': 'REST-API'}
           ]
}
目前,我无法创建查询或修改dict列表来查找条目,其中源和目标与当前条目相同(在列表中迭代),并且无法修改type字段

我将Django3.1与Python3.8一起使用


关于

问题发生在以下几行:

if {'source': connection.source_tool.id, 'target': connection.target_tool.id} in links:
    links.replace({'source': connection.source_tool.id, 'target': connection.target_tool.id, 'type': links['type'] + '/' + connection_type})
else:
    links.append({'source': connection.source_tool.id, 'target': connection.target_tool.id, 'type': connection.connection_type})
您正在检查
{'source':X,'target':Y}
是否在
链接中,但对于第一次出现的情况,您将
{'source':X,'target':Y,'type':Z1}
添加到
链接中。因此,对于链接中的
,您添加的项目将永远不会是
True
,因为它有一个额外的键“type”

另一方面,您不能直接检查链接中的
{'source':X,'target':Y,'type':Z1}
,因为当
'type':Z2
时,它将不匹配

要解决此问题,请执行以下操作之一:


1.(首选)使用带有键的字典作为源和目标的元组或仅作为源和目标的元组。由于元组和namedtuple是可散列的,因此它们可以用作dict键

import collections  # at the top

links = {}  # links is now a dict, not a list
SourceTarget = collections.namedtuple('SourceTarget', 'source target')
# >>> SourceTarget('X', 'Y')  # to show how they work
# SourceTarget(source='X', target='Y')
要像这样使用:

if (connection.source_tool.id, connection.target_tool.id) in links:  # tuples can match with namedtuples
    links[SourceTarget(connection.source_tool.id, connection.target_tool.id)] += '/' + connection.connection_type
else:
    links[SourceTarget(connection.source_tool.id, connection.target_tool.id)] = connection.connection_type
在您希望它们作为对象/词典列表的末尾:

json['links'] = [{'source': st.source, 'target': st.target, 'type': type_}
                 for st, type_ in links.items()]
                # I used `type_` so that it doesn't conflict with Python own `type()`

2.(1的变体)仍然需要使用元组或命名元组作为dict键,但随后使用with
列表来继续追加连接类型

您不需要
if/else
部分,只需执行以下操作:

import collections

links = collections.defaultdict(list)
...

# using tuples as the key instead of namedtuples....
links[(connection.source_tool.id, connection.target_tool.id)].append(connection.connection_type)
这将为每个
(源、目标)
创建新条目,并将该
类型作为列表中的单个值,或者将其附加到该列表中。如果需要检查,则无

并将其转换为json obj:

json['links'] = [{'source': st[0], 'target': st[1], 'type': '/'.join(types)}
                 for st, types) in links.items()]

顺便说一句,由于您使用的是Python 3.8,因此可以使用“walrus操作符”,以减少重复并使代码更加简洁

以选项1为例,它将使if块的第一部分更加清晰,因为很明显,如果它不存在,您正在添加它;无需阅读整条长线

if (src := {'name': connection.source_tool.name, 'id': connection.source_tool.id}) not in nodes:
    nodes.append(src)
if (trg := {'name': connection.target_tool.name, 'id': connection.target_tool.id}) not in nodes:
    nodes.append(trg)
if (st := (connection.source_tool.id, connection.target_tool.id)) in links:
    # used a tuple to update _existing_ NT element
    links[st] += '/' + connection.connection_type
else:
    # but must use namedtuples when adding new elements
    links[SourceTarget(*st)] = connection.connection_type

这个代码没有意义。links是一个列表,但您在一个地方将其视为dict,并使用一个方法“replace”,而list和dict都没有这个方法。然而,你从来没有遇到过这样的代码,因为你在链接中附加了带有3个键的dict,并测试链接是否有一个带有2个键的dict。这正是我的问题:找到dict的“子部分”(3个值中的2个)来修改第三个值是的,我后来发现这是多次尝试的结果,其中一点链接是一个字符串。FWIW,我更喜欢defaultdict解决方案(当我看到aneroid正在使用它时,我也在制作该解决方案)。我不会将类型加入字符串,但要保留一个列表,这样消费者就可以选择她希望的格式,而不必进行拆分。太棒了!那正是我要找的。非常感谢你!我使用了第一个变体,它就像一个符咒一样工作。我喜欢第二个变体,因为它使用的列表可以将项目分开(不仅仅是使用
/
),因此如果您必须进行其他检查,使用项目列表
['a','b','c']会更容易
而不是使用字符串
a/b/c
。使用元组还是命名元组实际上取决于用例,如果到处使用许多元组,并且会混淆操作的元组类型。如果使用命名元组,请记住将键添加为命名元组;即使键可以“检查”如上所示,针对元组。如果不使用
if/then
,则“仅追加”意味着代码更少更清晰。我添加了一个编辑,用于使用Python 3.8赋值表达式可以/应该进行的一些代码清理。使代码更具可读性。