├── .gitignore ├── CHANGELOG.md ├── Dockerfile ├── LICENSE.md ├── README.md ├── README_deploy.md ├── api ├── __init__.py ├── serializers.py ├── urls.py └── views.py ├── build.sh ├── core ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── docker-compose.yml ├── env.sample ├── gunicorn-cfg.py ├── home ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── templates │ ├── includes │ │ ├── footer.html │ │ └── menu-list.html │ ├── pages │ │ ├── api-generator.html │ │ └── dyn-datatb.html │ └── test-datatb.html ├── tests.py ├── urls.py └── views.py ├── manage.py ├── media └── test.postman_collection ├── nginx └── appseed-app.conf ├── render.yaml ├── requirements.txt └── static └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.DS_Store 3 | *.egg* 4 | /dist/ 5 | /.idea 6 | /docs/_build/ 7 | /node_modules/ 8 | build/ 9 | env 10 | /staticfiles/ 11 | 12 | #src 13 | *.sqlite* 14 | 15 | .env 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [1.0.6] 2023-03-08 4 | ### Changes 5 | 6 | - Bump [Dynamic DataTables](https://github.com/app-generator/django-dynamic-datatb) `1.0.21` 7 | 8 | ## [1.0.5] 2023-03-06 9 | ### Changes 10 | 11 | - Bump [Dynamic DataTables](https://github.com/app-generator/django-dynamic-datatb) `1.0.16` 12 | 13 | ## [1.0.4] 2023-03-04 14 | ### Changes 15 | 16 | - Bump [Dynamic DataTables](https://github.com/app-generator/django-dynamic-datatb) `1.0.12` 17 | 18 | ## [1.0.3] 2023-02-15 19 | ### Changes 20 | 21 | - Update CI/CD flow 22 | - Migrate latest DB changes 23 | - Generate the API 24 | 25 | ## [1.0.2] 2023-02-15 26 | ### Changes 27 | 28 | - Bump Versions 29 | - [API Generator](https://github.com/app-generator/django-api-generator) `1.0.10` 30 | - [Dynamic DataTables](https://github.com/app-generator/django-dynamic-datatb) `1.0.8` 31 | 32 | 33 | ## [1.0.1] 2023-02-15 34 | ### Changes 35 | 36 | - Integrate [Dynamic DataTables](https://github.com/app-generator/django-dynamic-datatb) 37 | - `Free Python Library` 38 | 39 | ## [1.0.0] 2023-02-14 40 | ### Changes 41 | 42 | - Initial Codebase 43 | - Stable Version. Tested with two linked Models 44 | - `Product` 45 | - `Sales`: linked to Product (one-to-one) 46 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9 2 | 3 | # set environment variables 4 | ENV PYTHONDONTWRITEBYTECODE 1 5 | ENV PYTHONUNBUFFERED 1 6 | 7 | COPY requirements.txt . 8 | # install python dependencies 9 | RUN pip install --upgrade pip 10 | RUN pip install --no-cache-dir -r requirements.txt 11 | 12 | COPY . . 13 | 14 | # running migrations 15 | RUN python manage.py makemigrations 16 | RUN python manage.py migrate 17 | 18 | # Generate the API 19 | RUN python manage.py generate-api -f 20 | 21 | # gunicorn 22 | CMD ["gunicorn", "--config", "gunicorn-cfg.py", "core.wsgi"] 23 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2019 - present [AppSeed](http://appseed.us/) 4 | 5 |
6 | 7 | ## Licensing Information 8 | 9 |
10 | 11 | | Item | - | 12 | | ---------------------------------- | --- | 13 | | License Type | MIT | 14 | | Use for print | **YES** | 15 | | Create single personal website/app | **YES** | 16 | | Create single website/app for client | **YES** | 17 | | Create multiple website/apps for clients | **YES** | 18 | | Create multiple SaaS applications | **YES** | 19 | | End-product paying users | **YES** | 20 | | Product sale | **YES** | 21 | | Remove footer credits | **YES** | 22 | | --- | --- | 23 | | Remove copyright mentions from source code | NO | 24 | | Production deployment assistance | NO | 25 | | Create HTML/CSS template for sale | NO | 26 | | Create Theme/Template for CMS for sale | NO | 27 | | Separate sale of our UI Elements | NO | 28 | 29 |
30 | 31 | --- 32 | For more information regarding licensing, please contact the AppSeed Service < *support@appseed.us* > 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Django Dynamic Services](https://app-generator.dev/docs/developer-tools/dynamic-django/index.html) 2 | 3 | Open-source **Django** project that showcases the **API Generator** and other **Dynamic Services** - actively supported by [App-Generator](https://app-generator.dev/). 4 | 5 | - 👉 [Django - Build Services without Coding](https://www.youtube.com/watch?v=EtMCK5AmdQI) - `video presentation` (learn how to use this starter) 6 | 7 | --- 8 | 9 | > 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: 10 | 11 | - ✅ [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 12 | - ✅ [Dynamic API](https://app-generator.dev/docs/developer-tools/dynamic-django/api.html): any model can become a secure API Endpoint using DRF 13 | - ✅ [Dynamic Charts](https://app-generator.dev/docs/developer-tools/dynamic-django/charts.html): extract relevant charts without coding all major types are supported 14 | - ✅ [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 15 | - ✅ 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) 16 | 17 |
18 | 19 | ## Start the app in Docker 20 | 21 | > 👉 **Step 1** - Download the code from the GH repository (using `GIT`) 22 | 23 | ```bash 24 | $ git clone https://github.com/app-generator/django-dynamic-services.git 25 | $ cd django-dynamic-services 26 | ``` 27 | 28 | > 👉 **Step 2** - Start the APP in `Docker` 29 | 30 | ```bash 31 | $ docker-compose up --build 32 | ``` 33 | 34 | Visit `http://localhost:5085` in your browser. The app should be up & running. 35 | 36 | 37 | > 👉 **Step 3** - Create Superuser in `Docker` 38 | 39 | ```bash 40 | $ # List containes & get the ID 41 | $ docker container ls 42 | $ # Create the superuser 43 | $ docker exec python manage.py createsuperuser 44 | ``` 45 | 46 |
47 | 48 | ## Manual Build 49 | 50 | > 👉 Download the code 51 | 52 | ```bash 53 | $ git clone https://github.com/app-generator/django-dynamic-services.git 54 | $ cd django-dynamic-services 55 | ``` 56 | 57 |
58 | 59 | > 👉 Install modules via `VENV` 60 | 61 | ```bash 62 | $ virtualenv env 63 | $ source env/bin/activate 64 | $ pip install -r requirements.txt 65 | ``` 66 | 67 |
68 | 69 | > 👉 Set Up Database 70 | 71 | ```bash 72 | $ python manage.py makemigrations 73 | $ python manage.py migrate 74 | ``` 75 | 76 |
77 | 78 | > 👉 Generate API 79 | 80 | ```bash 81 | $ python manage.py generate-api # requires confirmation 82 | // 83 | $ python manage.py generate-api -f # no input (API folder is overwritten) 84 | ``` 85 | 86 |
87 | 88 | > 👉 Create the Superuser 89 | 90 | ```bash 91 | $ python manage.py createsuperuser 92 | ``` 93 | 94 |
95 | 96 | > 👉 Start the app 97 | 98 | ```bash 99 | $ python manage.py runserver 100 | ``` 101 | 102 | At this point, the app runs at `http://127.0.0.1:8000/` and the generated API can be found at: 103 | 104 | - http://localhost:8000/api/product/ - For `products` 105 | - http://localhost:8000/api/sales/ - For `sales` 106 | 107 | The default API nodes can be tested via this [POSTMAN](./media/test.postman_collection) Collection. 108 | 109 |
110 | 111 | ## How to use the API 112 | 113 | - Start the app 114 | - Make sure the endpoints are up & running 115 | - Authenticate via API and het the access token 116 | - `http://localhost:8000/login/jwt/` usind existing credentials 117 | - Save the token in the requests `HEADER` 118 | - `GET Requests` are public (no token required) 119 | - GET ALL products: `http://localhost:8000/api/product/` 120 | - GET product by ID: `http://localhost:8000/api/product/1/` 121 | - GET ALL Sales: `http://localhost:8000/api/sales/` 122 | - `Create`, `Delete`, `Update` requires a token in the header 123 | 124 | For API sample requests, open and edit the [POSTMAN](./media/test.postman_collection) Collection sample. 125 | 126 |
127 | 128 | ## How Update the API 129 | 130 | - Define or update your models 131 | - Migrate the database 132 | - Update the configuration `API_GENERATOR` section 133 | - Regenerate the API 134 | - `python manage.py generate-api` 135 | 136 | At this point, you should be able to use the API. For more information regarding the library used to generate the code, access: 137 | 138 | > 👉 [API Generator for Django](https://github.com/app-generator/django-api-generator) - Open-Source Library 139 | 140 |
141 | 142 | ## Codebase structure 143 | 144 | The project is coded using a simple and intuitive structure presented below: 145 | 146 | ```bash 147 | < PROJECT ROOT > 148 | | 149 | |-- core/ 150 | | |-- settings.py # Project Configuration 151 | | |-- urls.py # Project Routing 152 | | 153 | |-- home/ 154 | | |-- views.py # APP Views 155 | | |-- urls.py # APP Routing 156 | | |-- models.py # APP Models 157 | | |-- tests.py # Tests 158 | | |-- templates/ # Theme Customisation 159 | | |-- includes # 160 | | |-- footer.py # Custom Footer 161 | | 162 | |-- requirements.txt # Project Dependencies 163 | | 164 | |-- env.sample # ENV Configuration (default values) 165 | |-- manage.py # Start the app - Django default start script 166 | | 167 | |-- ************************************************************************ 168 | ``` 169 | 170 |
171 | 172 | --- 173 | [Django Dynamic Services](https://app-generator.dev/docs/developer-tools/dynamic-django/index.html) - Open-Source **Django** starter provided by **[App-Generator](https://app-generator.dev/)** 174 | -------------------------------------------------------------------------------- /README_deploy.md: -------------------------------------------------------------------------------- 1 | # How to deploy on `Render` 2 | 3 | > This document should contains all the steps to deploy the app on render without much effort, using PostgreSQL 4 | 5 | https://render.com/docs/deploy-django 6 | 7 | ## ALL STEPS below 8 | 9 |
10 | 11 | ### 👉 Create `PostgreSQL` database on render 12 | - Go to https://dashboard.render.com/new/database this link. 13 | - Database name should be `berry`. 14 | - Keep the Database, User and Datadog API Key as it is. 15 | - If you want to change database name anything else then you have to change your `render.yaml` file database name too. 16 | 17 |
18 | 19 | ### 👉 Create a Blueprint instance 20 | - Go to https://dashboard.render.com/blueprints this link. 21 | - Click `New Blueprint Instance` button. 22 | - Connect your `repo` which you want to deploy. 23 | - Fill the `Service Group Name` and click on `Update Existing Resources` button. 24 | - After that your deployment will start automatically. -------------------------------------------------------------------------------- /api/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | -------------------------------------------------------------------------------- /api/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | 4 | from home.models import Product 5 | 6 | from home.models import Sales 7 | 8 | 9 | 10 | class ProductSerializer(serializers.ModelSerializer): 11 | class Meta: 12 | model = Product 13 | fields = '__all__' 14 | 15 | 16 | class SalesSerializer(serializers.ModelSerializer): 17 | class Meta: 18 | model = Sales 19 | fields = '__all__' 20 | 21 | -------------------------------------------------------------------------------- /api/urls.py: -------------------------------------------------------------------------------- 1 | import django.urls 2 | from django.views.decorators.csrf import csrf_exempt 3 | 4 | from api.views import * 5 | 6 | 7 | urlpatterns = [ 8 | 9 | django.urls.re_path("product/((?P\d+)/)?", csrf_exempt(ProductView.as_view())), 10 | django.urls.re_path("sales/((?P\d+)/)?", csrf_exempt(SalesView.as_view())), 11 | 12 | ] -------------------------------------------------------------------------------- /api/views.py: -------------------------------------------------------------------------------- 1 | from http import HTTPStatus 2 | from django.http import Http404 3 | from django.shortcuts import render 4 | from rest_framework.response import Response 5 | from rest_framework.views import APIView 6 | from rest_framework.generics import get_object_or_404 7 | from rest_framework.permissions import IsAuthenticatedOrReadOnly 8 | 9 | from api.serializers import * 10 | 11 | from home.models import Product 12 | 13 | from home.models import Sales 14 | 15 | 16 | class ProductView(APIView): 17 | permission_classes = (IsAuthenticatedOrReadOnly,) 18 | 19 | def post(self, request): 20 | serializer = ProductSerializer(data=request.POST) 21 | if not serializer.is_valid(): 22 | return Response(data={ 23 | **serializer.errors, 24 | 'success': False 25 | }, status=HTTPStatus.BAD_REQUEST) 26 | serializer.save() 27 | return Response(data={ 28 | 'message': 'Record Created.', 29 | 'success': True 30 | }, status=HTTPStatus.OK) 31 | 32 | def get(self, request, pk=None): 33 | if not pk: 34 | return Response({ 35 | 'data': [ProductSerializer(instance=obj).data for obj in Product.objects.all()], 36 | 'success': True 37 | }, status=HTTPStatus.OK) 38 | try: 39 | obj = get_object_or_404(Product, pk=pk) 40 | except Http404: 41 | return Response(data={ 42 | 'message': 'object with given id not found.', 43 | 'success': False 44 | }, status=HTTPStatus.NOT_FOUND) 45 | return Response({ 46 | 'data': ProductSerializer(instance=obj).data, 47 | 'success': True 48 | }, status=HTTPStatus.OK) 49 | 50 | def put(self, request, pk): 51 | try: 52 | obj = get_object_or_404(Product, pk=pk) 53 | except Http404: 54 | return Response(data={ 55 | 'message': 'object with given id not found.', 56 | 'success': False 57 | }, status=HTTPStatus.NOT_FOUND) 58 | serializer = ProductSerializer(instance=obj, data=request.POST, partial=True) 59 | if not serializer.is_valid(): 60 | return Response(data={ 61 | **serializer.errors, 62 | 'success': False 63 | }, status=HTTPStatus.BAD_REQUEST) 64 | serializer.save() 65 | return Response(data={ 66 | 'message': 'Record Updated.', 67 | 'success': True 68 | }, status=HTTPStatus.OK) 69 | 70 | def delete(self, request, pk): 71 | try: 72 | obj = get_object_or_404(Product, pk=pk) 73 | except Http404: 74 | return Response(data={ 75 | 'message': 'object with given id not found.', 76 | 'success': False 77 | }, status=HTTPStatus.NOT_FOUND) 78 | obj.delete() 79 | return Response(data={ 80 | 'message': 'Record Deleted.', 81 | 'success': True 82 | }, status=HTTPStatus.OK) 83 | 84 | 85 | class SalesView(APIView): 86 | permission_classes = (IsAuthenticatedOrReadOnly,) 87 | 88 | def post(self, request): 89 | serializer = SalesSerializer(data=request.POST) 90 | if not serializer.is_valid(): 91 | return Response(data={ 92 | **serializer.errors, 93 | 'success': False 94 | }, status=HTTPStatus.BAD_REQUEST) 95 | serializer.save() 96 | return Response(data={ 97 | 'message': 'Record Created.', 98 | 'success': True 99 | }, status=HTTPStatus.OK) 100 | 101 | def get(self, request, pk=None): 102 | if not pk: 103 | return Response({ 104 | 'data': [SalesSerializer(instance=obj).data for obj in Sales.objects.all()], 105 | 'success': True 106 | }, status=HTTPStatus.OK) 107 | try: 108 | obj = get_object_or_404(Sales, pk=pk) 109 | except Http404: 110 | return Response(data={ 111 | 'message': 'object with given id not found.', 112 | 'success': False 113 | }, status=HTTPStatus.NOT_FOUND) 114 | return Response({ 115 | 'data': SalesSerializer(instance=obj).data, 116 | 'success': True 117 | }, status=HTTPStatus.OK) 118 | 119 | def put(self, request, pk): 120 | try: 121 | obj = get_object_or_404(Sales, pk=pk) 122 | except Http404: 123 | return Response(data={ 124 | 'message': 'object with given id not found.', 125 | 'success': False 126 | }, status=HTTPStatus.NOT_FOUND) 127 | serializer = SalesSerializer(instance=obj, data=request.POST, partial=True) 128 | if not serializer.is_valid(): 129 | return Response(data={ 130 | **serializer.errors, 131 | 'success': False 132 | }, status=HTTPStatus.BAD_REQUEST) 133 | serializer.save() 134 | return Response(data={ 135 | 'message': 'Record Updated.', 136 | 'success': True 137 | }, status=HTTPStatus.OK) 138 | 139 | def delete(self, request, pk): 140 | try: 141 | obj = get_object_or_404(Sales, pk=pk) 142 | except Http404: 143 | return Response(data={ 144 | 'message': 'object with given id not found.', 145 | 'success': False 146 | }, status=HTTPStatus.NOT_FOUND) 147 | obj.delete() 148 | return Response(data={ 149 | 'message': 'Record Deleted.', 150 | 'success': True 151 | }, status=HTTPStatus.OK) 152 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # exit on error 3 | set -o errexit 4 | 5 | python -m pip install --upgrade pip 6 | 7 | pip install -r requirements.txt 8 | 9 | python manage.py collectstatic --no-input 10 | 11 | # Migrate DB 12 | python manage.py makemigrations 13 | python manage.py migrate 14 | 15 | # Generate API 16 | python manage.py generate-api -f 17 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/django-dynamic-services/b3b95ae4334dbc6621a2d6d2cdeabab913f8d765/core/__init__.py -------------------------------------------------------------------------------- /core/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for core project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings") 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /core/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for core project. 3 | 4 | Generated by 'django-admin startproject' using Django 4.1.2. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/4.1/ref/settings/ 11 | """ 12 | 13 | import os, random, string, inspect 14 | from pathlib import Path 15 | from dotenv import load_dotenv 16 | import django_dyn_dt 17 | 18 | load_dotenv() # take environment variables from .env. 19 | 20 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 21 | BASE_DIR = Path(__file__).resolve().parent.parent 22 | 23 | # Quick-start development settings - unsuitable for production 24 | # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ 25 | 26 | # SECURITY WARNING: keep the secret key used in production secret! 27 | SECRET_KEY = os.environ.get('SECRET_KEY') 28 | if not SECRET_KEY: 29 | SECRET_KEY = ''.join(random.choice( string.ascii_lowercase ) for i in range( 32 )) 30 | 31 | # Render Deployment Code 32 | DEBUG = 'RENDER' not in os.environ 33 | 34 | # Docker HOST 35 | ALLOWED_HOSTS = ['localhost', '127.0.0.1'] 36 | 37 | # Add here your deployment HOSTS 38 | CSRF_TRUSTED_ORIGINS = ['http://localhost:8000', 'http://localhost:5085', 'http://127.0.0.1:8000', 'http://127.0.0.1:5085'] 39 | 40 | #Render Context 41 | RENDER_EXTERNAL_HOSTNAME = os.environ.get('RENDER_EXTERNAL_HOSTNAME') 42 | if RENDER_EXTERNAL_HOSTNAME: 43 | ALLOWED_HOSTS.append(RENDER_EXTERNAL_HOSTNAME) 44 | 45 | # Application definition 46 | 47 | INSTALLED_APPS = [ 48 | 'admin_adminlte.apps.AdminAdminlteConfig', 49 | "django.contrib.admin", 50 | "django.contrib.auth", 51 | "django.contrib.contenttypes", 52 | "django.contrib.sessions", 53 | "django.contrib.messages", 54 | "django.contrib.staticfiles", 55 | 56 | 'django_api_gen', # Django API GENERATOR # <-- NEW 57 | 'rest_framework', # Include DRF # <-- NEW 58 | 'rest_framework.authtoken', # Include DRF Auth # <-- NEW 59 | 60 | 'django_dyn_dt', # <-- NEW App 61 | 62 | "home", 63 | ] 64 | 65 | MIDDLEWARE = [ 66 | "django.middleware.security.SecurityMiddleware", 67 | "whitenoise.middleware.WhiteNoiseMiddleware", 68 | "django.contrib.sessions.middleware.SessionMiddleware", 69 | "django.middleware.common.CommonMiddleware", 70 | "django.middleware.csrf.CsrfViewMiddleware", 71 | "django.contrib.auth.middleware.AuthenticationMiddleware", 72 | "django.contrib.messages.middleware.MessageMiddleware", 73 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 74 | ] 75 | 76 | ROOT_URLCONF = "core.urls" 77 | 78 | HOME_TEMPLATES = os.path.join(BASE_DIR, 'home', 'templates') 79 | DATATB_TEMPLATES = os.path.join(BASE_DIR, "django_dyn_dt/templates") 80 | 81 | TEMPLATES = [ 82 | { 83 | "BACKEND": "django.template.backends.django.DjangoTemplates", 84 | "DIRS": [HOME_TEMPLATES, DATATB_TEMPLATES], 85 | "APP_DIRS": True, 86 | "OPTIONS": { 87 | "context_processors": [ 88 | "django.template.context_processors.debug", 89 | "django.template.context_processors.request", 90 | "django.contrib.auth.context_processors.auth", 91 | "django.contrib.messages.context_processors.messages", 92 | ], 93 | }, 94 | }, 95 | ] 96 | 97 | WSGI_APPLICATION = "core.wsgi.application" 98 | 99 | 100 | # Database 101 | # https://docs.djangoproject.com/en/4.1/ref/settings/#databases 102 | 103 | DB_ENGINE = os.getenv('DB_ENGINE' , None) 104 | DB_USERNAME = os.getenv('DB_USERNAME' , None) 105 | DB_PASS = os.getenv('DB_PASS' , None) 106 | DB_HOST = os.getenv('DB_HOST' , None) 107 | DB_PORT = os.getenv('DB_PORT' , None) 108 | DB_NAME = os.getenv('DB_NAME' , None) 109 | 110 | if DB_ENGINE and DB_NAME and DB_USERNAME: 111 | DATABASES = { 112 | 'default': { 113 | 'ENGINE' : 'django.db.backends.' + DB_ENGINE, 114 | 'NAME' : DB_NAME, 115 | 'USER' : DB_USERNAME, 116 | 'PASSWORD': DB_PASS, 117 | 'HOST' : DB_HOST, 118 | 'PORT' : DB_PORT, 119 | }, 120 | } 121 | else: 122 | DATABASES = { 123 | 'default': { 124 | 'ENGINE': 'django.db.backends.sqlite3', 125 | 'NAME': 'db.sqlite3', 126 | } 127 | } 128 | 129 | # Password validation 130 | # https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators 131 | 132 | AUTH_PASSWORD_VALIDATORS = [ 133 | { 134 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 135 | }, 136 | { 137 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", 138 | }, 139 | { 140 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", 141 | }, 142 | { 143 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", 144 | }, 145 | ] 146 | 147 | 148 | # Internationalization 149 | # https://docs.djangoproject.com/en/4.1/topics/i18n/ 150 | 151 | LANGUAGE_CODE = "en-us" 152 | 153 | TIME_ZONE = "UTC" 154 | 155 | USE_I18N = True 156 | 157 | USE_TZ = True 158 | 159 | 160 | # Static files (CSS, JavaScript, Images) 161 | # https://docs.djangoproject.com/en/4.1/howto/static-files/ 162 | 163 | STATIC_URL = '/static/' 164 | STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') 165 | 166 | #if not DEBUG: 167 | # STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' 168 | 169 | # Default primary key field type 170 | # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field 171 | 172 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" 173 | 174 | LOGIN_REDIRECT_URL = '/' 175 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 176 | 177 | ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### 178 | # API GENERATOR Specific 179 | API_GENERATOR = { 180 | # API_SLUG -> Import_PATH 181 | 'product' : "home.models.Product", 182 | 'sales' : "home.models.Sales", 183 | } 184 | 185 | REST_FRAMEWORK = { 186 | 'DEFAULT_AUTHENTICATION_CLASSES': [ 187 | 'rest_framework.authentication.SessionAuthentication', 188 | 'rest_framework.authentication.TokenAuthentication', 189 | ], 190 | } 191 | ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### 192 | 193 | ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### 194 | # Dynamic DataTables Specific 195 | 196 | DYNAMIC_DATATB = { 197 | # SLUG -> Import_PATH 198 | 'product' : "home.models.Product", 199 | 'sales' : "home.models.Sales", 200 | } 201 | 202 | DYN_DB_PKG_ROOT = os.path.dirname( inspect.getfile( django_dyn_dt ) ) # <-- NEW App 203 | 204 | STATICFILES_DIRS = ( 205 | os.path.join(DYN_DB_PKG_ROOT, "templates/static"), 206 | ) 207 | -------------------------------------------------------------------------------- /core/urls.py: -------------------------------------------------------------------------------- 1 | """core URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/4.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import include, path 18 | from django.views.decorators.csrf import csrf_exempt 19 | from rest_framework.authtoken.views import obtain_auth_token 20 | from django_dyn_dt.datatb import DataTB, export 21 | 22 | urlpatterns = [ 23 | path('', include('home.urls')), 24 | path("admin/", admin.site.urls), 25 | 26 | path("api/", include("api.urls")), # <-- NEW (Used by API GEN) 27 | path('login/jwt/', view=obtain_auth_token), # <-- NEW (Used by API GEN) 28 | 29 | path('datatb///', csrf_exempt(DataTB.as_view())), # <-- NEW: (Used by Dynamic DataTables) 30 | path('datatb//' , csrf_exempt(DataTB.as_view())), # <-- NEW: (Used by Dynamic DataTables) 31 | path('datatb//export/' , csrf_exempt(export)), # <-- NEW: (Used by Dynamic DataTables) 32 | 33 | path("", include('admin_adminlte.urls')), 34 | ] 35 | -------------------------------------------------------------------------------- /core/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for core project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | appseed-app: 4 | container_name: appseed_app 5 | restart: always 6 | build: . 7 | networks: 8 | - db_network 9 | - web_network 10 | nginx: 11 | container_name: nginx 12 | restart: always 13 | image: "nginx:latest" 14 | ports: 15 | - "5085:5085" 16 | volumes: 17 | - ./nginx:/etc/nginx/conf.d 18 | networks: 19 | - web_network 20 | depends_on: 21 | - appseed-app 22 | networks: 23 | db_network: 24 | driver: bridge 25 | web_network: 26 | driver: bridge 27 | -------------------------------------------------------------------------------- /env.sample: -------------------------------------------------------------------------------- 1 | # True for development, False for production 2 | DEBUG=True 3 | 4 | SECRET_KEY= 5 | 6 | # DB_ENGINE=mysql 7 | # DB_HOST=localhost 8 | # DB_NAME=appseed_db 9 | # DB_USERNAME=appseed_db_usr 10 | # DB_PASS=pass 11 | # DB_PORT=3306 -------------------------------------------------------------------------------- /gunicorn-cfg.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Copyright (c) 2019 - present AppSeed.us 4 | """ 5 | 6 | bind = '0.0.0.0:5005' 7 | workers = 1 8 | accesslog = '-' 9 | loglevel = 'debug' 10 | capture_output = True 11 | enable_stdio_inheritance = True 12 | -------------------------------------------------------------------------------- /home/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/django-dynamic-services/b3b95ae4334dbc6621a2d6d2cdeabab913f8d765/home/__init__.py -------------------------------------------------------------------------------- /home/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /home/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class HomeConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "home" 7 | -------------------------------------------------------------------------------- /home/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/django-dynamic-services/b3b95ae4334dbc6621a2d6d2cdeabab913f8d765/home/migrations/__init__.py -------------------------------------------------------------------------------- /home/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | 5 | class Product(models.Model): 6 | id = models.AutoField(primary_key=True) 7 | name = models.CharField(max_length = 100) 8 | info = models.CharField(max_length = 100, default = '') 9 | info2 = models.CharField(max_length = 100, default = '') 10 | info3 = models.CharField(max_length = 100, default = '') 11 | info4 = models.CharField(max_length = 100, default = '') 12 | 13 | def __str__(self): 14 | return self.name 15 | 16 | class Sales(models.Model): 17 | id = models.AutoField(primary_key=True) 18 | product = models.OneToOneField(Product, on_delete=models.DO_NOTHING) 19 | note = models.CharField(max_length = 100) 20 | 21 | def __str__(self): 22 | return str(self.id) + self.product.name 23 | -------------------------------------------------------------------------------- /home/templates/includes/footer.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 |
4 | © Your Company HERE 5 |
6 | -------------------------------------------------------------------------------- /home/templates/includes/menu-list.html: -------------------------------------------------------------------------------- 1 | {% load i18n static admin_adminlte %} 2 | 3 | -------------------------------------------------------------------------------- /home/templates/pages/api-generator.html: -------------------------------------------------------------------------------- 1 | 2 | {% load static %} 3 | 4 | 5 | 6 | 7 | AdminLTE 3 | General UI 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 59 | 60 | 61 |
62 | 63 | {% include 'includes/navigation-light.html' %} 64 | 65 | 66 | 67 | {% include 'includes/sidebar.html' %} 68 | 69 | 70 |
71 | 72 |
73 |
74 |
75 |
76 |

77 | API Generator 78 |

79 |
80 |
81 | 85 |
86 |
87 |
88 |
89 | 90 | 91 |
92 |
93 | 94 |
95 |
96 |

97 | 98 | How to generate your own, secured api 99 |

100 |
101 |
102 | 103 |
104 |
105 |

106 |

    107 |
  • Open Home/models.py file
  • 108 |
  • Edit your models
  • 109 |
  • Migrate database
  • 110 |
  • Update Configuration (for new models) - API_GENERATOR section
  • 111 |
  • Access the API in the browser
  • 112 |
113 | Without any changes, the API is generated for two models: 114 | 122 |

123 |
124 |
125 |
126 | 127 | 130 |
131 | 132 | 133 | 134 | 135 |
136 |
137 | 138 |
139 | 140 |
141 |
142 | Version 3.2.0 143 |
144 | © AdminLTE.io - Coded by AppSeed 145 |
146 | 147 | 148 | 151 | 152 |
153 | 154 | 155 | 156 | {% include 'includes/scripts.html' %} 157 | 158 | 159 | -------------------------------------------------------------------------------- /home/templates/pages/dyn-datatb.html: -------------------------------------------------------------------------------- 1 | 2 | {% load static %} 3 | 4 | 5 | 6 | 7 | AdminLTE 3 | General UI 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 59 | 60 | 61 |
62 | 63 | {% include 'includes/navigation-light.html' %} 64 | 65 | 66 | 67 | {% include 'includes/sidebar.html' %} 68 | 69 | 70 |
71 | 72 |
73 |
74 |
75 |
76 |

77 | Dynamic DataTables 78 |

79 |
80 |
81 | 85 |
86 |
87 |
88 |
89 | 90 | 91 |
92 |
93 | 94 |
95 |
96 |

97 | 98 | How to generate your own dataTables 99 |

100 |
101 |
102 | 103 |
104 |
105 |

106 |

    107 |
  • Edit your models
  • 108 |
  • Migrate database
  • 109 |
  • Import "DataTB" object
  • 110 |
  • Link DataTB to a model: "DataTB(model_class_path="home.models.Product")"
  • 111 |
  • Render the widget: "ddt.render()"
  • 112 |
113 |

114 |

115 | Here is the result result for the Products Model. 116 |

117 |
118 |
119 | 120 |
121 |
122 | {{ data_table1 }} 123 |
124 |
125 | 126 |
127 | 128 | 131 |
132 | 133 | 134 | 135 | 136 |
137 |
138 | 139 |
140 | 141 |
142 |
143 | Version 3.2.0 144 |
145 | © AdminLTE.io - Coded by AppSeed 146 |
147 | 148 | 149 | 152 | 153 |
154 | 155 | 156 | 157 | {% include 'includes/scripts.html' %} 158 | 159 | 160 | -------------------------------------------------------------------------------- /home/templates/test-datatb.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | Dynamic Data Table 9 | 10 | 11 | 12 |

some content

13 |

some content

14 | {{ data_table1 }} 15 |

another contents

16 |

another contents

17 | 18 | -------------------------------------------------------------------------------- /home/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /home/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.index, name='index'), 7 | path('api-gen' , views.api_generator, name='api_generator'), 8 | path('dyn-datatb', views.dyn_datatb , name='dyn_datatb' ), 9 | ] 10 | -------------------------------------------------------------------------------- /home/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django_dyn_dt.datatb import DataTB 3 | from django.http import HttpResponse 4 | 5 | # Create your views here. 6 | 7 | def index(request): 8 | # Page from the theme 9 | return render(request, 'pages/index.html') 10 | 11 | def api_generator(request): 12 | return render(request, 'pages/api-generator.html') 13 | 14 | def dyn_datatb(request): 15 | 16 | context = {} 17 | 18 | ddt = DataTB(model_class_path="home.models.Product") 19 | context['data_table1'] = ddt.render() 20 | 21 | return render(request, 'pages/dyn-datatb.html', context=context) 22 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /media/test.postman_collection: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "5250852e-496a-4f71-9ad1-12269fe213d3", 4 | "name": "Django API GEN V2", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", 6 | "_exporter_id": "3902283" 7 | }, 8 | "item": [ 9 | { 10 | "name": "PRODUCTS Get ALL", 11 | "request": { 12 | "method": "GET", 13 | "header": [], 14 | "url": { 15 | "raw": "http://localhost:8000/api/product/", 16 | "protocol": "http", 17 | "host": [ 18 | "localhost" 19 | ], 20 | "port": "8000", 21 | "path": [ 22 | "api", 23 | "product", 24 | "" 25 | ] 26 | } 27 | }, 28 | "response": [] 29 | }, 30 | { 31 | "name": "PRODUCTS Get by ID", 32 | "request": { 33 | "method": "GET", 34 | "header": [], 35 | "url": { 36 | "raw": "http://localhost:8000/api/product/1/", 37 | "protocol": "http", 38 | "host": [ 39 | "localhost" 40 | ], 41 | "port": "8000", 42 | "path": [ 43 | "api", 44 | "product", 45 | "1", 46 | "" 47 | ] 48 | } 49 | }, 50 | "response": [] 51 | }, 52 | { 53 | "name": "PRODUCTS Create", 54 | "request": { 55 | "method": "POST", 56 | "header": [ 57 | { 58 | "key": "Authorization", 59 | "value": "token 81f198e3e5f85316e49d965fa4982bfd5439eb5b", 60 | "type": "text" 61 | } 62 | ], 63 | "body": { 64 | "mode": "formdata", 65 | "formdata": [ 66 | { 67 | "key": "name", 68 | "value": "Nike AIR", 69 | "type": "text" 70 | } 71 | ] 72 | }, 73 | "url": { 74 | "raw": "http://localhost:8000/api/product/", 75 | "protocol": "http", 76 | "host": [ 77 | "localhost" 78 | ], 79 | "port": "8000", 80 | "path": [ 81 | "api", 82 | "product", 83 | "" 84 | ] 85 | } 86 | }, 87 | "response": [] 88 | }, 89 | { 90 | "name": "PRODUCTS Update", 91 | "request": { 92 | "method": "PUT", 93 | "header": [ 94 | { 95 | "key": "Authorization", 96 | "value": "token 81f198e3e5f85316e49d965fa4982bfd5439eb5b", 97 | "type": "text" 98 | } 99 | ], 100 | "body": { 101 | "mode": "formdata", 102 | "formdata": [ 103 | { 104 | "key": "name", 105 | "value": "PUMA", 106 | "type": "text" 107 | } 108 | ] 109 | }, 110 | "url": { 111 | "raw": "http://localhost:8000/api/product/1/", 112 | "protocol": "http", 113 | "host": [ 114 | "localhost" 115 | ], 116 | "port": "8000", 117 | "path": [ 118 | "api", 119 | "product", 120 | "1", 121 | "" 122 | ] 123 | } 124 | }, 125 | "response": [] 126 | }, 127 | { 128 | "name": "PRODUCTS Delete", 129 | "request": { 130 | "method": "DELETE", 131 | "header": [ 132 | { 133 | "key": "Authorization", 134 | "value": "token 81f198e3e5f85316e49d965fa4982bfd5439eb5b", 135 | "type": "text" 136 | } 137 | ], 138 | "body": { 139 | "mode": "formdata", 140 | "formdata": [] 141 | }, 142 | "url": { 143 | "raw": "http://localhost:8000/api/product/2/", 144 | "protocol": "http", 145 | "host": [ 146 | "localhost" 147 | ], 148 | "port": "8000", 149 | "path": [ 150 | "api", 151 | "product", 152 | "2", 153 | "" 154 | ] 155 | } 156 | }, 157 | "response": [] 158 | }, 159 | { 160 | "name": "SALES Get ALL", 161 | "request": { 162 | "method": "GET", 163 | "header": [], 164 | "url": { 165 | "raw": "http://localhost:8000/api/sales/", 166 | "protocol": "http", 167 | "host": [ 168 | "localhost" 169 | ], 170 | "port": "8000", 171 | "path": [ 172 | "api", 173 | "sales", 174 | "" 175 | ] 176 | } 177 | }, 178 | "response": [] 179 | }, 180 | { 181 | "name": "SALES Create", 182 | "request": { 183 | "method": "POST", 184 | "header": [ 185 | { 186 | "key": "Authorization", 187 | "value": "token 81f198e3e5f85316e49d965fa4982bfd5439eb5b", 188 | "type": "text" 189 | } 190 | ], 191 | "body": { 192 | "mode": "formdata", 193 | "formdata": [ 194 | { 195 | "key": "note", 196 | "value": "PUMA Sale", 197 | "type": "text" 198 | }, 199 | { 200 | "key": "product", 201 | "value": "1", 202 | "type": "text" 203 | } 204 | ] 205 | }, 206 | "url": { 207 | "raw": "http://localhost:8000/api/sales/", 208 | "protocol": "http", 209 | "host": [ 210 | "localhost" 211 | ], 212 | "port": "8000", 213 | "path": [ 214 | "api", 215 | "sales", 216 | "" 217 | ] 218 | } 219 | }, 220 | "response": [] 221 | }, 222 | { 223 | "name": "SALES Update", 224 | "request": { 225 | "method": "PUT", 226 | "header": [ 227 | { 228 | "key": "Authorization", 229 | "value": "token 81f198e3e5f85316e49d965fa4982bfd5439eb5b", 230 | "type": "text" 231 | } 232 | ], 233 | "body": { 234 | "mode": "formdata", 235 | "formdata": [ 236 | { 237 | "key": "product", 238 | "value": "3", 239 | "type": "text" 240 | } 241 | ] 242 | }, 243 | "url": { 244 | "raw": "http://localhost:8000/api/sales/1/", 245 | "protocol": "http", 246 | "host": [ 247 | "localhost" 248 | ], 249 | "port": "8000", 250 | "path": [ 251 | "api", 252 | "sales", 253 | "1", 254 | "" 255 | ] 256 | } 257 | }, 258 | "response": [] 259 | }, 260 | { 261 | "name": "SALES Delete", 262 | "request": { 263 | "method": "DELETE", 264 | "header": [ 265 | { 266 | "key": "Authorization", 267 | "value": "token 81f198e3e5f85316e49d965fa4982bfd5439eb5b", 268 | "type": "text" 269 | } 270 | ], 271 | "body": { 272 | "mode": "formdata", 273 | "formdata": [] 274 | }, 275 | "url": { 276 | "raw": "http://localhost:8000/api/sales/2/", 277 | "protocol": "http", 278 | "host": [ 279 | "localhost" 280 | ], 281 | "port": "8000", 282 | "path": [ 283 | "api", 284 | "sales", 285 | "2", 286 | "" 287 | ] 288 | } 289 | }, 290 | "response": [] 291 | }, 292 | { 293 | "name": "Login", 294 | "request": { 295 | "method": "POST", 296 | "header": [], 297 | "body": { 298 | "mode": "formdata", 299 | "formdata": [ 300 | { 301 | "key": "username", 302 | "value": "admin", 303 | "type": "text" 304 | }, 305 | { 306 | "key": "password", 307 | "value": "Pass12__", 308 | "type": "text" 309 | } 310 | ] 311 | }, 312 | "url": { 313 | "raw": "http://localhost:8000/login/jwt/", 314 | "protocol": "http", 315 | "host": [ 316 | "localhost" 317 | ], 318 | "port": "8000", 319 | "path": [ 320 | "login", 321 | "jwt", 322 | "" 323 | ] 324 | } 325 | }, 326 | "response": [] 327 | } 328 | ], 329 | "event": [ 330 | { 331 | "listen": "prerequest", 332 | "script": { 333 | "type": "text/javascript", 334 | "exec": [ 335 | "" 336 | ] 337 | } 338 | }, 339 | { 340 | "listen": "test", 341 | "script": { 342 | "type": "text/javascript", 343 | "exec": [ 344 | "" 345 | ] 346 | } 347 | } 348 | ], 349 | "variable": [ 350 | { 351 | "key": "base_address", 352 | "value": "localhost:5085", 353 | "type": "string" 354 | } 355 | ] 356 | } -------------------------------------------------------------------------------- /nginx/appseed-app.conf: -------------------------------------------------------------------------------- 1 | upstream webapp { 2 | server appseed_app:5005; 3 | } 4 | 5 | server { 6 | listen 5085; 7 | server_name localhost; 8 | 9 | location / { 10 | proxy_pass http://webapp; 11 | proxy_set_header Host $host; 12 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /render.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | - type: web 3 | name: django-dynamic-services 4 | plan: starter 5 | env: python 6 | region: frankfurt # region should be same as your database region. 7 | buildCommand: "./build.sh" 8 | startCommand: "gunicorn core.wsgi:application" 9 | envVars: 10 | - key: DEBUG 11 | value: False 12 | - key: SECRET_KEY 13 | generateValue: true 14 | - key: WEB_CONCURRENCY 15 | value: 4 16 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | django 2 | gunicorn 3 | python-dotenv 4 | whitenoise 5 | django-admin-adminlte==1.0.5 6 | 7 | # Dbms 8 | # psycopg2-binary 9 | # mysqlclient 10 | 11 | # API Generator 12 | #git+https://github.com/app-generator/django-api-generator.git 13 | django-api-generator==1.0.10 14 | 15 | # Dynamic Datatables 16 | #git+https://github.com/app-generator/django-dynamic-datatb.git 17 | django-dynamic-datatb==1.0.22 18 | 19 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/app-generator/django-dynamic-services/b3b95ae4334dbc6621a2d6d2cdeabab913f8d765/static/.gitkeep --------------------------------------------------------------------------------