Django rest framework 简单模型序列化器列表视图永远显示 10 条记录
Django rest framework simple model serializer list view takes forever to show 10 records
我有这个模型 -
class News(BaseEntityBasicAbstract, HitCountMixin):
"""
News added from the dashboard with content
"""
NEWS_STATUS = (
('draft', _('Draft')),
('pending', _('Pending')),
('review', _('Review')),
('public', _('Public')),
('private', _('Private'))
)
backup = models.BooleanField(default=False)
prev_id = models.BigIntegerField(null=True, blank=True)
language = models.CharField(max_length=10, choices=LANGUAGES, default='bn', db_index=True)
heading = models.CharField(max_length=255, null=True, blank=True,
verbose_name=_('News Heading'),
help_text=_('Provide a news heading/caption.'))
sub_caption = models.TextField(max_length=255, null=True, blank=True,
verbose_name=_('Summary'),
help_text=_('Provide summary of the news.'))
url = models.CharField(max_length=255, unique=True, verbose_name=_('URL/Slug/Link'),
help_text=_('Unique url for the news without whitspace.'))
content = HTMLField(null=True, blank=True, verbose_name=_('Content'),
help_text=_('HTML content with texts, links & images.'))
featured_image = models.FileField(upload_to=FilePrefix('news/'), null=True, blank=True,
verbose_name=_('Featured Image'),
help_text=_('Upload a featured image for news.'))
image_caption = models.TextField(max_length=255, null=True, blank=True,
verbose_name=_('Image Caption'),
help_text=_('Provide a image caption.'))
status = models.CharField(max_length=20, choices=NEWS_STATUS, default='pending',
verbose_name=_('News Status'), db_index=True,
help_text=_('Only public news can be seen on front end.'))
source = models.ForeignKey(NewsSource, on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('News Source'),
help_text=_('Select a news source.'))
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('Category'),
help_text=_('Select a news category.'))
tags = tagulous.models.TagField(
blank=True,
to=Tags,
verbose_name=_('News Tags'),
help_text=_('Provide news tags separated with commas.')
)
published_at = models.DateTimeField(null=True, blank=True, db_index=True,
verbose_name=_('Published At'))
menu_items = GenericRelation(MenuItems, object_id_field='id',
related_query_name='news_as_menu')
hit_count_generic = GenericRelation(HitCount, object_id_field='object_pk',
related_query_name='news_hit_count')
created_by = models.ForeignKey(User, related_name='news_created_by',
on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('Created By'))
updated_by = models.ForeignKey(User, related_name='news_updated_by',
on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('Last Updated By'))
published_by = models.ForeignKey(User, related_name='news_published_by',
on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('Published By'))
deleted_by = models.ForeignKey(User, related_name='news_deleted_by',
on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('Deleted By'))
下面是序列化程序 -
class NewsSerializer(serializers.ModelSerializer):
class Meta:
model = News
fields = ['id', 'heading', 'sub_caption', 'url', 'content', 'featured_image',
'image_caption', 'category', 'source', 'tags', 'published_at']
这是视图 -
class NewsViewSets(viewsets.ModelViewSet):
queryset = News.objects.filter(
is_active=True,
status='public'
)
serializer_class = NewsSerializer
def get_queryset(self):
queryset = self.queryset.filter(
language=self.request.LANGUAGE_CODE
).order_by('-id')
return queryset
分页在settings.py中设置为仅10,当我点击新闻api url时,仅加载10条记录需要9/10秒。这是显示 django-debug-toolbar 报告的屏幕截图 -
我在数据库中有大约 40 万条记录 table,这可能是个问题,但我认为加载时间太长了。请帮我找到这里的问题!提前致谢。
过滤通常很慢。看起来您在相关字段上有数据库索引,但请注意,如果您有多个过滤器,则只会使用其中一个索引。
我是根据您的专栏猜测的,但似乎最常见的查询总是寻找 is_active=1
和 status='public'
。如果不是这种情况,您可能需要进行一些调整。
首先,删除 status
、is_active
和 language
字段上的 db_index=True
,否则您的数据库写入会不必要地变慢。
那么你可以制定这样的索引:
class Meta:
indexes = [
models.Index(
fields=["is_active", "status", "language"],
name="idx_filtering",
)
]
当您一次过滤所有三列时,这将有助于数据库。但是,如果您只对其中一列进行过滤,您可能希望保留原始 db_index=True
.
如果您使用的是 PostgreSQL,您可以做得更好:
class Meta:
indexes = [
models.Index(
fields=["is_active", "status", "language"],
name="idx_filtering",
condition=Q(is_active=True, status="public"),
)
这会将索引的大小减小到仅匹配 Q()
的索引,从而加快遍历速度。
另一件需要注意的事情是,一旦你达到更高的偏移量,使用 OFFSET
的分页就会非常慢。如果可能的话,您应该改用 DRF 的光标分页。
我有这个模型 -
class News(BaseEntityBasicAbstract, HitCountMixin):
"""
News added from the dashboard with content
"""
NEWS_STATUS = (
('draft', _('Draft')),
('pending', _('Pending')),
('review', _('Review')),
('public', _('Public')),
('private', _('Private'))
)
backup = models.BooleanField(default=False)
prev_id = models.BigIntegerField(null=True, blank=True)
language = models.CharField(max_length=10, choices=LANGUAGES, default='bn', db_index=True)
heading = models.CharField(max_length=255, null=True, blank=True,
verbose_name=_('News Heading'),
help_text=_('Provide a news heading/caption.'))
sub_caption = models.TextField(max_length=255, null=True, blank=True,
verbose_name=_('Summary'),
help_text=_('Provide summary of the news.'))
url = models.CharField(max_length=255, unique=True, verbose_name=_('URL/Slug/Link'),
help_text=_('Unique url for the news without whitspace.'))
content = HTMLField(null=True, blank=True, verbose_name=_('Content'),
help_text=_('HTML content with texts, links & images.'))
featured_image = models.FileField(upload_to=FilePrefix('news/'), null=True, blank=True,
verbose_name=_('Featured Image'),
help_text=_('Upload a featured image for news.'))
image_caption = models.TextField(max_length=255, null=True, blank=True,
verbose_name=_('Image Caption'),
help_text=_('Provide a image caption.'))
status = models.CharField(max_length=20, choices=NEWS_STATUS, default='pending',
verbose_name=_('News Status'), db_index=True,
help_text=_('Only public news can be seen on front end.'))
source = models.ForeignKey(NewsSource, on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('News Source'),
help_text=_('Select a news source.'))
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('Category'),
help_text=_('Select a news category.'))
tags = tagulous.models.TagField(
blank=True,
to=Tags,
verbose_name=_('News Tags'),
help_text=_('Provide news tags separated with commas.')
)
published_at = models.DateTimeField(null=True, blank=True, db_index=True,
verbose_name=_('Published At'))
menu_items = GenericRelation(MenuItems, object_id_field='id',
related_query_name='news_as_menu')
hit_count_generic = GenericRelation(HitCount, object_id_field='object_pk',
related_query_name='news_hit_count')
created_by = models.ForeignKey(User, related_name='news_created_by',
on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('Created By'))
updated_by = models.ForeignKey(User, related_name='news_updated_by',
on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('Last Updated By'))
published_by = models.ForeignKey(User, related_name='news_published_by',
on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('Published By'))
deleted_by = models.ForeignKey(User, related_name='news_deleted_by',
on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_('Deleted By'))
下面是序列化程序 -
class NewsSerializer(serializers.ModelSerializer):
class Meta:
model = News
fields = ['id', 'heading', 'sub_caption', 'url', 'content', 'featured_image',
'image_caption', 'category', 'source', 'tags', 'published_at']
这是视图 -
class NewsViewSets(viewsets.ModelViewSet):
queryset = News.objects.filter(
is_active=True,
status='public'
)
serializer_class = NewsSerializer
def get_queryset(self):
queryset = self.queryset.filter(
language=self.request.LANGUAGE_CODE
).order_by('-id')
return queryset
分页在settings.py中设置为仅10,当我点击新闻api url时,仅加载10条记录需要9/10秒。这是显示 django-debug-toolbar 报告的屏幕截图 -
我在数据库中有大约 40 万条记录 table,这可能是个问题,但我认为加载时间太长了。请帮我找到这里的问题!提前致谢。
过滤通常很慢。看起来您在相关字段上有数据库索引,但请注意,如果您有多个过滤器,则只会使用其中一个索引。
我是根据您的专栏猜测的,但似乎最常见的查询总是寻找 is_active=1
和 status='public'
。如果不是这种情况,您可能需要进行一些调整。
首先,删除 status
、is_active
和 language
字段上的 db_index=True
,否则您的数据库写入会不必要地变慢。
那么你可以制定这样的索引:
class Meta:
indexes = [
models.Index(
fields=["is_active", "status", "language"],
name="idx_filtering",
)
]
当您一次过滤所有三列时,这将有助于数据库。但是,如果您只对其中一列进行过滤,您可能希望保留原始 db_index=True
.
如果您使用的是 PostgreSQL,您可以做得更好:
class Meta:
indexes = [
models.Index(
fields=["is_active", "status", "language"],
name="idx_filtering",
condition=Q(is_active=True, status="public"),
)
这会将索引的大小减小到仅匹配 Q()
的索引,从而加快遍历速度。
另一件需要注意的事情是,一旦你达到更高的偏移量,使用 OFFSET
的分页就会非常慢。如果可能的话,您应该改用 DRF 的光标分页。