【问题标题】:optimizing django architecture to speed up list view within admin优化 django 架构以加快 admin 中的列表视图
【发布时间】:2014-02-26 12:11:59
【问题描述】:

我正在为我的公司开发一个中型网站,其中包含大量数据(研究出版物、数百名员工等)和安全限制,这让我想到了使用 django-guardian 来处理对象权限。但现在我意识到这在管理员中可能会很慢。

我们已经实现了一个 redis 缓存,它看起来工作得还不错,但是加载大型列表视图(数百个元素)仍然需要很长时间。

到目前为止,我们正在使用以下设置:

django             1.5.5
django-cms         2.4.3
django-redis-cache 0.10.2
django-guardian    1.1.1
hiredis            0.1.2
redis              2.9.1

python             2.7.5
postgresql
centos

例如,这是 Person 模块,它的列表视图需要很长时间才能加载(不是超级用户,在那种情况下它非常快:这就是我认为问题所在的原因关于 django-guardian 多重关系):

class Person(models.Model):
    TYPE_CHOICES = (
        ('S', _('Student')),
        ('E', _('Researcher')),
    )

    class Meta:
        permissions = (
            ('view_person', _('View person')),
        )
        index_together = (
            ('last_name', 'first_name'),
        )

    # Relations with other entities
    topics = models.ManyToManyField('topics.Topic', blank=True, related_name='people')
    competences = models.ManyToManyField('staff.Competence', blank=True, related_name='people', db_index=True)

    # Person properties
    cmsuser = models.OneToOneField(User, blank=True, related_name='person', null=True, db_index=True)
    sebra_username = models.CharField(max_length=20, blank=True, db_index=True)
    first_name = models.CharField(_('first name'), max_length=30, blank=True, db_index=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True, db_index=True)
    email = models.EmailField(_('email address'), blank=True, db_index=True)
    username = models.CharField(_('username'), max_length=30, unique=True, db_index=True)
    avatar = models.ImageField(upload_to='img/avatar/', blank=True)
    web = models.URLField(_("web site"), blank=True)
    cristin_profile = models.URLField(_('link to cristin profile'), blank=True)
    twitter = models.CharField(_("twitter username"), max_length=20, blank=True)
    telephone = models.CharField(
        blank=True,
        max_length=validators.MAX_LENGTH_PHONE,
        validators=[validators.validate_phone_format]
    )
    telephone_country_code = models.ForeignKey(Country, null=True, blank=True, related_name='phone_person')
    mobile = models.CharField(
        blank=True,
        max_length=validators.MAX_LENGTH_PHONE,
        validators=[validators.validate_phone_format]
    )
    mobile_country_code = models.ForeignKey(Country, null=True, blank=True, related_name='mobile_person')
    address = models.TextField(max_length=255, blank=True)
    cv = models.FileField(_('Curriculum Vitae'), upload_to='attachments/cv/', blank=True)
    vcard = models.FileField(_('Vcard'), upload_to='attachments/vcard/', blank=True)
    person_type = models.CharField(choices=TYPE_CHOICES, max_length=1, blank=True)
    extract = RichTextField(_('person extract'), blank=True, default='')
    slug = AutoSlugField(populate_from=('first_name', 'last_name'))

def __unicode__(self):
    if len(self.first_name) + len(self.last_name):
        return '%s %s' % (self.first_name, self.last_name)
    return self.username

def clean(self):
    super(Person, self).clean()
    if self.sebra_username.strip():
        # here goes validation and checks on the related objects

    def get_absolute_url(self):
        if PersonDepartmentMembership.objects.filter(active__exact=True, person__exact=self):
            return reverse('staff:profile_slug', kwargs={'slug': self.slug})
        return ''

我知道瓶颈也可能在我的管理类中。这是我们正在使用的:

class PersonAdmin(ModelAdmin):
    fields = (
        'sebra_username', 'first_name', 'last_name', 'avatar', 'email', 'person_type', 'extract',
        'topics', 'competences', 'web', 'cristin_profile', 'twitter', 'telephone_country_code',
        'telephone', 'address', 'mobile_country_code', 'mobile', 'cv', 'vcard'
    )
    search_fields = ('sebra_username', 'first_name', 'last_name', 'email', 'departments__name')
    list_filter = ('departments__name', 'research_groups__group_name', 'projects__project_name')

    inlines = (SomeInline,)

    class Media:
        js = (
            settings.STATIC_URL + 'js/jquery-1.9.0.min.js',
            settings.STATIC_URL + 'js/jquery-ui-1.9.2.custom.min.js',
            'modeltranslation/js/tabbed_translation_fields.js',
        )
        css = {
            'screen': ('modeltranslation/css/tabbed_translation_fields.css',),
        }

    def formfield_for_manytomany(self, db_field, request=None, **kwargs):
        if db_field.name == 'topics':
            kwargs['queryset'] = get_objects_for_user(user=request.user, perms=('topics.view_topic',))
        elif db_field.name == 'competences':
            kwargs['queryset'] = get_objects_for_user(user=request.user, perms=('staff.view_competence',))
        return super(PersonAdmin, self).formfield_for_manytomany(db_field, request, **kwargs)

    def queryset(self, request):
        if request.user.is_superuser:
            return super(PersonAdmin, self).queryset(request)
        return get_objects_for_user(user=request.user, perms=('staff.change_person',)).order_by('last_name')

    def has_add_permission(self, request):
        return request.user.has_perm('staff.add_person')

    def has_delete_permission(self, request, obj=None):
        return request.user.has_perm('staff.delete_person', obj)

    def has_change_permission(self, request, obj=None):
        return request.user.has_perm('staff.change_person', obj)

您能给我任何建议或建议我们可以集成到管理界面中的任何可能的解决方案吗? :-)

提前致谢!

编辑

使用 django-debug-toolbar,我可以看到对 django-guardian 的查询很少,而且速度很快(都在 6 毫秒以下)。另一方面,我有超过 7500 次查询 263 的列表视图,这会使视图的生成速度减慢到 46 秒。几乎所有这些都在我定义的模型中,用于加载(我认为)无用的数据:我想只需要名称和对象 ID。

如何限制在 queryset() 方法中加载的数据量?谢谢。

【问题讨论】:

    标签: python django redis django-admin django-guardian


    【解决方案1】:

    如果您正在执行 7500 个查询,您的问题可能是您没有加载所需的相关对象 - 这是我要查看的内容:

    1. 您的模型上是否有访问相关对象的方法?
    2. list_display 设置中是否有访问相关对象的项目?

    要么消除对这些东西的调用,要么查看select_related

    另外 - 尝试更改 pagination of your admin views - 如果减少显示的记录数 - 查询减少了多少?这会让你知道你有多少这样的问题。

    【讨论】:

    • 感谢您的回答。 1.- 是的,clean 和 get_absolute_url 都访问相关对象。但是……这有什么改变吗?我已尝试删除这些查询,但看不到任何更改。 2.- 我没有为这个模型设置list_display。
    • 将 list_per_page 设置为例如 5,不会改变任何东西。相同数量的查询,或多或少相同的运行时间:_(
    • list_display 设置为5? list_display 应该是要显示的字段列表,而不是数字。你的意思是list_per_page
    • 这只是一个错字,我提到了 list_per_page(见编辑):-)
    • OK - 因此,当您不是超级用户时,选择查询集中的查询,并使用调试工具栏找出查询的来源(您可以单击给定的查询以获取调用堆栈)。
    猜你喜欢
    • 2017-02-24
    • 2016-08-11
    • 1970-01-01
    • 2015-03-28
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 2016-12-11
    • 2011-10-12
    相关资源
    最近更新 更多