diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f9c4002 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,30 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{py,rst,ini}] +indent_style = space +indent_size = 4 + +[*.{css,html,json,scss,yml,xml}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.bat] +indent_style = tab +end_of_line = crlf + +[Makefile] +indent_style = tab + +[*.mk] +indent_style = tab diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..71a5c0b --- /dev/null +++ b/.flake8 @@ -0,0 +1,11 @@ +# The flake8 project doesn't and likely won't ever support putting +# the configuration in pyproject.toml so we need a separate file. +# There is a plugin that can lood the configuration from pyproject.toml +# if you are so inclined. See https://pypi.org/project/Flake8-pyproject/ + +[flake8] +# The line length defined for black and isort +max-line-length = 88 + +# Print the total number of errors. +count = true diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..551d5af --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +#https://git-scm.com/docs/gitattributes + +# end of line normalization +* text=auto diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6291cf7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +# Dependabot configuration +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file +# https://github.com/dependabot/dependabot-core/issues/3940 hints that +# dependabot works with requirements-like files. + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + + - package-ecosystem: "pip" + directory: "/requirements" + schedule: + interval: "daily" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1bb4032 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,38 @@ +name: CI + +on: [push] + +jobs: + + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: + - "3.8" + - "3.10" + steps: + - uses: actions/checkout@v1 + with: + fetch-depth: 1 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - uses: actions/cache@v1 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/tests.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox tox-gh-actions + + - name: Tox tests + run: tox diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..59ea6b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# Python +__pycache__/ +*.py[co] +*.egg* + +# Editor temp files +*~ + +# Ignore dot files except those required for the project +.* +!.editorconfig +!.flake8 +!.gitattributes +!.github +!.gitignore +!.gitkeep +!.readthedocs.yml + +# Project artefacts +build +dist +reports +venv + +# Django runtime files +db.sqlite3 + +# Any local makefiles +*.mk diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..88ef114 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,11 @@ +version: 2 +python: + version: 3.8 + install: + - requirements: requirements/docs.txt + - method: pip + path: . +sphinx: + builder: html + configuration: docs/conf.py + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e4c1771 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# Changelog + +## Latest diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a092e2f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,15 @@ +# Contributing + +## Report Bugs + +## Fix Bugs + +## Implement Features + +## Write Documentation + +## Give Feedback + +## Get Started! + +### Pull Requests diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt new file mode 100644 index 0000000..07bc776 --- /dev/null +++ b/COPYRIGHT.txt @@ -0,0 +1,13 @@ +Copyright (C) 2023 Stuart MacKay + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use the files in library except in compliance with the +License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..7a40f21 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,163 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by the + copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all other + entities that control, are controlled by, or are under common control + with that entity. For the purposes of this definition, "control" means + (i) the power, direct or indirect, to cause the direction or management + of such entity, whether by contract or otherwise, or (ii) ownership of + fifty percent (50%) or more of the outstanding shares, or (iii) beneficial + ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity exercising + permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation source, + and configuration files. + + "Object" form shall mean any form resulting from mechanical transformation + or translation of a Source form, including but not limited to compiled + object code, generated documentation, and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or Object form, + made available under the License, as indicated by a copyright notice that + is included in or attached to the work (an example is provided in the + Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object form, + that is based on (or derived from) the Work and for which the editorial + revisions, annotations, elaborations, or other modifications represent, + as a whole, an original work of authorship. For the purposes of this + License, Derivative Works shall not include works that remain separable + from, or merely link (or bind by name) to the interfaces of, the Work and + Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including the original + version of the Work and any modifications or additions to that Work or + Derivative Works thereof, that is intentionally submitted to Licensor for + inclusion in the Work by the copyright owner or by an individual or Legal + Entity authorized to submit on behalf of the copyright owner. For the + purposes of this definition, "submitted" means any form of electronic, + verbal, or written communication sent to the Licensor or its + representatives, including but not limited to communication on electronic + mailing lists, source code control systems, and issue tracking systems + that are managed by, or on behalf of, the Licensor for the purpose of + discussing and improving the Work, but excluding communication that is + conspicuously marked or otherwise designated in writing by the copyright + owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity on + behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this + License, each Contributor hereby grants to You a perpetual, worldwide, + non-exclusive, no-charge, royalty-free, irrevocable copyright license to + reproduce, prepare Derivative Works of, publicly display, publicly perform, + sublicense, and distribute the Work and such Derivative Works in Source + or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this + License, each Contributor hereby grants to You a perpetual, worldwide, + non-exclusive, no-charge, royalty-free, irrevocable (except as stated in + this section) patent license to make, have made, use, offer to sell, sell, + import, and otherwise transfer the Work, where such license applies only + to those patent claims licensable by such Contributor that are necessarily + infringed by their Contribution(s) alone or by combination of their + Contribution(s) with the Work to which such Contribution(s) was submitted. + If You institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work or a + Contribution incorporated within the Work constitutes direct or + contributory patent infringement, then any patent licenses granted to You + under this License for that Work shall terminate as of the date such + litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or + Derivative Works thereof in any medium, with or without modifications, + and in Source or Object form, provided that You meet the following + conditions: + + You must give any other recipients of the Work or Derivative Works a copy + of this License; and You must cause any modified files to carry prominent + notices stating that You changed the files; and You must retain, in the + Source form of any Derivative Works that You distribute, all copyright, + patent, trademark, and attribution notices from the Source form of the + Work, excluding those notices that do not pertain to any part of the + Derivative Works; and If the Work includes a "NOTICE" text file as part + of its distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained within such + NOTICE file, excluding those notices that do not pertain to any part of + the Derivative Works, in at least one of the following places: within a + NOTICE text file distributed as part of the Derivative Works; within the + Source form or documentation, if provided along with the Derivative Works; + or, within a display generated by the Derivative Works, if and wherever + such third-party notices normally appear. The contents of the NOTICE file + are for informational purposes only and do not modify the License. You + may add Your own attribution notices within Derivative Works that You + distribute, alongside or as an addendum to the NOTICE text from the Work, + provided that such additional attribution notices cannot be construed as + modifying the License. + + You may add Your own copyright statement to Your modifications and may + provide additional or different license terms and conditions for use, + reproduction, or distribution of Your modifications, or for any such + Derivative Works as a whole, provided Your use, reproduction, and + distribution of the Work otherwise complies with the conditions stated + in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any + Contribution intentionally submitted for inclusion in the Work by You to + the Licensor shall be under the terms and conditions of this License, + without any additional terms or conditions. Notwithstanding the above, + nothing herein shall supersede or modify the terms of any separate license + agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, + trademarks, service marks, or product names of the Licensor, except as + required for reasonable and customary use in describing the origin of the + Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to + in writing, Licensor provides the Work (and each Contributor provides its + Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + ANY KIND, either express or implied, including, without limitation, any + warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or + FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for + determining the appropriateness of using or redistributing the Work and + assume any risks associated with Your exercise of permissions under this + License. + +8. Limitation of Liability. In no event and under no legal theory, whether + in tort (including negligence), contract, or otherwise, unless required by + applicable law (such as deliberate and grossly negligent acts) or agreed to + in writing, shall any Contributor be liable to You for damages, including + any direct, indirect, special, incidental, or consequential damages of any + character arising as a result of this License or out of the use or inability + to use the Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all other + commercial damages or losses), even if such Contributor has been advised + of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work + or Derivative Works thereof, You may choose to offer, and charge a fee for, + acceptance of support, warranty, indemnity, or other liability obligations + and/or rights consistent with this License. However, in accepting such + obligations, You may act only on Your own behalf and on Your sole + responsibility, not on behalf of any other Contributor, and only if You + agree to indemnify, defend, and hold each Contributor harmless for any + liability incurred by, or claims asserted against, such Contributor by + reason of your accepting any such warranty or additional liability. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..af126d1 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,22 @@ +# Manifest used for source and binary distributions. + +# Only need to explicitly include the changelog. The readme, licence +# and manifest files all pet pulled in automatically or from the +# settings in setup.py + +include LICENSE.txt +include COPYRIGHT.txt +include CHANGELOG.rst + +exclude setup.cfg +exclude pyproject.toml + +recursive-include src/app_project/locale * +recursive-include src/app_project/templates * +recursive-include src/app_project/static * + +recursive-exclude src/app_project/tests * + +recursive-exclude * .cache +recursive-exclude * __pycache__ +recursive-exclude * *.py[co] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..576d25f --- /dev/null +++ b/Makefile @@ -0,0 +1,261 @@ +# +# Makefile: Commands to simplify development and releases +# +# Usage: +# +# make clean +# make checks +# make tests +# make patch build upload + +# This Makefile only works with GNU Make. + +PYTHON = python3.10 + +# Where everything lives +site_python := /usr/bin/env $(PYTHON) + +root_dir := $(realpath .) +app_dir = $(root_dir)/src/app_project +demo_dir = $(root_dir)/project + +venv_name = venv +venv_dir = $(root_dir)/$(venv_name) +python = $(venv_dir)/bin/python + +pip = $(python) -m pip +pip-compile = $(venv_dir)/bin/pip-compile +pip-sync = $(venv_dir)/bin/pip-sync +django = $(python) $(root_dir)/manage.py +flake8 = $(python) -m flake8 +black = $(python) -m black +isort = $(python) -m isort +mypy = $(python) -m mypy +pytest = $(python) -m pytest +coverage = $(python) -m coverage +bumpversion = $(venv_dir)/bin/bump2version +tox = $(venv_dir)/bin/tox +twine = $(venv_dir)/bin/twine + +# Options or flags +pytest_opts := + +commit_opts := --gpg-sign +upload_opts := --skip-existing --sign + + +# include additional targets or override variables from local makefiles +-include *.mk + +.PHONY: help +help: + @echo "Please use 'make ' where is one of:" + @echo "" + @echo " help to show this list" + @echo " clean-build to clean the files and directories generated by previous builds" + @echo " clean-docs to clean the generated HTML documentation" + @echo " clean-tests to clean the directories created during testing" + @echo " clean-coverage to clean the test coverage data and reports" + @echo " clean-venv to clean the virtualenv" + @echo " clean to clean everything EXCEPT the virtualenv" + @echo + @echo " build to build the package" + @echo " checks to run quality code checks" + @echo " coverage to measure the test coverage" + @echo " docs to build the HTML documentation" + @echo " install to install the project dependencies in the virtualenv" + @echo " major to update the version number for a major release, e.g. 2.1 to 3.0" + @echo " messages to run the makemessages and compilemessages management commands" + @echo " migrate to run migrate management command" + @echo " migrations to run makemigrations management command" + @echo " minor to update the version number for a minor release, e.g. 2.1 to 2.2" + @echo " patch to update the version number for a patch release, e.g. 2.1.1 to 2.1.2" + @echo " runserver to run the Django demo site" + @echo " test to run the tests during development" + @echo " test-all to run the tests for all the supported environments" + @echo " upload to upload a release to PyPI repository" + @echo " venv to create the virtualenv" + @echo + +# ######### +# Clean +# ######### +# +# Delete all the runtime files generated by the various targets, +# including the virtualenv. + +.PHONY: clean-venv +clean-venv: + rm -rf $(venv_dir) + +.PHONY: clean-build +clean-build: + rm -rf build + rm -rf src/*.egg-info + +.PHONY: clean-docs +clean-docs: + cd docs && make clean + +.PHONY: clean-tests +clean-tests: + rm -rf .tox + rm -rf .pytest_cache + +.PHONY: clean-mypy +clean-mypy: + rm -rf .mypy_cache + +.PHONY: clean-coverage +clean-coverage: + rm -rf .coverage + rm -rf coverage + +.PHONY: clean +clean: clean-venv clean-build clean-tests clean-mypy clean-coverage clean-docs + +# ############## +# Virtualenv +# ############## +# +# Create the virtualenv and install all the dependencies for development. +# If the virtualenv already exists then synchronise the installed packages +# with those listed in requirements/dev.txt. The list of packages will be +# updated if any of the input files change. +# +# pip-tools is pinned to version 6.10.0 as 6.11.0 bumped the required version +# of click to be version 8 or higher which is incompatible with the version +# installed by celery 5.0. It's simpler, for now, to pin the pip-tools version +# rather than recompile the requirements files. That would introduce another +# incompatibility between flake8 and the pytest-flake8 plugin which although +# documented as fixed, remains. See https://github.com/PyCQA/flake8/issues/367 + +$(venv_dir): + $(site_python) -m venv $(venv_dir) + $(pip) install --upgrade pip setuptools wheel + $(pip) install pip-tools==6.10.0 + +requirements/dev.txt: requirements/dev.in requirements/docs.in requirements/tests.in + $(pip-compile) requirements/dev.in + +requirements/docs.txt: requirements/docs.in + $(pip-compile) requirements/docs.in + +requirements/tests.txt: requirements/docs.in requirements/tests.in + $(pip-compile) requirements/tests.in + +.PHONY: requirements +requirements: requirements/dev.txt requirements/docs.txt requirements/tests.txt + +.PHONY: venv +venv: $(venv_dir) requirements + $(pip-sync) requirements/dev.txt + +.PHONY: install +install: venv + $(pip) install --upgrade pip setuptools wheel + $(pip) install pip-tools + test -f requirements/docs.txt || $(pip-compile) requirements/docs.in + test -f requirements/dev.txt || $(pip-compile) requirements/dev.in + test -f requirements/tests.txt || $(pip-compile) requirements/tests.in + $(pip-sync) requirements/dev.txt + +# ######## +# Demo +# ######## + +.PHONY: demo +runserver: + . bin/activate && $(django) migrate + . bin/activate && $(django) runserver + +# ########## +# Django +# ########## + +.PHONY: messages +messages: + cd $(app_dir) && \ + $(django) makemessages --no-obsolete --all + +.PHONY: translations +translations: + cd $(app_dir) && \ + $(django) compilemessages + +# ########## +# Checks +# ########## + +.PHONY: flake8 +flake8: + $(flake8) $(site_dir) + +.PHONY: isort +isort: + $(isort) --check $(site_dir) + +.PHONY: black +black: + $(black) --check $(site_dir) + +.PHONY: mypy +mypy: + $(mypy) $(site_dir) + +.PHONY: checks +checks: flake8 black isort mypy + +# ########### +# Testing +# ########### + +.PHONY: coverage +coverage: + $(pytest) --cov=app_project --cov-config=setup.cfg --cov-report html + +.PHONY: test +test: + $(pytest) $(pytest_opts) + +.PHONY: tox +tox: test + $(tox) + $(tox) -e docs + +# ######## +# Docs +# ######## + +.PHONY: docs +docs: + cd docs && make html + +# ########### +# Release +# ########### +# +# Usage: +# make patch build upload +# make minor build upload +# make major build upload + +.PHONY: build +build: clean-build + $(python) setup.py sdist bdist_wheel + +.PHONY: major +major: + $(bumpversion) major + +.PHONY: minor +minor: + $(bumpversion) minor + +.PHONY: patch +patch: + $(bumpversion) patch + +.PHONY: upload +upload: + $(twine) upload $(upload_opts) dist/* diff --git a/README.md b/README.md new file mode 100644 index 0000000..699ef4a --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# Django App Project + +[![Build Status](https://img.shields.io/github/actions/workflow/status/StuartMacKay/django-app-template/ci.yml?branch=master)](https://github.com/StuartMacKay/django-app-template/actions/workflows/ci.yml?query=branch%3Amaster) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) + +## Features + +* Development with [black](https://github.com/psf/black) so everybody gets the code formatting rules they deserve +* Development with [flake8](https://flake8.pycqa.org/en/latest/) so people using ed get syntax checking +* Development with [isort](https://pycqa.github.io/isort/) for automatically sorting imports +* Development with [mypy](https://mypy-lang.org/) for type-hinting to catch errors +* Testing with [pytest](https://docs.pytest.org/), [FactoryBoy](https://factoryboy.readthedocs.io/en/stable/) and [tox](https://tox.wiki/en/latest/) +* Manage versions with [bump2version](https://pypi.org/project/bump2version/) - for semantic version numbers +* Manage dependency versions with [pip-tools](https://github.com/jazzband/pip-tools) +* Manage dependency upgrades with [pip-upgrade](https://github.com/simion/pip-upgrader) + +## Quick start + +First, download and unzip the project files in the directory of your choice. +Then rename the project to something more useful: +```shell +mv django-app-template django-myapp +``` + +Change to the project directory and start setting things up: +```shell +cd django-myapp +``` + +First, build the virtualenv and install all the dependencies. This will +also build the library: +```shell +make install +``` + +Now run the demo site: +```shell +make demo +``` + + +Run the database migrations: +```shell +./manage.py migrate +``` + +Run the tests: +```shell +make tests +``` + +Run the django server: +```shell +./manage.py runserver +``` + +Open a browser and visit http://localhost:8000 and, voila, we have a working +site. Well cover the deployment later. + +Almost all steps used the project Makefile. That's great if you're running +Linux with GNU Make and not much fun if you're not. All is not lost, however. +All the Makefile's targets contain only one or two commands, so even if you +are running Windows you should still be able to get the site running without +too much effort. diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..b6dbda7 --- /dev/null +++ b/TODO.md @@ -0,0 +1,2 @@ +# TODO + diff --git a/demo/conf/__init__.py b/demo/conf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/demo/conf/asgi.py b/demo/conf/asgi.py new file mode 100644 index 0000000..c0d7d61 --- /dev/null +++ b/demo/conf/asgi.py @@ -0,0 +1,16 @@ +""" +WSGI config + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.conf.settings") + +application = get_asgi_application() diff --git a/demo/conf/settings.py b/demo/conf/settings.py new file mode 100644 index 0000000..115b01e --- /dev/null +++ b/demo/conf/settings.py @@ -0,0 +1,74 @@ +""" +MVS (Minimal Viable Settings) for running a basic site. + +""" +import os + + +DEBUG = True + +CONF_DIR = os.path.dirname(os.path.abspath(__file__)) +DEMO_DIR = os.path.dirname(CONF_DIR) +ROOT_DIR = os.path.dirname(DEMO_DIR) + +SECRET_KEY = "+^%b!5_!ul7prtm_y0w_xluJg32aqna&+)&thhy3)jqr2g*0%s" + +INSTALLED_APPS = ( + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "debug_toolbar", + "app_project.apps.Config", +) + +MIDDLEWARE = [ + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", +] + +ROOT_URLCONF = "demo.conf.urls" + +STATIC_URL = "/static/" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [os.path.join(DEMO_DIR, "templates")], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "django.template.context_processors.request" + ], + }, + }, +] + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(ROOT_DIR, "db.sqlite3"), + } +} + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +CACHES = { + "default": { + "BACKEND": "django.core.cache.backends.locmem.LocMemCache", + "LOCATION": "site", + } +} + +INTERNAL_IPS = [ + "127.0.0.1", +] + +USE_TZ = True diff --git a/demo/conf/urls.py b/demo/conf/urls.py new file mode 100644 index 0000000..921ba72 --- /dev/null +++ b/demo/conf/urls.py @@ -0,0 +1,17 @@ +""" +URL Configuration for the demonstration site. + +""" +from django.contrib import admin +from django.urls import path, include, reverse_lazy +from django.views.generic import RedirectView + +import debug_toolbar # type: ignore + + +urlpatterns = [ + path("", RedirectView.as_view(url=reverse_lazy("app_index"))), + path("admin/", admin.site.urls), + path("app/", include("app_project.urls")), + path("__debug__/toolbar/", include(debug_toolbar.urls)), # type: ignore +] diff --git a/demo/conf/wsgi.py b/demo/conf/wsgi.py new file mode 100644 index 0000000..062381a --- /dev/null +++ b/demo/conf/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.conf.settings") + +application = get_wsgi_application() diff --git a/demo/locale/.gitkeep b/demo/locale/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/demo/static/css/site.css b/demo/static/css/site.css new file mode 100644 index 0000000..e69de29 diff --git a/demo/static/favicon.ico b/demo/static/favicon.ico new file mode 100644 index 0000000..e1c1dd1 Binary files /dev/null and b/demo/static/favicon.ico differ diff --git a/demo/static/images/.gitkeep b/demo/static/images/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/demo/static/js/index.js b/demo/static/js/index.js new file mode 100644 index 0000000..e69de29 diff --git a/demo/templates/base.html b/demo/templates/base.html new file mode 100644 index 0000000..b7b2bf1 --- /dev/null +++ b/demo/templates/base.html @@ -0,0 +1,17 @@ +{% load static i18n %} + + + + + + {% block head_title %}{% endblock %} + + + {% block extra_style %}{% endblock %} + + + + {% block content %}{% endblock %} + + + diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..bf1dffd --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +# Ignore the file where the docs are built +_build diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/.gitkeep b/docs/_static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/_templates/.gitkeep b/docs/_templates/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..1874c03 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,59 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath(".")) + + +# -- Project information ----------------------------------------------------- + +project = "Django App Project" +copyright = "2023, Stuart MacKay" +author = "Stuart MacKay" + +version = "0.0" +release = "0.0.0" + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named "sphinx.ext.*") or your custom +# ones. + +# napoleon - generate docs from numpy and google docstrings + +extensions = [ + "sphinx.ext.napoleon", +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_rtd_theme" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..4d5ba8d --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,15 @@ +Welcome to Django App Project's documentation! +============================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..922152e --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..13e3aa2 --- /dev/null +++ b/manage.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +import os +import sys + + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "demo.conf.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..547a34f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,5 @@ +[tool.black] +line-length = 88 +target-version = [ + 'py311' +] diff --git a/requirements/dev.in b/requirements/dev.in new file mode 100644 index 0000000..09d518d --- /dev/null +++ b/requirements/dev.in @@ -0,0 +1,21 @@ + +-r docs.in +-r tests.in + +# Install the app and dependencies for development. It is done this way +# rather than using "setup.py develop" directly as that only makes Django +# visible to the app and not generally. + +-e . + +# bumpversion is no longer maintained so we use bump2version until the +# point where bump2version emerges as the official bumpversion, see +# https://github.com/c4urself/bump2version/issues/86 + +bump2version +django-debug-toolbar +django-stubs +pip-upgrader + +tox +twine diff --git a/requirements/dev.txt b/requirements/dev.txt new file mode 100644 index 0000000..7d6131a --- /dev/null +++ b/requirements/dev.txt @@ -0,0 +1,223 @@ +# +# This file is autogenerated by pip-compile with python 3.10 +# To update, run: +# +# pip-compile requirements/dev.in +# +-e file:///home/stuart/Development/django-app-template + # via -r requirements/dev.in +alabaster==0.7.13 + # via sphinx +asgiref==3.6.0 + # via django +attrs==22.2.0 + # via pytest +babel==2.12.1 + # via sphinx +black==23.1.0 + # via -r requirements/tests.in +bleach==6.0.0 + # via readme-renderer +bump2version==1.0.1 + # via -r requirements/dev.in +cachetools==5.3.0 + # via tox +certifi==2022.12.7 + # via requests +cffi==1.15.1 + # via cryptography +chardet==5.1.0 + # via tox +charset-normalizer==3.1.0 + # via requests +click==8.1.3 + # via black +colorama==0.4.6 + # via tox +coverage[toml]==7.2.1 + # via pytest-cov +cryptography==39.0.2 + # via secretstorage +distlib==0.3.6 + # via virtualenv +django==4.1.7 + # via django-library-project +docutils==0.18.1 + # via + # readme-renderer + # sphinx + # sphinx-rtd-theme +exceptiongroup==1.1.0 + # via pytest +factory-boy==3.2.1 + # via pytest-factoryboy +faker==17.6.0 + # via factory-boy +filelock==3.9.0 + # via + # tox + # virtualenv +flake8==6.0.0 + # via -r requirements/tests.in +idna==3.4 + # via requests +imagesize==1.4.1 + # via sphinx +importlib-metadata==6.0.0 + # via + # keyring + # twine +inflection==0.5.1 + # via pytest-factoryboy +iniconfig==2.0.0 + # via pytest +isort==5.12.0 + # via -r requirements/tests.in +jaraco-classes==3.2.3 + # via keyring +jeepney==0.8.0 + # via + # keyring + # secretstorage +jinja2==3.1.2 + # via sphinx +keyring==23.13.1 + # via twine +lack==0.0.0 + # via -r requirements/tests.in +markdown-it-py==2.2.0 + # via rich +markupsafe==2.1.2 + # via jinja2 +mccabe==0.7.0 + # via flake8 +mdurl==0.1.2 + # via markdown-it-py +more-itertools==9.1.0 + # via jaraco-classes +mypy==1.1.1 + # via -r requirements/tests.in +mypy-extensions==1.0.0 + # via + # black + # mypy +packaging==23.0 + # via + # black + # pyproject-api + # pytest + # sphinx + # tox +pathspec==0.11.0 + # via + # black + # twine +pkginfo==1.9.6 + # via twine +platformdirs==3.1.1 + # via + # black + # tox + # virtualenv +pluggy==1.0.0 + # via + # pytest + # tox +pycodestyle==2.10.0 + # via flake8 +pycparser==2.21 + # via cffi +pyflakes==3.0.1 + # via flake8 +pygments==2.14.0 + # via + # readme-renderer + # rich + # sphinx +pyproject-api==1.5.0 + # via tox +pytest==7.2.2 + # via + # -r requirements/tests.in + # pytest-cov + # pytest-django + # pytest-factoryboy +pytest-cov==4.0.0 + # via -r requirements/tests.in +pytest-django==4.5.2 + # via -r requirements/tests.in +pytest-factoryboy==2.5.1 + # via -r requirements/tests.in +python-dateutil==2.8.2 + # via faker +readme-renderer==37.3 + # via twine +requests==2.28.2 + # via + # requests-toolbelt + # sphinx + # twine +requests-toolbelt==0.10.1 + # via twine +rfc3986==2.0.0 + # via twine +rich==13.3.2 + # via twine +secretstorage==3.3.3 + # via keyring +six==1.16.0 + # via + # bleach + # python-dateutil +snowballstemmer==2.2.0 + # via sphinx +sphinx==6.1.3 + # via + # -r requirements/docs.in + # sphinx-rtd-theme +sphinx-rtd-theme==1.2.0 + # via -r requirements/docs.in +sphinxcontrib-applehelp==1.0.4 + # via sphinx +sphinxcontrib-devhelp==1.0.2 + # via sphinx +sphinxcontrib-htmlhelp==2.0.1 + # via sphinx +sphinxcontrib-jquery==2.0.0 + # via sphinx-rtd-theme +sphinxcontrib-jsmath==1.0.1 + # via sphinx +sphinxcontrib-qthelp==1.0.3 + # via sphinx +sphinxcontrib-serializinghtml==1.1.5 + # via sphinx +sqlparse==0.4.3 + # via django +tomli==2.0.1 + # via + # coverage + # mypy + # pyproject-api + # pytest + # tox +tox==4.4.6 + # via -r requirements/dev.in +twine==4.0.2 + # via -r requirements/dev.in +typing-extensions==4.5.0 + # via + # mypy + # pytest-factoryboy +urllib3==1.26.14 + # via + # requests + # twine +virtualenv==20.20.0 + # via tox +webencodings==0.5.1 + # via bleach +zipp==3.15.0 + # via importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +# setuptools diff --git a/requirements/docs.in b/requirements/docs.in new file mode 100644 index 0000000..cbf1e36 --- /dev/null +++ b/requirements/docs.in @@ -0,0 +1,2 @@ +sphinx +sphinx-rtd-theme diff --git a/requirements/docs.txt b/requirements/docs.txt new file mode 100644 index 0000000..110edaf --- /dev/null +++ b/requirements/docs.txt @@ -0,0 +1,59 @@ +# +# This file is autogenerated by pip-compile with python 3.10 +# To update, run: +# +# pip-compile requirements/docs.in +# +alabaster==0.7.13 + # via sphinx +babel==2.12.1 + # via sphinx +certifi==2022.12.7 + # via requests +charset-normalizer==3.1.0 + # via requests +docutils==0.18.1 + # via + # sphinx + # sphinx-rtd-theme +idna==3.4 + # via requests +imagesize==1.4.1 + # via sphinx +jinja2==3.1.2 + # via sphinx +markupsafe==2.1.2 + # via jinja2 +packaging==23.0 + # via sphinx +pygments==2.14.0 + # via sphinx +requests==2.28.2 + # via sphinx +snowballstemmer==2.2.0 + # via sphinx +sphinx==6.1.3 + # via + # -r requirements/docs.in + # sphinx-rtd-theme +sphinx-rtd-theme==1.2.0 + # via -r requirements/docs.in +sphinxcontrib-applehelp==1.0.4 + # via sphinx +sphinxcontrib-devhelp==1.0.2 + # via sphinx +sphinxcontrib-htmlhelp==2.0.1 + # via sphinx +sphinxcontrib-jquery==2.0.0 + # via sphinx-rtd-theme +sphinxcontrib-jsmath==1.0.1 + # via sphinx +sphinxcontrib-qthelp==1.0.3 + # via sphinx +sphinxcontrib-serializinghtml==1.1.5 + # via sphinx +urllib3==1.26.14 + # via requests + +# The following packages are considered to be unsafe in a requirements file: +# setuptools diff --git a/requirements/tests.in b/requirements/tests.in new file mode 100644 index 0000000..ecd5d57 --- /dev/null +++ b/requirements/tests.in @@ -0,0 +1,9 @@ +black +flake8 +isort +mypy + +pytest +pytest-django +pytest-factoryboy +pytest-cov diff --git a/requirements/tests.txt b/requirements/tests.txt new file mode 100644 index 0000000..6fb9879 --- /dev/null +++ b/requirements/tests.txt @@ -0,0 +1,76 @@ +# +# This file is autogenerated by pip-compile with python 3.10 +# To update, run: +# +# pip-compile requirements/tests.in +# +attrs==22.2.0 + # via pytest +black==23.1.0 + # via -r requirements/tests.in +click==8.1.3 + # via black +coverage[toml]==7.2.1 + # via pytest-cov +exceptiongroup==1.1.0 + # via pytest +factory-boy==3.2.1 + # via pytest-factoryboy +faker==17.6.0 + # via factory-boy +flake8==6.0.0 + # via -r requirements/tests.in +inflection==0.5.1 + # via pytest-factoryboy +iniconfig==2.0.0 + # via pytest +isort==5.12.0 + # via -r requirements/tests.in +mccabe==0.7.0 + # via flake8 +mypy==1.1.1 + # via -r requirements/tests.in +mypy-extensions==1.0.0 + # via + # black + # mypy +packaging==23.0 + # via + # black + # pytest +pathspec==0.11.0 + # via black +platformdirs==3.1.1 + # via black +pluggy==1.0.0 + # via pytest +pycodestyle==2.10.0 + # via flake8 +pyflakes==3.0.1 + # via flake8 +pytest==7.2.2 + # via + # -r requirements/tests.in + # pytest-cov + # pytest-django + # pytest-factoryboy +pytest-cov==4.0.0 + # via -r requirements/tests.in +pytest-django==4.5.2 + # via -r requirements/tests.in +pytest-factoryboy==2.5.1 + # via -r requirements/tests.in +python-dateutil==2.8.2 + # via faker +six==1.16.0 + # via python-dateutil +tomli==2.0.1 + # via + # black + # coverage + # mypy + # pytest +typing-extensions==4.5.0 + # via + # mypy + # pytest-factoryboy diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..2500be9 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,150 @@ +# +# Django App Project +# +# Each of the tools used in the project have their configuration options +# defined here. The exception is black where the configuration is in +# pyproject.toml. There is a plugin for black, +# https://github.com/danie1k/python-black-configparser, that would allow +# the configuration to be located in setup.cfg. Since these are unlikely +# to change much it is not worth the extra dependency. +# +# All the various tools have their options synced to the values +# recommended for black since it's the most unforgiving of them all: +# https://github.com/psf/black/blob/master/docs/compatible_configs.md +# + +[bumpversion] +parse = (?P\d+)\.(?P\d+)(\.(?P\d+))? +current_version = 0.0.0 +commit = True +commit_args = -S +message = New version: {new_version} +tag = True +tag_name = v{new_version} +sign_tags = True + +[bumpversion:file:src/app_project/__init__.py] +search = __version__ = "{current_version}" +replace = __version__ = "{new_version}" + +# The order in which the version numbers in docs/conf.py is important. +# If the release number is not processed first (using the default +# regex for parsing) then you will get the following output: +# +# version = "0.0" +# release = "version = "0.0"" +# +# This has been reported as an issue to bump2version: +# https://github.com/c4urself/bump2version/issues/159 + +[bumpversion:file:docs/conf.py] +search = release = "{current_version}" +replace = release = "{new_version}" + +[bumpversion:file:./docs/conf.py] +parse = (?P\d+)\.(?P\d+) +serialize = {major}.{minor} +search = version = "{current_version}" +replace = version = "{new_version}" + +[bumpversion:file:CHANGELOG.md] +search = + # Latest +replace = + # Latest + + # {new_version} ({now:%%Y-%%m-%%d}) + +[bumpversion:file:setup.py] +search = version="{current_version}" +replace = version="{new_version}" + + +[flake8] +# exclude = .git,.mypy_cache,.pytest_cache,__pycache__,.tox + +extend_exclude = + */migrations/*, + build/, + docs/, + venv/ + +ignore = + E123, ; closing bracket does not match indentation of opening bracket’s line + W503 ; line break before binary operator + +max-line-length = 88 + +statistics = True + + +[isort] +multi_line_output = 3 +include_trailing_comma = True +force_grid_wrap = 0 +use_parentheses = True +ensure_newline_before_comments = True +line_length = 88 + +default_section = THIRDPARTY +known_django = django +known_first_party = app_project +sections = + FUTURE, + STDLIB, + DJANGO, + THIRDPARTY, + FIRSTPARTY, + LOCALFOLDER + +skip = + migrations, + venv + + +[tool:pytest] +DJANGO_SETTINGS_MODULE = demo.conf.settings + +testpaths = + src +[coverage:run] +branch = true +data_file = .coverage +omit = + */tests/* + demo/* + venv/* + +[coverage:report] +show_missing = True +skip_covered = True +fail_under = 30 +exclude_lines = + raise AssertionError + raise NotImplementedError + +[coverage:html] +directory = coverage + +[tox:tox] +envlist = + {py38,py310}-django{32,40} + + +[testenv:docs] +basepython=python +changedir=docs +deps= -r requirements/docs.txt +commands= + sphinx-build -b html -d {envtmpdir}/doctrees . {envtmpdir}/html +[testenv] +commands = pytest +deps = -r requirements/tests.txt +setenv = + PYTHONPATH = src + +[wheel] +universal = 1 + +[bdist_wheel] +universal = 1 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..95a045d --- /dev/null +++ b/setup.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +""" +setup.py + +setup() is configured with the project metadata so setup.cfg is used +primarily for options for the various tools used. + +""" +import os + +from setuptools import setup + + +def read(filename): + with open(os.path.join(os.path.dirname(__file__), filename)) as fp: + return fp.read() + + +setup( + name="django-library-project", + version="0.0.0", + description="A deployable Django app.", + long_description=read("README.md"), + long_description_content_type="text/x-rst", + author="Stuart MacKay", + author_email="smackay@flagstonesoftware.com", + keywords="django, app, template", + url="https://github.com/StuartMacKay/django-app-template", + packages=[ + "app_project", + "app_project/migrations" + ], + package_dir={"": "src"}, + include_package_data=True, + zip_safe=False, + python_requires=">=3.8", + install_requires="Django>=3.2", + license="License :: OSI Approved :: Apache Software License", + classifiers=[ + "Development Status :: 3 - Alpha", + "Framework :: Django :: 3.2", + "Framework :: Django :: 4.0", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Natural Language :: English", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.10", + ] +) diff --git a/src/app_project/__init__.py b/src/app_project/__init__.py new file mode 100644 index 0000000..b16bdfb --- /dev/null +++ b/src/app_project/__init__.py @@ -0,0 +1,5 @@ +__version__ = "0.0.0" +__author__ = "" +__email__ = "" +__license__ = "Apache Software License 2.0" +__copyright__ = "" diff --git a/src/app_project/admin/__init__.py b/src/app_project/admin/__init__.py new file mode 100644 index 0000000..a816332 --- /dev/null +++ b/src/app_project/admin/__init__.py @@ -0,0 +1,5 @@ +from .example import ExampleAdmin + +__all__ = ( + "ExampleAdmin", +) diff --git a/src/app_project/admin/example.py b/src/app_project/admin/example.py new file mode 100644 index 0000000..adba364 --- /dev/null +++ b/src/app_project/admin/example.py @@ -0,0 +1,9 @@ +from django.contrib import admin + +from app_project import models + + +@admin.register(models.Example) +class ExampleAdmin(admin.ModelAdmin): + list_display = ("name",) + list_filter = ("name",) diff --git a/src/app_project/apps.py b/src/app_project/apps.py new file mode 100644 index 0000000..e2e05f8 --- /dev/null +++ b/src/app_project/apps.py @@ -0,0 +1,19 @@ +from django.apps import AppConfig + + +def setup_app_settings(): + from django.conf import settings + + from . import settings as defaults + + for name in dir(defaults): + if name.isupper() and not hasattr(settings, name): + setattr(settings, name, getattr(defaults, name)) + + +class Config(AppConfig): + name = "app_project" + verbose_name = "App Project" + + def ready(self): + setup_app_settings() diff --git a/src/app_project/locale/en/LC_MESSAGES/django.mo b/src/app_project/locale/en/LC_MESSAGES/django.mo new file mode 100644 index 0000000..4a21fcc Binary files /dev/null and b/src/app_project/locale/en/LC_MESSAGES/django.mo differ diff --git a/src/app_project/locale/en/LC_MESSAGES/django.po b/src/app_project/locale/en/LC_MESSAGES/django.po new file mode 100644 index 0000000..384ca00 --- /dev/null +++ b/src/app_project/locale/en/LC_MESSAGES/django.po @@ -0,0 +1,23 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-03-10 14:10-0600\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: models/example.py:6 +msgid "Name" +msgstr "Name" diff --git a/src/app_project/migrations/0001_initial.py b/src/app_project/migrations/0001_initial.py new file mode 100644 index 0000000..b868677 --- /dev/null +++ b/src/app_project/migrations/0001_initial.py @@ -0,0 +1,31 @@ +# Generated by Django 3.1.1 on 2020-09-06 13:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="Example", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField(blank=True, max_length=32, verbose_name="Name"), + ), + ], + ), + ] diff --git a/src/app_project/migrations/__init__.py b/src/app_project/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/app_project/models/__init__.py b/src/app_project/models/__init__.py new file mode 100644 index 0000000..f04a629 --- /dev/null +++ b/src/app_project/models/__init__.py @@ -0,0 +1,5 @@ +from .example import Example + +__all__ = ( + "Example", +) diff --git a/src/app_project/models/example.py b/src/app_project/models/example.py new file mode 100644 index 0000000..0302494 --- /dev/null +++ b/src/app_project/models/example.py @@ -0,0 +1,6 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + + +class Example(models.Model): + name = models.CharField(verbose_name=_("Name"), max_length=32, blank=True) diff --git a/src/app_project/settings.py b/src/app_project/settings.py new file mode 100644 index 0000000..ecfa14d --- /dev/null +++ b/src/app_project/settings.py @@ -0,0 +1,3 @@ +# app-specific settings.py +# These will be added to the main settings when the app is loaded. +# See ./apps.py diff --git a/src/app_project/templates/app_project/base.html b/src/app_project/templates/app_project/base.html new file mode 100644 index 0000000..7219eb5 --- /dev/null +++ b/src/app_project/templates/app_project/base.html @@ -0,0 +1,3 @@ +{% extends "base.html" %} +{% load static i18n %} + diff --git a/src/app_project/templates/app_project/index.html b/src/app_project/templates/app_project/index.html new file mode 100644 index 0000000..b8fe5d6 --- /dev/null +++ b/src/app_project/templates/app_project/index.html @@ -0,0 +1,6 @@ +{% extends "app_project/base.html" %} +{% load static i18n %} + +{% block content %} +

