Home > Software design >  Django LRU_Cache with API Calls
Django LRU_Cache with API Calls

Time:10-27

I'm trying to use the Reddit API via PRAW (https://praw.readthedocs.io/en/stable/) with Django, and am thinking of trying to use functools lru_cache decorator to implement some kind of caching so I can cache the results of similar API calls to reduce overall calls being made. I've never done anything like this so I've been mainly following examples of implementing the @lru_cache decorator.

I have 3 files that are primarily involved with the API calls / display here. I have:

account.html

{% extends 'myapp/base.html' %}
<h1> {{ name }} </h1>
<h3> {{ comment_karma }} </h3>

<h5> Top Posts </h5>
<table>
    <tr>
        <th> Post </th>
        <th> Subreddit </th>
    </tr>
    {% for s in top_submissions %}
        <tr>
           <td> {{ s.title }} </td>
            <td> {{ s.subreddit }} </td>
        </tr>
    {% endfor %}
</table>

views.py

from . import reddit

reddit = reddit.Reddit()

def account_page():
    context = reddit.details(request.user.reddit_username)
    return render(request, 'stats/dashboard.html', context)

reddit.py

 from functools import lru_cache

 class Reddit:
    def __init__(self):
        self.praw = praw.Reddit(
            client_id = CLIENT_ID,
            client_secret = CLIENT_SECRET,
            user_agent = USER_AGENT
        )

     @lru_cache(maxsize = 256)
     def details(self, redditor):
        redditor = self.praw.redditor(redditor)

        overview = {
            'name': redditor.name,
            'comment_karma': redditor.comment_karma,
            'top_submissions': redditor.submissions.top(limit=10),
        }
        return overview

Here's the problem: when I don't have the lru_cache, then everything works fine and all the data comes in as always. However, when I do put the lru_cache option, then only the name and comment_karma come, while the submissions (an iterable list) just does not display on my page (so I assume it doesn't end up having any values).

Am I using lru_cache wrong? Essentially, my goal is if a redditor is passed into the function overview, I don't want to keep making the same API calls over and over, but instead want to put it into the cache and pull that same data if it's in the cache.

CodePudding user response:

PRAW returns generator objects that are lazily evaluated. You want to evaluate them inside your cached function. Otherwise, after the generator is exhausted, you can't get the results again.

So the working version should look like this:

     @lru_cache(maxsize = 256)
     def details(self, redditor):
        redditor = self.praw.redditor(redditor)

        overview = {
            'name': redditor.name,
            'comment_karma': redditor.comment_karma,
            'top_submissions': list(redditor.submissions.top(limit=10)),
        }
        return overview

list(redditor.submissions.top(limit=10)) will consume the generator and the cached result will contain the list, rather than the generator object which can only be consumed once.

  • Related