prefetch_related用于经过身份验证的用户

时间:2017-10-20 17:32:28

标签: python django

我正在开发一个django Web应用程序,我正在使用prefetch_relatedselect_related方法最小化单个数据库命中量,我有一定的方法User模型从中提取了几个不同的相关对象。

def get_profile_info(self):
    *fetch data from a couple of models*

然后我在view中使用此方法。

def profile(request):
    profile_info = request.user.get_profile_info()
    *rest of the view*

问题是,由于普通查询方法无法检索到request.user,因此我无法使用prefetch_relatedselect_related来拉动用户,我可以找不到任何方法来检索相关数据以及该用户的模型。

是否有办法覆盖用户模型的检索,以便我可以运行prefetch_relatedselect_related方法?

3 个答案:

答案 0 :(得分:7)

很抱歉,这个主题很重要,而且答案很简单,只需为您的用户模型创建一个自定义管理器,然后使用n-grams覆盖get方法,如下所示:

select_related

现在,每当Django为from django.contrib.auth.models import AbstractUser, UserManager class CustomUserManager(UserManager): def get(self, *args, **kwargs): return super().select_related('<put fields that you want>').get(*args, **kwargs) class CustomUser(AbstractUser): ... objects = CustomUserManager() 检索用户实例时,它将使用此管理器。同样,您所有的request.user查询也会选择指定的相关字段。

答案 1 :(得分:1)

您始终可以使用select_relatedprefetch_related从数据库中重新获取用户。如果select_relatedprefetch_related保存了大量查询,则需要额外的查询来获取用户。

def profile(request):
    user = User.objects.select_related(
        ...
    ).prefetch_related(
        ...
    ).get(pk=request.user.pk)

请注意,根据视图,prefetch_related在这种情况下可能不是很有用。它为每个模型引发一个额外的查询,因此当您为整个查询集而不是单个对象获取相关对象时,它最有用。

答案 2 :(得分:1)

更细粒度的方法可能是使用自定义身份验证后端。通过使用这种方法,人们将能够使用 UserModel.objects.get,而不会出现在其他代码段中使用此管理器时进行不必要的连接 (.select_related()) 或数据库查找 (.prefetch_related()) 的缺点。

# auth_backends.py
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend


UserModel = get_user_model()


class RelatedModelBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        if username is None:
            username = kwargs.get(UserModel.USERNAME_FIELD)
        if username is None or password is None:
            return
        try:
            user = UserModel._default_manager.select_related(
                ...  # Do your magic here
            ).prefetch_related(
                ...  # Do your magic here
            )
            get(
                **{UserModel.USERNAME_FIELD: username}
            )
        except UserModel.DoesNotExist:
            # Run the default password hasher once to reduce the timing
            # difference between an existing and a nonexistent user (#20760).
            UserModel().set_password(password)
        else:
            if user.check_password(password) and self.user_can_authenticate(user):
                return user

    def get_user(self, user_id):
        try:
            user = UserModel._default_manager.select_related(
                ...  # Do your magic here
            ).prefetch_related(
                ...  # Do your magic here
            ).get(pk=user_id)
        except UserModel.DoesNotExist:
            return None
        return user if self.user_can_authenticate(user) else None

现在我们需要将新后端添加到设置文件中,阅读有关自定义身份验证后端的更多信息 here.

# settings.py
...
AUTHENTICATION_BACKENDS = ['myproject.auth_backends.RelatedModelBackend']
...