This repository holds the django library that StarFields uses for the django-rest-framework generic views.

Differences with the DRF generic views

The generic views of DRF use the serializers in such a way that the model serializers directly integrate the functionality of a specific model (db table) with CRUD. As a result simple operations such as deleting a list of n table rows ends up using n queries. This serves automated CRUD well but control and performance suffer, in particular changing the view and serializer methods to use a single query in order to perform a write operation becomes a very non-uniform experience between request methods. This library changes the DRF generic views to be more uniform in the way they use the serializers, in particular a single query is made for GET requests with elaborate filtering capabilities and calls to serializer .create(), .update() and .destroy() methods for write methods.

In particular:

  • Single Create operations create the model instance as expected
  • Single Retrive, Update and Destroy work with the .get_object() callable to find and manipulate the instance
  • List Retrieve operations work through elaborate filters to get results starting with the .get_queryset() callable.
  • List Create, Update and Destroy need to implement ListSerializer .create(), .update() and .destroy() methods for bulk operations. None of those methods use the .get_queryset() callable.

It is easy to notice that the single item apis are useful but limited. List apis allow you to perform much more flexible operations and organize frontends better as well, allowing for example out of step syncing (such as shopping carts). As a result it is recommended to use strictly list based generic views for all apis that are supposed to be flexibly used and restrict yourself to using single item generic views to apis whose access you want to partially restrict to the outside world from scanning (such as users) or are inherently simple.

The generics are also enhanced to fit within them automated caching functionality. Caching and deleting cache keys is handled by the library in a way that the cache keys have no duplicates. The generic views offered include single item CRUD and list-based CRUD.

To manage automated caching this the library replaces (and appends to) the DRF filters. These filters need a get_unique_dict method in order to avoid the duplicate cache keys problem.

Usage

Making views in views.py:

from starfields_drf_generics import generics
from starfields_drf_generics import filters as libfilters

class CategoriesView(generics.CachedListRetrieveAPIView):
    """
    This view lists all the categories. Usually this API is called on the shop initialization and the result is saved on the front-end for use shop-wide.
    """
    cache_prefix = "shop.products.categories"
    cache_vary_on_user = False
    cache_timeout_mins = ShopSettings.get_solo().cache_timeout
    queryset = Category.objects.all()
    serializer_class = CategorySerializer
    paged = False
    filter_backends = []
    logger = logger
    cache = cache

class SearchView(generics.CachedListRetrieveAPIView):
    """
    This view lists the gallery pictures with extensive searching and filtering features. You can use this API to get the latest pictures, perform picture searches among others.
    """
    cache_prefix = "gallery.search"
    cache_vary_on_user = False
    cache_timeout_mins = GallerySettings.get_solo().cache_timeout
    queryset = Picture.objects.filter(published=True)
    serializer_class = PictureSerializer
    ordering_fields = ('similarity','date_added','updated')
    category_class = Category
    search_fields = ['name','slug','tag__name','tag__slug']
    paged = True
    default_page_size = 20
    filter_backends = (libfilters.CategoryFilter,
                       libfilters.TrigramSearchFilter,
                       libfilters.OrderingFilter,
                       )
    logger = logger
    cache = cache

New class attributes that are used

Attribute Description
cache_prefix Defines the prefix that the module will use when saving values in the cache
cache_vary_on_user Defines whether keys saved in the cache are different for each user, in which case extra user information will be added to the cache prefix
cache_timeout_mins The cache key timeout
filter_backends The filters that you want the view to have, each can be configured with view class attributes
ordering_fields If you use the OrderingFilter you must indicate what fields the user can order by, the first element is used as the default order
search_fields If you use the TrigramSearchFilter you must indicate the fields to search through
paged Your generic view can have a pager for the user to choose pages or it can be a full listing
default_page_size The default size of the pages if a user has not indicated a page size
logger You should register a logger in order to get error feedback in your deployments
cache The main feature of this module is automated and organized caching, you should register your cache here

Extras

The source code is similar to the django-rest-framework's generic classes and related objects, it should be eminently readable. As with the rest framework's generics multiple inheritance is used through mixing to organize the behavior of each class in a standalone setting.

Duplicate Cache Keys Problem

The problem arises in the way cache keys are created, the naive method is to just use the information from the url of the request and just save it to the cache. This creates a problem in that a request such as https://example.com/api/v1/pictures/search?search=mypic&category=mycat and a request https://example.com/api/v1/pictures/search?category=mycat&search=mypic contain the same information in their cache values. So the order of each filter or the order within the filters (such as the facet filter I made for e-commerce APIs) affects the caching behavior and creates more work for our APIs.

The way this module fixes the duplicate cache keys problem is by systematically ordering the filters through each filter's get_unique_dict method that are called in cache_mixins.py and then running the sorted_params_string utility function in the resulting dict.

Why this instead of webserver caching

Using web server caching, in particular API microcaching, eg Benefits of Microcaching, is recommended to be used along with this library. This way the microcaching at the web server level manages the bulk of the caching while this cache that sits further back manages more flexible caching. This permits among others, runtime cache timeout configuration, handling of the duplicate cache keys problem above, more cache redundancy and more flexible and complicated network topologies for your servers.

Description
An extension to django-rest-framework's generic classes and filters to support caching.
Readme 118 KiB
2024-10-01 00:18:36 +00:00
Languages
Python 97.2%
HTML 2.8%