Package: django-admin-client
Django Admin clientInstallationpip install django-admin-clientUsage3 main usages:Convenient HTTP GET and POST methodsDynamic Django Admin python clientGenerating specific Django Admin python client1. Convenient HTTP GET and POST methodsDjangoAdminBasewill do auto login, session management, and auto csrf token inclusion and auto error detectionfromdjango_admin_clientimportDjangoAdminBasebasic_client=DjangoAdminBase('http://127.0.0.1:9000/admin','superuser','why-dont-tell-mom?')# auto login:basic_client.get_with_auto_login('/')# <Response [200]># response is just our favorite and well-known response from 'requests' librarytype(basic_client.get_with_auto_login('/'))# requests.models.Response# auto login (if not yet logged in or session expired),# csrf token included in POST request automatically,# errors in response html form detected and providedbasic_client.post_with_csrf_and_auto_login('/auth/user/add/',{'username':'test','password1':'123','password2':'123'})#{'response': <Response [200]>,# 'errors': [<div class="form-row errors field-password2">#  <ul class="errorlist"><li>This password is too short. It must contain at least 8 characters.</li><li>This password is too common.</li><li>This password is entirely numeric.</li></ul>#  <div>#  <label class="required" for="id_password2">Password confirmation:</label>#  <input id="id_password2" name="password2" required="" type="password"/>#  <div class="help">Enter the same password as before, for verification.</div>#  </div>#  </div>]}basic_client.post_with_csrf_and_auto_login('/auth/user/add/',{'username':'test','password1':'isthislongenough','password2':'isthislongenough'})# {'response': <Response [200]>, 'errors': []}2. Dynamic Django Admin python clientDjangoAdminBasecan also do introspection on Django Admin site and generate specification.In[1]:spec=basic_client.generate_spec()In[2]:importjsonIn[3]:json.dumps(spec,indent=2)Out[3]:{"models":{"groups":{"id":"groups","app":"auth","name":"group","fields":{"name":{"name":"name","required":true,"default_value":""},"permissions":{"name":"permissions","required":false,"default_value":""}}},"users":{"id":"users","app":"auth","name":"user","fields":{"username":{"name":"username","required":true,"default_value":""},"password1":{"name":"password1","required":true,"default_value":""},"password2":{"name":"password2","required":true,"default_value":""}}}}}This specification can then be fed toDjangoAdminDynamicclass to get dynamic admin python client.fromdjango_admin_clientimportDjangoAdminBase,DjangoAdminDynamicbasic_client=DjangoAdminBase('http://127.0.0.1:9000/admin','superuser','why-dont-tell-mom?')spec=basic_client.generate_spec()dynamic_client=DjangoAdminDynamic(spec=spec,client=basic_client)dynamic_client.users.all()# {'response': <Response [200]>, 'ids': ['1', '2']}At the moment you can add objects with<model-name>.add(item: dict):dynamic_client.users.add({})#{'id': None,# 'created': False,# 'errors': [<div class="form-row errors field-username">#  <ul class="errorlist"><li>This field is required.</li></ul>#  <div>#  <label class="required" for="id_username">Username:</label>#  <input autofocus="" class="vTextField" id="id_username" maxlength="150" name="username" required="" type="text"/>#  <div class="help">Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.</div>#  </div>#  </div>, <div class="form-row errors field-password1">#  <ul class="errorlist"><li>This field is required.</li></ul>#  <div>#  <label class="required" for="id_password1">Password:</label>#  <input id="id_password1" name="password1" required="" type="password"/>#  <div class="help"><ul><li>Your password can't be too similar to your other personal information.</li><li>Your password must contain at least 8 characters.</li><li>Your password can't be a commonly used password.</li><li>Your password can't be entirely numeric.</li></ul></div>#  </div>#  </div>, <div class="form-row errors field-password2">#  <ul class="errorlist"><li>This field is required.</li></ul>#  <div>#  <label class="required" for="id_password2">Password confirmation:</label>#  <input id="id_password2" name="password2" required="" type="password"/>#  <div class="help">Enter the same password as before, for verification.</div>#  </div>#  </div>],# 'response': <Response [200]>}dynamic_client.users.add({'username':'fromdynamic','password1':'qwertyuio!','password2':'qwertyuio!'})# {'id': '3', 'created': True, 'errors': [], 'response': <Response [200]>}Get all object ids with<model-name>.all():dynamic_client.users.all()# {'ids': ['3', '1', '2'], 'response': <Response [200]>}Search object id with<model-name>.find(query: str):dynamic_client.users.find('fromdynamic')# {'id': '3', 'response': <Response [200]>}Get object fields with<model-name>.get(object_id: str):>>>dynamic_client.users.get('3'){'response':<Response[200]>,'details':{'username':'fromdynamic','password':'<N/A>','first_name':'','last_name':'','email':'','is_active':'','is_staff':'','is_superuser':'','groups':[],'user_permissions':[],'last_login_0':'','last_login_1':'','date_joined_0':'2019-03-06','date_joined_1':'13:00:00'}}Change object with<model-name>.change(object_id: str, fields: dict):In[1]:dynamic_client.users.change('3',{'is_superuser':'1'})Out[1]:{'success':False,'errors':[<divclass="form-row errors field-username"><ulclass="errorlist"><li>Thisfieldisrequired.</li></ul><div><labelclass="required"for="id_username">Username:</label><inputclass="vTextField"id="id_username"maxlength="150"name="username"required=""type="text"/><divclass="help">Required.150charactersorfewer.Letters,digitsand@/./+/-/_only.</div></div></div>,<divclass="form-row errors field-date_joined"><ulclass="errorlist"><li>Thisfieldisrequired.</li></ul><div><labelclass="required"for="id_date_joined_0">Datejoined:</label><pclass="datetime">Date:<inputclass="vDateField"id="id_date_joined_0"name="date_joined_0"required=""size="10"type="text"/><br/>Time:<inputclass="vTimeField"id="id_date_joined_1"name="date_joined_1"required=""size="8"type="text"/></p><inputid="initial-id_date_joined_0"name="initial-date_joined_0"type="hidden"/><inputid="initial-id_date_joined_1"name="initial-date_joined_1"type="hidden"/></div></div>],'response':<Response[200]>}In[2]:dynamic_client.users.change('3',...:{'username':'fromdynamic',...:'is_active':'1',...:'is_staff':'1',...:'is_superuser':'1',...:'date_joined_0':'2019-03-06','date_joined_1':'13:00'})Out[2]:{'success':True,'errors':[],'response':<Response[200]>}In[3]:dynamic_client.users.get('3')Out[3]:{'response':<Response[200]>,'details':{'username':'fromdynamic','password':'<N/A>','first_name':'','last_name':'','email':'','is_active':'1','is_staff':'1','is_superuser':'1','groups':[],'user_permissions':[],'last_login_0':'','last_login_1':'','date_joined_0':'2019-03-06','date_joined_1':'13:00:00'}}And delete the object with<model-name>.delete(object_id: str):dynamic_client.users.delete('3')# {'response': <Response [200]>, 'success': True}dynamic_client.users.all()# {'response': <Response [200]>, 'ids': ['1', '2']}3. Generating specific Django Admin python clientDjangoAdminDynamicis quite useful for quick terminal sessions.Auto-completion forDjangoAdminDynamicclients is provided in python interpreter when hitting<tab>because attributes
of a client are introspected at run time withdirbuilt-in python funciton.But writing source code withDjangoAdminDynamicis not that pleasant because
there's no way for IDE to know what attributes will object have at run time.For this purposegenerate-packagecommand is provided withdjango-admin-package.Example$ generate-package
Base url (including /admin): http://localhost:9000/admin
Superuser username: superuser
Superuser password:
Site name: fresh project
Path to package (default: /tmp): .
Version (default: 1.0):
Generated package in ./freshproject-admin-clientGenerated project structure:$ tree freshproject-admin-client
freshproject-admin-client
├── freshproject_admin_client
│   ├── client.py
│   ├── __init__.py
│   └── spec.json
├── README.md
└── setup.py

