Quantcast
Channel: Planet Python
Viewing all articles
Browse latest Browse all 23746

Marcos Dione: callable-choices-for-django-rest-framework

$
0
0

At work I'm writing an API using Django/DRF. Suddenly I had to write an application (just a few pages for calling a few endpoints), so I (ab)used DRF's Serializers to build them. One of the problems I faced while doing this was that DRF's ChoiceField accepts only a sequence with the values for the dropdown, unlike Django's, who also accepts callables. This means that once you gave it a set of values, it never ever changes, at least until you restart the application.

Unless, of course, you cheat. Or hack. Aren't those synonyms?

classUpdatedSequence:def__init__(self, update_func):
        self.update_func = update_func
        self.restart =True

        self.data =None
        self.index =0def__iter__(self):# we&aposre our own iteratorreturn self


    def__next__(self):# if we&aposre iterating from the beginning, call the function# and cache the resultif self.restart:
            self.data = self.update_func()
            self.index =0try:
            datum = self.data[self.index]exceptIndexError:# we reached the limit, start all over
            self.restart =TrueraiseStopIterationelse:
            self.index +=1
            self.restart =Falsereturn datum

This simple class tracks when you start iterating over it and calls the function you pass to obtain the data. Then it iterates over the result. When you reach the end, it marks it to start all over, so the next time you iterate over it, it will call the function again. The function you pass can be the all() method of a QuerySet or anything else that goes fetch data and returns an iterable.

In my case in particular, I also added a TimedCache so I don't read twice the db to fill two dropdown with the same info in the same form:

classTimedCache:&apos&apos&aposA function wrapper that caches the result for a while.&apos&apos&aposdef__init__(self, f, timeout):
        self.f = f
        self.timeout = timeout
        self.last_executed =None
        self.cache =None
        self.__name__= f.__name__+&apos (Cached%ds)&apos% timeout


    def__call__(self):
        now = time.monotonic()if self.cache is None or(now - self.last_executed) > self.timeout:
            self.cache = self.f()
            self.last_executed = now

        return self.cache

I hope this helps someone.


pythondjangodrf


Viewing all articles
Browse latest Browse all 23746

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>