collections.rst 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. ***********
  2. Collections
  3. ***********
  4. lazr.restful makes collections of data available through Pythonic
  5. mechanisms like slices.
  6. >>> from lazr.restfulclient.tests.example import CookbookWebServiceClient
  7. >>> service = CookbookWebServiceClient()
  8. You can iterate through all the items in a collection.
  9. >>> names = sorted([recipe.dish.name for recipe in service.recipes])
  10. >>> len(names)
  11. 5
  12. >>> names
  13. [u'Baked beans', ..., u'Roast chicken']
  14. But it's almost always better to slice them.
  15. >>> sorted([recipe.dish.name for recipe in service.recipes[:2]])
  16. [u'Roast chicken', u'Roast chicken']
  17. You can get a slice of any collection, so long as you provide start
  18. and end points keyed to the beginning of the list. You can't key a
  19. slice to the end of the list because it might be expensive to
  20. calculate how big the list is.
  21. This set-up code creates a regular Python list of all recipes on the
  22. site, for comparison with a lazr.restful Collection object
  23. representing the same list.
  24. >>> all_recipes = [recipe for recipe in service.recipes]
  25. >>> recipes = service.recipes
  26. Calling len() on the Collection object makes sure that the first page
  27. of representations is cached, which forces this test to test an
  28. optimization.
  29. >>> ignored = len(recipes)
  30. These tests demonstrate that slicing the collection resource gives the
  31. same results as collecting all the entries in the collection, and
  32. slicing an ordinary list.
  33. >>> def slices_match(slice):
  34. ... """Slice two lists of recipes, then make sure they're the same."""
  35. ... list1 = recipes[slice]
  36. ... list2 = all_recipes[slice]
  37. ... if len(list1) != len(list2):
  38. ... raise ("Lists are different sizes: %d vs. %d" %
  39. ... (len(list1), len(list2)))
  40. ... for index in range(0, len(list1)):
  41. ... if list1[index].id != list2[index].id:
  42. ... raise ("%s doesn't match %s in position %d" %
  43. ... (list1[index].id, list2[index].id, index))
  44. ... return True
  45. >>> slices_match(slice(3))
  46. True
  47. >>> slices_match(slice(50))
  48. True
  49. >>> slices_match(slice(1,2))
  50. True
  51. >>> slices_match(slice(2,21))
  52. True
  53. >>> slices_match(slice(2,21,3))
  54. True
  55. >>> slices_match(slice(0, 200))
  56. True
  57. >>> slices_match(slice(30, 200))
  58. True
  59. >>> slices_match(slice(60, 100))
  60. True
  61. >>> recipes[5:]
  62. Traceback (most recent call last):
  63. ...
  64. ValueError: Collection slices must have a definite, nonnegative end point.
  65. >>> recipes[10:-1]
  66. Traceback (most recent call last):
  67. ...
  68. ValueError: Collection slices must have a definite, nonnegative end point.
  69. >>> recipes[-1:]
  70. Traceback (most recent call last):
  71. ...
  72. ValueError: Collection slices must have a nonnegative start point.
  73. >>> recipes[:]
  74. Traceback (most recent call last):
  75. ...
  76. ValueError: Collection slices must have a definite, nonnegative end point.
  77. You can slice a collection that's the return value of a named
  78. operation.
  79. >>> e_recipes = service.cookbooks.find_recipes(search='e')
  80. >>> len(e_recipes[1:3])
  81. 2
  82. You can also access individual items in this collection by index.
  83. >>> print e_recipes[1].dish.name
  84. Foies de voilaille en aspic
  85. >>> e_recipes[1000]
  86. Traceback (most recent call last):
  87. ...
  88. IndexError: list index out of range
  89. When are representations fetched?
  90. =================================
  91. To avoid unnecessary HTTP requests, a representation of a collection
  92. is fetched at the last possible moment. Let's see what that means.
  93. >>> import httplib2
  94. >>> httplib2.debuglevel = 1
  95. >>> service = CookbookWebServiceClient()
  96. send: ...
  97. ...
  98. Just accessing a top-level collection doesn't trigger an HTTP request.
  99. >>> recipes = service.recipes
  100. >>> dishes = service.dishes
  101. >>> cookbooks = service.cookbooks
  102. Getting the length of the collection, or any entry from the
  103. collection, triggers an HTTP request.
  104. >>> len(recipes)
  105. send: 'GET /1.0/recipes...
  106. ...
  107. >>> dish = dishes[1]
  108. send: 'GET /1.0/dishes...
  109. ...
  110. Invoking a named operation will also trigger an HTTP request.
  111. >>> cookbooks.find_recipes(search="foo")
  112. send: ...
  113. ...
  114. Scoped collections work the same way: just getting a reference to the
  115. collection doesn't trigger an HTTP request.
  116. >>> recipes = dish.recipes
  117. But getting any information about the collection triggers an HTTP request.
  118. >>> len(recipes)
  119. send: 'GET /1.0/dishes/.../recipes ...
  120. ...
  121. Cleanup.
  122. >>> httplib2.debuglevel = None