我无法处理restframework
N+1
问题。
例如,我有以下模型:
class User(models.Model):
username = ...
email = ...
avatar = ...
last_login = ...
class Article(models.Model):
user = models.ForeignKey(User)
title = ...
abstract = ...
content = ...
category = ...
class Comment(models.Model):
user = models.ForeignKey(User)
article = models.ForeignKey(Article)
content = ...
## This field! ##
reply_to = models.ForeignKey('self', null=True)
time = ...
也就是说,用户可以评论文章或文章评论。
现在我需要构建一个通知系统,所以我添加:
class Notification(models.Model):
actor_type = models.ForeignKey(
ContentType, related_name='notify_actor', on_delete=models.CASCADE)
actor_id = models.PositiveIntegerField()
actor = GenericForeignKey('actor_type', 'actor_id')
verb = models.CharField(max_length=255)
receiver = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='notifications')
is_read = models.BooleanField(default=False, db_index=True)
target_type = models.ForeignKey(
ContentType,
related_name='notify_target',
blank=True,
null=True,
on_delete=models.CASCADE
)
target_id = models.PositiveIntegerField()
target = GenericForeignKey('target_type', 'target_id')
time = models.DateTimeField(auto_now_add=True)
source_type = models.ForeignKey(
ContentType, blank=True, null=True,
related_name='notify_subject_object',
on_delete=models.CASCADE
)
source_id = models.PositiveIntegerField()
source = GenericForeignKey('source_type', 'source_id')
通知如下:
Yriuns commented Article 1: good job
<actor> <verb> <target> <source>
或:
Yriuns commented Your Comment of Article 1: I doubt it
<actor> <verb> <target> <source>
还有serializers
:
class UserSeriazlier(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'avatar')
class ArticleSeriazlier(serializers.ModelSerializer):
class Meta:
model = Article
fields = ('title', 'abstract')
class SimpleCommentSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = ArticleComment
fields = ('id', 'user', 'content')
class CommentSeriazlier(serializers.ModelSerializer):
user = UserSeriazlier()
class Meta:
model = Comment
fields = ('id', 'content', 'user', 'time', 'reply_to')
class NotificationSerializer(serializers.ModelSerializer):
actor = GenericRelatedField(read_only=True)
target = GenericRelatedField(read_only=True)
subject = GenericRelatedField(read_only=True)
@classmethod
def setup_eager_loading(cls, queryset):
queryset = queryset.prefetch_related(
'actor',
'verb',
'target',
'source',
)
return queryset
class Meta:
model = Notification
fields = ('actor', 'verb', 'target', 'source', 'time', 'is_read')
class GenericRelatedField(serializers.RelatedField):
def to_representation(self, value):
if isinstance(value, User):
return UserSerializser(value).data
elif isinstance(value, Article):
return ArticleSerializser(value).data
elif isinstance(value, Comment):
return CommentSerializser(value).data
raise Exception('Unexpected type')
问题1
当我序列化用户的通知列表时:
qs = Notification.objects.filter(receiver=user)
qs = NotificationSerializer.setup_eager_loading(qs)
return NotificationSerializer(qs, many=True).data
它一步一步地查询数据库中的source
,这确实很慢。
问题2
此外,它还会选择许多我不需要的字段。
例如,我只需要username
的{{1}}和avatar
,但是它将选择所有字段。而且我只需要UserSeriazlier
的{{1}}和title
,但是它将选择所有字段。
我该如何解决这些问题?