1

我正在为我的公司开发一个中型站点,其中包含大量数据(研究出版物、数百名员工等)和安全限制,这让我想到了使用 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 ''

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

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 毫秒)。另一方面,对于 263 个列表视图,我有超过 7500 个查询,这会使视图的生成速度减慢到 46 秒。几乎所有这些都在我定义的模型中,用于加载(我认为)无用的数据:我想只需要名称和对象 ID。

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

4

1 回答 1

1

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

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

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

另外 - 尝试更改管理视图的分页- 如果减少显示的记录数量 - 查询减少了多少?这会给你一个线索,你有多少这些问题。

于 2014-02-26T13:06:39.840 回答