Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Function to get all objects in paged results #6

Merged
merged 6 commits into from

2 participants

@paulgb

I have a use-case where I need to access all available objects rather than just the first page of results from the API. I've added a get_all function to the GraphAPI class to do so.

@jgorset
Owner

This is really cool, but I'd rather refactor _query and get to facilitate for this behavior than introduce get_all. What are your thoughts on implementing a page argument to get that defaults to False, but returns an interator instead of a dictionary if set to True?

@paulgb

Good idea, I've refactored this as suggested.

@jgorset jgorset merged commit 1482ab7 into from
@jgorset
Owner

I've merged this and bumped the version to 0.3. Thanks, @paulgb – this is an awesome feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 61 additions and 19 deletions.
  1. +49 −19 facepy/graph_api.py
  2. +12 −0 tests/test_graph_api.py
View
68 facepy/graph_api.py
@@ -28,7 +28,7 @@ def __init__(self, oauth_token=None, signed_request=None, app_secret=None):
else:
self.oauth_token = oauth_token
- def get(self, path='', **options):
+ def get(self, path='', page=False, **options):
"""
Get an item from the Graph API.
@@ -37,7 +37,7 @@ def get(self, path='', **options):
**options -- Graph API parameters such as 'limit', 'offset' or 'since' (see http://developers.facebook.com/docs/reference/api/).
"""
- response = self._query('GET', path, options)
+ response = self._query('GET', path, options, page)
if response is False:
raise self.Error('Could not get "%s".' % path)
@@ -100,7 +100,40 @@ def search(self, term, type, **options):
return response
- def _query(self, method, path, data={}):
+ def _load_url(self, method, url, data):
+ """
+ Fetch an object from the Graph API and parse the output.
+
+ Arguments:
+ method -- A string describing the HTTP method.
+ url -- A string describing the URL.
+ data -- A dictionary of HTTP GET parameters (for GET requests) or POST data (for POST requests).
+
+ Returns a pair (obj, next_url) where obj is the object returned from the graph API (parsed
+ into a python object) and next_url is the URL for more results or None if this is the last
+ page of results.
+ """
+
+ if method in ['GET', 'DELETE']:
+ response = requests.request(method, url, params=data)
+ else:
+ response = requests.request(method, url, data=data)
+
+ result = self._parse(response.content)
+
+ try:
+ obj = result['data']
+ except (KeyError, TypeError):
+ obj = result
+
+ try:
+ next_url = result['paging']['next']
+ except (KeyError, TypeError):
+ next_url = None
+
+ return obj, next_url
+
+ def _query(self, method, path, data={}, page=False):
"""
Low-level access to Facebook's Graph API.
@@ -116,17 +149,21 @@ def _query(self, method, path, data={}):
if type(value) is list and all([type(item) in (str, unicode) for item in value]):
data[key] = ','.join(value)
+ url = 'https://graph.facebook.com/%s' % path
if self.oauth_token:
data.update({'access_token': self.oauth_token })
-
- url = 'https://graph.facebook.com/%s' % path
-
- if method in ['GET', 'DELETE']:
- response = requests.request(method, url, params=data)
+
+ if page:
+ def make_generator(url, data):
+ while url is not None:
+ objs, url = self._load_url(method, url, data)
+ data = {}
+ for obj in objs:
+ yield objs
+ return make_generator(url, data)
else:
- response = requests.request(method, url, data=data)
-
- return self._parse(response.content)
+ obj, next_url = self._load_url(method, url, data)
+ return obj
def _parse(self, data):
"""
@@ -154,19 +191,12 @@ def _parse(self, data):
#
# We'll handle this discrepancy as gracefully as we can by implementing logic to deal with this behavior
# in the high-level access functions (get, post, delete etc.).
- if type(data) is bool:
- return data
-
if type(data) is dict:
if 'error' in data:
raise self.Error(data['error']['message'])
- # If the response contains a 'data' key, strip everything else (it serves no purpose)
- if 'data' in data:
- data = data['data']
-
- return data
+ return data
class Error(FacepyError):
pass
View
12 tests/test_graph_api.py
@@ -28,6 +28,18 @@ def test_get():
assert isinstance(graph.get('me'), dict)
assert isinstance(graph.get('me/picture'), str)
+def test_paged_get():
+ graph = GraphAPI(TEST_ACCESS_TOKEN)
+
+ posts = graph.get('Facebook/posts', until=1314742370, limit=6, page=True)
+
+ for (i, post) in enumerate(posts):
+ # require three pages of results
+ if i >= 21:
+ return
+
+ assert False, 'only first page of results returned'
+
def test_post():
graph = GraphAPI(TEST_ACCESS_TOKEN)
Something went wrong with that request. Please try again.