Сериализация Django-MPTT с помощью DRF в шаблоне Vue

#vue.js #django-rest-framework

#vue.js #django-rest-framework

Вопрос:

Я пытаюсь отобразить комментарии, которые являются моделью MPTT в шаблоне Vue. Я разобрался с сериализатором:

 class ArticleCommentSerializer(serializers.ModelSerializer):
    article = serializers.StringRelatedField(read_only=True)
    parent = serializers.StringRelatedField(read_only=True)
    user = serializers.StringRelatedField(read_only=True)
    user_image = serializers.StringRelatedField(source='user.image.url', read_only=True)
    
    class Meta:
        model = ArticleComment
        fields = ["article", "user","parent", "children", "user_image", "content"]
  

Я делаю свой запрос в наборах представлений:

 class ArticleCommentsListAPIView(generics.ListAPIView):
    queryset = Article.objects.all()
    serializer_class = ArticleCommentSerializer
    pagination_class = None
    
    def get_queryset(self):
        kwarg_slug = self.kwargs.get("slug")
        article = get_object_or_404(Article, slug=kwarg_slug)
        return ArticleComment.objects.filter(article__slug=kwarg_slug).order_by("-created_at")
  

теперь я получаю ответ API в виде JSON:

 [
    {
        "article": "ytsejam - this is title 2",
        "user": "ytsejam",
        "parent": "Comment by üçüncü yorum",
        "children": [
            {
                "article": "ytsejam - this is title 2",
                "user": "ytsejam",
                "parent": "Comment by beşinci yorum",
                "children": [],
                "user_image": "/media/19_106778977_10158653245217074_823439017239771650_o.jpg",
                "content": "altıncı yorum",
                "user_thumbnail": "http://localhost:8000/media/cache/d3/40/d340307fa76bde380e9d1677ad9e3a04.jpg",
                "voters": 1,
                "created_at": "31 May 2020"
            }
        ],
        "user_image": "/media/19_106778977_10158653245217074_823439017239771650_o.jpg",
        "content": "beşinci yorum",
        "user_thumbnail": "http://localhost:8000/media/cache/d3/40/d340307fa76bde380e9d1677ad9e3a04.jpg",
        "voters": 1,
        "created_at": "31 May 2020"
    },
]
  

Мне нужна помощь, чтобы рекурсивно отобразить их в шаблоне Vue.До сих пор я сталкивался с этим решением, но только на первом уровне:

 <ul
    v-for="comment in articlesComments"
    :key="comment.id">
    <li v-if="!comment.parent" class="comment-item has-children">
         <div class="comments-content">
            <p>{{comment.content}}</p>
        </div>
                
        <ul v-if="comment.children" class="children">
            <li class="comment-item" 
                v-for="comment in comment.children" 
                :key="comment-id">          
                <div class="comments-content">
                    <p>{{comment.content}}</p>
                </div>
            </li>
        </ul>
    </li> 
 </ul>
  

Можете ли вы направить меня на их рекурсивный рендеринг?

Ответ №1:

Если кто-то хочет решить эту проблему, я внес некоторые изменения в serializer и подготовил шаблон для комментариев на стороне Vue,

 class ArticleCommentSerializer(serializers.ModelSerializer):
    article = serializers.StringRelatedField(read_only=True)
    
    parent = serializers.StringRelatedField(read_only=True)
    user = serializers.StringRelatedField(read_only=True)
    is_child_node = serializers.StringRelatedField(read_only=True)

    created_at = serializers.SerializerMethodField(read_only=True)
    voters = serializers.SerializerMethodField(read_only=True)
    user_image = serializers.StringRelatedField(source='user.image.url', read_only=True)
    user_thumbnail = HyperlinkedSorlImageField(
        '55x55',
        options={"crop": "center"},
        source='user.image',
        read_only=True
    )
    children_comments = serializers.ListField(read_only=True, source='get_children', child=RecursiveField())
    class Meta:
        model = ArticleComment
        fields = ["article", "user","parent","is_child_node", "children", "user_image", "content", "user_thumbnail", "voters","children_comments", "get_descendant_count","created_at"]


    def get_created_at(self, instance):
        return instance.created_at.strftime("%d %B %Y")

    def get_voters(self, instance):
        return instance.voters.count()
#this part is alternative field for children
ArticleCommentSerializer._declared_fields['children'] = ArticleCommentSerializer(
    many=True,
    source='get_children',
)   
  

и в шаблоне Vue

 <script>
import Tree from '@/components/comments/Tree';

export default {
    name: "Article",
    data(){
        return {
            newArticleCommentBody: null,
            error: null,
            showForm: false,
        }
    },
    components:{
        Tree
    },
    computed: {
    ...mapGetters('articles', {article: 'article'}),
    ...mapState({
                mostLikedArticles: state => state.articles.mostLikedArticles,
                articlesComments: state => state.articles.article.comments,
      }),
  },
  methods: {
    ...mapActions(['articles/fetchAnArticle']),
  },
  
  created() {
    this.$store.dispatch('articles/fetchAnArticle', this.$route.params).
    then(() => {
            this.isLoading = false;
            this.$store.dispatch('articles/fetchMostLikedArticles');
    })
  }
};
</script>
<template>
<ul   
    class="comments-list  style-3" 
    v-for="comment in articlesComments"
    :key="comment.id">
    <tree v-if="comment.parent==null" :tree-data="comment"></tree>
</ul>
</template
  

Tree.vue:

 <template>
  <div class="tree">
    <ul class="tree-list">

      <node-tree :node="treeData"></node-tree>
      
    </ul>
  </div>
</template>

<script>
import NodeTree from "./NodeTree";

export default {
  props: {
    treeData: Object
  },
  components: {
    NodeTree
  }
};
</script>
  

NodeTree.vue:

 <template>
     
  <li class="comment-item" >
        <div class="post__author-thumb">
            <img :src="node.user_thumbnail" alt="node.user">
        </div>
            
        <div class="comments-content">
            <div class="post__author author vcard">

                <div class="author-date">
                    <a class="h6 post__author-name fn" href="#">{{node.user}}</a>
                    <div class="post__date">
                        <time class="published" datetime="node.created_at">
                            amp;nbsp; -  {{node.created_at}} 
                        </time>
                    </div>
                </div>

            </div>

            <p>{{node.content}}</p>

            <a href="#" class="reply">Reply</a>
            <a href="#" class="report">Report</a>
        </div>
                    
    <ul v-if="node.children amp;amp; node.children.length" class="children">
      <node v-for="child in node.children" :key="child.id" :node="child"></node>
    </ul>
  </li>
</template>

<script>
export default {
  name: "node",
  props: {
    node: Object
  }
};
</script>
  

Надеюсь, это кому-нибудь поможет.