Skip to content

Commit 6f3e5d8

Browse files
committed
Merge branch 'main' of https://github.com/MojixCoder/qscache into main
2 parents 6af24b2 + bd4e72d commit 6f3e5d8

File tree

1 file changed

+90
-12
lines changed

1 file changed

+90
-12
lines changed

README.md

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
# qscache
23

34
A package for caching Django querysets with type hinting in mind.
@@ -10,6 +11,7 @@ A package for caching Django querysets with type hinting in mind.
1011
## Installation
1112

1213
pip install qscache
14+
Don't install yet :)
1315

1416
## Versioning
1517
This package is young so consider pining it with the exact version of package in production.
@@ -33,7 +35,7 @@ in `models.py`:
3335
A simple example Django model like before
3436
"""
3537
36-
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="example_user")
38+
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="example_user")
3739
users = models.ManyToManyField(User, related_name="examples")
3840
is_active = models.BooleanField(default=True)
3941

@@ -76,31 +78,94 @@ This is all you need to do. now you are good to use your cache manager.
7678
unique_identifier=1, # pk = 1
7779
filter_kwargs={"pk": 1},
7880
)
81+
Now let's see a better example in `rest_framework`:
7982

80-
## Developer Guide
83+
from rest_framework.viewsets import ModelViewSet
8184

82-
Now lets look at how everything is working by detail.
85+
from qscache import clear_cache_detail, clear_cache_keys
86+
87+
from .models import Example
88+
from .cache import example_cache_manager
89+
from .serializers import ExampleSerializer
8390

8491

85-
`BaseCacheManager` options:
92+
class ExampleViewSet(ModelViewSet):
93+
94+
serializer_class = ExampleSerializer
95+
http_method_names = ["get", "post", "put"]
96+
97+
def get_queryset(self):
98+
example_list = example_cache_manager.all()
99+
return example_list
100+
101+
def get_object(self):
102+
pk = self.kwargs.get(self.lookup_field)
103+
obj = province_cache_manager.get(
104+
unique_identifier=pk,
105+
filter_kwargs={"pk": pk},
106+
)
107+
self.check_object_permissions(self.request, obj)
108+
return obj
109+
110+
@clear_cache_keys(keys=[example_cache_manager.get_cache_key()])
111+
def perform_create(self, serializer):
112+
serializer.save()
113+
114+
@clear_cache_keys(
115+
manager=example_cache_manager,
116+
additional_fields=[example_cache_manager.get_cache_key()],
117+
)
118+
def perform_update(self, serializer):
119+
return serializer.save()
120+
121+
Here the queries for our `list` and `retrieve` actions will be cached.
122+
Now after we create an object we delete our list cache so next time we get our list cache will be updated. And after updating an object we delete our list cache key and the object from cache, so next time when we get our list and object they will be updated and cached again.
123+
Now you have a cached `ModelViewSet`.
124+
It was easy, wasn't it?
125+
126+
## BaseCacheManager Options
127+
128+
Now lets look at how everything is working by detail.
129+
86130

87131
- **model:** This is the only required field that you should specify in your model cache manager. We use this model to query database and fetch data.
88-
- **cache_key:** The default value is `None`. If it's `None` we use the model lowercase class name. if you want to override it just use a string as cache key. We use this cache key as our cache key separator from other model cache keys. So make sure it's unique. Defaults to `None`.
132+
133+
- **cache_key:** The default value is `None`. If it's `None` we use the model lowercase class name. if you want to override it just use a string as cache key. We use this cache key as our cache key separator from other model cache keys. So make sure it's unique. Defaults to `None`.
134+
135+
89136
1. `cache_key` is our list cache key. `cache_manager.all()` will be stored in `cache_key`.
90-
2. `{cache_key}_{unique_identifier}` is our detail cache key. if your unique identifier is pk(for example 1) then your detail cache key is `{cache_key}_1`. `cache_manager.get()` uses this cache key. your unique identifier can be anything but make sure it's unique so your objects won't be overridden in cache. For example `slug`, `username`, etc.
137+
138+
139+
2. `{cache_key}_{unique_identifier}` is our detail cache key. if your unique identifier is pk(for example 1) then your detail cache key is `{cache_key}_1`. `cache_manager.get()` uses this cache key. your unique identifier can be anything but make sure it's unique so your objects won't be overridden in cache. For example `slug`, `username`, etc.
140+
141+
142+
91143
- **related_objects:** If your model has foreign keys and you want to use `select_related` in your queries. Just pass a list containing your foreign key field names. Defaults to `None`.
144+
92145
- **prefetch_related_objects:** if you wanna use `prefetch_related` in your query just add a list containing your many to many fields. Defaults to `None`.
146+
147+
93148
- **use_prefetch_related_for_list:** Your list query can be heavy and you may not need to `prefetch_related` for your list query but you need it for the detail of your objects. If it's True then we use `prefetch_related_objects` for our list query but if not we don't use `prefetch_related_objects` for our list even though it's set we only use it for getting an object not getting list of objects. Defaults to `True`.
149+
150+
94151
- **list_timeout:** This is the timeout of your list cache key in seconds. Defaults to 86400 (1 day).
152+
153+
95154
- **detail_timeout:** This is the timeout of your detail cache key in seconds. Defaults to 60 (1 minute).
155+
156+
96157
- **exception_class:** This the exception that we raise when object is not found in `cache_manager.get()` method. Defaults to `Http404`. But if you are using `rest_framework` you may want to raise `rest_framework.exceptions.NotFound` instead of `Http404`.
97158

98-
Here are `BaseCacheManager` that you may want to override.
99-
Now lets look at your model `cache_manager` methods.
159+
Here are `BaseCacheManager` that you may want to override.
160+
161+
## BaseCacheManager Instance Options
162+
163+
- **all(suffix: Optional[str] = None, filter_kwargs: Optional[Dict[str, Any]] = None) -> QuerySet[ModelType]:** This is the equivalent to `Model.objects.all()`. We store it in `cache_key` and we fetch it from cache if queryset was in the cache otherwise sets queryset to the cache.
164+
165+
1. **suffix: Optional[str] = None** : This suffix is added to the end of `cache_key` if provided. It's useful when you want to store a filtered queryset in cache and you also don't want to override your `cache_manager.all()` queryset.
166+
167+
2. **filter_kwargs: Optional[Dict[str, Any]] = None** : If you want to filter your queryset you can use it and pass your filter as a dict. For example `{"name__icontains": "mojix", "is_active": True}` same as Django API. but it's better to use `suffix` and `filter_kwargs` at the same time. Then you can cache your filters when you are using them so much. For example if you have a page that you show all of the active products and you want to cache your active products instead of all of the products. You can do this `product_cache_manager.all(suffix="active", filter_kwargs={"is_active": True})`.
100168

101-
- **all(suffix: Optional[str] = None, filter_kwargs: Optional[Dict[str, Any]] = None) -> QuerySet[ModelType]:** This is the equivalent to `Model.objects.all()`. But we store it in `cache_key` and we fetch it from cache if queryset was in the cache otherwise sets queryset to the cache.
102-
1. **suffix: Optional[str] = None** : This suffix is added to the end of `cache_key` if provided. It's useful when you want to store a filtered queryset in cache and you also don't want to override your `cache_manager.all()` queryset.
103-
2. **filter_kwargs: Optional[Dict[str, Any]] = None** : If you want to filter your queryset you can use it and pass your filter as a dict. For example `{"name__icontains": "mojix", "is_active": True}` same as Django API. but it's better to use `suffix` and `filter_kwargs` at the same time. Then you can cache your filters when you are using them so much. For example if you have a page that you show all of the active products and you want to cache your active products instead of all of the products. You can do this `product_cache_manager.all(suffix="active", filter_kwargs={"is_active": True})`.
104169

105170
**Notice:** What if you wanted to filter your queryset without caching it? imagine if it was a simple search that you don't want to cache it. Remember that `cache_manager.all()` returns `Queryset[ModelType]`. So you can use everything on it that you do in Django and you can use it even as your model manager. look that this example below:
106171

@@ -113,7 +178,20 @@ Now lets look at your model `cache_manager` methods.
113178
# It's like Model.objects.all().filter(is_active=True)
114179
# So it's not stored in cache and feel free to use it
115180
cache_manager.all().filter(is_active=True)
116-
181+
182+
- **get(unique_identifier: Any, filter_kwargs: Dict[str, Any], raise_exception: bool = True) -> Optional[ModelType]:** This is the equivalent to `Model.objects.get()`. We store it in `f"{cache_key}_{unique_identifier}"` and we fetch it from cache if object was in the cache otherwise sets the object to the cache.
183+
184+
1. **unique_identifier: Any** We try to get object from cache with `unique_identifier`
185+
2. **filter_kwargs: Dict[str, Any]** If the object was not found we try to get object using `filter_kwargs`. Remember your filters should only return one object. So it's better to use pk, slug , etc.
186+
3. **raise_exception: bool = True** If the object was not found raises `exception_class` if it's True otherwise returns **`None`**.
187+
188+
```
189+
pk = 1
190+
example_object = cache_manager.get(
191+
unique_identifier=pk,
192+
filter_kwargs={"pk": pk},
193+
)
194+
```
117195

118196
So far so good and easy.
119197
More coming soon :)

0 commit comments

Comments
 (0)