0

I am using the python requests library to intereact with an api.

I am first authenticating, getting a session-id and then creating a request session to persist that connection.

So I created a class to do this, but every time I use the class I am reinitialising and doing the authentication again. Which I want to avoid. Essentially I created an API between the API I am calling using DRF. How can I ensure that only 1 authenticated session is used across the entire app and that it persists through multiple request?

The class:

class RestClient:

    session = None

    def create_rest_client(self):
        auth = requests.auth.HTTPBasicAuth(
            USERNAME,
            PASSWORD
        )
        response = requests.post(
            f'https://{ settings.HOST }/rest/session',
            auth=auth
        )

        session = requests.Session()
        session_id = response.json().get('value')
        session.headers.update({'api-session-id': session_id})

        return session

    def get_rest_client(self):
        if self.session:
            return self.session
        else:
            return self.create_rest_client()

Using the class I instantiate and get the client (naturally redoing the auth). I think this should either be global or a singleton.

Using class:

class ProductDetail(APIView):

    def get(self, request, format=None, **kwargs):
        response = []

        rest_client = RestClient()
        session = rest_client.get_rest_client()

        response = session.get(
            ....use authenticated session
        )

        return Response(response.json())
4
  • Any reason you're don't store the session on an instance of a RestClient object and just pass that instance around where needed? Commented Mar 22, 2019 at 8:42
  • You might also want to consider creating a custom class that inherits from requests.Session and override the __init__ accordingly to make the initial request to update its own headers... then just pass that object around and treat it as though it was any other requests.Session object... Commented Mar 22, 2019 at 8:47
  • Ah that sounds good. I updated the usage in the quesitons to show I am doing this in response to a call to my api. I'm just not sure on the pass the object around bit. Could you give an example?
    – tread
    Commented Mar 22, 2019 at 8:55
  • Have you looked at how Django manages its establishing a DB connection just once that's available throughout the entire framework? I believe re-reading your question that's the sort of thing you're trying to do? Commented Mar 22, 2019 at 9:02

1 Answer 1

3

I'd wire up a property like this:

class RestClient:
    _session = None

    @property
    def session(self):
        if self._session:
            return self._session

        auth = requests.auth.HTTPBasicAuth(USERNAME, PASSWORD)
        response = requests.post(
            f"https://{ settings.HOST }/rest/session", auth=auth
        )

        session = requests.Session()
        session_id = response.json().get("value")
        session.headers.update({"api-session-id": session_id})

        self._session = session
        return session

Now you can simply do client.session and it will be set up on the first access and reused thereafter.

EDIT: To persist this between RestClient instances, change references to self._session to RestClient._session.

6
  • That neatened it up real nice, thanks. Although, it doesn't persist as every time I use it, it has to authenticate again.
    – tread
    Commented Mar 22, 2019 at 10:21
  • If you create the client object once, it should persist.
    – AKX
    Commented Mar 22, 2019 at 10:26
  • Added an edit about persisting it as a class variable.
    – AKX
    Commented Mar 22, 2019 at 10:27
  • 1
    self refers to the instance of RestClient you instantiate with RestClient(); RestClient refers to the class RestClient. Setting an attribute (_session) on the class object is, well, it's practically a global, but namespaced into the class.
    – AKX
    Commented Mar 22, 2019 at 10:48
  • 1
    When the session expires, you can refresh it with RestClient._session = None, so the next call to .session will get a new one with a new token. It may also be worth it to subclass requests.Session itself and add this automatic refresh in the .request() method.
    – AKX
    Commented Aug 8, 2019 at 13:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.