├── apps ├── __init__.py └── common │ ├── __init__.py │ ├── migrations │ └── __init__.py │ ├── tests.py │ ├── admin.py │ ├── apps.py │ ├── urls.py │ ├── models.py │ └── views.py ├── core ├── settings │ ├── __init__.py │ ├── __pycache__ │ │ ├── base.cpython-312.pyc │ │ ├── __init__.cpython-312.pyc │ │ └── develop.cpython-312.pyc │ ├── develop.py │ ├── production.py │ ├── base.py │ └── jazzmin.py ├── __pycache__ │ ├── urls.cpython-312.pyc │ ├── wsgi.cpython-312.pyc │ ├── celery.cpython-312.pyc │ ├── schema.cpython-312.pyc │ ├── __init__.cpython-312.pyc │ └── generator.cpython-312.pyc ├── __init__.py ├── generator.py ├── asgi.py ├── wsgi.py ├── celery.py ├── urls.py └── schema.py ├── requirements ├── develop.txt ├── production.txt └── base.txt ├── .gitignore ├── .env.example ├── manage.py └── README.md /apps/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/common/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/settings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/common/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/common/tests.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /requirements/develop.txt: -------------------------------------------------------------------------------- 1 | -r base.txt 2 | 3 | 4 | -------------------------------------------------------------------------------- /requirements/production.txt: -------------------------------------------------------------------------------- 1 | -r base.txt 2 | 3 | gunicorn 4 | -------------------------------------------------------------------------------- /apps/common/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | 4 | -------------------------------------------------------------------------------- /core/__pycache__/urls.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abduvalimurodullayev1/boilerplate_Drf/HEAD/core/__pycache__/urls.cpython-312.pyc -------------------------------------------------------------------------------- /core/__pycache__/wsgi.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abduvalimurodullayev1/boilerplate_Drf/HEAD/core/__pycache__/wsgi.cpython-312.pyc -------------------------------------------------------------------------------- /core/__pycache__/celery.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abduvalimurodullayev1/boilerplate_Drf/HEAD/core/__pycache__/celery.cpython-312.pyc -------------------------------------------------------------------------------- /core/__pycache__/schema.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abduvalimurodullayev1/boilerplate_Drf/HEAD/core/__pycache__/schema.cpython-312.pyc -------------------------------------------------------------------------------- /core/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abduvalimurodullayev1/boilerplate_Drf/HEAD/core/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /core/__pycache__/generator.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abduvalimurodullayev1/boilerplate_Drf/HEAD/core/__pycache__/generator.cpython-312.pyc -------------------------------------------------------------------------------- /core/settings/__pycache__/base.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abduvalimurodullayev1/boilerplate_Drf/HEAD/core/settings/__pycache__/base.cpython-312.pyc -------------------------------------------------------------------------------- /core/settings/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abduvalimurodullayev1/boilerplate_Drf/HEAD/core/settings/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /core/settings/__pycache__/develop.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abduvalimurodullayev1/boilerplate_Drf/HEAD/core/settings/__pycache__/develop.cpython-312.pyc -------------------------------------------------------------------------------- /apps/common/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CommonConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "apps.common" 7 | -------------------------------------------------------------------------------- /core/__init__.py: -------------------------------------------------------------------------------- 1 | # This will make sure the app is always imported when 2 | # Django starts so that shared_task will use this app. 3 | from .celery import app as celery_app 4 | 5 | __all__ = ("celery_app",) 6 | -------------------------------------------------------------------------------- /apps/common/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from apps.common.views import health_check 4 | 5 | app_name = "common" 6 | 7 | urlpatterns = [ 8 | 9 | path("health-check/redis/", health_check, name="health-check-redis"), 10 | ] 11 | -------------------------------------------------------------------------------- /core/settings/develop.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | 3 | DEBUG = True 4 | 5 | CORS_ALLOW_ALL_ORIGINS = True 6 | CORS_ALLOW_CREDENTIALS = True 7 | 8 | CSRF_TRUSTED_ORIGINS = ["http://localhost:8000", "http://127.0.0.1:8000"] 9 | 10 | CELERY_TASK_ALWAYS_EAGER = False 11 | 12 | INTERNAL_IPS = ["127.0.0.1"] 13 | 14 | 15 | ALLOWED_HOSTS = ["*"] 16 | -------------------------------------------------------------------------------- /apps/common/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.utils.translation import gettext_lazy as _ 3 | 4 | 5 | class BaseModel(models.Model): 6 | created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created at")) 7 | updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated at")) 8 | 9 | class Meta: 10 | abstract = True 11 | -------------------------------------------------------------------------------- /core/generator.py: -------------------------------------------------------------------------------- 1 | from drf_yasg.generators import OpenAPISchemaGenerator 2 | 3 | 4 | class BothHttpAndHttpsSchemaGenerator(OpenAPISchemaGenerator): 5 | def get_schema(self, request=None, public=False): 6 | schema = super().get_schema(request, public) 7 | if request and request.is_secure(): 8 | schema.schemes = ["https", "http"] 9 | else: 10 | schema.schemes = ["http", "https"] 11 | 12 | return schema 13 | -------------------------------------------------------------------------------- /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 | 11 | import os 12 | 13 | from django.core.asgi import get_asgi_application 14 | 15 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings") 16 | 17 | application = get_asgi_application() 18 | -------------------------------------------------------------------------------- /requirements/base.txt: -------------------------------------------------------------------------------- 1 | Django==5.1.3 2 | psycopg2-binary==2.9.10 3 | pillow==11.0.0 4 | 5 | python-environ==0.4.54 6 | django-cors-headers==4.6.0 7 | drf-yasg== 1.21.8 8 | djangorestframework==3.15.2 9 | django-filter==24.3 10 | django-modeltranslation== 0.19.10 11 | django-redis== 5.4.0 12 | cryptography==41.0.7 13 | celery==5.3.6 14 | redis==5.0.1 15 | flower==2.0.1 16 | django-jazzmin==3.0.1 17 | django-debug-toolbar==4.4.6 18 | djangorestframework-simplejwt==5.3.1 19 | -------------------------------------------------------------------------------- /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.production") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | __pycache__/ 3 | *.py[cod] 4 | *.pyo 5 | *.pyd 6 | *.so 7 | *.egg-info/ 8 | dist/ 9 | build/ 10 | 11 | # Virtual environment 12 | .venv/ 13 | venv/ 14 | env/ 15 | 16 | # Django Static / Media 17 | staticfiles/ 18 | media/ 19 | 20 | # IDE/Editor 21 | .idea/ 22 | .vscode/ 23 | *.swp 24 | 25 | # OS system files 26 | .DS_Store 27 | Thumbs.db 28 | 29 | # Env files and secrets 30 | .env 31 | .env.* 32 | *.secret 33 | 34 | 35 | *.log 36 | 37 | # Docker 38 | docker-compose.override.yml 39 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | PROJECT_NAME=NEW_PROJECT 2 | 3 | # Django 4 | SECRET_KEY= 5 | DEBUG= 6 | DJANGO_SETTINGS_MODULE=core.settings.development 7 | 8 | # Database 9 | DB_ENGINE=django.db.backends.postgresql 10 | DB_NAME= 11 | DB_USER= 12 | DB_PASSWORD= 13 | DB_HOST= 14 | DB_PORT= 15 | 16 | # Redis / Celery 17 | REDIS_URL=redis://127.0.0.1:6379/0 18 | CELERY_BROKER_URL=redis://127.0.0.1:6379/0 19 | REDIS_HOST= 20 | REDIS_PORT= 21 | REDIS_DB= 22 | 23 | # App Public URL 24 | BASE_URL= 25 | 26 | # AES Key 27 | AES_KEY= 28 | 29 | # Email 30 | EMAIL_USE_TLS=True 31 | EMAIL_HOST= 32 | EMAIL_PORT= 33 | EMAIL_HOST_USER= 34 | EMAIL_HOST_PASSWORD= 35 | 36 | -------------------------------------------------------------------------------- /core/celery.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from celery import Celery 4 | 5 | # Set the default Django settings module for the 'celery' program. 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings.base") 7 | 8 | app = Celery("core") 9 | 10 | # Using a string here means the worker doesn't have to serialize 11 | # the configuration object to child processes. 12 | # - namespace='CELERY' means all celery-related configuration keys 13 | # should have a `CELERY_` prefix. 14 | app.config_from_object("django.conf:settings", namespace="CELERY") 15 | 16 | # Load task modules from all registered Django apps. 17 | app.autodiscover_tasks() 18 | -------------------------------------------------------------------------------- /core/settings/production.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | 3 | DEBUG = False 4 | 5 | USE_X_FORWARDED_HOST = True 6 | SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") 7 | 8 | CSRF_COOKIE_SECURE = True 9 | SESSION_COOKIE_SECURE = True 10 | SECURE_SSL_REDIRECT = True 11 | 12 | SECURE_HSTS_SECONDS = 3600 13 | SECURE_HSTS_INCLUDE_SUBDOMAINS = True 14 | SECURE_HSTS_PRELOAD = True 15 | SECURE_CONTENT_TYPE_NOSNIFF = True 16 | 17 | CORS_ALLOW_CREDENTIALS = True 18 | CORS_ALLOWED_ORIGINS = env.list("CORS_ALLOWED_ORIGINS", default=[]) 19 | 20 | CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[]) 21 | 22 | REDIS_HOST = env.str("REDIS_HOST", default="localhost") 23 | REDIS_PORT = env.int("REDIS_PORT", default=6379) 24 | REDIS_DB = env.int("REDIS_DB", default=0) 25 | -------------------------------------------------------------------------------- /apps/common/views.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from django.db import connection 4 | from rest_framework.decorators import api_view 5 | from rest_framework.response import Response 6 | from django.conf import settings 7 | import redis 8 | 9 | @api_view(["GET"]) 10 | def health_check(request): 11 | # Check DB 12 | try: 13 | connection.cursor() 14 | except Exception: 15 | return Response({"status": "error", "service": "db"}, status=500) 16 | 17 | # Check Redis 18 | try: 19 | redis.StrictRedis( 20 | host=settings.REDIS_HOST, 21 | port=settings.REDIS_PORT, 22 | db=settings.REDIS_DB, 23 | ).ping() 24 | except Exception: 25 | return Response({"status": "error", "service": "redis"}, status=500) 26 | 27 | return Response({"status": "ok"}, status=200) 28 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | from pathlib import Path 6 | 7 | import environ 8 | 9 | 10 | def main(): 11 | """Run administrative tasks.""" 12 | base_dir = Path(__file__).resolve().parent 13 | environ.Env().read_env(os.path.join(base_dir, ".env")) 14 | 15 | """ 16 | change settings module to core.settings.test when running tests 17 | """ 18 | 19 | try: 20 | from django.core.management import execute_from_command_line 21 | except ImportError as exc: 22 | raise ImportError( 23 | "Couldn't import Django. Are you sure it's installed and " 24 | "available on your PYTHONPATH environment variable? Did you " 25 | "forget to activate a virtual environment?" 26 | ) from exc 27 | execute_from_command_line(sys.argv) 28 | 29 | 30 | if __name__ == "__main__": 31 | main() 32 | -------------------------------------------------------------------------------- /core/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.conf.urls.static import static 3 | from django.contrib import admin 4 | from django.urls import include, path 5 | from django.views.i18n import set_language 6 | 7 | from .schema import swagger_urlpatterns 8 | 9 | urlpatterns = [ 10 | path("admin/", admin.site.urls), 11 | path("api/v1/common/", include("apps.common.urls", namespace="common")), 12 | path("rest_framework/", include("rest_framework.urls", namespace="rest_framework")), 13 | path('i18n/setlang/', set_language, name='set_language'), # set_language URL 14 | 15 | ] 16 | 17 | if settings.DEBUG: 18 | import debug_toolbar 19 | urlpatterns += [ 20 | path('__debug__/', include(debug_toolbar.urls)), 21 | ] + swagger_urlpatterns 22 | 23 | # Static va media fayllar uchun marshrutlar 24 | if settings.DEBUG: 25 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 26 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 27 | -------------------------------------------------------------------------------- /core/schema.py: -------------------------------------------------------------------------------- 1 | from django.urls import re_path 2 | from drf_yasg import openapi 3 | from drf_yasg.views import get_schema_view 4 | from rest_framework import permissions 5 | 6 | from .generator import BothHttpAndHttpsSchemaGenerator 7 | 8 | schema_view = get_schema_view( 9 | openapi.Info( 10 | title="BoilerPlate API", 11 | default_version="v1", 12 | description="This is the django boilerplate", 13 | terms_of_service="https://www.google.com/policies/terms/", 14 | contact=openapi.Contact(email="info@uic.group"), 15 | license=openapi.License(name="BSD License"), 16 | ), 17 | public=True, 18 | generator_class=BothHttpAndHttpsSchemaGenerator, 19 | permission_classes=(permissions.AllowAny,), 20 | ) 21 | swagger_urlpatterns = [ 22 | re_path( 23 | r"^swagger(?P\.json|\.yaml)$", 24 | schema_view.without_ui(cache_timeout=0), 25 | name="schema-json", 26 | ), 27 | re_path( 28 | r"^swagger/$", 29 | schema_view.with_ui("swagger", cache_timeout=0), 30 | name="schema-swagger-ui", 31 | ), 32 | re_path( 33 | r"^redoc/$", 34 | schema_view.with_ui("redoc", cache_timeout=0), 35 | name="schema-redoc", 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /core/settings/base.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | import environ 4 | 5 | BASE_DIR = Path(__file__).resolve().parent.parent.parent 6 | 7 | # Load .env 8 | env = environ.Env() 9 | env.read_env(os.path.join(BASE_DIR, ".env")) 10 | 11 | # Security 12 | SECRET_KEY = env.str("SECRET_KEY", "supersecret") 13 | DEBUG = env.bool("DEBUG", default=True) 14 | ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=["*"]) 15 | 16 | # Apps 17 | INSTALLED_APPS = [ 18 | "django.contrib.admin", 19 | "django.contrib.auth", 20 | "django.contrib.contenttypes", 21 | "django.contrib.sessions", 22 | "django.contrib.messages", 23 | "django.contrib.staticfiles", 24 | 25 | # Your apps 26 | "apps.common", 27 | 28 | # Third-party 29 | "rest_framework", 30 | ] 31 | 32 | # Middleware 33 | MIDDLEWARE = [ 34 | "django.middleware.security.SecurityMiddleware", 35 | "django.contrib.sessions.middleware.SessionMiddleware", 36 | "corsheaders.middleware.CorsMiddleware", 37 | "django.middleware.common.CommonMiddleware", 38 | "django.middleware.csrf.CsrfViewMiddleware", 39 | "django.contrib.auth.middleware.AuthenticationMiddleware", 40 | "django.contrib.messages.middleware.MessageMiddleware", 41 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 42 | ] 43 | 44 | # URLs and templates 45 | ROOT_URLCONF = "core.urls" 46 | TEMPLATES = [ 47 | { 48 | "BACKEND": "django.template.backends.django.DjangoTemplates", 49 | "DIRS": [BASE_DIR / "templates"], 50 | "APP_DIRS": True, 51 | "OPTIONS": { 52 | "context_processors": [ 53 | "django.template.context_processors.request", 54 | "django.contrib.auth.context_processors.auth", 55 | "django.contrib.messages.context_processors.messages", 56 | ], 57 | }, 58 | }, 59 | ] 60 | 61 | WSGI_APPLICATION = "core.wsgi.application" 62 | 63 | # Database 64 | DATABASES = { 65 | "default": { 66 | "ENGINE": env.str("DB_ENGINE", "django.db.backends.sqlite3"), 67 | "NAME": env.str("DB_NAME", BASE_DIR / "db.sqlite3"), 68 | "USER": env.str("DB_USER", ""), 69 | "PASSWORD": env.str("DB_PASSWORD", ""), 70 | "HOST": env.str("DB_HOST", "localhost"), 71 | "PORT": env.str("DB_PORT", "5432"), 72 | } 73 | } 74 | 75 | # Password validation 76 | AUTH_PASSWORD_VALIDATORS = [ 77 | {"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"}, 78 | {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, 79 | {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, 80 | {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"}, 81 | ] 82 | 83 | # Internationalization 84 | LANGUAGE_CODE = "en-us" 85 | TIME_ZONE = "Asia/Tashkent" 86 | USE_I18N = True 87 | USE_TZ = True 88 | 89 | # Static / Media 90 | STATIC_URL = "static/" 91 | STATIC_ROOT = BASE_DIR / "static" 92 | MEDIA_URL = "media/" 93 | MEDIA_ROOT = BASE_DIR / "media" 94 | 95 | # Default primary key 96 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" 97 | 98 | # DRF 99 | REST_FRAMEWORK = { 100 | "DEFAULT_AUTHENTICATION_CLASSES": ( 101 | "rest_framework.authentication.SessionAuthentication", 102 | "rest_framework_simplejwt.authentication.JWTAuthentication", 103 | ), 104 | "DEFAULT_FILTER_BACKENDS": ( 105 | "django_filters.rest_framework.DjangoFilterBackend", 106 | ), 107 | "DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",), 108 | "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination", 109 | "PAGE_SIZE": 10, 110 | } 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Django-Boilerplate 2 | 3 | Django-Boilerplate for start a new project 4 | 5 | 6 | ## How to set up project (with Docker) 7 | 8 | Give permission to docker script: ```chmod +x ./docker-compose``` 9 | Give permission to docker script: ```chmod +x entrypoint.dev.sh``` 10 | 11 | ### Docker compose file 12 | 13 | Build and docker up containers: ```docker-compose -f docker-compose.dev.yml up -d --build``` 14 | 15 | ### Use docker-compose file 16 | 17 | ```./docker-compose makemigrations``` 18 | or ```docker-compose -f docker-compose.dev.yml exec web python manage.py makemigrations``` 19 | 20 | ## How to run project locally bash script (Linux, Mac) 21 | 22 | ### install requirements 23 | 24 | ```bash 25 | python3 -m venv env 26 | source env/bin/activate 27 | pip install -r requirements/develop.text 28 | ``` 29 | 30 | ### create .env file 31 | 32 | ```bash 33 | cp .env.example .env 34 | ``` 35 | 36 | ### create database 37 | 38 | ```bash 39 | sudo -u postgres psql 40 | CREATE DATABASE django_boilerplate; 41 | CREATE USER django_boilerplate WITH PASSWORD 'django_boilerplate'; 42 | ALTER ROLE django_boilerplate SET client_encoding TO 'utf8'; 43 | ALTER ROLE django_boilerplate SET default_transaction_isolation TO 'read committed'; 44 | ALTER ROLE django_boilerplate SET timezone TO 'UTC'; 45 | GRANT ALL PRIVILEGES ON DATABASE django_boilerplate TO django_boilerplate; 46 | \q 47 | ``` 48 | 49 | ### set up .env file with your database credentials 50 | 51 | ```bash 52 | nano .env 53 | ``` 54 | 55 | ### run migrations 56 | 57 | ```bash 58 | python manage.py migrate 59 | ``` 60 | 61 | ### run server 62 | 63 | ```bash 64 | python manage.py runserver 65 | ``` 66 | 67 | ## Pre-commit must be installed for all projects 68 | 69 | ```bash 70 | pip install pre-commit 71 | pre-commit install 72 | ``` 73 | 74 | # Back-End Checklist 75 | 76 | ## 1. Environment Configuration: 77 | 78 | - [ ] Ensure that the Django project settings are properly configured for the production environment. 79 | - [ ] Set `DEBUG` to `False` in the production settings (`settings.py`). 80 | - [ ] Verify that the `ALLOWED_HOSTS` setting includes the production domain names or IP addresses. 81 | 82 | ## 2. Security: 83 | 84 | - [ ] Secure sensitive data, such as secret keys and database credentials, by storing them in environment variables or a 85 | secure secrets management system. 86 | - [ ] Implement Cross-Site Request Forgery (CSRF) protection. 87 | - [ ] Require Google reCAPTCHA in login forms. 88 | 89 | ## 3. Database: 90 | 91 | - [ ] Ensure all migrations are correctly created. 92 | - [ ] Optimize database queries for performance. 93 | 94 | ## 4. Static and Media Files: 95 | 96 | - [ ] Collect and compress static files using `collectstatic` and configure their storage. 97 | - [ ] Handle user-uploaded media files securely and efficiently. 98 | - [ ] Compress media files. 99 | - [ ] Create media models for all media. 100 | 101 | ## 5. Logging and Monitoring: 102 | 103 | - [ ] Implement monitoring and alerting using tools like Prometheus, Grafana, Flower, and Sentry (must-have). 104 | 105 | ## 6. Performance Optimization: 106 | 107 | - [ ] Profile and optimize database queries, views, and templates for performance (use `DEBUGTOOLBAR`, `django-silk`). 108 | - [ ] Implement caching mechanisms for frequently accessed data (depends on project). 109 | - [ ] Configure web server settings, such as Gunicorn or Uvicorn, for optimal performance. 110 | 111 | ## 7. Testing: 112 | 113 | - [ ] Conduct integration tests and unit tests. 114 | - [ ] Set up a staging environment that closely mirrors the production environment for testing purposes (if needed). 115 | 116 | ## 8. Documentation: 117 | 118 | - [ ] Ensure that the codebase is well-documented, including comments and docstrings, and add API documentation to 119 | Swagger. 120 | 121 | ## 9. Common Coding Requirements: 122 | 123 | - [ ] Implement pre-commit hooks. 124 | - [ ] Use appropriate branches like `dev` and `master`. 125 | - [ ] Add search history API. 126 | - [ ] Implement initial notifications. 127 | - [ ] Provide a complete README (including deployment and project setup guide). 128 | - [ ] Use Docker for production deployment. 129 | 130 | ## 10. Testing the Production Environment: 131 | 132 | - [ ] Conduct load testing to ensure the application can handle expected traffic volumes (using Locust.io). 133 | -------------------------------------------------------------------------------- /core/settings/jazzmin.py: -------------------------------------------------------------------------------- 1 | JAZZMIN_SETTINGS = { 2 | "site_title": "BoilerPlateAdmin", 3 | 4 | "site_header": "BoilerPlate", 5 | 6 | "site_brand": "BoilerPlateAdmin", 7 | 8 | "site_logo": "images/razrabotchik.png", 9 | 10 | "login_logo": 'images/razrabotchik.png', 11 | 12 | "login_logo_dark": True, 13 | 14 | "site_logo_classes": "images-circle", 15 | 16 | "site_icon": 'images/razrabotchik.png', 17 | 18 | "welcome_sign": "Welcome to the BoilerPlateAdmin", 19 | 20 | "copyright": "Acme Library Ltd", 21 | 22 | "search_model": ["auth.User", "auth.Group"], 23 | 24 | "user_avatar": None, 25 | 26 | "topmenu_links": [ 27 | 28 | {"name": "Home", "url": "admin:index", "permissions": ["auth.view_user"]}, 29 | 30 | {"name": "Support", "url": "https://github.com/farridav/django-jazzmin/issues", "new_window": True}, 31 | 32 | {"model": "auth.User"}, 33 | {"app": "books"}, 34 | ], 35 | 36 | "usermenu_links": [ 37 | {"name": "Support", "url": "https://github.com/farridav/django-jazzmin/issues", "new_window": True}, 38 | {"model": "auth.user"} 39 | ], 40 | 41 | "show_sidebar": True, 42 | 43 | "navigation_expanded": True, 44 | 45 | "hide_apps": [], 46 | 47 | "hide_models": [], 48 | 49 | "order_with_respect_to": ["auth", "books", "books.author", "books.book"], 50 | "custom_links": { 51 | "books": [{ 52 | "name": "Make Messages", 53 | "url": "make_messages", 54 | "icon": "fas fa-comments", 55 | "permissions": ["books.view_book"] 56 | }] 57 | }, 58 | 59 | "icons": { 60 | "auth": "fas fa-users-cog", 61 | "auth.user": "fas fa-user", 62 | "auth.Group": "fas fa-users", 63 | }, 64 | "default_icon_parents": "fas fa-chevron-circle-right", 65 | "default_icon_children": "fas fa-circle", 66 | "related_modal_active": False, 67 | 68 | "custom_css": None, 69 | "custom_js": None, 70 | "use_google_fonts_cdn": True, 71 | "show_ui_builder": False, 72 | 73 | "changeform_format": "horizontal_tabs", 74 | "changeform_format_overrides": {"auth.user": "collapsible", "auth.group": "vertical_tabs"}, 75 | "language_chooser": True, 76 | } 77 | 78 | JAZZMIN_UI_TWEAKS = { 79 | "dark_mode_theme": "darkly", 80 | "theme": "darkly", 81 | "custom_css": "common/css/main.css", 82 | "custom_js": "common/js/main.js" 83 | 84 | } 85 | 86 | import copy 87 | import logging 88 | from typing import Any, Dict 89 | 90 | from django.conf import settings 91 | from django.templatetags.static import static 92 | 93 | # from django.utils import get_admin_url, get_model_meta 94 | 95 | logger = logging.getLogger(__name__) 96 | 97 | DEFAULT_SETTINGS: Dict[str, Any] = { 98 | # title of the window (Will default to current_admin_site.site_title) 99 | "site_title": None, 100 | # Title on the login screen (19 chars max) (will default to current_admin_site.site_header) 101 | "site_header": None, 102 | # Title on the brand (19 chars max) (will default to current_admin_site.site_header) 103 | "site_brand": None, 104 | # Relative path to logo for your site, used for brand on top left (must be present in static files) 105 | "site_logo": "vendor/adminlte/img/AdminLTELogo.png", 106 | # Relative path to logo for your site, used for login logo (must be present in static files. Defaults to site_logo) 107 | "login_logo": 'images/razrabotchik.png', 108 | # Logo to use for login form in dark themes (must be present in static files. Defaults to login_logo) 109 | "login_logo_dark": None, 110 | "site_logo_classes": "img-circle", 111 | "site_icon": None, 112 | # Welcome text on the login screen 113 | "welcome_sign": "Welcome", 114 | # Copyright on the footer 115 | "copyright": "", 116 | # The model admin to search from the search bar, search bar omitted if excluded 117 | "search_model": None, 118 | # Field name on user model that contains avatar ImageField/URLField/Charfield or a callable that receives the user 119 | "user_avatar": None, 120 | ############ 121 | # Top Menu # 122 | ############ 123 | # Links to put along the nav bar 124 | "topmenu_links": [], 125 | ############# 126 | # User Menu # 127 | ############# 128 | # Additional links to include in the user menu on the top right ('app' url type is not allowed) 129 | "usermenu_links": [], 130 | ############# 131 | # Side Menu # 132 | ############# 133 | # Whether to display the side menu 134 | "show_sidebar": True, 135 | # Whether to aut expand the menu 136 | "navigation_expanded": True, 137 | # Hide these apps when generating side menu e.g (auth) 138 | "hide_apps": [], 139 | # Hide these models when generating side menu (e.g auth.user) 140 | "hide_models": [], 141 | # List of apps to base side menu ordering off of 142 | "order_with_respect_to": [], 143 | # Custom links to append to side menu app groups, keyed on app name 144 | "custom_links": {}, 145 | # Custom icons for side menu apps/models See the link below 146 | # https://fontawesome.com/icons?d=gallery&m=free&v=5.0.0,5.0.1,5.0.10,5.0.11,5.0.12,5.0.13,5.0.2,5.0.3,5.0.4,5.0.5,5.0.6,5.0.7,5.0.8,5.0.9,5.1.0, 147 | # 5.1.1,5.2.0,5.3.0,5.3.1,5.4.0,5.4.1,5.4.2,5.13.0,5.12.0, 148 | # 5.11.2,5.11.1,5.10.0,5.9.0,5.8.2,5.8.1,5.7.2,5.7.1,5.7.0,5.6.3,5.5.0,5.4.2 149 | # for the full list of 5.13.0 free icon classes 150 | "icons": {"auth": "fas fa-users-cog", "auth.user": "fas fa-user", "auth.Group": "fas fa-users"}, 151 | # Icons that are used when one is not manually specified 152 | "default_icon_parents": "fas fa-chevron-circle-right", 153 | "default_icon_children": "fas fa-circle", 154 | ################# 155 | # Related Modal # 156 | ################# 157 | # Activate Bootstrap modal 158 | "related_modal_active": False, 159 | ############# 160 | # UI Tweaks # 161 | ############# 162 | # Relative paths to custom CSS/JS scripts (must be present in static files) 163 | "custom_css": None, 164 | "custom_js": None, 165 | # Whether to link font from fonts.googleapis.com (use custom_css to supply font otherwise) 166 | "use_google_fonts_cdn": True, 167 | # Whether to show the UI customizer on the sidebar 168 | "show_ui_builder": False, 169 | ############### 170 | # Change view # 171 | ############### 172 | # Render out the change view as a single form, or in tabs, current options are 173 | # - single 174 | # - horizontal_tabs (default) 175 | # - vertical_tabs 176 | # - collapsible 177 | # - carousel 178 | "changeform_format": "horizontal_tabs", 179 | # override change forms on a per modeladmin basis 180 | "changeform_format_overrides": {}, 181 | # Add a language dropdown into the admin 182 | "language_chooser": False, 183 | } 184 | 185 | DEFAULT_UI_TWEAKS: Dict[str, Any] = { 186 | # Small text on the top navbar 187 | "navbar_small_text": False, 188 | # Small text on the footer 189 | "footer_small_text": False, 190 | # Small text everywhere 191 | "body_small_text": False, 192 | "brand_small_text": False, 193 | "brand_colour": False, 194 | "accent": "accent-primary", 195 | "navbar": "navbar-white navbar-light", 196 | "no_navbar_border": False, 197 | "navbar_fixed": False, 198 | "layout_boxed": False, 199 | "footer_fixed": False, 200 | "sidebar_fixed": False, 201 | "sidebar": "sidebar-dark-primary", 202 | "sidebar_nav_small_text": False, 203 | "sidebar_disable_expand": False, 204 | # Indent child menu items on sidebar 205 | "sidebar_nav_child_indent": False, 206 | # Use a compact sidebar 207 | "sidebar_nav_compact_style": False, 208 | # Use the AdminLTE2 style sidebar 209 | "sidebar_nav_legacy_style": False, 210 | # Use a flat style sidebar 211 | "sidebar_nav_flat_style": False, 212 | # Bootstrap theme to use (default, or from bootswatch, see THEMES below) 213 | "theme": "default", 214 | # Theme to use instead if the user has opted for dark mode (e.g darkly/cyborg/slate/solar/superhero) 215 | "dark_mode_theme": None, 216 | # The classes/styles to use with buttons 217 | "button_classes": { 218 | "primary": "btn-primary", 219 | "secondary": "btn-secondary", 220 | "info": "btn-info", 221 | "warning": "btn-warning", 222 | "danger": "btn-danger", 223 | "success": "btn-success", 224 | }, 225 | } 226 | 227 | THEMES = { 228 | # light themes 229 | "default": "vendor/bootswatch/default/bootstrap.min.css", 230 | "cerulean": "vendor/bootswatch/cerulean/bootstrap.min.css", 231 | "cosmo": "vendor/bootswatch/cosmo/bootstrap.min.css", 232 | "flatly": "vendor/bootswatch/flatly/bootstrap.min.css", 233 | "journal": "vendor/bootswatch/journal/bootstrap.min.css", 234 | "litera": "vendor/bootswatch/litera/bootstrap.min.css", 235 | "lumen": "vendor/bootswatch/lumen/bootstrap.min.css", 236 | "lux": "vendor/bootswatch/lux/bootstrap.min.css", 237 | "materia": "vendor/bootswatch/materia/bootstrap.min.css", 238 | "minty": "vendor/bootswatch/minty/bootstrap.min.css", 239 | "pulse": "vendor/bootswatch/pulse/bootstrap.min.css", 240 | "sandstone": "vendor/bootswatch/sandstone/bootstrap.min.css", 241 | "simplex": "vendor/bootswatch/simplex/bootstrap.min.css", 242 | "sketchy": "vendor/bootswatch/sketchy/bootstrap.min.css", 243 | "spacelab": "vendor/bootswatch/spacelab/bootstrap.min.css", 244 | "united": "vendor/bootswatch/united/bootstrap.min.css", 245 | "yeti": "vendor/bootswatch/yeti/bootstrap.min.css", 246 | # dark themes 247 | "darkly": "vendor/bootswatch/darkly/bootstrap.min.css", 248 | "cyborg": "vendor/bootswatch/cyborg/bootstrap.min.css", 249 | "slate": "vendor/bootswatch/slate/bootstrap.min.css", 250 | "solar": "vendor/bootswatch/solar/bootstrap.min.css", 251 | "superhero": "vendor/bootswatch/superhero/bootstrap.min.css", 252 | } 253 | 254 | DARK_THEMES = ("darkly", "cyborg", "slate", "solar", "superhero") 255 | 256 | CHANGEFORM_TEMPLATES = { 257 | "single": "jazzmin/includes/single.html", 258 | "carousel": "jazzmin/includes/carousel.html", 259 | "collapsible": "jazzmin/includes/collapsible.html", 260 | "horizontal_tabs": "jazzmin/includes/horizontal_tabs.html", 261 | "vertical_tabs": "jazzmin/includes/vertical_tabs.html", 262 | } 263 | 264 | 265 | def get_search_model_string(search_model: str) -> str: 266 | """ 267 | Get a search model string for reversing an admin url. 268 | 269 | Ensure the model name is lower cased but remain the app name untouched. 270 | """ 271 | 272 | app, model_name = search_model.split(".") 273 | return "{app}.{model_name}".format(app=app, model_name=model_name.lower()) 274 | 275 | 276 | def get_ui_tweaks() -> Dict: 277 | raw_tweaks = copy.deepcopy(DEFAULT_UI_TWEAKS) 278 | raw_tweaks.update(getattr(settings, "JAZZMIN_UI_TWEAKS", {})) 279 | tweaks = {x: y for x, y in raw_tweaks.items() if y not in (None, "", False)} 280 | 281 | # These options dont work well together 282 | if tweaks.get("layout_boxed"): 283 | tweaks.pop("navbar_fixed", None) 284 | tweaks.pop("footer_fixed", None) 285 | 286 | bool_map = { 287 | "navbar_small_text": "text-sm", 288 | "footer_small_text": "text-sm", 289 | "body_small_text": "text-sm", 290 | "brand_small_text": "text-sm", 291 | "sidebar_nav_small_text": "text-sm", 292 | "no_navbar_border": "border-bottom-0", 293 | "sidebar_disable_expand": "sidebar-no-expand", 294 | "sidebar_nav_child_indent": "nav-child-indent", 295 | "sidebar_nav_compact_style": "nav-compact", 296 | "sidebar_nav_legacy_style": "nav-legacy", 297 | "sidebar_nav_flat_style": "nav-flat", 298 | "layout_boxed": "layout-boxed", 299 | "sidebar_fixed": "layout-fixed", 300 | "navbar_fixed": "layout-navbar-fixed", 301 | "footer_fixed": "layout-footer-fixed", 302 | "actions_sticky_top": "sticky-top", 303 | } 304 | 305 | for key, value in bool_map.items(): 306 | if key in tweaks: 307 | tweaks[key] = value 308 | 309 | def classes(*args: str) -> str: 310 | return " ".join([tweaks.get(arg, "") for arg in args]).strip() 311 | 312 | theme = tweaks["theme"] 313 | if theme not in THEMES: 314 | logger.warning("{} not found in {}, using default".format(theme, THEMES.keys())) 315 | theme = "default" 316 | 317 | dark_mode_theme = tweaks.get("dark_mode_theme", None) 318 | if dark_mode_theme and dark_mode_theme not in DARK_THEMES: 319 | logger.warning("{} is not a dark theme, using darkly".format(dark_mode_theme)) 320 | dark_mode_theme = "darkly" 321 | 322 | theme_body_classes = " theme-{}".format(theme) 323 | if theme in DARK_THEMES: 324 | theme_body_classes += " dark-mode" 325 | 326 | ret = { 327 | "raw": raw_tweaks, 328 | "theme": {"name": theme, "src": static(THEMES[theme])}, 329 | "sidebar_classes": classes("sidebar", "sidebar_disable_expand"), 330 | "navbar_classes": classes("navbar", "no_navbar_border", "navbar_small_text"), 331 | "body_classes": classes( 332 | "accent", "body_small_text", "navbar_fixed", "footer_fixed", "sidebar_fixed", "layout_boxed" 333 | ) 334 | + theme_body_classes, 335 | "actions_classes": classes("actions_sticky_top"), 336 | "sidebar_list_classes": classes( 337 | "sidebar_nav_small_text", 338 | "sidebar_nav_flat_style", 339 | "sidebar_nav_legacy_style", 340 | "sidebar_nav_child_indent", 341 | "sidebar_nav_compact_style", 342 | ), 343 | "brand_classes": classes("brand_small_text", "brand_colour"), 344 | "footer_classes": classes("footer_small_text"), 345 | "button_classes": tweaks["button_classes"], 346 | } 347 | 348 | if dark_mode_theme: 349 | ret["dark_mode_theme"] = {"name": dark_mode_theme, "src": static(THEMES[dark_mode_theme])} 350 | 351 | return ret 352 | --------------------------------------------------------------------------------