2016-04-12 16 views
1

django-treebeardを使用してdjango-rest-frameworkの階層データ構造を持つアプリケーションを開発しています。私(簡体字)メインモデルは、私が現在達成しようとしていること(例えば、すべての子どもたちが開始されているかどうかなど)の余分なフィールドを示し、すべてのルート・ノードのリストビューで、このDjango-treebeard MPノードのプリフェッチ子孫

class Task(MP_Node): 
    name = models.CharField(_('name'), max_length=64) 
    started = models.BooleanField(default=True) 

のように見えます。これを行うには、私は、ビュー指定:

class TaskViewSet(viewsets.ViewSet): 

    def retrieve(self, request, pk=None): 
     queryset = Task.get_tree().filter(depth=1, job__isnull=True) 
     operation = get_object_or_404(queryset, pk=pk) 
     serializer = TaskSerializer(operation) 
     return Response(serializer.data) 

とシリアライザ

class TaskSerializer(serializers.ModelSerializer): 
    are_children_started = serializers.SerializerMethodField() 

    def get_are_children_started(self, obj): 
     return all(task.started for task in Task.get_tree(obj)) 

このすべての作品を、私は期待どおりの結果を得ることができます。しかし、私はN + 1のクエリ問題にぶつかります。それぞれのルートタスクに対して、すべての子を個別にフェッチする必要があります。通常これはprefetch_relatedを使って解くことができますが、django-treebeardのマテリアライズドパス構造を使用すると、タスクモデル間にDjangoの関係はないので、prefetch_relatedは何をするのか分かりません。私はカスタムプリフェッチオブジェクトを使用しようとしましたが、これはまだDjangoリレーションパスが必要なので、動作させることができませんでした。

私の現在の考えは、このようなのようなそのルートノードを指す外部キーでタスクモデルを拡張することです。それが照会できるように、MPの関係を明示的にするために

root_node = models.ForeignKey('self', null=True, 
           related_name='descendant_tasks', 
           verbose_name=_('root task') 
          ) 

。しかし、これは非乾式の方法のように感じるので、誰かがそれに取り組む方法について別の提案をしているかどうかは疑問です。私は必ず私は常にを指すようにするために私のタスクモデルにメソッドを保存し、更新

root_node = models.ForeignKey('self', null=True, 
          related_name='descendant_tasks', 
          verbose_name=_('root task') 
         ) 

:私は、このようなのようなそのルートノードを指し示す各​​タスクへの外部キーを追加することになってしまいました最後に

答えて

1

正しいルートノード

def save(self, force_insert=False, force_update=False, using=None, update_fields=None): 
    try: 
     self.root_task = self.get_root() 
    except ObjectDoesNotExist: 
     self.root_task = None 

    return super(Task, self).save(force_insert=False, force_update=False, using=None, 
            update_fields=None 
           ) 

、これは私が単にprefetch_related('descendants')を使用して、すべての子孫をプリフェッチすることができます。

私は巣に子孫の平坦化されたリストを、次の機能を使用し、ネストされた形で子孫を持っている必要があるとき、再び

def build_nested(tasks): 

    def get_basepath(path, depth): 
     return path[0:depth * Task.steplen] 

    container, link = [], {} 
    for task in sorted(tasks, key=attrgetter('depth')): 
     depth = int(len(task.path)/Task.steplen) 
     try: 
      parent_path = get_basepath(task.path, depth - 1) 
      parent_obj = link[parent_path] 
      if not hasattr(parent_obj, 'sub_tasks'): 
       parent_obj.sub_tasks = [] 
      parent_obj.sub_tasks.append(task) 
     except KeyError: # Append it as root task if no parent exists 
      container.append(task) 

     link[task.path] = task 

    return container