【问题标题】:Django-MPTT serializing with DRF in Vue templateDjango-MPTT 在 Vue 模板中使用 DRF 进行序列化
【发布时间】:2020-09-25 21:17:44
【问题描述】:

我尝试在 Vue 模板中呈现一个 MPTT 模型的评论。我想出了序列化器:

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")

现在我得到了 JSON 格式的 API 响应:

[
    {
        "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>

你能指导我递归地渲染它们吗?

【问题讨论】:

    标签: vue.js django-rest-framework


    【解决方案1】:

    如果有人想解决这个问题,我已经对序列化器进行了一些更改,并为 Vue 端的 cmets 准备了一个模板,

    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">
                                &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 && 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>
    

    希望对大家有帮助。

    【讨论】:

      最近更新 更多