#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>
Надеюсь, это кому-нибудь поможет.