├── .gitignore
├── CHANGELOG.md
├── LICENSE.md
├── MANIFEST.in
├── README.md
├── django_dyn_api
├── __init__.py
├── admin.py
├── apps.py
├── helpers.py
├── tests.py
├── urls.py
└── views.py
├── docs
└── blank.txt
├── publish.txt
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.DS_Store
3 | *.egg*
4 | /dist/
5 | /.idea
6 | /docs/_build/
7 | /node_modules/
8 | build/
9 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## [1.0.5] 2024-10-17
4 | ### Changes
5 |
6 | - Update RM
7 | - [Django Dynamic API](https://app-generator.dev/docs/developer-tools/dynamic-api.html)
8 | - Mention [Dynamic Django](https://app-generator.dev/docs/developer-tools/dynamic-django/index.html) Starter (commercial)
9 |
10 | ## [1.0.4] 2022-11-01
11 | ### Changes
12 |
13 | - DOCS Update
14 |
15 | ## [1.0.3] 2022-10-30
16 | ### Changes
17 |
18 | - `DOCS` Update
19 | - Remove `settings` module hard coding
20 | - Now use `getattr()` to bind the configuration
21 |
22 | ## [1.0.2] 2022-10-24
23 | ### Changes
24 |
25 | - DOCS Update
26 | - Added [Dynamic API](https://www.youtube.com/watch?v=nPQMUafTrNY) `video presentation`
27 |
28 | ## [1.0.1] 2022-10-24
29 | ### Changes
30 |
31 | - DOCS Update
32 |
33 | ## [1.0.0] 2022-10-24
34 | ### STABLE_RELEASE
35 |
36 | - Flag the stable version
37 | - DOCS Update
38 |
39 | ## [0.0.3] 2022-10-24
40 | ### Improvements
41 |
42 | - CRUD Works
43 | - JWT added
44 |
45 | ## [0.0.2] 2022-10-24
46 | ### Improvements
47 |
48 | - Draft Code Added
49 |
50 | ## [0.0.1] 2022-10-24
51 | ### Initial Version
52 |
53 | - Codebase design
54 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 [App Generator](https://appseed.us)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE.md
2 | include README.md
3 | recursive-include django_dyn_api *
4 | recursive-include docs *
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [Django Dynamic API](https://app-generator.dev/docs/developer-tools/dynamic-api.html)
2 |
3 | Simple tool that **Generates Secure APIs** on top of `DRF` with minimum effort - actively supported by **[App-Generator](https://app-generator.dev/)**.
4 |
5 | - [Dynamic API Features](https://www.youtube.com/watch?v=nPQMUafTrNY) - video presentation
6 |
7 |
8 |
9 | ---
10 |
11 | > For a **complete set of features** and long-term support, check out **[Dynamic Django](https://app-generator.dev/docs/developer-tools/dynamic-django/index.html)**, a powerful starter that incorporates:
12 |
13 | - [Dynamic DataTables](https://app-generator.dev/docs/developer-tools/dynamic-django/datatables.html): using a single line of configuration, the data saved in any table is automatically managed
14 | - [Dynamic API](https://app-generator.dev/docs/developer-tools/dynamic-django/api.html): any model can become a secure API Endpoint using DRF
15 | - [Dynamic Charts](https://app-generator.dev/docs/developer-tools/dynamic-django/charts.html): extract relevant charts without coding all major types are supported
16 | - [CSV Loader](https://app-generator.dev/docs/developer-tools/dynamic-django/csv-loader.html): translate CSV files into Django Models and (optional) load the information
17 | - Powerful [CLI Tools](https://app-generator.dev/docs/developer-tools/dynamic-django/cli.html) for the GIT interface, configuration editing, updating the configuration and database (create models, migrate DB)
18 |
19 |
20 |
21 | ## `Dynamic API Features`
22 |
23 | - `API engine` provided by `DRF`
24 | - `Minimal Configuration` (single line in config for each model)
25 | - `Handles any model` defined across the project
26 |
27 |
28 |
29 | 
30 |
31 |
32 |
33 | ## How to use it
34 |
35 |
36 |
37 | > **Step #1** - `Install the package`
38 |
39 | ```bash
40 | $ pip install django-dynamic-api
41 | // OR
42 | $ pip install git+https://github.com/app-generator/django-dynamic-api.git
43 | ```
44 |
45 |
46 |
47 | > **Step #2** - `Update Configuration`, include the new APPs
48 |
49 | ```python
50 | INSTALLED_APPS = [
51 | 'django_dyn_api', # Django Dynamic API # <-- NEW
52 | 'rest_framework', # Include DRF # <-- NEW
53 | 'rest_framework.authtoken', # Include DRF Auth # <-- NEW
54 | ]
55 | ```
56 |
57 |
58 |
59 | > **Step #3** - `Register the model` in `core/settings.py` (DYNAMIC_API section)
60 |
61 | This sample code assumes that `app1` exists and model `Book` is defined and migrated.
62 |
63 | ```python
64 |
65 | DYNAMIC_API = {
66 | # pattern:
67 | # API_SLUG -> Import_PATH
68 | 'books' : "app1.models.Book",
69 | }
70 |
71 | REST_FRAMEWORK = {
72 | 'DEFAULT_AUTHENTICATION_CLASSES': [
73 | 'rest_framework.authentication.SessionAuthentication',
74 | 'rest_framework.authentication.TokenAuthentication',
75 | ],
76 | }
77 |
78 | ```
79 |
80 |
81 |
82 | > **Step #4** - `Migrate DB` and create the tables used by `DRF`
83 |
84 | ```bash
85 | $ python manage.py makemigrations
86 | $ python manage.py migrate
87 | ```
88 |
89 |
90 |
91 | > **Step #5** - `Update routing`, include APIs
92 |
93 | ```python
94 | from django.contrib import admin
95 | from django.urls import path, include # <-- UPD: 'include` directive
96 | from rest_framework.authtoken.views import obtain_auth_token # <-- NEW
97 |
98 | urlpatterns = [
99 | path("admin/", admin.site.urls),
100 | path('', include('django_dyn_api.urls')), # <-- NEW
101 | path('login/jwt/', view=obtain_auth_token), # <-- NEW
102 | ]
103 | ```
104 |
105 |
106 |
107 | > **Step #6** - `Use API`
108 |
109 | If the managed model is `Books`, the API interface is `/api/books/` and all CRUD methods are available.
110 |
111 | > Note: for mutating requests, the `JWT Token` is provided by `http://localhost:8000/login/jwt/` route (the user should exist).
112 |
113 |
114 |
115 | 
116 |
117 |
118 |
119 | ### Links & resources
120 |
121 | - [DRF](https://www.django-rest-framework.org/) - HOMEpage
122 | - More [Developer Tools](https://appseed.us/developer-tools/) provided by `AppSeed`
123 | - Ask for [Support](https://appseed.us/support/) via `Email` & `Discord`
124 |
125 |
126 |
127 | ---
128 | [Django Dynamic API](https://app-generator.dev/docs/developer-tools/dynamic-api.html) - Open-source library provided by **[App-Generator](https://app-generator.dev/)**
129 |
--------------------------------------------------------------------------------
/django_dyn_api/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | """
3 | Copyright (c) 2019 - present AppSeed.us
4 | """
5 |
6 |
--------------------------------------------------------------------------------
/django_dyn_api/admin.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | """
3 | Copyright (c) 2019 - present AppSeed.us
4 | """
5 |
6 | from django.contrib import admin
7 |
8 | # Register your models here.
9 |
--------------------------------------------------------------------------------
/django_dyn_api/apps.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | """
3 | Copyright (c) 2019 - present AppSeed.us
4 | """
5 |
6 | from django.apps import AppConfig
7 |
8 | class DynApiConfig(AppConfig):
9 | default_auto_field = 'django.db.models.BigAutoField'
10 | name = 'django_dyn_api'
11 |
--------------------------------------------------------------------------------
/django_dyn_api/helpers.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | """
3 | Copyright (c) 2019 - present AppSeed.us
4 | """
5 |
6 | import datetime, sys, inspect, importlib
7 |
8 | from functools import wraps
9 |
10 | from django.db import models
11 | from django.http import HttpResponseRedirect, HttpResponse
12 |
13 | from rest_framework import serializers
14 |
15 | class Utils:
16 | @staticmethod
17 | def get_class(config, name: str) -> models.Model:
18 | return Utils.model_name_to_class(config[name])
19 |
20 | @staticmethod
21 | def get_manager(config, name: str) -> models.Manager:
22 | return Utils.get_class(config, name).objects
23 |
24 | @staticmethod
25 | def get_serializer(config, name: str):
26 | class Serializer(serializers.ModelSerializer):
27 | class Meta:
28 | model = Utils.get_class(config, name)
29 | fields = '__all__'
30 |
31 | return Serializer
32 |
33 | @staticmethod
34 | def model_name_to_class(name: str):
35 |
36 | model_name = name.split('.')[-1]
37 | model_import = name.replace('.'+model_name, '')
38 |
39 | module = importlib.import_module(model_import)
40 | cls = getattr(module, model_name)
41 |
42 | return cls
43 |
44 | def check_permission(function):
45 | @wraps(function)
46 | def wrap(viewRequest, *args, **kwargs):
47 |
48 | try:
49 |
50 | # Check user
51 | if viewRequest.request.user.is_authenticated:
52 | return function(viewRequest, *args, **kwargs)
53 |
54 | # All good - allow the processing
55 | return HttpResponseRedirect('/login/')
56 |
57 | except Exception as e:
58 |
59 | # On error
60 | return HttpResponse( 'Error: ' + str( e ) )
61 |
62 | return function(viewRequest, *args, **kwargs)
63 |
64 | return wrap
65 |
--------------------------------------------------------------------------------
/django_dyn_api/tests.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | """
3 | Copyright (c) 2019 - present AppSeed.us
4 | """
5 |
6 | from django.test import TestCase
7 |
8 | # Create your tests here.
9 |
--------------------------------------------------------------------------------
/django_dyn_api/urls.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | """
3 | Copyright (c) 2019 - present AppSeed.us
4 | """
5 |
6 | from django.contrib import admin
7 | from django.urls import path
8 | from .views import DynamicAPI
9 |
10 | urlpatterns = [
11 | path('api//' , DynamicAPI.as_view()),
12 | path('api//' , DynamicAPI.as_view()),
13 | path('api///' , DynamicAPI.as_view()),
14 | ]
15 |
--------------------------------------------------------------------------------
/django_dyn_api/views.py:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | """
3 | Copyright (c) 2019 - present AppSeed.us
4 | """
5 |
6 | from django.http import Http404
7 |
8 | from django.contrib.auth.decorators import login_required
9 | from django.utils.decorators import method_decorator
10 |
11 | from rest_framework.generics import get_object_or_404
12 | from rest_framework.views import APIView
13 | from rest_framework.response import Response
14 |
15 | from django.conf import settings
16 |
17 | DYNAMIC_API = {}
18 |
19 | try:
20 | DYNAMIC_API = getattr(settings, 'DYNAMIC_API')
21 | except:
22 | pass
23 |
24 | from .helpers import Utils
25 |
26 | #from .helpers import check_permission
27 |
28 | class DynamicAPI(APIView):
29 |
30 | # READ : GET api/model/id or api/model
31 | def get(self, request, **kwargs):
32 |
33 | model_id = kwargs.get('id', None)
34 | try:
35 | if model_id is not None:
36 |
37 | # Validate for integer
38 | try:
39 | model_id = int(model_id)
40 |
41 | if model_id < 0:
42 | raise ValueError('Expect positive int')
43 |
44 | except ValueError as e:
45 | return Response(data={
46 | 'message': 'Input Error = ' + str(e),
47 | 'success': False
48 | }, status=400)
49 |
50 | thing = get_object_or_404(Utils.get_manager(DYNAMIC_API, kwargs.get('model_name')), id=model_id)
51 | model_serializer = Utils.get_serializer(DYNAMIC_API, kwargs.get('model_name'))(instance=thing)
52 | output = model_serializer.data
53 | else:
54 | all_things = Utils.get_manager(DYNAMIC_API, kwargs.get('model_name')).all()
55 | thing_serializer = Utils.get_serializer(DYNAMIC_API, kwargs.get('model_name'))
56 | output = []
57 | for thing in all_things:
58 | output.append(thing_serializer(instance=thing).data)
59 | except KeyError:
60 | return Response(data={
61 | 'message': 'this model is not activated or not exist.',
62 | 'success': False
63 | }, status=400)
64 | except Http404:
65 | return Response(data={
66 | 'message': 'object with given id not found.',
67 | 'success': False
68 | }, status=404)
69 | return Response(data={
70 | 'data': output,
71 | 'success': True
72 | }, status=200)
73 |
74 | # CREATE : POST api/model/
75 | #@check_permission
76 | def post(self, request, **kwargs):
77 | try:
78 | model_serializer = Utils.get_serializer(DYNAMIC_API, kwargs.get('model_name'))(data=request.data)
79 | if model_serializer.is_valid():
80 | model_serializer.save()
81 | else:
82 | return Response(data={
83 | **model_serializer.errors,
84 | 'success': False
85 | }, status=400)
86 | except KeyError:
87 | return Response(data={
88 | 'message': 'this model is not activated or not exist.',
89 | 'success': False
90 | }, status=400)
91 | return Response(data={
92 | 'message': 'Record Created.',
93 | 'success': True
94 | }, status=200)
95 |
96 | # UPDATE : PUT api/model/id/
97 | #@check_permission
98 | def put(self, request, **kwargs):
99 | try:
100 | thing = get_object_or_404(Utils.get_manager(DYNAMIC_API, kwargs.get('model_name')), id=kwargs.get('id'))
101 | model_serializer = Utils.get_serializer(DYNAMIC_API, kwargs.get('model_name'))(instance=thing,
102 | data=request.data,
103 | partial=True)
104 | if model_serializer.is_valid():
105 | model_serializer.save()
106 | else:
107 | return Response(data={
108 | **model_serializer.errors,
109 | 'success': False
110 | }, status=400)
111 | except KeyError:
112 | return Response(data={
113 | 'message': 'this model is not activated or not exist.',
114 | 'success': False
115 | }, status=400)
116 | except Http404:
117 | return Response(data={
118 | 'message': 'object with given id not found.',
119 | 'success': False
120 | }, status=404)
121 | return Response(data={
122 | 'message': 'Record Updated.',
123 | 'success': True
124 | }, status=200)
125 |
126 | # DELETE : DELETE api/model/id/
127 | #@check_permission
128 | def delete(self, request, **kwargs):
129 | try:
130 | model_manager = Utils.get_manager(DYNAMIC_API, kwargs.get('model_name'))
131 | to_delete_id = kwargs.get('id')
132 | model_manager.get(id=to_delete_id).delete()
133 | except KeyError:
134 | return Response(data={
135 | 'message': 'this model is not activated or not exist.',
136 | 'success': False
137 | }, status=400)
138 | except Utils.get_class(DYNAMIC_API, kwargs.get('model_name')).DoesNotExist as e:
139 | return Response(data={
140 | 'message': 'object with given id not found.',
141 | 'success': False
142 | }, status=404)
143 | return Response(data={
144 | 'message': 'Record Deleted.',
145 | 'success': True
146 | }, status=200)
147 |
--------------------------------------------------------------------------------
/docs/blank.txt:
--------------------------------------------------------------------------------
1 | "coming soon"
--------------------------------------------------------------------------------
/publish.txt:
--------------------------------------------------------------------------------
1 | python setup.py sdist
2 |
3 | twine check dist/*
4 |
5 | twine upload .\dist\THE_GENERATED_PACKAGE
6 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 | from setuptools import find_packages, setup
3 |
4 | with open(os.path.join(os.path.dirname(__file__), 'README.md')) as readme:
5 | README = readme.read()
6 |
7 | os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
8 |
9 | setup(
10 | name='django-dynamic-api',
11 | version='1.0.5',
12 | zip_safe=False,
13 | packages=find_packages(),
14 | include_package_data=True,
15 | description='Django Dynamic API over DRF',
16 | long_description=README,
17 | long_description_content_type="text/markdown",
18 | url='https://app-generator.dev/docs/developer-tools/dynamic-api.html',
19 | author='AppSeed.us',
20 | author_email='support@appseed.us',
21 | license='MIT License',
22 | install_requires=[
23 | 'djangorestframework',
24 | ],
25 | classifiers=[
26 | 'Intended Audience :: Developers',
27 | 'Intended Audience :: System Administrators',
28 | 'License :: OSI Approved :: MIT License',
29 | 'Operating System :: OS Independent',
30 | 'Programming Language :: Python',
31 | 'Programming Language :: Python :: 2.6',
32 | 'Programming Language :: Python :: 2.7',
33 | 'Programming Language :: Python :: 3.2',
34 | 'Programming Language :: Python :: 3.3',
35 | 'Programming Language :: Python :: 3.4',
36 | 'Programming Language :: Python :: 3.5',
37 | 'Programming Language :: Python :: 3.6',
38 | 'Environment :: Web Environment',
39 | 'Topic :: Software Development',
40 | 'Topic :: Software Development :: User Interfaces',
41 | ],
42 | )
43 |
--------------------------------------------------------------------------------