kopia lustrzana https://github.com/jedie/PyInventory
Minimal Django project setup
rodzic
4fa1633eb0
commit
a5b0235b12
|
@ -11,7 +11,7 @@
|
|||
# from test projects:
|
||||
inventory_project/static/
|
||||
inventory_project/media/
|
||||
test_project_db.sqlite3
|
||||
*.sqlite3
|
||||
|
||||
# Coverage HTML Report files:
|
||||
htmlcov
|
||||
|
|
7
Makefile
7
Makefile
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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/')
|
||||
)
|
|
@ -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")
|
||||
|
|
|
@ -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},
|
||||
},
|
||||
}
|
|
@ -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
|
|
@ -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()
|
|
@ -0,0 +1,3 @@
|
|||
from inventory_project.settings import * # noqa
|
||||
|
||||
DEBUG = True
|
|
@ -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/")
|
|
@ -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()
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
exec poetry run python3 manage.py "$@"
|
Plik diff jest za duży
Load Diff
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue