Reagan Kirby
Reagan Kirby

Reputation: 111

Most Pythonic/Django way to dynamically load client libraries at runtime

I'm writing a Django application in which we will have multiple client libraries that make calls to third party APIs. We want to be able to determine which client library to load at runtime when calling different endpoints. I'm trying to determine the most Django/Pythonic way to do this

The current idea is to store class name in a model field, query the model in the View, and pass the class name to a service factory that instantiates the relevant service and makes the query. I've also considered writing a model method that uses reflection to query the class name

For example lets say we have a model called Exchange that has an id, a name, and stores the client name.

class Exchange(models.Model):
    exchange_name = models.CharField(max_length=255)
    client_name = models.CharField(max_length=255)

    def __str__(self):
        return self.exchange_name

Then in the View we might have something like:

from rest_framework.views import APIView
from MyProject.trades.models import Exchange
from django.http import Http404
from MyProject.services import *


class GetLatestPrice(APIView):

    def get_object(self, pk):
        try:
            exchange = Exchange.objects.get(pk=pk)
        except Exchange.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        exchange = self.get_objectt(pk)
        service = service_factory.get_service(exchange.client_name)
        latest_price = service.get_latest_price()
        serializer = PriceSerializer(latest_price)
        return Response(serializer.data)

This syntax may not be perfect but it hasn't been tested. But the idea is that maybe we have

ExchangeA - client1 
ExchangeB - client2
ExchangeC - client3

And for each client we have a service class that all inherit from the same Abstract Base. Each service has an override that are all the same. That way when you call /api/get_latest_price you pass the Exchange id as a query param and the code loads whatever service and client library is relevant. This would be so we could easily add new types to the system, have consistent and small Views, and decouple the business logic. Is this an acceptable, scalable pythonic method? Or is there a better solution for it? Essentially the problem is implementing Polymorphism in django and seperating the domain model from the data model. This has to be a solved problem so I'm curious on the way other people might be doing this. I know it's unusual to be calling a third party API like this but since we are forced to I want to do it in the cleanest most scalable way possible.

Upvotes: 0

Views: 254

Answers (2)

bruno desthuilliers
bruno desthuilliers

Reputation: 77912

We have a similar situation here (and some other that are closely related), and use exactly this pattern: storing the name of the "service" class in the model and using some registry (your "service_factory") to get the class. It JustWorks(tm), no one (from the various devs that joined - and eventually left) ever complained about this design nor suggested any better solution, and the only small complication we've had so far is handling "discontinued" services that are still referenced from the database (and this really was no rocket science either).

FWIW, this is mostly a variant on the "strategy" design pattern, and I really fail to imagine a better (for "better" = "simpler, more effective and easier to maintain") solution for this need.

EDIT

Note that my above answer assumes that Exchange has other responsabilites not mentionned in your question - else, as Alexandr Zayets states, this model seems rather useless ;-)

Upvotes: 1

Alexandr Zayets
Alexandr Zayets

Reputation: 331

TL;DR

There's no silver bullet.

More useful answer:

There's no universal and convenient approach to solve your problem. solution may vary greatly depending on the implementation of client libraries and third party APIs. If they all are similar, you can use your approach and you'll be fine. If APIs have some differences, for instance, one API don't have endpoint to get latest price, you'll need to find some workarounds. If API's have some big differences in workflow you will need to find completely different approach, for instance, if one API can return a report right away, and some other will return report via callback after 5 minutes, you'll need to find workaround for this.

So the one real advise i can give you is to examine differences between third party APIs and focus more on the design of the highest abstractions of your own project, that will use low level client libraries. Try to minimize the impact of differences between third parties on your project.

Upvotes: 1

Related Questions