App Project

+{% endblock %} diff --git a/src/app_project/tests/__init__.py b/src/app_project/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/app_project/tests/test_views.py b/src/app_project/tests/test_views.py new file mode 100644 index 0000000..f7bf597 --- /dev/null +++ b/src/app_project/tests/test_views.py @@ -0,0 +1,8 @@ +from django.test import TestCase +from django.urls import reverse + + +class IndexViewTests(TestCase): + def test_view(self): + response = self.client.get(reverse("app_index")) + self.assertEqual(200, response.status_code) diff --git a/src/app_project/urls.py b/src/app_project/urls.py new file mode 100644 index 0000000..0752870 --- /dev/null +++ b/src/app_project/urls.py @@ -0,0 +1,5 @@ +from django.urls import path + +from .views import IndexView + +urlpatterns = [path("", IndexView.as_view(), name="app_index")] diff --git a/src/app_project/views/__init__.py b/src/app_project/views/__init__.py new file mode 100644 index 0000000..7789c52 --- /dev/null +++ b/src/app_project/views/__init__.py @@ -0,0 +1,5 @@ +from .index import IndexView + +__all__ = ( + "IndexView", +) diff --git a/src/app_project/views/index.py b/src/app_project/views/index.py new file mode 100644 index 0000000..6490fec --- /dev/null +++ b/src/app_project/views/index.py @@ -0,0 +1,5 @@ +from django.views.generic import TemplateView + + +class IndexView(TemplateView): + template_name = "app_project/index.html"