Minimal Django project setup

pull/2/head
JensDiemer 2020-10-16 17:54:34 +02:00
rodzic 4fa1633eb0
commit a5b0235b12
19 zmienionych plików z 789 dodań i 341 usunięć

2
.gitignore vendored
Wyświetl plik

@ -11,7 +11,7 @@
# from test projects:
inventory_project/static/
inventory_project/media/
test_project_db.sqlite3
*.sqlite3
# Coverage HTML Report files:
htmlcov

Wyświetl plik

@ -28,6 +28,11 @@ install-poetry: ## install or update poetry
install: check-poetry ## install PyInventory via poetry
poetry install
manage-update: ## Collectstatic + makemigration + migrate
./manage.sh collectstatic --noinput --link
./manage.sh makemigrations
./manage.sh migrate
update: check-poetry ## update the sources and installation
git fetch --all
git pull origin master
@ -70,7 +75,7 @@ publish: ## Release new version to PyPi
run-dev-server: ## Run the django dev server in endless loop.
poetry run inventory run-dev-server
./manage.sh runserver
run-server: ## Run the gunicorn server in endless loop.
poetry run inventory run-server

Wyświetl plik

@ -1,10 +1 @@
"""
created 19.07.2018 by Jens Diemer <opensource@jensdiemer.de>
:copyleft: 2018 by the PyInventory team, see AUTHORS for more details.
:license: GNU GPL v3 or above, see LICENSE for more details.
"""
from inventory.admin.discipline import DisciplineModelAdmin # noqa
from inventory.admin.distance import DistanceModelAdmin # noqa
from inventory.admin.event import EventLinkModelAdmin, EventModelAdmin, ParticipationModelAdmin # noqa
from inventory.admin.gpx import GpxModelAdmin # noqa
from inventory.admin.item import ItemModelAdmin # noqa

Wyświetl plik

@ -0,0 +1,9 @@
from django.contrib import admin
from reversion_compare.admin import CompareVersionAdmin
from inventory.models import ItemModel
@admin.register(ItemModel)
class ItemModelAdmin(CompareVersionAdmin):
pass

Wyświetl plik

@ -1,17 +1 @@
"""
created 28.06.2018 by Jens Diemer <opensource@jensdiemer.de>
:copyleft: 2018 by the PyInventory team, see AUTHORS for more details.
:license: GNU GPL v3 or above, see LICENSE for more details.
"""
from django.db.models.signals import post_save, pre_save
# https://github.com/jedie/PyInventory
from inventory.models.discipline import DisciplineModel # noqa
from inventory.models.distance import DistanceModel # noqa
from inventory.models.event import CostModel, EventLinkModel, EventModel, ParticipationModel # noqa
from inventory.models.gpx import GpxModel # noqa
from inventory.signal_handlers.gpx import gpx_post_save_handler, gpx_pre_save_handler # noqa
pre_save.connect(receiver=gpx_pre_save_handler, sender=GpxModel)
post_save.connect(receiver=gpx_post_save_handler, sender=GpxModel)
from inventory.models.item import ItemModel # noqa

Wyświetl plik

@ -0,0 +1,56 @@
import uuid
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
class BaseModel(models.Model):
id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
editable=False,
verbose_name=_('ID')
)
create_dt = models.DateTimeField(
blank=True,
null=True,
editable=False,
verbose_name=_('BaseApproveModel.create_dt.verbose_name'),
help_text=_('BaseApproveModel.create_dt.help_text')
)
update_dt = models.DateTimeField(
blank=True,
null=True,
editable=False,
verbose_name=_('BaseApproveModel.update_dt.verbose_name'),
help_text=_('BaseApproveModel.update_dt.help_text')
)
def save(self, update_dt=True, **kwargs):
if update_dt:
if 'update_fields' in kwargs:
update_fields = list(kwargs['update_fields'])
else:
update_fields = None
self.update_dt = timezone.now()
if update_fields:
assert 'update_dt' not in update_fields
update_fields.append('update_dt')
if self.create_dt is None:
self.create_dt = self.update_dt
if update_fields:
assert 'create_dt' not in update_fields
update_fields.append('create_dt')
if update_fields:
kwargs['update_fields'] = update_fields
self.full_clean()
return super().save(**kwargs)
class Meta:
abstract = True

Wyświetl plik

@ -0,0 +1,20 @@
from ckeditor_uploader.fields import RichTextUploadingField
from django.db import models
from django.utils.translation import ugettext_lazy as _
from inventory.models.base import BaseModel
class ItemModel(BaseModel):
"""
A Item that can be described and store somewhere ;)
"""
description = RichTextUploadingField(
config_name='ItemModel.description'
)
fcc_id = models.CharField(
max_length=20,
blank=True, null=True,
verbose_name='FCC ID',
help_text=_('FCC ID-Number for links to: https://fccid.io/')
)

Wyświetl plik

Wyświetl plik

