django-rest-framework-client

Python client for a DjangoRestFramework based web site


Keywords
django, djangorestframework, drf, rest-client
License
MIT
Install
pip install django-rest-framework-client==0.10.0

Documentation

Django Rest Framework Python API Package

PyPI version

A python library for interacting with any Django web server base on django-rest-framework

Package is based on https://github.com/samgiles/slumber, but enhanced to support tokens and other features.

Features

  • Support for tokens. Both

    • django-rest-framework's own tokens: rest_framework.authentication.TokenAuthentication
    • JWT tokens: rest_framework_jwt.authentication.JSONWebTokenAuthentication
  • Support for arguments (e.g. ?name1=val1&name2=val2)

  • Support for custom methods (e.g. ``/ap1/v1/object/custom/`)

Requirements

restframeworkclient requires the following modules.

* Python 3.9+
* requests

Installation

python3 -m venv ~/.virtualenv/drf_client
source ~/.virtualenv/drf_client/bin/activate
pip install django-rest-framework-client

Usage Guide

Example

from drf_client.connection import Api as RestApi

options = {
    'DOMAIN': 'http://127.0.0.1:8000',
    'API_PREFIX': 'api/v1',
    'TOKEN_TYPE': 'jwt',
    'TOKEN_FORMAT': 'JWT {token}',
    'USERNAME_KEY': 'username',
    'LOGIN': 'auth/login/',
    'LOGOUT': 'auth/logout/',
    'USE_DASHES': False,    # Set to True to tell API to replace undercore ("_") with dashes ("-")
}

c = RestApi(options)

ok = c.login(username=username, password=password)
if ok:

    # GET some data
    my_object = c.myresourcename.get()
    for obj in my_object['results']:
        pprint(obj)
        logger.info('------------------------------')

    payload = {
        'data1': 'val1',
        'data2': 'val2',
    }

    resp = c.myresourcename.post(data=payload)

    # If the URL includes "-", add under parenthesis:
    # GET: /api/v1/someresource/some-path/
    my_object = c.someresource('some-path').get()

Example using Tokens

from drf_client.helpers.base_main import BaseMain

class MyClass(Main):

    options = {
        'DOMAIN': None,
        'API_PREFIX': 'api/v1',
        'TOKEN_TYPE': 'bearer',
        'TOKEN_FORMAT': 'Bearer {token}',
        'USERNAME_KEY': 'username',
        'LOGIN': 'auth/login/',
        'LOGOUT': 'auth/logout/',
        'USE_DASHES': False,
    }

export DRF_CLIENT_AUTH_TOKEN=1fe171f65917db0072abc6880196989dd2a20025
python -m my_script.MyClass --server https://mysite.com --use-token t

Django Setup

Client assumes by default that all urls should end with a slash (tested with the default router: routers.DefaultRouter())

Apart from the regular Django and Rest Framework setup, this package currently relies on the following custom login and logout API functions:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'email', 'username')


class APILogoutViewSet(APIView):
    permission_classes = (permissions.IsAuthenticated,)

    def post(self, request, format=None):
        logout(request)
        return Response({}, status=status.HTTP_204_NO_CONTENT)


urlpatterns = [
    url(r'^auth/logout/$', APILogoutViewSet.as_view(), name='api-logout'),

Helpers

BaseMain Helper

This class helps write a script with a flexible template that helps avoid having to duplicate boiler plate code from script to script.

The class assumes that most scripts include the basic folliwing flow:

# Parse arguments
# Setup LOG configuration
# Login
# Do something after logging in

The opinionated class will execute the basic main flow:

   # Initialize arguments and LOG in the init function
   # Add additional arguments by implemenenting self.add_extra_args()
   self.domain = self.get_domain()
   self.api = Api(self.domain)
   self.before_login()
   ok = self.login()
   if ok:
       self.after_login()

Any of the above functions can be overwritten by derving from this class.

Here is a sample script:

from drf_client.helper.base_main import BaseMain
from drf_client.helper.base_facade import BaseFacade

class MyScript(BaseMain):

    def add_extra_args(self):
        # Add extra positional argument (as example)
        self.parser.add_argument('foo', metavar='foo', type=str, help='RTFM')

    def before_login(self):
        logger.info('-----------')

    def after_login(self):
        # Main function to OVERWITE and do real work
        resp = self.api.foo.bar.get()
        # You can also access the API from the global Facade
        resp = BaseFacade.api.foo.bar.get()


if __name__ == '__main__':

    work = MyScript()
    work.main()

Given the above script, you will run it with

python myscript.py -u <USERNAME> --foo bar

Development

To test, run python setup.py test or to run coverage analysis:

python3 -m venv .virtualenv/drf_client
source .virtualenv/drf_client/bin/activate
pip install -r requirements-test.txt
pip install -e .

py.test

CI Deployment

  1. Update setup.py with new version
  2. Update CHANGELOG.md with description of new version
  3. Create new tag with same version
git tag v0.4.1 -m "v0.4.1"
git push --tags
  1. Create new release using GitHub Web Site. Github action will run automatically to deploy to PyPi.

Manual Deployment

pip install -r requirements-build.txt

python setup.py sdist bdist_wheel
twine check dist/*
# Publish
twine upload dist/*