1 directory, 5 filesYou can version control generated package, install it, upload to PyPI.Example usage of a generated client:$ ipython
Python 3.6.7 (default, Nov  9 2018, 21:20:52)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.3.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from freshproject_admin_client import FreshProjectDjangoAdminClient

In [2]: client = FreshProjectDjangoAdminClient('http://localhost:9000/admin', 'superuser', 'why-dont-tell-mom?')

In [3]: client.auth.users.all()
Out[3]: {'response': <Response [200]>, 'ids': ['4', '1', '2']}

In [4]: client.auth.users.add()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-213c8509773c> in <module>
----> 1 client.auth.users.add()

TypeError: add() missing 3 required positional arguments: 'username', 'password1', and 'password2'

In [5]: client.auth.users.add('from_freshproject_client', '123qweASD)_+', '123qweASD)_+')
Out[5]: {'response': <Response [200]>, 'id': '5', 'created': True}

In [6]: client.auth.users.get('5')
Out[6]:
{'response': <Response [200]>,
 'details': {'username': 'from_freshproject_client',
  'password': '<N/A>',
  'first_name': '',
  'last_name': '',
  'email': '',
  'is_active': '',
  'is_staff': '',
  'is_superuser': '',
  'groups': [],
  'user_permissions': [],
  'last_login': '',
  'date_joined': '2019-03-05'}}

In [7]: client.auth.users.find('from_freshproject_client')
Out[7]: {'response': <Response [200]>, 'id': '5'}

In [8]: client.auth.users.delete('5')
Out[8]: {'response': <Response [200]>, 'success': True}