@ -1,15 +1,12 @@
"""
Just print version line on every call from commandline ;)
"""
import sys
from inventory import __version__
if __name__ == "inventory_project":
#
# This will be called before the click cli
#
if "--version" not in sys.argv:
print(f"PyInventory v{__version__}")
if len(sys.argv) == 1:
# FIXME: How can be a "default" action set in click?
sys.argv.append("run-server")

Wyświetl plik

@ -0,0 +1,236 @@
'''
Django settings
'''
import logging
import sys as __sys
from pathlib import Path as __Path
from debug_toolbar.settings import CONFIG_DEFAULTS as DEBUG_TOOLBAR_CONFIG
from django.utils.translation import ugettext_lazy as _
print('Use settings:', __file__)
# Build paths inside the project:
BASE_PATH = __Path(__file__).resolve().parent
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'TODO: Read secret.txt ;)'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
SITE_ID = 1
# Required for the debug toolbar to be displayed:
INTERNAL_IPS = '*'
ALLOWED_HOSTS = INTERNAL_IPS
# Application definition
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'debug_toolbar', # https://github.com/jazzband/django-debug-toolbar/
'import_export', # https://github.com/django-import-export/django-import-export
'ckeditor', # https://github.com/django-ckeditor/django-ckeditor
'reversion', # https://github.com/etianen/django-reversion
'reversion_compare', # https://github.com/jedie/django-reversion-compare
'inventory.apps.InventoryConfig',
)
ROOT_URLCONF = 'inventory_project.urls'
WSGI_APPLICATION = 'inventory_project.wsgi.application'
MIDDLEWARE = (
'debug_toolbar.middleware.DebugToolbarMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django_tools.middlewares.ThreadLocal.ThreadLocalMiddleware',
)
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [str(__Path(BASE_PATH, 'templates/'))],
'OPTIONS': {
'loaders': [
(
'django.template.loaders.cached.Loader',
('django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader'),
)
],
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'django.template.context_processors.i18n',
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.template.context_processors.media',
'django.template.context_processors.csrf',
'django.template.context_processors.tz',
'django.template.context_processors.static',
],
},
}
]
if DEBUG:
# Disable caches:
CACHES = {'default': {'BACKEND': 'django.core.cache.backends.dummy.DummyCache'}}
# Disable CacheLoader:
TEMPLATES[0]['OPTIONS']['loaders'] = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)
# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': str(__Path(BASE_PATH.parent, 'PyInventory-database.sqlite3')),
# 'NAME': ':memory:'
# https://docs.djangoproject.com/en/dev/ref/databases/#database-is-locked-errors
'timeout': 30,
}
}
print(f'Use Database: {DATABASES["default"]["NAME"]!r}')
# _____________________________________________________________________________
# Internationalization
LANGUAGE_CODE = 'en'
LANGUAGES = [
('de', _('German')),
('en', _('English')),
]
USE_I18N = True
USE_L10N = True
TIME_ZONE = 'Europe/Paris'
USE_TZ = True
# _____________________________________________________________________________
# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/'
STATIC_ROOT = str(__Path(BASE_PATH, 'static'))
MEDIA_URL = '/media/'
MEDIA_ROOT = str(__Path(BASE_PATH, 'media'))
# _____________________________________________________________________________
# Django-Debug-Toolbar
# Disable some more panels that will slow down the page:
DEBUG_TOOLBAR_CONFIG['DISABLE_PANELS'].add('debug_toolbar.panels.sql.SQLPanel')
DEBUG_TOOLBAR_CONFIG['DISABLE_PANELS'].add('debug_toolbar.panels.cache.CachePanel')
# don't load jquery from ajax.googleapis.com, just use django's version:
DEBUG_TOOLBAR_CONFIG['JQUERY_URL'] = STATIC_URL + 'admin/js/vendor/jquery/jquery.min.js'
DEBUG_TOOLBAR_CONFIG['SHOW_COLLAPSED'] = True # Show toolbar collapsed by default.
# _____________________________________________________________________________
# django-ckeditor
CKEDITOR_BASEPATH = STATIC_URL + 'ckeditor/ckeditor/'
CKEDITOR_UPLOAD_PATH = 'uploads/'
CKEDITOR_FILENAME_GENERATOR = 'utils.get_filename'
CKEDITOR_CONFIGS = {
# 'ItemModel.description': {
# 'toolbar': 'full',
# 'height': '25em',
# 'width': '100%',
# 'removeButtons': 'Language,Flash,iframes,bidiltr'
# },
'ItemModel.description': {
'skin': 'moono-lisa',
# 'toolbar_Basic': [['Source', '-', 'Bold', 'Italic']],
# 'toolbar_Full': [
# [
# 'Styles',
# 'Format',
# 'Bold',
# 'Italic',
# 'Underline',
# 'Strike',
# 'SpellChecker',
# 'Undo',
# 'Redo',
# ],
# ['Link', 'Unlink', 'Anchor'],
# ['Image', 'Flash', 'Table', 'HorizontalRule'],
# ['TextColor', 'BGColor'],
# ['Smiley', 'SpecialChar'],
# ['Source'],
# ],
'removeButtons': 'Language',
# plugins are here: site-packages/ckeditor/static/ckeditor/ckeditor/plugins
'removePlugins': 'wsc,div,flash,iframe,bidi',
'toolbar': 'full',
'height': '25em',
'width': '100%',
'filebrowserWindowWidth': 940,
'filebrowserWindowHeight': 725,
}
}
# _____________________________________________________________________________
# cut 'pathname' in log output
old_factory = logging.getLogRecordFactory()
def cut_path(pathname, max_length):
if len(pathname) <= max_length:
return pathname
return f'...{pathname[-(max_length - 3):]}'
def record_factory(*args, **kwargs):
record = old_factory(*args, **kwargs)
record.cut_path = cut_path(record.pathname, 30)
return record
logging.setLogRecordFactory(record_factory)
# -----------------------------------------------------------------------------
LOGGING = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'colored': { # https://github.com/borntyping/python-colorlog
'()': 'colorlog.ColoredFormatter',
'format': '%(log_color)s%(asctime)s %(levelname)8s %(cut_path)s:%(lineno)-3s %(message)s',
}
},
'handlers': {'console': {'class': 'colorlog.StreamHandler', 'formatter': 'colored'}},
'loggers': {
'': {'handlers': ['console'], 'level': 'DEBUG', 'propagate': False},
'django': {'handlers': ['console'], 'level': 'INFO', 'propagate': False},
'inventory': {'handlers': ['console'], 'level': 'DEBUG', 'propagate': False},
},
}

