mirror of
https://github.com/jazzband/django-dbtemplates.git
synced 2026-04-26 17:54:51 +00:00
Compare commits
No commits in common. "master" and "2.0" have entirely different histories.
38 changed files with 321 additions and 694 deletions
|
|
@ -3,4 +3,4 @@ source = dbtemplates
|
||||||
branch = 1
|
branch = 1
|
||||||
|
|
||||||
[report]
|
[report]
|
||||||
omit = *tests*,*/migrations/*,test_*
|
omit = *tests*,*migrations*
|
||||||
|
|
|
||||||
40
.github/workflows/release.yml
vendored
40
.github/workflows/release.yml
vendored
|
|
@ -1,40 +0,0 @@
|
||||||
name: Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- '*'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
if: github.repository == 'jazzband/django-dbtemplates'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: "3.10"
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
python -m pip install -U pip
|
|
||||||
python -m pip install -U setuptools twine wheel
|
|
||||||
|
|
||||||
- name: Build package
|
|
||||||
run: |
|
|
||||||
python setup.py --version
|
|
||||||
python setup.py sdist --format=gztar bdist_wheel
|
|
||||||
twine check dist/*
|
|
||||||
|
|
||||||
- name: Upload packages to Jazzband
|
|
||||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
|
||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
|
||||||
with:
|
|
||||||
user: jazzband
|
|
||||||
password: ${{ secrets.JAZZBAND_RELEASE_KEY }}
|
|
||||||
repository_url: https://jazzband.co/projects/django-dbtemplates/upload
|
|
||||||
48
.github/workflows/test.yml
vendored
48
.github/workflows/test.yml
vendored
|
|
@ -1,48 +0,0 @@
|
||||||
name: Test
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
max-parallel: 6
|
|
||||||
matrix:
|
|
||||||
python-version: ['3.8', '3.9', '3.10', '3.11', '3.13', '3.14']
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
|
|
||||||
- name: Get pip cache dir
|
|
||||||
id: pip-cache
|
|
||||||
run: |
|
|
||||||
echo "::set-output name=dir::$(pip cache dir)"
|
|
||||||
|
|
||||||
- name: Cache
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pip-cache.outputs.dir }}
|
|
||||||
key:
|
|
||||||
${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }}-${{ hashFiles('**/tox.ini') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ matrix.python-version }}-v1-
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
python -m pip install --upgrade tox tox-gh-actions
|
|
||||||
|
|
||||||
- name: Tox tests
|
|
||||||
run: |
|
|
||||||
tox -v
|
|
||||||
|
|
||||||
- name: Upload coverage
|
|
||||||
uses: codecov/codecov-action@v2
|
|
||||||
with:
|
|
||||||
name: Python ${{ matrix.python-version }}
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,6 +1,3 @@
|
||||||
.*
|
|
||||||
!.gitignore
|
|
||||||
!.coveragerc
|
|
||||||
*.pyc
|
*.pyc
|
||||||
.*.swp
|
.*.swp
|
||||||
MANIFEST
|
MANIFEST
|
||||||
|
|
@ -11,4 +8,3 @@ docs/_build
|
||||||
.tox/
|
.tox/
|
||||||
*.egg/
|
*.egg/
|
||||||
.coverage
|
.coverage
|
||||||
coverage.xml
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
repos:
|
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
|
||||||
rev: v3.21.2
|
|
||||||
hooks:
|
|
||||||
- id: pyupgrade
|
|
||||||
args: [--py37-plus]
|
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
||||||
rev: v4.3.0
|
|
||||||
hooks:
|
|
||||||
- id: check-merge-conflict
|
|
||||||
- id: check-yaml
|
|
||||||
|
|
||||||
ci:
|
|
||||||
autoupdate_schedule: quarterly
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
# .readthedocs.yaml
|
|
||||||
# Read the Docs configuration file
|
|
||||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
|
||||||
|
|
||||||
# Required
|
|
||||||
version: 2
|
|
||||||
|
|
||||||
# Set the version of Python and other tools you might need
|
|
||||||
build:
|
|
||||||
os: ubuntu-20.04
|
|
||||||
tools:
|
|
||||||
python: "3.9"
|
|
||||||
|
|
||||||
# Build documentation in the docs/ directory with Sphinx
|
|
||||||
sphinx:
|
|
||||||
configuration: docs/conf.py
|
|
||||||
|
|
||||||
# If using Sphinx, optionally build your docs in additional formats such as PDF
|
|
||||||
formats:
|
|
||||||
- pdf
|
|
||||||
|
|
||||||
# Optionally declare the Python requirements required to build your docs
|
|
||||||
python:
|
|
||||||
install:
|
|
||||||
- requirements: requirements/docs.txt
|
|
||||||
39
.travis.yml
Normal file
39
.travis.yml
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
language: python
|
||||||
|
python: 3.5
|
||||||
|
sudo: false
|
||||||
|
cache: pip
|
||||||
|
env:
|
||||||
|
- TOXENV=flake8-py27
|
||||||
|
- TOXENV=flake8-py35
|
||||||
|
- TOXENV=readme-py27
|
||||||
|
- TOXENV=py27-dj18
|
||||||
|
- TOXENV=py27-dj19
|
||||||
|
- TOXENV=py27-dj110
|
||||||
|
- TOXENV=py27-djmaster
|
||||||
|
- TOXENV=py34-dj18
|
||||||
|
- TOXENV=py34-dj19
|
||||||
|
- TOXENV=py34-dj110
|
||||||
|
- TOXENV=py34-djmaster
|
||||||
|
- TOXENV=py35-dj18
|
||||||
|
- TOXENV=py35-dj19
|
||||||
|
- TOXENV=py35-dj110
|
||||||
|
- TOXENV=py35-djmaster
|
||||||
|
- TOXENV=pypy-dj18
|
||||||
|
- TOXENV=pypy-dj19
|
||||||
|
- TOXENV=pypy-dj110
|
||||||
|
- TOXENV=pypy-djmaster
|
||||||
|
install:
|
||||||
|
- pip install tox codecov
|
||||||
|
script: tox -v
|
||||||
|
after_success:
|
||||||
|
- codecov
|
||||||
|
deploy:
|
||||||
|
provider: pypi
|
||||||
|
user: jazzband
|
||||||
|
distributions: "sdist bdist_wheel"
|
||||||
|
password:
|
||||||
|
secure: B7imNYNndd2HEr79+3/jXLjFo/MdzzHUkH1NJ7G+YFtYjEkRFEaVuPXhuX1LYO9/qzYDcqHVrnsg65ZUXCftAg0/zFZ7zhEn/WEYZA2nkPosoSrYNV+s3XH/DMmpXdG6mKRN4D0mLQXaCwGeuQ2wHFOg7HCsz+tojFwYnEyN9ag=
|
||||||
|
on:
|
||||||
|
tags: true
|
||||||
|
repo: jazzband/django-dbtemplates
|
||||||
|
condition: "$TOXENV = py27-dj110"
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
# Code of Conduct
|
|
||||||
|
|
||||||
As contributors and maintainers of the Jazzband projects, and in the interest of
|
|
||||||
fostering an open and welcoming community, we pledge to respect all people who
|
|
||||||
contribute through reporting issues, posting feature requests, updating documentation,
|
|
||||||
submitting pull requests or patches, and other activities.
|
|
||||||
|
|
||||||
We are committed to making participation in the Jazzband a harassment-free experience
|
|
||||||
for everyone, regardless of the level of experience, gender, gender identity and
|
|
||||||
expression, sexual orientation, disability, personal appearance, body size, race,
|
|
||||||
ethnicity, age, religion, or nationality.
|
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
|
||||||
|
|
||||||
- The use of sexualized language or imagery
|
|
||||||
- Personal attacks
|
|
||||||
- Trolling or insulting/derogatory comments
|
|
||||||
- Public or private harassment
|
|
||||||
- Publishing other's private information, such as physical or electronic addresses,
|
|
||||||
without explicit permission
|
|
||||||
- Other unethical or unprofessional conduct
|
|
||||||
|
|
||||||
The Jazzband roadies have the right and responsibility to remove, edit, or reject
|
|
||||||
comments, commits, code, wiki edits, issues, and other contributions that are not
|
|
||||||
aligned to this Code of Conduct, or to ban temporarily or permanently any contributor
|
|
||||||
for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
|
||||||
|
|
||||||
By adopting this Code of Conduct, the roadies commit themselves to fairly and
|
|
||||||
consistently applying these principles to every aspect of managing the jazzband
|
|
||||||
projects. Roadies who do not follow or enforce the Code of Conduct may be permanently
|
|
||||||
removed from the Jazzband roadies.
|
|
||||||
|
|
||||||
This code of conduct applies both within project spaces and in public spaces when an
|
|
||||||
individual is representing the project or its community.
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
|
|
||||||
contacting the roadies at `roadies@jazzband.co`. All complaints will be reviewed and
|
|
||||||
investigated and will result in a response that is deemed necessary and appropriate to
|
|
||||||
the circumstances. Roadies are obligated to maintain confidentiality with regard to the
|
|
||||||
reporter of an incident.
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version
|
|
||||||
1.3.0, available at [https://contributor-covenant.org/version/1/3/0/][version]
|
|
||||||
|
|
||||||
[homepage]: https://contributor-covenant.org
|
|
||||||
[version]: https://contributor-covenant.org/version/1/3/0/
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2007-2019, Jannis Leidel and contributors
|
Copyright (c) 2007-2016, Jannis Leidel and contributors
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@ django-dbtemplates
|
||||||
:alt: Jazzband
|
:alt: Jazzband
|
||||||
:target: https://jazzband.co/
|
:target: https://jazzband.co/
|
||||||
|
|
||||||
.. image:: https://github.com/jazzband/django-dbtemplates/workflows/Test/badge.svg
|
.. image:: https://travis-ci.org/jazzband/django-dbtemplates.svg?branch=master
|
||||||
:target: https://github.com/jazzband/django-dbtemplates/actions
|
:alt: Build Status
|
||||||
|
:target: http://travis-ci.org/jazzband/django-dbtemplates
|
||||||
|
|
||||||
.. image:: https://codecov.io/github/jazzband/django-dbtemplates/coverage.svg?branch=master
|
.. image:: https://codecov.io/github/jazzband/django-dbtemplates/coverage.svg?branch=master
|
||||||
:alt: Codecov
|
:alt: Codecov
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
import importlib.metadata
|
__version__ = "2.0"
|
||||||
|
|
||||||
__version__ = importlib.metadata.version("django-dbtemplates")
|
default_app_config = 'dbtemplates.apps.DBTemplatesConfig'
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,17 @@ import posixpath
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import ungettext, ugettext_lazy as _
|
||||||
from django.utils.translation import ngettext
|
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from dbtemplates.conf import settings
|
from dbtemplates.conf import settings
|
||||||
from dbtemplates.models import Template, add_template_to_cache, remove_cached_template
|
from dbtemplates.models import (Template, remove_cached_template,
|
||||||
|
add_template_to_cache)
|
||||||
from dbtemplates.utils.template import check_template_syntax
|
from dbtemplates.utils.template import check_template_syntax
|
||||||
|
|
||||||
# Check if either django-reversion-compare or django-reversion is installed and
|
# Check if django-reversion is installed and use reversions' VersionAdmin
|
||||||
# use reversion_compare's CompareVersionAdmin or reversion's VersionAdmin as
|
# as the base admin class if yes
|
||||||
# the base admin class if yes
|
if settings.DBTEMPLATES_USE_REVERSION:
|
||||||
if settings.DBTEMPLATES_USE_REVERSION_COMPARE:
|
|
||||||
from reversion_compare.admin import CompareVersionAdmin \
|
|
||||||
as TemplateModelAdmin
|
|
||||||
elif settings.DBTEMPLATES_USE_REVERSION:
|
|
||||||
from reversion.admin import VersionAdmin as TemplateModelAdmin
|
from reversion.admin import VersionAdmin as TemplateModelAdmin
|
||||||
else:
|
else:
|
||||||
from django.contrib.admin import ModelAdmin as TemplateModelAdmin # noqa
|
from django.contrib.admin import ModelAdmin as TemplateModelAdmin # noqa
|
||||||
|
|
@ -31,14 +27,13 @@ class CodeMirrorTextArea(forms.Textarea):
|
||||||
class Media:
|
class Media:
|
||||||
css = dict(screen=[posixpath.join(
|
css = dict(screen=[posixpath.join(
|
||||||
settings.DBTEMPLATES_MEDIA_PREFIX, 'css/editor.css')])
|
settings.DBTEMPLATES_MEDIA_PREFIX, 'css/editor.css')])
|
||||||
js = [posixpath.join(settings.DBTEMPLATES_MEDIA_PREFIX,
|
js = [posixpath.join(settings.DBTEMPLATES_MEDIA_PREFIX, 'js/codemirror.js')]
|
||||||
'js/codemirror.js')]
|
|
||||||
|
|
||||||
def render(self, name, value, attrs=None, renderer=None):
|
def render(self, name, value, attrs=None):
|
||||||
result = []
|
result = []
|
||||||
result.append(
|
result.append(
|
||||||
super().render(name, value, attrs))
|
super(CodeMirrorTextArea, self).render(name, value, attrs))
|
||||||
result.append("""
|
result.append(u"""
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var editor = CodeMirror.fromTextArea('id_%(name)s', {
|
var editor = CodeMirror.fromTextArea('id_%(name)s', {
|
||||||
path: "%(media_prefix)sjs/",
|
path: "%(media_prefix)sjs/",
|
||||||
|
|
@ -52,8 +47,7 @@ class CodeMirrorTextArea(forms.Textarea):
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
""" % dict(media_prefix=settings.DBTEMPLATES_MEDIA_PREFIX, name=name))
|
""" % dict(media_prefix=settings.DBTEMPLATES_MEDIA_PREFIX, name=name))
|
||||||
return mark_safe("".join(result))
|
return mark_safe(u"".join(result))
|
||||||
|
|
||||||
|
|
||||||
if settings.DBTEMPLATES_USE_CODEMIRROR:
|
if settings.DBTEMPLATES_USE_CODEMIRROR:
|
||||||
TemplateContentTextArea = CodeMirrorTextArea
|
TemplateContentTextArea = CodeMirrorTextArea
|
||||||
|
|
@ -86,7 +80,7 @@ class TemplateAdminForm(forms.ModelForm):
|
||||||
Custom AdminForm to make the content textarea wider.
|
Custom AdminForm to make the content textarea wider.
|
||||||
"""
|
"""
|
||||||
content = forms.CharField(
|
content = forms.CharField(
|
||||||
widget=TemplateContentTextArea(attrs={'rows': '24'}),
|
widget=TemplateContentTextArea({'rows': '24'}),
|
||||||
help_text=content_help_text, required=False)
|
help_text=content_help_text, required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -97,7 +91,6 @@ class TemplateAdminForm(forms.ModelForm):
|
||||||
|
|
||||||
class TemplateAdmin(TemplateModelAdmin):
|
class TemplateAdmin(TemplateModelAdmin):
|
||||||
form = TemplateAdminForm
|
form = TemplateAdminForm
|
||||||
readonly_fields = ['creation_date', 'last_changed']
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {
|
(None, {
|
||||||
'fields': ('name', 'content'),
|
'fields': ('name', 'content'),
|
||||||
|
|
@ -122,7 +115,7 @@ class TemplateAdmin(TemplateModelAdmin):
|
||||||
for template in queryset:
|
for template in queryset:
|
||||||
remove_cached_template(template)
|
remove_cached_template(template)
|
||||||
count = queryset.count()
|
count = queryset.count()
|
||||||
message = ngettext(
|
message = ungettext(
|
||||||
"Cache of one template successfully invalidated.",
|
"Cache of one template successfully invalidated.",
|
||||||
"Cache of %(count)d templates successfully invalidated.",
|
"Cache of %(count)d templates successfully invalidated.",
|
||||||
count)
|
count)
|
||||||
|
|
@ -134,7 +127,7 @@ class TemplateAdmin(TemplateModelAdmin):
|
||||||
for template in queryset:
|
for template in queryset:
|
||||||
add_template_to_cache(template)
|
add_template_to_cache(template)
|
||||||
count = queryset.count()
|
count = queryset.count()
|
||||||
message = ngettext(
|
message = ungettext(
|
||||||
"Cache successfully repopulated with one template.",
|
"Cache successfully repopulated with one template.",
|
||||||
"Cache successfully repopulated with %(count)d templates.",
|
"Cache successfully repopulated with %(count)d templates.",
|
||||||
count)
|
count)
|
||||||
|
|
@ -147,19 +140,18 @@ class TemplateAdmin(TemplateModelAdmin):
|
||||||
for template in queryset:
|
for template in queryset:
|
||||||
valid, error = check_template_syntax(template)
|
valid, error = check_template_syntax(template)
|
||||||
if not valid:
|
if not valid:
|
||||||
errors.append(f'{template.name}: {error}')
|
errors.append('%s: %s' % (template.name, error))
|
||||||
if errors:
|
if errors:
|
||||||
count = len(errors)
|
count = len(errors)
|
||||||
message = ngettext(
|
message = ungettext(
|
||||||
"Template syntax check FAILED for %(names)s.",
|
"Template syntax check FAILED for %(names)s.",
|
||||||
"Template syntax check FAILED for "
|
"Template syntax check FAILED for %(count)d templates: %(names)s.",
|
||||||
"%(count)d templates: %(names)s.",
|
|
||||||
count)
|
count)
|
||||||
self.message_user(request, message %
|
self.message_user(request, message %
|
||||||
{'count': count, 'names': ', '.join(errors)})
|
{'count': count, 'names': ', '.join(errors)})
|
||||||
else:
|
else:
|
||||||
count = queryset.count()
|
count = queryset.count()
|
||||||
message = ngettext(
|
message = ungettext(
|
||||||
"Template syntax OK.",
|
"Template syntax OK.",
|
||||||
"Template syntax OK for %(count)d templates.", count)
|
"Template syntax OK for %(count)d templates.", count)
|
||||||
self.message_user(request, message % {'count': count})
|
self.message_user(request, message % {'count': count})
|
||||||
|
|
@ -169,5 +161,4 @@ class TemplateAdmin(TemplateModelAdmin):
|
||||||
return ", ".join([site.name for site in template.sites.all()])
|
return ", ".join([site.name for site in template.sites.all()])
|
||||||
site_list.short_description = _('sites')
|
site_list.short_description = _('sites')
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Template, TemplateAdmin)
|
admin.site.register(Template, TemplateAdmin)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class DBTemplatesConfig(AppConfig):
|
class DBTemplatesConfig(AppConfig):
|
||||||
name = 'dbtemplates'
|
name = 'dbtemplates'
|
||||||
verbose_name = _('Database templates')
|
verbose_name = _('Database templates')
|
||||||
|
|
||||||
default_auto_field = 'django.db.models.AutoField'
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import posixpath
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.six import string_types
|
||||||
|
|
||||||
from appconf import AppConf
|
from appconf import AppConf
|
||||||
|
|
||||||
|
|
@ -9,7 +10,6 @@ from appconf import AppConf
|
||||||
class DbTemplatesConf(AppConf):
|
class DbTemplatesConf(AppConf):
|
||||||
USE_CODEMIRROR = False
|
USE_CODEMIRROR = False
|
||||||
USE_REVERSION = False
|
USE_REVERSION = False
|
||||||
USE_REVERSION_COMPARE = False
|
|
||||||
USE_TINYMCE = False
|
USE_TINYMCE = False
|
||||||
USE_REDACTOR = False
|
USE_REDACTOR = False
|
||||||
ADD_DEFAULT_SITE = True
|
ADD_DEFAULT_SITE = True
|
||||||
|
|
@ -32,7 +32,7 @@ class DbTemplatesConf(AppConf):
|
||||||
return "dbtemplates"
|
return "dbtemplates"
|
||||||
else:
|
else:
|
||||||
return "default"
|
return "default"
|
||||||
if isinstance(value, str) and value.startswith("dbtemplates."):
|
if isinstance(value, string_types) and value.startswith("dbtemplates."):
|
||||||
raise ImproperlyConfigured("Please upgrade to one of the "
|
raise ImproperlyConfigured("Please upgrade to one of the "
|
||||||
"supported backends as defined "
|
"supported backends as defined "
|
||||||
"in the Django docs.")
|
"in the Django docs.")
|
||||||
|
|
@ -45,13 +45,6 @@ class DbTemplatesConf(AppConf):
|
||||||
"use of it in dbtemplates.")
|
"use of it in dbtemplates.")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def configure_use_reversion_compare(self, value):
|
|
||||||
if value and 'reversion_compare' not in settings.INSTALLED_APPS:
|
|
||||||
raise ImproperlyConfigured("Please add 'reversion_compare' to your"
|
|
||||||
" INSTALLED_APPS setting to make "
|
|
||||||
"use of it in dbtemplates.")
|
|
||||||
return value
|
|
||||||
|
|
||||||
def configure_use_tinymce(self, value):
|
def configure_use_tinymce(self, value):
|
||||||
if value and 'tinymce' not in settings.INSTALLED_APPS:
|
if value and 'tinymce' not in settings.INSTALLED_APPS:
|
||||||
raise ImproperlyConfigured("Please add 'tinymce' to your "
|
raise ImproperlyConfigured("Please add 'tinymce' to your "
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.db import router
|
from django.db import router
|
||||||
from django.template import Origin, TemplateDoesNotExist
|
from django.template import TemplateDoesNotExist
|
||||||
from django.template.loaders.base import Loader as BaseLoader
|
from django.template.loaders.base import Loader as BaseLoader
|
||||||
|
|
||||||
from dbtemplates.models import Template
|
from dbtemplates.models import Template
|
||||||
|
|
@ -19,25 +19,13 @@ class Loader(BaseLoader):
|
||||||
"""
|
"""
|
||||||
is_usable = True
|
is_usable = True
|
||||||
|
|
||||||
def get_template_sources(self, template_name, template_dirs=None):
|
def load_and_store_template(self, template_name, cache_key, site, **params):
|
||||||
yield Origin(
|
|
||||||
name=template_name,
|
|
||||||
template_name=template_name,
|
|
||||||
loader=self,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_contents(self, origin):
|
|
||||||
content, _ = self._load_template_source(origin.template_name)
|
|
||||||
return content
|
|
||||||
|
|
||||||
def _load_and_store_template(self, template_name, cache_key, site,
|
|
||||||
**params):
|
|
||||||
template = Template.objects.get(name__exact=template_name, **params)
|
template = Template.objects.get(name__exact=template_name, **params)
|
||||||
db = router.db_for_read(Template, instance=template)
|
db = router.db_for_read(Template, instance=template)
|
||||||
display_name = f'dbtemplates:{db}:{template_name}:{site.domain}'
|
display_name = 'dbtemplates:%s:%s:%s' % (db, template_name, site.domain)
|
||||||
return set_and_return(cache_key, template.content, display_name)
|
return set_and_return(cache_key, template.content, display_name)
|
||||||
|
|
||||||
def _load_template_source(self, template_name, template_dirs=None):
|
def load_template_source(self, template_name, template_dirs=None):
|
||||||
# The logic should work like this:
|
# The logic should work like this:
|
||||||
# * Try to find the template in the cache. If found, return it.
|
# * Try to find the template in the cache. If found, return it.
|
||||||
# * Now check the cache if a lookup for the given template
|
# * Now check the cache if a lookup for the given template
|
||||||
|
|
@ -57,7 +45,7 @@ class Loader(BaseLoader):
|
||||||
backend_template = cache.get(cache_key)
|
backend_template = cache.get(cache_key)
|
||||||
if backend_template:
|
if backend_template:
|
||||||
return backend_template, template_name
|
return backend_template, template_name
|
||||||
except Exception:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Not found in cache, move on.
|
# Not found in cache, move on.
|
||||||
|
|
@ -67,18 +55,18 @@ class Loader(BaseLoader):
|
||||||
notfound = cache.get(cache_notfound_key)
|
notfound = cache.get(cache_notfound_key)
|
||||||
if notfound:
|
if notfound:
|
||||||
raise TemplateDoesNotExist(template_name)
|
raise TemplateDoesNotExist(template_name)
|
||||||
except Exception:
|
except:
|
||||||
raise TemplateDoesNotExist(template_name)
|
raise TemplateDoesNotExist(template_name)
|
||||||
|
|
||||||
# Not marked as not-found, move on...
|
# Not marked as not-found, move on...
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self._load_and_store_template(template_name, cache_key,
|
return self.load_and_store_template(template_name, cache_key,
|
||||||
site, sites__in=[site.id])
|
site, sites__in=[site.id])
|
||||||
except (Template.MultipleObjectsReturned, Template.DoesNotExist):
|
except (Template.MultipleObjectsReturned, Template.DoesNotExist):
|
||||||
try:
|
try:
|
||||||
return self._load_and_store_template(template_name, cache_key,
|
return self.load_and_store_template(template_name, cache_key,
|
||||||
site, sites__isnull=True)
|
site, sites__isnull=True)
|
||||||
except (Template.MultipleObjectsReturned, Template.DoesNotExist):
|
except (Template.MultipleObjectsReturned, Template.DoesNotExist):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -16,6 +16,7 @@ msgstr ""
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
#"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
||||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ class Command(BaseCommand):
|
||||||
for template in Template.objects.all():
|
for template in Template.objects.all():
|
||||||
valid, error = check_template_syntax(template)
|
valid, error = check_template_syntax(template)
|
||||||
if not valid:
|
if not valid:
|
||||||
errors.append(f'{template.name}: {error}')
|
errors.append('%s: %s' % (template.name, error))
|
||||||
if errors:
|
if errors:
|
||||||
raise CommandError(
|
raise CommandError(
|
||||||
'Some templates contained errors\n%s' % '\n'.join(errors))
|
'Some templates contained errors\n%s' % '\n'.join(errors))
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class Command(BaseCommand):
|
||||||
verbosity = int(options.get('verbosity', 1))
|
verbosity = int(options.get('verbosity', 1))
|
||||||
for error_code in (404, 500):
|
for error_code in (404, 500):
|
||||||
template, created = Template.objects.get_or_create(
|
template, created = Template.objects.get_or_create(
|
||||||
name=f"{error_code}.html")
|
name="%s.html" % error_code)
|
||||||
if created or (not created and force):
|
if created or (not created and force):
|
||||||
template.content = TEMPLATES.get(error_code, '')
|
template.content = TEMPLATES.get(error_code, '')
|
||||||
template.save()
|
template.save()
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,22 @@
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
|
from django.contrib.sites.models import Site
|
||||||
|
from django.core.management.base import CommandError, BaseCommand
|
||||||
|
from django.template.utils import get_app_template_dirs
|
||||||
|
from django.template.loader import _engine_list
|
||||||
|
try:
|
||||||
|
from django.utils.six import input as raw_input
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
from dbtemplates.models import Template
|
from dbtemplates.models import Template
|
||||||
from django.contrib.sites.models import Site
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
|
||||||
from django.template.loader import _engine_list
|
|
||||||
from django.template.utils import get_app_template_dirs
|
|
||||||
|
|
||||||
ALWAYS_ASK, FILES_TO_DATABASE, DATABASE_TO_FILES = ("0", "1", "2")
|
ALWAYS_ASK, FILES_TO_DATABASE, DATABASE_TO_FILES = ('0', '1', '2')
|
||||||
|
|
||||||
DIRS = []
|
DIRS = []
|
||||||
for engine in _engine_list():
|
for engine in _engine_list():
|
||||||
DIRS.extend(engine.dirs)
|
DIRS.extend(engine.dirs)
|
||||||
DIRS = tuple(DIRS)
|
app_template_dirs = get_app_template_dirs('templates')
|
||||||
app_template_dirs = get_app_template_dirs("templates")
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
|
|
@ -20,142 +24,88 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-e",
|
"-e", "--ext",
|
||||||
"--ext",
|
dest="ext", action="store", default="html",
|
||||||
dest="ext",
|
|
||||||
action="store",
|
|
||||||
default="html",
|
|
||||||
help="extension of the files you want to "
|
help="extension of the files you want to "
|
||||||
"sync with the database [default: %(default)s]",
|
"sync with the database [default: %default]")
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-f",
|
"-f", "--force",
|
||||||
"--force",
|
action="store_true", dest="force", default=False,
|
||||||
action="store_true",
|
help="overwrite existing database templates")
|
||||||
dest="force",
|
|
||||||
default=False,
|
|
||||||
help="overwrite existing database templates",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-o",
|
"-o", "--overwrite",
|
||||||
"--overwrite",
|
action="store", dest="overwrite", default='0',
|
||||||
action="store",
|
|
||||||
dest="overwrite",
|
|
||||||
default="0",
|
|
||||||
help="'0' - ask always, '1' - overwrite database "
|
help="'0' - ask always, '1' - overwrite database "
|
||||||
"templates from template files, '2' - overwrite "
|
"templates from template files, '2' - overwrite "
|
||||||
"template files from database templates",
|
"template files from database templates")
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-a",
|
"-a", "--app-first",
|
||||||
"--app-first",
|
action="store_true", dest="app_first", default=False,
|
||||||
action="store_true",
|
|
||||||
dest="app_first",
|
|
||||||
default=False,
|
|
||||||
help="look for templates in applications "
|
help="look for templates in applications "
|
||||||
"directories before project templates",
|
"directories before project templates")
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-d",
|
"-d", "--delete",
|
||||||
"--delete",
|
action="store_true", dest="delete", default=False,
|
||||||
action="store_true",
|
help="Delete templates after syncing")
|
||||||
dest="delete",
|
|
||||||
default=False,
|
|
||||||
help="Delete templates after syncing",
|
|
||||||
)
|
|
||||||
group = parser.add_mutually_exclusive_group()
|
|
||||||
group.add_argument(
|
|
||||||
"-y",
|
|
||||||
"--yes",
|
|
||||||
action="store_true",
|
|
||||||
dest="auto_answer",
|
|
||||||
default=None,
|
|
||||||
help="Answer yes to all template creation questions."
|
|
||||||
)
|
|
||||||
group.add_argument(
|
|
||||||
"-n",
|
|
||||||
"--no",
|
|
||||||
action="store_false",
|
|
||||||
dest="auto_answer",
|
|
||||||
default=None,
|
|
||||||
help="Answer no to all template creation questions."
|
|
||||||
)
|
|
||||||
|
|
||||||
def handle(self, **options):
|
def handle(self, **options):
|
||||||
extension = options.get("ext")
|
extension = options.get('ext')
|
||||||
force = options.get("force")
|
force = options.get('force')
|
||||||
overwrite = options.get("overwrite")
|
overwrite = options.get('overwrite')
|
||||||
app_first = options.get("app_first")
|
app_first = options.get('app_first')
|
||||||
delete = options.get("delete")
|
delete = options.get('delete')
|
||||||
auto_answer = options.get('auto_answer')
|
|
||||||
|
|
||||||
if not extension.startswith("."):
|
if not extension.startswith("."):
|
||||||
extension = f".{extension}"
|
extension = ".%s" % extension
|
||||||
|
|
||||||
try:
|
try:
|
||||||
site = Site.objects.get_current()
|
site = Site.objects.get_current()
|
||||||
except Exception:
|
except:
|
||||||
raise CommandError(
|
raise CommandError("Please make sure to have the sites contrib "
|
||||||
"Please make sure to have the sites contrib "
|
"app installed and setup with a site object")
|
||||||
"app installed and setup with a site object"
|
|
||||||
)
|
|
||||||
|
|
||||||
if app_first:
|
if app_first:
|
||||||
tpl_dirs = app_template_dirs + DIRS
|
tpl_dirs = app_template_dirs + DIRS
|
||||||
else:
|
else:
|
||||||
tpl_dirs = DIRS + app_template_dirs
|
tpl_dirs = DIRS + app_template_dirs
|
||||||
templatedirs = [str(d) for d in tpl_dirs if os.path.isdir(d)]
|
templatedirs = [d for d in tpl_dirs if os.path.isdir(d)]
|
||||||
|
|
||||||
for templatedir in templatedirs:
|
for templatedir in templatedirs:
|
||||||
for dirpath, subdirs, filenames in os.walk(templatedir):
|
for dirpath, subdirs, filenames in os.walk(templatedir):
|
||||||
for f in [
|
for f in [f for f in filenames
|
||||||
f
|
if f.endswith(extension) and not f.startswith(".")]:
|
||||||
for f in filenames
|
|
||||||
if f.endswith(extension) and not f.startswith(".")
|
|
||||||
]:
|
|
||||||
path = os.path.join(dirpath, f)
|
path = os.path.join(dirpath, f)
|
||||||
name = path.split(str(templatedir))[1]
|
name = path.split(templatedir)[1]
|
||||||
if name.startswith("/"):
|
if name.startswith('/'):
|
||||||
name = name[1:]
|
name = name[1:]
|
||||||
try:
|
try:
|
||||||
t = Template.on_site.get(name__exact=name)
|
t = Template.on_site.get(name__exact=name)
|
||||||
except Template.DoesNotExist:
|
except Template.DoesNotExist:
|
||||||
if not force:
|
if not force:
|
||||||
if auto_answer is not None:
|
confirm = raw_input(
|
||||||
confirm = "y" if auto_answer else "n"
|
"\nA '%s' template doesn't exist in the "
|
||||||
else:
|
"database.\nCreate it with '%s'?"
|
||||||
confirm = input(
|
" (y/[n]): """ % (name, path))
|
||||||
"\nA '%s' template doesn't exist in the "
|
if force or confirm.lower().startswith('y'):
|
||||||
"database.\nCreate it with '%s'?"
|
with io.open(path, encoding='utf-8') as f:
|
||||||
" (y/[n]): "
|
|
||||||
"" % (name, path)
|
|
||||||
)
|
|
||||||
if force or confirm.lower().startswith("y"):
|
|
||||||
with open(path, encoding="utf-8") as f:
|
|
||||||
t = Template(name=name, content=f.read())
|
t = Template(name=name, content=f.read())
|
||||||
t.save()
|
t.save()
|
||||||
t.sites.add(site)
|
t.sites.add(site)
|
||||||
else:
|
else:
|
||||||
while True:
|
while 1:
|
||||||
if overwrite == ALWAYS_ASK:
|
if overwrite == ALWAYS_ASK:
|
||||||
_i = (
|
confirm = raw_input(
|
||||||
"\n%(template)s exists in the database.\n"
|
"\n%(template)s exists in the database.\n"
|
||||||
"(1) Overwrite %(template)s with '%(path)s'\n" # noqa
|
"(1) Overwrite %(template)s with '%(path)s'\n"
|
||||||
"(2) Overwrite '%(path)s' with %(template)s\n" # noqa
|
"(2) Overwrite '%(path)s' with %(template)s\n"
|
||||||
"Type 1 or 2 or press <Enter> to skip: "
|
"Type 1 or 2 or press <Enter> to skip: " %
|
||||||
% {"template": t.__repr__(), "path": path}
|
{'template': t.__repr__(), 'path': path})
|
||||||
)
|
|
||||||
|
|
||||||
confirm = input(_i)
|
|
||||||
else:
|
else:
|
||||||
confirm = overwrite
|
confirm = overwrite
|
||||||
if confirm in (
|
if confirm in ('', FILES_TO_DATABASE,
|
||||||
"",
|
DATABASE_TO_FILES):
|
||||||
FILES_TO_DATABASE,
|
|
||||||
DATABASE_TO_FILES,
|
|
||||||
):
|
|
||||||
if confirm == FILES_TO_DATABASE:
|
if confirm == FILES_TO_DATABASE:
|
||||||
with open(path, encoding="utf-8") as f:
|
with io.open(path, encoding='utf-8') as f:
|
||||||
t.content = f.read()
|
t.content = f.read()
|
||||||
t.save()
|
t.save()
|
||||||
t.sites.add(site)
|
t.sites.add(site)
|
||||||
|
|
@ -164,10 +114,9 @@ class Command(BaseCommand):
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
except OSError:
|
except OSError:
|
||||||
raise CommandError(
|
raise CommandError(
|
||||||
f"Couldn't delete {path}"
|
u"Couldn't delete %s" % path)
|
||||||
)
|
|
||||||
elif confirm == DATABASE_TO_FILES:
|
elif confirm == DATABASE_TO_FILES:
|
||||||
with open(path, "w", encoding="utf-8") as f: # noqa
|
with io.open(path, 'w', encoding='utf-8') as f:
|
||||||
f.write(t.content)
|
f.write(t.content)
|
||||||
if delete:
|
if delete:
|
||||||
t.delete()
|
t.delete()
|
||||||
|
|
|
||||||
|
|
@ -1,73 +1,44 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import django
|
import django
|
||||||
|
from django.db import models, migrations
|
||||||
import django.utils.timezone
|
import django.utils.timezone
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
("sites", "0001_initial"),
|
('sites', '0001_initial'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="Template",
|
name='Template',
|
||||||
fields=[
|
fields=[
|
||||||
(
|
('id', models.AutoField(
|
||||||
"id",
|
verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||||
models.AutoField(
|
('name', models.CharField(
|
||||||
verbose_name="ID",
|
help_text="Example: 'flatpages/default.html'", max_length=100, verbose_name='name')),
|
||||||
serialize=False,
|
('content', models.TextField(verbose_name='content', blank=True)),
|
||||||
auto_created=True,
|
('creation_date', models.DateTimeField(
|
||||||
primary_key=True,
|
default=django.utils.timezone.now, verbose_name='creation date')),
|
||||||
),
|
('last_changed', models.DateTimeField(
|
||||||
),
|
default=django.utils.timezone.now, verbose_name='last changed')),
|
||||||
(
|
('sites', models.ManyToManyField(
|
||||||
"name",
|
to='sites.Site', verbose_name='sites', blank=True)),
|
||||||
models.CharField(
|
|
||||||
help_text="Example: 'flatpages/default.html'",
|
|
||||||
max_length=100,
|
|
||||||
verbose_name="name",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"content",
|
|
||||||
models.TextField(verbose_name="content", blank=True),
|
|
||||||
), # noqa
|
|
||||||
(
|
|
||||||
"creation_date",
|
|
||||||
models.DateTimeField(
|
|
||||||
default=django.utils.timezone.now,
|
|
||||||
verbose_name="creation date", # noqa
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"last_changed",
|
|
||||||
models.DateTimeField(
|
|
||||||
default=django.utils.timezone.now,
|
|
||||||
verbose_name="last changed", # noqa
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"sites",
|
|
||||||
models.ManyToManyField(
|
|
||||||
to="sites.Site", verbose_name="sites", blank=True
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
"ordering": ("name",),
|
'ordering': ('name',),
|
||||||
"db_table": "django_template",
|
'db_table': 'django_template',
|
||||||
"verbose_name": "template",
|
'verbose_name': 'template',
|
||||||
"verbose_name_plural": "templates",
|
'verbose_name_plural': 'templates',
|
||||||
},
|
},
|
||||||
bases=(models.Model,),
|
bases=(models.Model,),
|
||||||
managers=[
|
managers=[
|
||||||
("objects", django.db.models.manager.Manager()),
|
('objects', django.db.models.manager.Manager()),
|
||||||
(
|
('on_site', django.contrib.sites.managers.CurrentSiteManager(
|
||||||
"on_site",
|
b'sites')),
|
||||||
django.contrib.sites.managers.CurrentSiteManager("sites"),
|
|
||||||
), # noqa
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
# Generated by Django 5.1 on 2025-05-26 19:59
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('dbtemplates', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='template',
|
|
||||||
name='creation_date',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, verbose_name='creation date'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='template',
|
|
||||||
name='last_changed',
|
|
||||||
field=models.DateTimeField(auto_now=True, verbose_name='last changed'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
# Generated by Django 4.2.23 on 2025-07-28 13:34
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
("dbtemplates", "0002_alter_template_creation_date_and_more"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="template",
|
|
||||||
name="id",
|
|
||||||
field=models.BigAutoField(
|
|
||||||
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
from dbtemplates.conf import settings
|
from dbtemplates.conf import settings
|
||||||
from dbtemplates.utils.cache import (
|
from dbtemplates.utils.cache import (add_template_to_cache,
|
||||||
add_template_to_cache,
|
remove_cached_template)
|
||||||
remove_cached_template,
|
|
||||||
)
|
|
||||||
from dbtemplates.utils.template import get_template_source
|
from dbtemplates.utils.template import get_template_source
|
||||||
|
|
||||||
from django.contrib.sites.managers import CurrentSiteManager
|
from django.contrib.sites.managers import CurrentSiteManager
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from django.template import TemplateDoesNotExist
|
from django.template import TemplateDoesNotExist
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.utils.timezone import now
|
||||||
|
|
||||||
|
|
||||||
class Template(models.Model):
|
class Template(models.Model):
|
||||||
|
|
@ -18,15 +17,15 @@ class Template(models.Model):
|
||||||
Defines a template model for use with the database template loader.
|
Defines a template model for use with the database template loader.
|
||||||
The field ``name`` is the equivalent to the filename of a static template.
|
The field ``name`` is the equivalent to the filename of a static template.
|
||||||
"""
|
"""
|
||||||
id = models.BigAutoField(primary_key=True, verbose_name=_('ID'),
|
|
||||||
serialize=False, auto_created=True)
|
|
||||||
name = models.CharField(_('name'), max_length=100,
|
name = models.CharField(_('name'), max_length=100,
|
||||||
help_text=_("Example: 'flatpages/default.html'"))
|
help_text=_("Example: 'flatpages/default.html'"))
|
||||||
content = models.TextField(_('content'), blank=True)
|
content = models.TextField(_('content'), blank=True)
|
||||||
sites = models.ManyToManyField(Site, verbose_name=_('sites'),
|
sites = models.ManyToManyField(Site, verbose_name=_(u'sites'),
|
||||||
blank=True)
|
blank=True)
|
||||||
creation_date = models.DateTimeField(_('creation date'), auto_now_add=True)
|
creation_date = models.DateTimeField(_('creation date'),
|
||||||
last_changed = models.DateTimeField(_('last changed'), auto_now=True)
|
default=now)
|
||||||
|
last_changed = models.DateTimeField(_('last changed'),
|
||||||
|
default=now)
|
||||||
|
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
on_site = CurrentSiteManager('sites')
|
on_site = CurrentSiteManager('sites')
|
||||||
|
|
@ -37,7 +36,7 @@ class Template(models.Model):
|
||||||
verbose_name_plural = _('templates')
|
verbose_name_plural = _('templates')
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
|
|
||||||
def __str__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def populate(self, name=None):
|
def populate(self, name=None):
|
||||||
|
|
@ -55,11 +54,12 @@ class Template(models.Model):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
self.last_changed = now()
|
||||||
# If content is empty look for a template with the given name and
|
# If content is empty look for a template with the given name and
|
||||||
# populate the template instance with its content.
|
# populate the template instance with its content.
|
||||||
if settings.DBTEMPLATES_AUTO_POPULATE_CONTENT and not self.content:
|
if settings.DBTEMPLATES_AUTO_POPULATE_CONTENT and not self.content:
|
||||||
self.populate()
|
self.populate()
|
||||||
super().save(*args, **kwargs)
|
super(Template, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def add_default_site(instance, **kwargs):
|
def add_default_site(instance, **kwargs):
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
from django.conf import settings as django_settings
|
from django.conf import settings as django_settings
|
||||||
from django.core.cache.backends.base import BaseCache
|
from django.core.cache.backends.base import BaseCache
|
||||||
|
|
@ -22,12 +22,11 @@ from dbtemplates.management.commands.sync_templates import (FILES_TO_DATABASE,
|
||||||
|
|
||||||
class DbTemplatesTestCase(TestCase):
|
class DbTemplatesTestCase(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.old_TEMPLATES = settings.TEMPLATES
|
self.old_template_loaders = settings.TEMPLATE_LOADERS
|
||||||
if 'dbtemplates.loader.Loader' not in settings.TEMPLATES:
|
if 'dbtemplates.loader.Loader' not in settings.TEMPLATE_LOADERS:
|
||||||
loader.template_source_loaders = None
|
loader.template_source_loaders = None
|
||||||
settings.TEMPLATES = list(settings.TEMPLATES) + [
|
settings.TEMPLATE_LOADERS = (list(settings.TEMPLATE_LOADERS) +
|
||||||
'dbtemplates.loader.Loader'
|
['dbtemplates.loader.Loader'])
|
||||||
]
|
|
||||||
|
|
||||||
self.site1, created1 = Site.objects.get_or_create(
|
self.site1, created1 = Site.objects.get_or_create(
|
||||||
domain="example.com", name="example.com")
|
domain="example.com", name="example.com")
|
||||||
|
|
@ -41,9 +40,9 @@ class DbTemplatesTestCase(TestCase):
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
loader.template_source_loaders = None
|
loader.template_source_loaders = None
|
||||||
settings.TEMPLATES = self.old_TEMPLATES
|
settings.TEMPLATE_LOADERS = self.old_template_loaders
|
||||||
|
|
||||||
def test_basics(self):
|
def test_basiscs(self):
|
||||||
self.assertEqual(list(self.t1.sites.all()), [self.site1])
|
self.assertEqual(list(self.t1.sites.all()), [self.site1])
|
||||||
self.assertTrue("base" in self.t1.content)
|
self.assertTrue("base" in self.t1.content)
|
||||||
self.assertEqual(list(Template.objects.filter(sites=self.site1)),
|
self.assertEqual(list(Template.objects.filter(sites=self.site1)),
|
||||||
|
|
@ -103,9 +102,9 @@ class DbTemplatesTestCase(TestCase):
|
||||||
old_template_dirs = settings.TEMPLATES[0].get('DIRS', [])
|
old_template_dirs = settings.TEMPLATES[0].get('DIRS', [])
|
||||||
temp_template_dir = tempfile.mkdtemp('dbtemplates')
|
temp_template_dir = tempfile.mkdtemp('dbtemplates')
|
||||||
temp_template_path = os.path.join(temp_template_dir, 'temp_test.html')
|
temp_template_path = os.path.join(temp_template_dir, 'temp_test.html')
|
||||||
temp_template = open(temp_template_path, 'w', encoding='utf-8')
|
temp_template = io.open(temp_template_path, 'w', encoding='utf-8')
|
||||||
try:
|
try:
|
||||||
temp_template.write('temp test')
|
temp_template.write(u'temp test')
|
||||||
settings.TEMPLATES[0]['DIRS'] = (temp_template_dir,)
|
settings.TEMPLATES[0]['DIRS'] = (temp_template_dir,)
|
||||||
# these works well if is not settings patched at runtime
|
# these works well if is not settings patched at runtime
|
||||||
# for supporting django < 1.7 tests we must patch dirs in runtime
|
# for supporting django < 1.7 tests we must patch dirs in runtime
|
||||||
|
|
@ -120,13 +119,13 @@ class DbTemplatesTestCase(TestCase):
|
||||||
Template.objects.filter(name='temp_test.html').exists())
|
Template.objects.filter(name='temp_test.html').exists())
|
||||||
|
|
||||||
t = Template.objects.get(name='temp_test.html')
|
t = Template.objects.get(name='temp_test.html')
|
||||||
t.content = 'temp test modified'
|
t.content = u'temp test modified'
|
||||||
t.save()
|
t.save()
|
||||||
call_command('sync_templates', force=True,
|
call_command('sync_templates', force=True,
|
||||||
verbosity=0, overwrite=DATABASE_TO_FILES)
|
verbosity=0, overwrite=DATABASE_TO_FILES)
|
||||||
self.assertEqual('temp test modified',
|
self.assertEqual(u'temp test modified',
|
||||||
open(temp_template_path,
|
io.open(temp_template_path,
|
||||||
encoding='utf-8').read())
|
encoding='utf-8').read())
|
||||||
|
|
||||||
call_command('sync_templates', force=True, verbosity=0,
|
call_command('sync_templates', force=True, verbosity=0,
|
||||||
delete=True, overwrite=DATABASE_TO_FILES)
|
delete=True, overwrite=DATABASE_TO_FILES)
|
||||||
|
|
@ -152,24 +151,3 @@ class DbTemplatesTestCase(TestCase):
|
||||||
def test_get_cache_name(self):
|
def test_get_cache_name(self):
|
||||||
self.assertEqual(get_cache_key('name with spaces'),
|
self.assertEqual(get_cache_key('name with spaces'),
|
||||||
'dbtemplates::name-with-spaces::1')
|
'dbtemplates::name-with-spaces::1')
|
||||||
|
|
||||||
def test_cache_invalidation(self):
|
|
||||||
# Add t1 into the cache of site2
|
|
||||||
self.t1.sites.add(self.site2)
|
|
||||||
with mock.patch('django.contrib.sites.models.SiteManager.get_current',
|
|
||||||
return_value=self.site2):
|
|
||||||
result = loader.get_template("base.html").render()
|
|
||||||
self.assertEqual(result, 'base')
|
|
||||||
|
|
||||||
# Update content
|
|
||||||
self.t1.content = 'new content'
|
|
||||||
self.t1.save()
|
|
||||||
result = loader.get_template("base.html").render()
|
|
||||||
self.assertEqual(result, 'new content')
|
|
||||||
|
|
||||||
# Cache invalidation should work across sites.
|
|
||||||
# Site2 should see the new content.
|
|
||||||
with mock.patch('django.contrib.sites.models.SiteManager.get_current',
|
|
||||||
return_value=self.site2):
|
|
||||||
result = loader.get_template("base.html").render()
|
|
||||||
self.assertEqual(result, 'new content')
|
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,12 @@ DATABASES = {
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.sites',
|
'django.contrib.sites',
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.messages',
|
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'dbtemplates',
|
'dbtemplates',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = (
|
MIDDLEWARE_CLASSES = (
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
)
|
)
|
||||||
|
|
@ -42,10 +39,6 @@ TEMPLATES = [
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'loaders': TEMPLATE_LOADERS,
|
'loaders': TEMPLATE_LOADERS,
|
||||||
'context_processors': [
|
|
||||||
'django.contrib.auth.context_processors.auth',
|
|
||||||
'django.contrib.messages.context_processors.messages',
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,32 @@
|
||||||
from dbtemplates.conf import settings
|
|
||||||
from django.contrib.sites.models import Site
|
|
||||||
from django.core import signals
|
from django.core import signals
|
||||||
|
from django.contrib.sites.models import Site
|
||||||
from django.template.defaultfilters import slugify
|
from django.template.defaultfilters import slugify
|
||||||
|
|
||||||
|
from dbtemplates.conf import settings
|
||||||
|
|
||||||
|
|
||||||
def get_cache_backend():
|
def get_cache_backend():
|
||||||
"""
|
"""
|
||||||
Compatibilty wrapper for getting Django's cache backend instance
|
Compatibilty wrapper for getting Django's cache backend instance
|
||||||
"""
|
"""
|
||||||
from django.core.cache import caches
|
from django.core.cache import _create_cache
|
||||||
cache = caches.create_connection(settings.DBTEMPLATES_CACHE_BACKEND)
|
cache = _create_cache(settings.DBTEMPLATES_CACHE_BACKEND)
|
||||||
|
|
||||||
# Some caches -- python-memcached in particular -- need to do a cleanup at
|
# Some caches -- python-memcached in particular -- need to do a cleanup at
|
||||||
# the end of a request cycle. If not implemented in a particular backend
|
# the end of a request cycle. If not implemented in a particular backend
|
||||||
# cache.close is a no-op
|
# cache.close is a no-op
|
||||||
signals.request_finished.connect(cache.close)
|
signals.request_finished.connect(cache.close)
|
||||||
return cache
|
return cache
|
||||||
|
|
||||||
|
|
||||||
cache = get_cache_backend()
|
cache = get_cache_backend()
|
||||||
|
|
||||||
|
|
||||||
def get_cache_key(name, site=None):
|
def get_cache_key(name):
|
||||||
if site is None:
|
current_site = Site.objects.get_current()
|
||||||
site = Site.objects.get_current()
|
return 'dbtemplates::%s::%s' % (slugify(name), current_site.pk)
|
||||||
return f"dbtemplates::{slugify(name)}::{site.pk}"
|
|
||||||
|
|
||||||
|
|
||||||
def get_cache_notfound_key(name):
|
def get_cache_notfound_key(name):
|
||||||
return get_cache_key(name) + "::notfound"
|
return get_cache_key(name) + '::notfound'
|
||||||
|
|
||||||
|
|
||||||
def remove_notfound_key(instance):
|
def remove_notfound_key(instance):
|
||||||
|
|
@ -58,5 +56,4 @@ def remove_cached_template(instance, **kwargs):
|
||||||
Called via Django's signals to remove cached templates, if the template
|
Called via Django's signals to remove cached templates, if the template
|
||||||
in the database was changed or deleted.
|
in the database was changed or deleted.
|
||||||
"""
|
"""
|
||||||
for site in instance.sites.all():
|
cache.delete(get_cache_key(instance.name))
|
||||||
cache.delete(get_cache_key(instance.name, site=site))
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from django.template import (Template, TemplateDoesNotExist,
|
from django.template import (Template, TemplateDoesNotExist,
|
||||||
TemplateSyntaxError)
|
TemplateSyntaxError)
|
||||||
|
from importlib import import_module
|
||||||
|
|
||||||
|
|
||||||
def get_loaders():
|
def get_loaders():
|
||||||
|
|
@ -16,13 +17,20 @@ def get_template_source(name):
|
||||||
if loader.__module__.startswith('dbtemplates.'):
|
if loader.__module__.startswith('dbtemplates.'):
|
||||||
# Don't give a damn about dbtemplates' own loader.
|
# Don't give a damn about dbtemplates' own loader.
|
||||||
continue
|
continue
|
||||||
for origin in loader.get_template_sources(name):
|
module = import_module(loader.__module__)
|
||||||
try:
|
load_template_source = getattr(
|
||||||
source = loader.get_contents(origin)
|
module, 'load_template_source', None)
|
||||||
except (NotImplementedError, TemplateDoesNotExist):
|
if load_template_source is None:
|
||||||
continue
|
load_template_source = loader.load_template_source
|
||||||
|
try:
|
||||||
|
source, origin = load_template_source(name)
|
||||||
if source:
|
if source:
|
||||||
return source
|
return source
|
||||||
|
except NotImplementedError:
|
||||||
|
pass
|
||||||
|
except TemplateDoesNotExist:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def check_template_syntax(template):
|
def check_template_syntax(template):
|
||||||
|
|
|
||||||
|
|
@ -61,17 +61,8 @@ Short installation howto
|
||||||
3. Sync your database with ``python manage.py syncdb``
|
3. Sync your database with ``python manage.py syncdb``
|
||||||
4. Set ``DBTEMPLATES_USE_REVERSION`` setting to ``True``
|
4. Set ``DBTEMPLATES_USE_REVERSION`` setting to ``True``
|
||||||
|
|
||||||
History compare view
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
You can also use ``dbtemplates`` together with `django-reversion-compare`_ which
|
|
||||||
provides a history compare view to compare two versions of a model which is under
|
|
||||||
reversion.
|
|
||||||
|
|
||||||
.. _django-reversion: https://github.com/etianen/django-reversion
|
.. _django-reversion: https://github.com/etianen/django-reversion
|
||||||
.. _django-reversion's documentation: https://django-reversion.readthedocs.io/en/latest/
|
.. _django-reversion's documentation: https://django-reversion.readthedocs.io/en/latest/
|
||||||
.. _django-reversion-compare: https://github.com/jedie/django-reversion-compare
|
|
||||||
|
|
||||||
|
|
||||||
.. _commands:
|
.. _commands:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,6 @@
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
v5.0 (unreleased)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
This is a backwards-incompatible release!
|
|
||||||
|
|
||||||
* Dropped support for Python 3.7 and Django < 4.2.
|
|
||||||
|
|
||||||
* Added support for Python 3.11, 3.12, 3.13, 3.14.
|
|
||||||
|
|
||||||
* Django 5.x and 6.0 support
|
|
||||||
|
|
||||||
v4.0 (2022-09-3)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
This is a backwards-incompatible release!
|
|
||||||
|
|
||||||
* Dropped support for Python 2.7 and Django < 3.2.
|
|
||||||
|
|
||||||
* Added support for Python 3.8, 3.9, 3.10.
|
|
||||||
|
|
||||||
* Moved test runner to GitHub Actions:
|
|
||||||
|
|
||||||
http://github.com/jazzband/django-dbtemplates/actions
|
|
||||||
|
|
||||||
* Django 4.x support
|
|
||||||
|
|
||||||
v3.0 (2019-01-27)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
This is a backwards-incompatible release!
|
|
||||||
|
|
||||||
* Dropped support for Django < 1.11.
|
|
||||||
|
|
||||||
* Added support for Django 2.0 and 2.1.
|
|
||||||
|
|
||||||
* Added support for Python 3.7.
|
|
||||||
|
|
||||||
* Recompiled Russian locale.
|
|
||||||
|
|
||||||
* Fixed byte string in migration file that caused the migration
|
|
||||||
system to falsely think that there are new changes.
|
|
||||||
|
|
||||||
* Fixed string representation of template model, e.g. to improve
|
|
||||||
readability in choice fields.
|
|
||||||
|
|
||||||
v2.0 (2016-09-29)
|
v2.0 (2016-09-29)
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# django-dbtemplates documentation build configuration file, created by
|
# django-dbtemplates documentation build configuration file, created by
|
||||||
# sphinx-quickstart on Fri Oct 9 14:52:11 2009.
|
# sphinx-quickstart on Fri Oct 9 14:52:11 2009.
|
||||||
|
|
@ -36,8 +37,8 @@ source_suffix = '.txt'
|
||||||
master_doc = 'index'
|
master_doc = 'index'
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = 'django-dbtemplates'
|
project = u'django-dbtemplates'
|
||||||
copyright = '2007-2019, Jannis Leidel and contributors'
|
copyright = u'2007-2012, Jannis Leidel and contributors'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
|
@ -176,8 +177,8 @@ htmlhelp_basename = 'django-dbtemplatesdoc'
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
('index', 'django-dbtemplates.tex', 'django-dbtemplates Documentation',
|
('index', 'django-dbtemplates.tex', u'django-dbtemplates Documentation',
|
||||||
'Jannis Leidel and contributors', 'manual'),
|
u'Jannis Leidel and contributors', 'manual'),
|
||||||
]
|
]
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top of
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ Setup
|
||||||
|
|
||||||
1. Get the source from the `Git repository`_ or install it from the
|
1. Get the source from the `Git repository`_ or install it from the
|
||||||
Python Package Index by running ``pip install django-dbtemplates``.
|
Python Package Index by running ``pip install django-dbtemplates``.
|
||||||
2. Edit the settings.py of your Django site:
|
2. Follow the instructions in the INSTALL file
|
||||||
|
3. Edit the settings.py of your Django site:
|
||||||
|
|
||||||
* Add ``dbtemplates`` to the ``INSTALLED_APPS`` setting
|
* Add ``dbtemplates`` to the ``INSTALLED_APPS`` setting
|
||||||
|
|
||||||
|
|
@ -60,8 +61,8 @@ Setup
|
||||||
from the database to be used to override templates in other locations,
|
from the database to be used to override templates in other locations,
|
||||||
put ``dbtemplates.loader.Loader`` at the beginning of ``loaders``.
|
put ``dbtemplates.loader.Loader`` at the beginning of ``loaders``.
|
||||||
|
|
||||||
3. Sync your database ``python manage.py migrate``
|
4. Sync your database ``python manage.py migrate``
|
||||||
4. Restart your Django server
|
5. Restart your Django server
|
||||||
|
|
||||||
.. _Git repository: https://github.com/jazzband/django-dbtemplates/
|
.. _Git repository: https://github.com/jazzband/django-dbtemplates/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,11 +41,6 @@ Set to ``False`` by default.
|
||||||
|
|
||||||
A boolean, if enabled triggers the use of ``django-reversion``.
|
A boolean, if enabled triggers the use of ``django-reversion``.
|
||||||
|
|
||||||
``DBTEMPLATES_USE_REVERSION_COMPARE``
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
A boolean, if enabled triggers the use of ``django-reversion-compare``.
|
|
||||||
|
|
||||||
``DBTEMPLATES_MEDIA_PREFIX``
|
``DBTEMPLATES_MEDIA_PREFIX``
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
[build-system]
|
|
||||||
requires = [
|
|
||||||
"setuptools>=61.2",
|
|
||||||
"setuptools_scm",
|
|
||||||
]
|
|
||||||
build-backend = "setuptools.build_meta"
|
|
||||||
|
|
||||||
[project]
|
|
||||||
name = "django-dbtemplates"
|
|
||||||
authors = [{name = "Jannis Leidel", email = "jannis@leidel.info"}]
|
|
||||||
description = "Template loader for templates stored in the database"
|
|
||||||
readme = "README.rst"
|
|
||||||
classifiers = [
|
|
||||||
"Development Status :: 5 - Production/Stable",
|
|
||||||
"Environment :: Web Environment",
|
|
||||||
"Intended Audience :: Developers",
|
|
||||||
"License :: OSI Approved :: BSD License",
|
|
||||||
"Operating System :: OS Independent",
|
|
||||||
"Programming Language :: Python",
|
|
||||||
"Programming Language :: Python :: 3",
|
|
||||||
"Programming Language :: Python :: 3 :: Only",
|
|
||||||
"Programming Language :: Python :: 3.8",
|
|
||||||
"Programming Language :: Python :: 3.9",
|
|
||||||
"Programming Language :: Python :: 3.10",
|
|
||||||
"Programming Language :: Python :: 3.11",
|
|
||||||
"Programming Language :: Python :: 3.12",
|
|
||||||
"Programming Language :: Python :: 3.13",
|
|
||||||
"Programming Language :: Python :: 3.14",
|
|
||||||
"Framework :: Django",
|
|
||||||
]
|
|
||||||
requires-python = ">=3.8"
|
|
||||||
dependencies = ["django-appconf >= 0.4"]
|
|
||||||
dynamic = ["version"]
|
|
||||||
|
|
||||||
[project.urls]
|
|
||||||
Documentation = "https://django-dbtemplates.readthedocs.io/"
|
|
||||||
Changelog = "https://django-dbtemplates.readthedocs.io/en/latest/changelog.html"
|
|
||||||
Source = "https://github.com/jazzband/django-dbtemplates"
|
|
||||||
|
|
||||||
[tool.setuptools]
|
|
||||||
zip-safe = false
|
|
||||||
include-package-data = false
|
|
||||||
|
|
||||||
[tool.setuptools.packages]
|
|
||||||
find = {namespaces = false}
|
|
||||||
|
|
||||||
[tool.setuptools.package-data]
|
|
||||||
dbtemplates = [
|
|
||||||
"locale/*/LC_MESSAGES/*",
|
|
||||||
"static/dbtemplates/css/*.css",
|
|
||||||
"static/dbtemplates/js/*.js",
|
|
||||||
]
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
django
|
|
||||||
13
setup.cfg
Normal file
13
setup.cfg
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[egg_info]
|
||||||
|
#tag_build = a1
|
||||||
|
|
||||||
|
[build_sphinx]
|
||||||
|
source-dir = docs/
|
||||||
|
build-dir = docs/_build
|
||||||
|
all_files = 1
|
||||||
|
|
||||||
|
[upload_docs]
|
||||||
|
upload-dir = docs/_build/html
|
||||||
|
|
||||||
|
[upload_sphinx]
|
||||||
|
upload-dir = docs/_build/html
|
||||||
60
setup.py
Normal file
60
setup.py
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
import ast
|
||||||
|
import os
|
||||||
|
import io
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
|
||||||
|
class VersionFinder(ast.NodeVisitor):
|
||||||
|
def __init__(self):
|
||||||
|
self.version = None
|
||||||
|
|
||||||
|
def visit_Assign(self, node):
|
||||||
|
if node.targets[0].id == '__version__':
|
||||||
|
self.version = node.value.s
|
||||||
|
|
||||||
|
|
||||||
|
def read(*parts):
|
||||||
|
filename = os.path.join(os.path.dirname(__file__), *parts)
|
||||||
|
with io.open(filename, encoding='utf-8') as fp:
|
||||||
|
return fp.read()
|
||||||
|
|
||||||
|
|
||||||
|
def find_version(*parts):
|
||||||
|
finder = VersionFinder()
|
||||||
|
finder.visit(ast.parse(read(*parts)))
|
||||||
|
return finder.version
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='django-dbtemplates',
|
||||||
|
version=find_version('dbtemplates', '__init__.py'),
|
||||||
|
description='Template loader for templates stored in the database',
|
||||||
|
long_description=read('README.rst'),
|
||||||
|
author='Jannis Leidel',
|
||||||
|
author_email='jannis@leidel.info',
|
||||||
|
url='https://django-dbtemplates.readthedocs.io/',
|
||||||
|
packages=find_packages(),
|
||||||
|
zip_safe=False,
|
||||||
|
package_data={
|
||||||
|
'dbtemplates': [
|
||||||
|
'locale/*/LC_MESSAGES/*',
|
||||||
|
'static/dbtemplates/css/*.css',
|
||||||
|
'static/dbtemplates/js/*.js',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
classifiers=[
|
||||||
|
'Development Status :: 5 - Production/Stable',
|
||||||
|
'Environment :: Web Environment',
|
||||||
|
'Intended Audience :: Developers',
|
||||||
|
'License :: OSI Approved :: BSD License',
|
||||||
|
'Operating System :: OS Independent',
|
||||||
|
'Programming Language :: Python',
|
||||||
|
'Programming Language :: Python :: 2',
|
||||||
|
'Programming Language :: Python :: 2.7',
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
|
'Programming Language :: Python :: 3.4',
|
||||||
|
'Programming Language :: Python :: 3.5',
|
||||||
|
'Framework :: Django',
|
||||||
|
],
|
||||||
|
install_requires=['django-appconf >= 0.4'],
|
||||||
|
)
|
||||||
70
tox.ini
70
tox.ini
|
|
@ -1,59 +1,43 @@
|
||||||
[tox]
|
[tox]
|
||||||
minversion = 4.0
|
skipsdist = True
|
||||||
|
usedevelop = True
|
||||||
|
minversion = 1.8
|
||||||
envlist =
|
envlist =
|
||||||
flake8
|
flake8-py27,
|
||||||
py3{8,9,10,11,12}-dj42
|
flake8-py35,
|
||||||
py3{10,11,12,13,14}-dj52
|
readme-py27,
|
||||||
py3{12,13,14}-dj60
|
py{27,34,35,py}-dj{18,19,110,master}
|
||||||
py3{12,13,14}-djmain
|
|
||||||
coverage
|
|
||||||
|
|
||||||
[gh-actions]
|
|
||||||
python =
|
|
||||||
3.8: py38
|
|
||||||
3.9: py39
|
|
||||||
3.10: py310
|
|
||||||
3.10: py310, flake8
|
|
||||||
3.11: py311
|
|
||||||
3.12: py312
|
|
||||||
3.13: py313
|
|
||||||
3.14: py314
|
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
skipsdist = true
|
|
||||||
package = editable
|
|
||||||
basepython =
|
basepython =
|
||||||
py38: python3.8
|
py27: python2.7
|
||||||
py39: python3.9
|
py34: python3.4
|
||||||
py310: python3.10
|
py35: python3.5
|
||||||
py311: python3.11
|
pypy: pypy
|
||||||
py312: python3.12
|
usedevelop = true
|
||||||
py313: python3.13
|
|
||||||
py314: python3.14
|
|
||||||
setenv =
|
setenv =
|
||||||
DJANGO_SETTINGS_MODULE = dbtemplates.test_settings
|
DJANGO_SETTINGS_MODULE = dbtemplates.test_settings
|
||||||
deps =
|
deps =
|
||||||
-r requirements/tests.txt
|
-rrequirements/tests.txt
|
||||||
dj42: Django>=4.2,<4.3
|
dj18: https://github.com/django/django/archive/stable/1.8.x.tar.gz#egg=django
|
||||||
dj52: Django>=5.2,<5.3
|
dj19: https://github.com/django/django/archive/stable/1.9.x.tar.gz#egg=django
|
||||||
dj60: Django>=6.0,<6.1
|
dj110: https://github.com/django/django/archive/stable/1.10.x.tar.gz#egg=django
|
||||||
djmain: https://github.com/django/django/archive/main.tar.gz#egg=django
|
djmaster: https://github.com/django/django/archive/master.tar.gz#egg=django
|
||||||
|
|
||||||
commands =
|
commands =
|
||||||
python --version
|
python --version
|
||||||
python -c "import django ; print(django.VERSION)"
|
coverage run {envbindir}/django-admin.py test -v2 {posargs:dbtemplates}
|
||||||
coverage run --branch --parallel-mode {envbindir}/django-admin test -v2 {posargs:dbtemplates}
|
|
||||||
|
|
||||||
[testenv:coverage]
|
|
||||||
basepython = python3.10
|
|
||||||
deps = coverage
|
|
||||||
commands =
|
|
||||||
coverage combine
|
|
||||||
coverage report
|
coverage report
|
||||||
coverage xml
|
|
||||||
|
|
||||||
[testenv:flake8]
|
[testenv:readme-py27]
|
||||||
basepython = python3.10
|
commands = python setup.py check -r -s
|
||||||
|
deps = readme_renderer
|
||||||
|
|
||||||
|
[testenv:flake8-py27]
|
||||||
|
commands = flake8 dbtemplates
|
||||||
|
deps = flake8
|
||||||
|
|
||||||
|
[testenv:flake8-py35]
|
||||||
commands = flake8 dbtemplates
|
commands = flake8 dbtemplates
|
||||||
deps = flake8
|
deps = flake8
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue