|
8 | 8 | from typing import Any, ClassVar, Literal, Type |
9 | 9 |
|
10 | 10 | from attrs import define |
| 11 | +from django.core.cache import cache |
11 | 12 | from opaque_keys import InvalidKeyError |
12 | 13 | from opaque_keys.edx.locator import LibraryLocatorV2 |
13 | 14 |
|
@@ -361,6 +362,8 @@ class ContentLibraryData(ScopeData): |
361 | 362 | """ |
362 | 363 |
|
363 | 364 | NAMESPACE: ClassVar[str] = "lib" |
| 365 | + CACHE_TIMEOUT: ClassVar[int] = 5 |
| 366 | + CACHE_KEY_PREFIX: ClassVar[str] = "authz:content_library" |
364 | 367 |
|
365 | 368 | @property |
366 | 369 | def library_id(self) -> str: |
@@ -393,22 +396,67 @@ def get_object(self) -> ContentLibrary | None: |
393 | 396 | """Retrieve the ContentLibrary instance associated with this scope. |
394 | 397 |
|
395 | 398 | This method converts the library_id to a LibraryLocatorV2 key and queries the |
396 | | - database to fetch the corresponding ContentLibrary object. |
| 399 | + database to fetch the corresponding ContentLibrary object. Results are cached |
| 400 | + using Django's cache framework with a configurable timeout. |
397 | 401 |
|
398 | 402 | Returns: |
399 | 403 | ContentLibrary | None: The ContentLibrary instance if found in the database, |
400 | 404 | or None if the library does not exist. |
401 | 405 |
|
402 | 406 | Examples: |
403 | 407 | >>> library_scope = ContentLibraryData(external_key='lib:DemoX:CSPROB') |
404 | | - >>> library_obj = library_scope.get_object() # Returns a ContentLibrary instance |
| 408 | + >>> library_obj = library_scope.get_object() # First call: queries DB |
| 409 | + >>> library_obj = library_scope.get_object() # Cached for 5 seconds |
| 410 | + >>> # Even with a new instance: |
| 411 | + >>> library_scope2 = ContentLibraryData(external_key='lib:DemoX:CSPROB') |
| 412 | + >>> library_obj2 = library_scope2.get_object() # Uses cache |
| 413 | +
|
| 414 | + Note: |
| 415 | + - Uses Django cache with timeout (default: 5 seconds) |
| 416 | + - Cache key format: 'authz:content_library:<library_id>' |
| 417 | + - To clear cache: ContentLibraryData.clear_cache(library_id) |
| 418 | + - Cache timeout can be configured via CACHE_TIMEOUT class variable |
405 | 419 | """ |
| 420 | + cache_key = f"{self.CACHE_KEY_PREFIX}:{self.library_id}" |
| 421 | + |
| 422 | + cached_library = cache.get(cache_key) |
| 423 | + if cached_library is not None: |
| 424 | + return cached_library |
| 425 | + |
406 | 426 | try: |
407 | 427 | library_key = LibraryLocatorV2.from_string(self.library_id) |
408 | | - return ContentLibrary.objects.get_by_key(library_key=library_key) |
| 428 | + library_obj = ContentLibrary.objects.get_by_key(library_key=library_key) |
| 429 | + cache.set(cache_key, library_obj, self.CACHE_TIMEOUT) |
| 430 | + return library_obj |
409 | 431 | except ContentLibrary.DoesNotExist: |
| 432 | + cache.set(cache_key, None, self.CACHE_TIMEOUT) |
410 | 433 | return None |
411 | 434 |
|
| 435 | + @classmethod |
| 436 | + def clear_cache(cls, library_id: str | None = None) -> None: |
| 437 | + """Clear the ContentLibrary object cache. |
| 438 | +
|
| 439 | + Args: |
| 440 | + library_id: Specific library ID to clear from cache. If None, clears all |
| 441 | + library caches (requires cache backend that supports pattern deletion). |
| 442 | +
|
| 443 | + Examples: |
| 444 | + >>> # Clear specific library |
| 445 | + >>> ContentLibraryData.clear_cache('lib:DemoX:CSPROB') |
| 446 | + >>> # Clear all libraries (if supported by cache backend) |
| 447 | + >>> ContentLibraryData.clear_cache() |
| 448 | + """ |
| 449 | + if library_id: |
| 450 | + cache_key = f"{cls.CACHE_KEY_PREFIX}:{library_id}" |
| 451 | + cache.delete(cache_key) |
| 452 | + else: |
| 453 | + # Clear all libraries |
| 454 | + try: |
| 455 | + cache.delete_pattern(f"{cls.CACHE_KEY_PREFIX}:*") |
| 456 | + except AttributeError: |
| 457 | + # Fallback: cache backend doesn't support pattern deletion |
| 458 | + pass |
| 459 | + |
412 | 460 | def __str__(self): |
413 | 461 | """Human readable string representation of the content library.""" |
414 | 462 | return self.library_id |
|
0 commit comments