1
class ProfileContextMixin(generic_base.ContextMixin, generic_view.View):

    def get_context_data(self, **kwargs):
        context = super(ProfileContextMixin, self).get_context_data(**kwargs)
        profile = get_object_or_404(Profile, user__username=self.request.user)
        context['profile'] = profile
        return context

class CourseListView(ProfileContextMixin, generic_view.ListView):
    model = Course
    template_name = 'course_list.html'
    object_list = None

    def get_queryset(self):
        profile = self.get_context_data()['profile']
        return super(CourseListView, self).get_queryset().filter(creator=profile)

I have the following two class-based-views. CourseListView inherits ProfileContextMixin which I wrote so that I don't have to repeat overriding get_context_data to get the profile every time in my other views.

Now in my CourseListView, I need to filter the result based on the creator argument, which is the same one retrieved in get_context_data

I know my get_queryset works, and it will call get_context_data() to get the profile, but this will also cause my get_context_data to be called twice, executing the same SQL two times.

Is there a way I can access the context efficiently?

UPDATE:

After reading ListView method flowchart, I ended up doing this, but not sure if it's the best way. Feedback is appreciated.

    object_list = []
    context = None

    def get_context_data(self, **kwargs):
        return self.context

    def get_queryset(self):
        self.context = super(CourseListView, self).get_context_data()
        profile = self.context['profile']
        queryset = super(CourseListView, self).get_queryset()
        queryset = queryset.filter(creator=profile)
        self.context['object_list'] = queryset
        return queryset

1 Answer 1

2

You can move getting profile out of get_context_data to upper function, like dispatch, or use cached_property decorator. This way your profile will be stored in _profile argument of view and you will not do second get to DB after calling self.profile second time.

from django.utils.functional import cached_property

class ProfileContextMixin(generic_base.ContextMixin, generic_view.View):
    @cached_property
    def profile(self):
        return get_object_or_404(Profile, user__username=self.request.user)

    def get_context_data(self, **kwargs):
        context = super(ProfileContextMixin, self).get_context_data(**kwargs)
        context['profile'] = self.profile
        return context

class CourseListView(ProfileContextMixin, generic_view.ListView):
    model = Course
    template_name = 'course_list.html'
    object_list = None

    def get_queryset(self):
        return super(CourseListView, self).get_queryset().filter(creator=self.profile)
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks! I've been reading about cached_property and I haven't seen it used in views. Is it safe to put it in a view? How do you invalidate the cached property if you wanted to?
It is safe. It is stored on class instance. So it will exists only from the moment when you do request on view, till this view returns response, because views are instantiated each time you do request. It is almost same as doing assigning in __init__ so your self.argument can be called in any other method. But in Django you can't just do it in __init__ so they came up with this method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.