获取嵌套层次结构中自引用Django模型的所有子级

z9gpfhce  于 2022-12-01  发布在  Go
关注(0)|答案(1)|浏览(149)

简介

我们目前正在开发一个Django REST框架项目。它连接到一个Postgres数据库,这个数据库保存了一些层次结构(树形结构)的数据,这些数据有很多层深。我们应该为GET请求提供一个端点,当没有提供参数时,它会返回整个嵌套的树形结构(父、子、孙等)。

示例数据

下表显示了区域的样本数据,其中每个区域都可以有一个父代,表示区域的层次结构。在本例中,层次结构有三个层次(世界〉洲〉国家)。但实际上,树可以更深,具有未知数量的层次(世界〉洲〉国家〉省〉城市〉社区〉等等)。
| 标识符|区域|父区域标识|
| - -|- -|- -|
| 一个|全世界|空值|
| 2个|欧洲|一个|
| 三个|亚洲|一个|
| 四个|非洲|一个|
| 五个|比利时|2个|
| 六个|德国|2个|
| 七个|西班牙|2个|
| 八个|日本|三个|
| 九个|印度尼西亚|三个|
| 10个|越南|三个|
| 十一|坦桑尼亚|四个|
| 十二|埃及|四个|
| 十三|塞内加尔的|四个|

我们的目标

下面显示的JSON输出是我们尝试实现的,它是/region资源的GET请求的响应主体的目标。

{
   "id":1,
   "region":"world",
   "children":[
      {
         "id":2,
         "region":"europe",
         "children":[
            {
               "id":5,
               "region":"belgium"
            },
            {
               "id":6,
               "region":"germany"
            },
            {
               "id":7,
               "region":"spain"
            }
         ]
      },
      {
         "id":3,
         "region":"asia",
         "children":[
            {
               "id":8,
               "region":"japan"
            },
            {
               "id":9,
               "region":"indonesia"
            },
            {
               "id":10,
               "region":"vietnam"
            }
         ]
      },
      {
         "id":4,
         "region":"africa",
         "children":[
            {
               "id":11,
               "region":"tanzania"
            },
            {
               "id":12,
               "region":"egypt"
            },
            {
               "id":13,
               "region":"senegal"
            }
         ]
      }
   ]
}

"我们迄今为止的努力和成就"
下面是我们实现目标的方法。参见下面的代码,了解模型、序列化器和视图:

Models.py
________
class HierarchyData:
                region = models.CharField(max_length=100, null=False, default=None)
                parent = models.ForeignKey("self", models.DO_NOTHING, null=True, blank=True, db_column='parent', related_name="children")
 
 
Serializers.py
__________
class HeirarchyDataSerializer(serialisers.ModelSerializer):
                class Meta:
                                model = HierarchyData
                                fields = [“id”,”region”, “children”]
               
Views.py
__________
Class ListHierarchyData(generics.ListAPIView):
                queryset = HierarchyData.objects.all()
                serializer_class = HeirarchyDataSerializer
                permission_classes = [isAuthenticated]

当我调用给定场景的端点时,我得到如下格式的JSON响应:

{
                                “id”: 1,
                                “region”: “world”,
                                “children”: [ 2,3,4]
                }

似乎没有回答我的问题的相关堆栈溢出问题

  1. How to recursively query in django efficiently?
  2. Django - Models - Recursively retrieve parents of a leaf node
  3. Django self-recursive foreignkey filter query for all childs
    上面提到的问题部分解决了我的问题,但我仍然无法得到想要的结果。请参阅下面的详细信息:
    1:我不能直接接触数据库,我只能通过ORM与数据库交互。
    2:递归超时,无法序列化,表示“Model”类型的对象不可序列化。
    3:这一个对我部分有效:基于这篇文章,我尝试在模型中添加以下内容:
def get_children(self):
          children = list()
          children.append(self)
          for child in self.children.all():
              children.extend(children.get_children())
          return children

然后我得到所有嵌套的子级,但是所有嵌套的值都在同一层。例如world有子级[2,3,4],而那些子级本身有(grand)子级。然后它在同一行列出这些子级,例如children = [2,3,4,5,6,7,8,9,10,11,12,13]。这并不代表示例数据中的层。
然后,我尝试了以下模型解决方案:

def get_all_children(self, include_self=True):
    r = []
    if include_self:
        r.append(self)
    for c in Person.objects.filter(parent=self):
        _r = c.get_all_children(include_self=True)
        if 0 < len(_r):
            r.extend(_r)
    return r

这一个工作;它会找到嵌套的子代,但会产生两个问题:a.当我按原样使用代码时,它会给我序列化器错误,但是如果我在序列化器中添加'get_all_children',并为该属性添加一个不同的序列化器,那么它会序列化对象,这我可以接受。b.它不能以嵌套方式追加对象,它只是将一个列表嵌套在另一个列表中,而没有子列表。它显示的数据如下所示(仅限于欧洲,这里没有一个巨大的例子):

{
   "id":1,
   "region":"world",
   "get_all_children":[
      [
         {
            "id":2,
            "region":"europe"
         }
      ],
      [
         [
            {
               "id":5,
               "region":"belgium"
            }
         ],
         [
            {
               "id":6,
               "region":"germany"
            }
         ],
         [
            {
               "id":7,
               "region":"spain"
            }
         ]
      ]
   ]
}

现在的数据是好的,除了在Europe之后,它没有开始在同一个数组中嵌套子数组,它只是为子数组开始一个新的数组,并将它们与外部列表一起追加。它基本上添加了一个嵌套结构,而没有将其嵌套在父数组中。
"我们的问题"
我们如何返回在'我们的目标'中提到的这个区域数据的输出,它拥有一个树结构,去一个未知数量的层次深?当然,它是有限的深度。
我必须遵循的唯一约束是我不能编辑视图部分!

pcww981p

pcww981p1#

您可以在序列化器上使用depth属性,例如

class Meta:
    model = Model
    fields = ['id', 'region', 'children', 'parent']
    depth = 2

或者在序列化程序上使用to_representation方法:

def to_representation(self, instance):
        self.fields['parent'] = SerializerClass(many=False, read_only=True)
        self.fields['children'] = SerializerClass(many=True, read_only=True)
        return super(SerializerClass, self).to_representation(instance)

因此,这将允许您使用模型上的related_name集以及parent来查询children

相关问题