Wyświetl plik

@ -0,0 +1,26 @@
from django.conf.urls import include, static, url
from django.conf.urls.i18n import i18n_patterns
from django.contrib import admin
from django.urls import path
from django.views.generic import RedirectView
from inventory_project import settings
admin.autodiscover()
urlpatterns = i18n_patterns(
path('admin/', admin.site.urls),
url(r'^$', RedirectView.as_view(url='/admin/')),
path('ckeditor/', include('ckeditor_uploader.urls')), # TODO: check permissions?
)
if settings.DEBUG:
urlpatterns += static.static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static.static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
import debug_toolbar
urlpatterns = [url(r'^__debug__/', include(debug_toolbar.urls))] + urlpatterns

Wyświetl plik

@ -0,0 +1,12 @@
"""
WSGI config
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "inventory_project.settings")
application = get_wsgi_application()

Wyświetl plik

@ -0,0 +1,3 @@
from inventory_project.settings import * # noqa
DEBUG = True

Wyświetl plik

@ -0,0 +1,17 @@
import pytest
from django.test import TestCase
@pytest.mark.django_db
class AdminAnonymousTests(TestCase):
"""
Anonymous will be redirected to the login page.
"""
def test_login_en(self):
response = self.client.get("/en/admin/", HTTP_ACCEPT_LANGUAGE="en")
self.assertRedirects(response, expected_url="/en/admin/login/?next=/en/admin/")
def test_login_de(self):
response = self.client.get("/de/admin/", HTTP_ACCEPT_LANGUAGE="de")
self.assertRedirects(response, expected_url="/de/admin/login/?next=/de/admin/")

21
manage.py 100644
Wyświetl plik

@ -0,0 +1,21 @@
#!/usr/bin/env python3
import os
import sys
def main():
os.environ['DJANGO_SETTINGS_MODULE'] = 'inventory_project.settings'
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
'Couldn\'t import Django. Are you sure it\'s installed and '
'available on your PYTHONPATH environment variable? Did you '
'forget to activate a virtual environment?'
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

3
manage.sh 100755
Wyświetl plik

@ -0,0 +1,3 @@
#!/bin/sh
exec poetry run python3 manage.py "$@"

668
poetry.lock wygenerowano

Plik diff jest za duży Load Diff

Wyświetl plik

@ -45,6 +45,9 @@ gunicorn = "*" # https://gunicorn.org/
django = "2.2.*"
django-debug-toolbar = "*" # http://django-debug-toolbar.readthedocs.io/en/stable/changes.html
django-import-export = "*" # https://github.com/django-import-export/django-import-export
django-tools = "*" # https://github.com/jedie/django-tools/
django-reversion-compare = "*" # https://github.com/jedie/django-reversion-compare/
django-ckeditor = "*" # https://github.com/django-ckeditor/django-ckeditor
[tool.poetry.dev-dependencies]
poetry-publish = "*" # https://github.com/jedie/poetry-publish
@ -61,10 +64,7 @@ autopep8 = "*"
pyupgrade = "*"
[tool.poetry.scripts]
# run the dev. server:
inventory = "inventory_project.cli:cli"
# run manage commands:
manage = "inventory_project.__main__:manage"
manage = "inventory_project.manage:main"
update_rst_readme = "inventory_project.publish:update_readme"
publish = "inventory_project.publish:publish"

Wyświetl plik

@ -3,7 +3,7 @@
# https://pytest-django.readthedocs.io/en/latest/
[pytest]
DJANGO_SETTINGS_MODULE=inventory_project.settings_tests
DJANGO_SETTINGS_MODULE=inventory_tests.settings
testpaths =
inventory
inventory_tests