Compare commits

..

15 commits

Author SHA1 Message Date
Thomas Güttler
10e7de2eda
Fix cache invalidation (#131) 2025-06-17 16:38:05 -07:00
Viktor Kálmán
930ce5c65c
add project urls (#156) 2025-06-09 11:47:43 -07:00
blag
8e284b54d8
Revert "Split out an AbstractTemplate model for easier reuse" (#154)
This reverts commit 46be8fc748.
2025-05-29 23:31:49 +02:00
blag
05f1ee1193
Split out an AbstractTemplate model for easier reuse (#150) 2025-05-26 22:09:25 +02:00
blag
303bd0cabe
Add missing migration for creation_date and last_changed changes (#151) 2025-05-26 22:08:21 +02:00
Łukasz Chojnacki
64d112cc4f
Add explicit id field to avoid creating migration with DEFAULT_AUTO_FIELD set to BigAutoField (#142)
Co-authored-by: blag <blag@users.noreply.github.com>
2025-05-26 20:13:03 +02:00
blag
7f1c6701c1
Properly add Django 5.2 and tweak coverage collection (#148) 2025-05-26 20:12:55 +02:00
blag
873c90b777
Convert setup.py and setup.cfg to pyproject.toml (#149) 2025-05-26 20:12:37 +02:00
Thomas Güttler
e64a457281
There is no INSTALL file. (#128)
removed "Follow the instructions in the INSTALL file", since there is no INSTALL file.

Co-authored-by: blag <blag@users.noreply.github.com>
2025-05-26 20:00:16 +02:00
blag
a7f4e0bbe8
Make creation_date and last_updated fields readonly in admin (#144) 2025-05-26 19:59:50 +02:00
blag
218b28b7aa
Let Django handle creation_date and last_changed (#145) 2025-05-26 19:59:18 +02:00
blag
602717af95
Add default_auto_field to AppConfig (#146) 2025-05-26 19:53:42 +02:00
Jannis Leidel
8769e29057
Use correct release branch of pypa/publish action. (#138) 2025-05-20 21:40:09 +02:00
Viktor Kálmán
ac740e06f3
Support for Python 3.12 and up (#143)
* support for Python 3.12 and up

* removed unused deprecated ugettext imports

* fix django main being Python 3.12+

* missed some copypaste
2025-02-16 21:08:16 +01:00
Jannis Leidel
233a401e75
Fix docs rendering (#127)
* Create .readthedocs.yaml

* Add docs requirements
2022-08-29 12:09:54 +02:00
18 changed files with 190 additions and 113 deletions

View file

@ -3,4 +3,4 @@ source = dbtemplates
branch = 1
[report]
omit = *tests*,*migrations*
omit = *tests*,*/migrations/*,test_*

View file

@ -33,7 +33,7 @@ jobs:
- name: Upload packages to Jazzband
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@master
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: jazzband
password: ${{ secrets.JAZZBAND_RELEASE_KEY }}

View file

@ -9,7 +9,7 @@ jobs:
fail-fast: false
max-parallel: 5
matrix:
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11-dev']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.13']
steps:
- uses: actions/checkout@v3

25
.readthedocs.yaml Normal file
View file

@ -0,0 +1,25 @@
# .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

View file

@ -1,10 +1,3 @@
from pkg_resources import get_distribution, DistributionNotFound
import importlib.metadata
try:
__version__ = get_distribution("django-dbtemplates").version
except DistributionNotFound:
# package is not installed
__version__ = None
default_app_config = 'dbtemplates.apps.DBTemplatesConfig'
__version__ = importlib.metadata.version("django-dbtemplates")

View file

@ -2,15 +2,8 @@ import posixpath
from django import forms
from django.contrib import admin
from django.core.exceptions import ImproperlyConfigured
try:
# Django 4.0
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
except ImportError:
# Before Django 4.0
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ungettext as ngettext
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from django.utils.safestring import mark_safe
from dbtemplates.conf import settings
@ -104,6 +97,7 @@ class TemplateAdminForm(forms.ModelForm):
class TemplateAdmin(TemplateModelAdmin):
form = TemplateAdminForm
readonly_fields = ['creation_date', 'last_changed']
fieldsets = (
(None, {
'fields': ('name', 'content'),

View file

@ -1,10 +1,9 @@
from django.apps import AppConfig
try:
from django.utils.translation import ugettext_lazy as _
except ImportError:
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext_lazy as _
class DBTemplatesConfig(AppConfig):
name = 'dbtemplates'
verbose_name = _('Database templates')
default_auto_field = 'django.db.models.AutoField'

View file

@ -0,0 +1,23 @@
# 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'),
),
]

View file

@ -1,20 +1,16 @@
from dbtemplates.conf import settings
from dbtemplates.utils.cache import (add_template_to_cache,
remove_cached_template)
from dbtemplates.utils.cache import (
add_template_to_cache,
remove_cached_template,
)
from dbtemplates.utils.template import get_template_source
from django.contrib.sites.managers import CurrentSiteManager
from django.contrib.sites.models import Site
from django.db import models
from django.db.models import signals
from django.template import TemplateDoesNotExist
try:
# Django >= 4.0
from django.utils.translation import gettext_lazy as _
except ImportError:
# Django 3.2
from django.utils.translation import ugettext_lazy as _
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
class Template(models.Model):
@ -22,15 +18,15 @@ class Template(models.Model):
Defines a template model for use with the database template loader.
The field ``name`` is the equivalent to the filename of a static template.
"""
id = models.AutoField(primary_key=True, verbose_name=_('ID'),
serialize=False, auto_created=True)
name = models.CharField(_('name'), max_length=100,
help_text=_("Example: 'flatpages/default.html'"))
content = models.TextField(_('content'), blank=True)
sites = models.ManyToManyField(Site, verbose_name=_('sites'),
blank=True)
creation_date = models.DateTimeField(_('creation date'),
default=now)
last_changed = models.DateTimeField(_('last changed'),
default=now)
creation_date = models.DateTimeField(_('creation date'), auto_now_add=True)
last_changed = models.DateTimeField(_('last changed'), auto_now=True)
objects = models.Manager()
on_site = CurrentSiteManager('sites')
@ -59,7 +55,6 @@ class Template(models.Model):
pass
def save(self, *args, **kwargs):
self.last_changed = now()
# If content is empty look for a template with the given name and
# populate the template instance with its content.
if settings.DBTEMPLATES_AUTO_POPULATE_CONTENT and not self.content:

View file

@ -1,6 +1,7 @@
import os
import shutil
import tempfile
from unittest import mock
from django.conf import settings as django_settings
from django.core.cache.backends.base import BaseCache
@ -151,3 +152,24 @@ class DbTemplatesTestCase(TestCase):
def test_get_cache_name(self):
self.assertEqual(get_cache_key('name with spaces'),
'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')

View file

@ -21,9 +21,10 @@ def get_cache_backend():
cache = get_cache_backend()
def get_cache_key(name):
current_site = Site.objects.get_current()
return f"dbtemplates::{slugify(name)}::{current_site.pk}"
def get_cache_key(name, site=None):
if site is None:
site = Site.objects.get_current()
return f"dbtemplates::{slugify(name)}::{site.pk}"
def get_cache_notfound_key(name):
@ -57,4 +58,5 @@ def remove_cached_template(instance, **kwargs):
Called via Django's signals to remove cached templates, if the template
in the database was changed or deleted.
"""
cache.delete(get_cache_key(instance.name))
for site in instance.sites.all():
cache.delete(get_cache_key(instance.name, site=site))

View file

@ -1,7 +1,20 @@
Changelog
=========
v4.0 (2022-08-29)
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.
* Django 5.x support
v4.0 (2022-09-3)
-----------------
.. warning::

View file

@ -3,8 +3,7 @@ Setup
1. Get the source from the `Git repository`_ or install it from the
Python Package Index by running ``pip install django-dbtemplates``.
2. Follow the instructions in the INSTALL file
3. Edit the settings.py of your Django site:
2. Edit the settings.py of your Django site:
* Add ``dbtemplates`` to the ``INSTALLED_APPS`` setting
@ -61,8 +60,8 @@ Setup
from the database to be used to override templates in other locations,
put ``dbtemplates.loader.Loader`` at the beginning of ``loaders``.
4. Sync your database ``python manage.py migrate``
5. Restart your Django server
3. Sync your database ``python manage.py migrate``
4. Restart your Django server
.. _Git repository: https://github.com/jazzband/django-dbtemplates/

51
pyproject.toml Normal file
View file

@ -0,0 +1,51 @@
[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",
"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
requirements/docs.txt Normal file
View file

@ -0,0 +1 @@
django

View file

@ -1,4 +0,0 @@
[build_sphinx]
source-dir = docs/
build-dir = docs/_build
all_files = 1

View file

@ -1,48 +0,0 @@
import os
import io
from setuptools import setup, find_packages
def read(*parts):
filename = os.path.join(os.path.dirname(__file__), *parts)
with open(filename, encoding="utf-8") as fp:
return fp.read()
setup(
name="django-dbtemplates",
use_scm_version={"version_scheme": "post-release"},
setup_requires=["setuptools_scm"],
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 :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Framework :: Django",
],
python_requires=">=3.7",
install_requires=["django-appconf >= 0.4"],
)

36
tox.ini
View file

@ -1,41 +1,53 @@
[tox]
skipsdist = True
usedevelop = True
minversion = 1.8
minversion = 4.0
envlist =
flake8
py3{7,8,9,10,11}-dj32
py3{8,9,10,11}-dj{40,41,main}
py3{8,9,10,11,12}-dj42
py3{10,11,12}-dj{50}
py3{10,11,12,13}-dj{51,52}
py3{12,13}-dj{main}
coverage
[gh-actions]
python =
3.7: py37
3.8: py38
3.9: py39
3.10: py310
3.10: py310, flake8
3.11: py311
3.12: py312
3.13: py313
[testenv]
skipsdist = true
package = editable
basepython =
py37: python3.7
py38: python3.8
py39: python3.9
py310: python3.10
py311: python3.11
usedevelop = true
py312: python3.12
py313: python3.13
setenv =
DJANGO_SETTINGS_MODULE = dbtemplates.test_settings
deps =
-r requirements/tests.txt
dj32: Django<3.3
dj40: Django<4.1
dj41: Django<4.2
dj42: Django>=4.2,<4.3
dj50: Django>=5.0,<5.1
dj51: Django>=5.1,<5.2
dj52: Django>=5.2,<5.3
djmain: https://github.com/django/django/archive/main.tar.gz#egg=django
commands =
python --version
coverage run {envbindir}/django-admin test -v2 {posargs:dbtemplates}
python -c "import django ; print(django.VERSION)"
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 xml