简体   繁体   English

已认证用户的 prefetch_related

[英]prefetch_related for Authenticated user

I'm working on a django web application and i'm in the process of minimizing the amount of individual database hits by using the prefetch_related and select_related methods, i have a certain method in my User model that pulls a couple of different related objects from it.我正在开发一个 django web 应用程序,我正在通过使用prefetch_relatedselect_related方法来最小化单个数据库的点击量,我的User模型中有一个特定的方法可以从它。

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

And then i use this method in my view .然后我在我view使用这种方法。

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

The problem is, since request.user is not retrieved by the normal query methods, i don't get to use the prefetch_related and select_related along with pulling the user, and i can't find any way to retrieve the related data along with the model of that user.问题是,由于request.user不是通过普通查询方法检索的,所以我不能在拉用户的同时使用prefetch_relatedselect_related ,而且我找不到任何方法来检索相关数据以及该用户的模型。

Is there a way to, say, override the retrieving of the user model so i can run the prefetch_related and select_related methods?有没有办法,比如说,覆盖用户模型的检索,以便我可以运行prefetch_relatedselect_related方法?

Sorry for this necroposting, but this theme is so important and really simple answer exists, just create a custom manager for your user model and override the get method with select_related like this:很抱歉这个 necroposting,但是这个主题非常重要并且存在非常简单的答案,只需为您的用户模型创建一个自定义管理器并使用select_related覆盖get方法,如下所示:

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()

Now, whenever Django will retrieve user instance for request.user , it will be using this manager.现在,每当 Django 检索request.user用户实例时,它都会使用这个管理器。 Also all your CustomUser.objects.get() queries will select specified related fields too.此外,您所有的CustomUser.objects.get()查询也将选择指定的相关字段。

A more granual way could be using a custom authentication backend.更精细的方法可能是使用自定义身份验证后端。 By using this approach one will be able to use UserModel.objects.get without the drawback of making unnecessary joins(.select_related()) or DB lookups(.prefetch_related()) while using this manager in other segments of code.通过使用这种方法,人们将能够使用 UserModel.objects.get 而不会在其他代码段中使用此管理器时进行不必要的连接(.select_related()) 或 DB 查找(.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

Now we need to add new backend into settings file, read more about custom auth backends here.现在我们需要将新后端添加到设置文件中, 在此处阅读有关自定义身份验证后端的更多信息

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

You could always refetch the user from the database using select_related and prefetch_related .您始终可以使用select_relatedprefetch_related从数据库中重新获取用户。 If select_related and prefetch_related save a lot of queries, then it will be worth the extra query to fetch the user.如果select_relatedprefetch_related保存了很多查询,那么额外的查询来获取用户是值得的。

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

Note that depending on the view, prefetch_related might not be very useful in this case.请注意,根据视图,在这种情况下prefetch_related可能不是很有用。 It causes one extra query per model, so it's most useful when you are fetching the related objects for an entire queryset, not a single object.它会导致每个模型一个额外的查询,因此当您获取整个查询集的相关对象而不是单个对象时,它最有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM