mirror of
https://github.com/jazzband/django-dbtemplates.git
synced 2026-03-16 22:20:28 +00:00
Compare commits
252 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10e7de2eda | ||
|
|
930ce5c65c | ||
|
|
8e284b54d8 | ||
|
|
05f1ee1193 | ||
|
|
303bd0cabe | ||
|
|
64d112cc4f | ||
|
|
7f1c6701c1 | ||
|
|
873c90b777 | ||
|
|
e64a457281 | ||
|
|
a7f4e0bbe8 | ||
|
|
218b28b7aa | ||
|
|
602717af95 | ||
|
|
8769e29057 | ||
|
|
ac740e06f3 | ||
|
|
233a401e75 | ||
|
|
13bacacef8 | ||
|
|
fa72b2771b | ||
|
|
fac9f6a807 | ||
|
|
f53244ce54 | ||
|
|
6ca53981d3 | ||
|
|
45e216b8ac | ||
|
|
5a363dbe34 | ||
|
|
2c8dc82721 | ||
|
|
98bc9921b0 | ||
|
|
b6b0bf48ba | ||
|
|
36fbb80fc0 | ||
|
|
e08a8d3950 | ||
|
|
6dd23d2325 | ||
|
|
a68caedc2d | ||
|
|
2b5034951f | ||
|
|
b138cafcfa | ||
|
|
f98fb8ca24 | ||
|
|
2e3f009426 | ||
|
|
8e8b76fc0a | ||
|
|
49dd3be520 | ||
|
|
74ee8a2fe3 | ||
|
|
1a20742b71 | ||
|
|
9455b36281 | ||
|
|
165ffd0b15 | ||
|
|
a3054c7724 | ||
|
|
e5e4e55d22 | ||
|
|
9a27f4938c | ||
|
|
013057425f | ||
|
|
7ffc0fa33f | ||
|
|
de4babcad4 | ||
|
|
6b18f19ffd | ||
|
|
5281b74ea7 | ||
|
|
9dbdb95227 | ||
|
|
e1e11c42cb | ||
|
|
9f664ea43c | ||
|
|
e477561810 | ||
|
|
2d622ee28c | ||
|
|
8901551893 | ||
|
|
9e4a6f7c78 | ||
|
|
9486f0c78b | ||
|
|
376f33916f | ||
|
|
8cd8a17bc1 | ||
|
|
f45cd228f9 | ||
|
|
24b5f469a2 | ||
|
|
2b747bc4af | ||
|
|
ac86ca5ec9 | ||
|
|
012fe061a8 | ||
|
|
68ac54a72b | ||
|
|
7ea4655b57 | ||
|
|
1d53b25cc2 | ||
|
|
81ba1364f8 | ||
|
|
ae97ad7e24 | ||
|
|
5c6c35bf3d | ||
|
|
50f02a50bc | ||
|
|
230157640c | ||
|
|
f443f29047 | ||
|
|
79d1d3986f | ||
|
|
73a33361d3 | ||
|
|
a04933e5ce | ||
|
|
78ca3f48dd | ||
|
|
727746add1 | ||
|
|
07d62d8d26 | ||
|
|
4ece48c7b2 | ||
|
|
966b68ee5c | ||
|
|
039f6f419f | ||
|
|
24c8d2005a | ||
|
|
df358a53dd | ||
|
|
f5b9e36fc7 | ||
|
|
16afa49831 | ||
|
|
cef1ec49c0 | ||
|
|
745e64f3e0 | ||
|
|
28bde8c32d | ||
|
|
16a80d4635 | ||
|
|
6522a39250 | ||
|
|
149a16e308 | ||
|
|
92e0e1ca0c | ||
|
|
c2009596ef | ||
|
|
9cce7cd03b | ||
|
|
34e34b7259 | ||
|
|
db5e4ec4b6 | ||
|
|
3ba01f425c | ||
|
|
77d8fc4e33 | ||
|
|
3939947a0e | ||
|
|
1c80410a5b | ||
|
|
18afb50582 | ||
|
|
e3318658a5 | ||
|
|
3ee69a58ce | ||
|
|
9a84b1af86 | ||
|
|
bc206765fa | ||
|
|
8176a137dd | ||
|
|
1c9467fa37 | ||
|
|
49fc6c47ff | ||
|
|
d3221c3acd | ||
|
|
70fb31c190 | ||
|
|
6fbde482af | ||
|
|
62ba054698 | ||
|
|
e4acf6e754 | ||
|
|
3d5d70db41 | ||
|
|
8c1e33de72 | ||
|
|
44c040523a | ||
|
|
39c8420511 | ||
|
|
7454e109ea | ||
|
|
d7ecd93cce | ||
|
|
1e2a4f0dff | ||
|
|
f0cfdfa9ce | ||
|
|
279fba2635 | ||
|
|
cd2a06ea51 | ||
|
|
892bc8b0aa | ||
|
|
2427759736 | ||
|
|
f1fb4577cc | ||
|
|
9c297392dc | ||
|
|
7619f9fb71 | ||
|
|
d1b83e1145 | ||
|
|
ca8841ce9c | ||
|
|
a3334a05ec | ||
|
|
483db6616a | ||
|
|
0b4cec538e | ||
|
|
6815ce6c6c | ||
|
|
2c7afb6a1a | ||
|
|
676a410517 | ||
|
|
9648555473 | ||
|
|
b6912fc339 | ||
|
|
308d03ba98 | ||
|
|
b9b2ac5885 | ||
|
|
db74e84604 | ||
|
|
35a57bbcb1 | ||
|
|
c8a048352e | ||
|
|
bb9199b0be | ||
|
|
fbc2891c41 | ||
|
|
59c65c56ab | ||
|
|
6dd19d3a17 | ||
|
|
c55e2220b3 | ||
|
|
70da2e6a48 | ||
|
|
04c2d12dbd | ||
|
|
42590af744 | ||
|
|
464425b236 | ||
|
|
926a3215eb | ||
|
|
acdf9dc8e5 | ||
|
|
201077e11b | ||
|
|
99ed8b2ce3 | ||
|
|
edbb5e1ef0 | ||
|
|
51b62d9a62 | ||
|
|
c9569a81db | ||
|
|
b47af2e1ff | ||
|
|
16fe97575c | ||
|
|
0e97716488 | ||
|
|
b03803010d | ||
|
|
078d556390 | ||
|
|
658aca88dc | ||
|
|
984f83c0a8 | ||
|
|
685c77e718 | ||
|
|
15c4a5930b | ||
|
|
50a877a7d5 | ||
|
|
c2d49e1d71 | ||
|
|
5d3a392e4c | ||
|
|
b371cc6518 | ||
|
|
4352fb639e | ||
|
|
1372f43992 | ||
|
|
bfd7186db0 | ||
|
|
91d5e137f1 | ||
|
|
12ffd84380 | ||
|
|
f668370eee | ||
|
|
b051161aea | ||
|
|
4b024de965 | ||
|
|
2fc79eda7a | ||
|
|
d2f595ce82 | ||
|
|
f7378df802 | ||
|
|
1c520b0fad | ||
|
|
c36d649e4d | ||
|
|
6d356db2ef | ||
|
|
0672fa42bf | ||
|
|
7802fbe328 | ||
|
|
327384645c | ||
|
|
8844180d6c | ||
|
|
25d9283758 | ||
|
|
ca9d2fc468 | ||
|
|
c59de762f9 | ||
|
|
ee29bbe46e | ||
|
|
4c3c459e82 | ||
|
|
60215fe947 | ||
|
|
a1cec65e29 | ||
|
|
e7b6b8ce00 | ||
|
|
d018f826da | ||
|
|
07fe3cc7ee | ||
|
|
842e08cf2e | ||
|
|
2e430d5370 | ||
|
|
2f27327beb | ||
|
|
f1e680aa31 | ||
|
|
0e43258b5b | ||
|
|
d926d6c934 | ||
|
|
f3fc408385 | ||
|
|
20aebf894d | ||
|
|
d3ccb0c42a | ||
|
|
222c7947d2 | ||
|
|
bea453548f | ||
|
|
abc2fe5cd2 | ||
|
|
87c5c46999 | ||
|
|
0ff18227f9 | ||
|
|
20a5d9fc66 | ||
|
|
3c67628a63 | ||
|
|
45700c7e1c | ||
|
|
bb4e7ce36f | ||
|
|
33c1197ad1 | ||
|
|
45407e1624 | ||
|
|
3aa3132255 | ||
|
|
29f08281d7 | ||
|
|
f503013b0f | ||
|
|
832b000c49 | ||
|
|
311875c68b | ||
|
|
0cfd927938 | ||
|
|
ad7b258308 | ||
|
|
68d4342df4 | ||
|
|
edaf6ea258 | ||
|
|
e866e52074 | ||
|
|
02aaad635a | ||
|
|
b13ffbb248 | ||
|
|
3e18bc9784 | ||
|
|
cb3fe58a6e | ||
|
|
6a6229648e | ||
|
|
5a6053fef0 | ||
|
|
1c10468529 | ||
|
|
4ae9392e08 | ||
|
|
9cc07437ee | ||
|
|
65684c1243 | ||
|
|
fcca3742e0 | ||
|
|
cc94071fc4 | ||
|
|
357954a83c | ||
|
|
41842de12d | ||
|
|
b6d5bfa226 | ||
|
|
1224ab4005 | ||
|
|
d5be3e42d5 | ||
|
|
fcc9045829 | ||
|
|
e3c53f6b17 | ||
|
|
9e81e53099 | ||
|
|
f53536fdcd | ||
|
|
9843cb8190 | ||
|
|
431813c9b1 |
54 changed files with 1294 additions and 972 deletions
6
.coveragerc
Normal file
6
.coveragerc
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[run]
|
||||||
|
source = dbtemplates
|
||||||
|
branch = 1
|
||||||
|
|
||||||
|
[report]
|
||||||
|
omit = *tests*,*/migrations/*,test_*
|
||||||
40
.github/workflows/release.yml
vendored
Normal file
40
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
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
Normal file
48
.github/workflows/test.yml
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
name: Test
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
max-parallel: 5
|
||||||
|
matrix:
|
||||||
|
python-version: ['3.8', '3.9', '3.10', '3.11', '3.13']
|
||||||
|
|
||||||
|
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 }}
|
||||||
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -1,10 +1,14 @@
|
||||||
|
.*
|
||||||
|
!.gitignore
|
||||||
|
!.coveragerc
|
||||||
*.pyc
|
*.pyc
|
||||||
.*.swp
|
.*.swp
|
||||||
MANIFEST
|
MANIFEST
|
||||||
build
|
build
|
||||||
dist
|
dist
|
||||||
*.egg-info
|
*.egg-info
|
||||||
example/example.db
|
|
||||||
docs/_build
|
docs/_build
|
||||||
.tox/
|
.tox/
|
||||||
*.egg/
|
*.egg/
|
||||||
|
.coverage
|
||||||
|
coverage.xml
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
syntax: glob
|
|
||||||
*.pyc
|
|
||||||
.*.swp
|
|
||||||
MANIFEST
|
|
||||||
build
|
|
||||||
dist
|
|
||||||
django_dbtemplates.egg-info/
|
|
||||||
example/example.db
|
|
||||||
docs/_build
|
|
||||||
24
.hgtags
24
.hgtags
|
|
@ -1,24 +0,0 @@
|
||||||
bd537cd8beba30f1a26328e02126d3d1b14ebf8f 0.2.5
|
|
||||||
1b426859f05b8a003411964883ed5d42ec6c1b01 0.3.0
|
|
||||||
97da228cc698bfae05f60a68ec978030722b0777 0.3.1
|
|
||||||
50c69325d3758d2e82541b13be2794ebbe9ee449 0.4.0
|
|
||||||
d35a41ea96d3604a3c2654590c5c76b8865c4251 0.4.1
|
|
||||||
a4bd56a7c2ea4c6f16a726e47bb185101934fe08 0.4.2
|
|
||||||
447247c1ce1fbc77ac79c5855630af306f4f8c42 0.4.3
|
|
||||||
5b2e4f7fc267daf71325991e913f98e6f96259bb 0.4.4
|
|
||||||
ea4d636f3459ddbb51d87e921cf23f87e41d658d 0.4.5
|
|
||||||
9a30f34bc5b07376ed6752eed94d9d58e791fbac 0.4.6
|
|
||||||
9dc2a0e48494d6a354f5ca25db31638cede4bae4 0.4.7
|
|
||||||
a3be97628ed8633e2fe232e6680474e7fd3e9fea 0.5.0
|
|
||||||
bf3db2fe192d4a02bf531e61e23df342c36d6b1b 0.5.1
|
|
||||||
67a86cf9f7c8ac8d9da855c13abbef2547033cce 0.5.2
|
|
||||||
6967bbbee378f470e4b1df02b57112dd050d424b 0.5.3
|
|
||||||
5965315c03c1a8c1cfb34752cca3802d68156e27 0.5.4
|
|
||||||
4109e0db4340042cb85ea8a7d2b6ce37245738c6 0.5.5
|
|
||||||
ade167225d06cb6888ea8bfa84e7d020590171c6 0.5.6
|
|
||||||
dff01be9c8af328f8fcbc2fc97edcbe8d97840e2 0.5.7
|
|
||||||
f8f7eaf275c5e8ac52174642265af55691abef7c 0.5.8
|
|
||||||
4b36382cdfd756f45f81b0d042aaf331c3eabe30 0.6.0
|
|
||||||
0ac352fec2c2a03ac801ff0c40f0649ef16e1f64 0.6.1
|
|
||||||
34a0511928629872ce8cc5f94c6bed65f82ac343 0.7.0
|
|
||||||
74c22fa8c4a64ea37f9b6b2515a4162b8b8b1a2a 0.7.1
|
|
||||||
15
.pre-commit-config.yaml
Normal file
15
.pre-commit-config.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
|
rev: v2.34.0
|
||||||
|
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
|
||||||
25
.readthedocs.yaml
Normal file
25
.readthedocs.yaml
Normal 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
|
||||||
|
|
@ -4,5 +4,4 @@ source_file = dbtemplates/locale/en/LC_MESSAGES/django.po
|
||||||
source_lang = en
|
source_lang = en
|
||||||
|
|
||||||
[main]
|
[main]
|
||||||
host = https://www.transifex.net
|
host = https://www.transifex.com
|
||||||
|
|
||||||
|
|
|
||||||
4
AUTHORS
4
AUTHORS
|
|
@ -5,10 +5,14 @@ Alexander Artemenko
|
||||||
Arne Brodowski
|
Arne Brodowski
|
||||||
David Paccoud
|
David Paccoud
|
||||||
Diego Búrigo Zacarão
|
Diego Búrigo Zacarão
|
||||||
|
Dmitry Falk
|
||||||
Jannis Leidel
|
Jannis Leidel
|
||||||
|
Jure Cuhalev
|
||||||
Jason Mayfield
|
Jason Mayfield
|
||||||
Kevin Mooney
|
Kevin Mooney
|
||||||
|
Mark Stahler
|
||||||
Matt Dorn
|
Matt Dorn
|
||||||
Oliver George
|
Oliver George
|
||||||
|
Selwin Ong
|
||||||
Stephan Peijnik <spe@anexia.at>, ANEXIA Internetdienstleistungs GmbH, http://www.anexia.at/
|
Stephan Peijnik <spe@anexia.at>, ANEXIA Internetdienstleistungs GmbH, http://www.anexia.at/
|
||||||
Zhang Kun
|
Zhang Kun
|
||||||
|
|
|
||||||
46
CODE_OF_CONDUCT.md
Normal file
46
CODE_OF_CONDUCT.md
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
# 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/
|
||||||
3
CONTRIBUTING.md
Normal file
3
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[](https://jazzband.co/)
|
||||||
|
|
||||||
|
This is a [Jazzband](https://jazzband.co/) project. By contributing you agree to abide by the [Contributor Code of Conduct](https://jazzband.co/docs/conduct) and follow the [guidelines](https://jazzband.co/docs/guidelines).
|
||||||
17
INSTALL
17
INSTALL
|
|
@ -1,17 +0,0 @@
|
||||||
To install it, run the following command inside this directory:
|
|
||||||
|
|
||||||
python setup.py install
|
|
||||||
|
|
||||||
Or if you'd prefer you can simply place the included ``dbtemplates``
|
|
||||||
directory somewhere on your Python path, or symlink to it from
|
|
||||||
somewhere on your Python path; this is useful if you're working from a
|
|
||||||
Subversion checkout. Since ``dbtemplates`` is registered in the
|
|
||||||
Python Package Index you can also run ``easy_install django-dbtemplates``
|
|
||||||
or ``pip install django-dbtemplates`` optionally.
|
|
||||||
|
|
||||||
Note that this application requires Python 2.3 or later, and a recent
|
|
||||||
Subversion checkout of Django. You can obtain Python from
|
|
||||||
http://www.python.org/ and Django from http://www.djangoproject.com/.
|
|
||||||
|
|
||||||
This install notice was bluntly stolen from James Bennett's registration
|
|
||||||
package, http://www.bitbucket.org/ubernostrum/django-registration/
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2007-2011, Jannis Leidel and contributors
|
Copyright (c) 2007-2019, 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
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
include INSTALL
|
include LICENSE AUTHORS README.rst MANIFEST.in tox.ini .coveragerc CONTRIBUTING.md
|
||||||
include LICENSE
|
|
||||||
include AUTHORS
|
|
||||||
include README.rst
|
|
||||||
include MANIFEST.in
|
|
||||||
recursive-include docs *.txt
|
recursive-include docs *.txt
|
||||||
recursive-include dbtemplates/locale *
|
recursive-include dbtemplates/locale *
|
||||||
recursive-include dbtemplates/static/dbtemplates *.css *.js
|
recursive-include dbtemplates/static/dbtemplates *.css *.js
|
||||||
|
|
|
||||||
17
README.rst
17
README.rst
|
|
@ -1,6 +1,17 @@
|
||||||
django-dbtemplates
|
django-dbtemplates
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
.. image:: https://jazzband.co/static/img/badge.svg
|
||||||
|
:alt: Jazzband
|
||||||
|
:target: https://jazzband.co/
|
||||||
|
|
||||||
|
.. image:: https://github.com/jazzband/django-dbtemplates/workflows/Test/badge.svg
|
||||||
|
:target: https://github.com/jazzband/django-dbtemplates/actions
|
||||||
|
|
||||||
|
.. image:: https://codecov.io/github/jazzband/django-dbtemplates/coverage.svg?branch=master
|
||||||
|
:alt: Codecov
|
||||||
|
:target: https://codecov.io/github/jazzband/django-dbtemplates?branch=master
|
||||||
|
|
||||||
``dbtemplates`` is a Django app that consists of two parts:
|
``dbtemplates`` is a Django app that consists of two parts:
|
||||||
|
|
||||||
1. It allows you to store templates in your database
|
1. It allows you to store templates in your database
|
||||||
|
|
@ -10,10 +21,10 @@ django-dbtemplates
|
||||||
It also features optional support for versioned storage and django-admin
|
It also features optional support for versioned storage and django-admin
|
||||||
command, integrates with Django's caching system and the admin actions.
|
command, integrates with Django's caching system and the admin actions.
|
||||||
|
|
||||||
Please see http://django-dbtemplates.readthedocs.org/ for more details.
|
Please see https://django-dbtemplates.readthedocs.io/ for more details.
|
||||||
|
|
||||||
The source code and issue tracker can be found on Github:
|
The source code and issue tracker can be found on Github:
|
||||||
|
|
||||||
https://github.com/jezdez/django-dbtemplates
|
https://github.com/jazzband/django-dbtemplates
|
||||||
|
|
||||||
.. _template loader: http://docs.djangoproject.com/en/dev/ref/templates/api/#loader-types
|
.. _template loader: http://docs.djangoproject.com/en/dev/ref/templates/api/#loader-types
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,3 @@
|
||||||
VERSION = (1, 2, 0, "f", 0) # following PEP 386
|
import importlib.metadata
|
||||||
DEV_N = None
|
|
||||||
|
|
||||||
|
__version__ = importlib.metadata.version("django-dbtemplates")
|
||||||
def get_version():
|
|
||||||
version = "%s.%s" % (VERSION[0], VERSION[1])
|
|
||||||
if VERSION[2]:
|
|
||||||
version = "%s.%s" % (version, VERSION[2])
|
|
||||||
if VERSION[3] != "f":
|
|
||||||
version = "%s%s%s" % (version, VERSION[3], VERSION[4])
|
|
||||||
if DEV_N:
|
|
||||||
version = "%s.dev%s" % (version, DEV_N)
|
|
||||||
return version
|
|
||||||
|
|
||||||
|
|
||||||
__version__ = get_version()
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,29 @@
|
||||||
import posixpath
|
import posixpath
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.utils.translation import ungettext, ugettext_lazy as _
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
from django.utils.translation import gettext_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,
|
from dbtemplates.models import Template, add_template_to_cache, remove_cached_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 django-reversion is installed and use reversions' VersionAdmin
|
# Check if either django-reversion-compare or django-reversion is installed and
|
||||||
# as the base admin class if yes
|
# use reversion_compare's CompareVersionAdmin or reversion's VersionAdmin as
|
||||||
if settings.DBTEMPLATES_USE_REVERSION:
|
# the base admin class if yes
|
||||||
|
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
|
from django.contrib.admin import ModelAdmin as TemplateModelAdmin # noqa
|
||||||
|
|
||||||
|
|
||||||
class CodeMirrorTextArea(forms.Textarea):
|
class CodeMirrorTextArea(forms.Textarea):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
A custom widget for the CodeMirror browser editor to be used with the
|
A custom widget for the CodeMirror browser editor to be used with the
|
||||||
content field of the Template model.
|
content field of the Template model.
|
||||||
|
|
@ -25,13 +31,14 @@ 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/codemirror.js')]
|
js = [posixpath.join(settings.DBTEMPLATES_MEDIA_PREFIX,
|
||||||
|
'js/codemirror.js')]
|
||||||
|
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None, renderer=None):
|
||||||
result = []
|
result = []
|
||||||
result.append(
|
result.append(
|
||||||
super(CodeMirrorTextArea, self).render(name, value, attrs))
|
super().render(name, value, attrs))
|
||||||
result.append(u"""
|
result.append("""
|
||||||
<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/",
|
||||||
|
|
@ -45,7 +52,8 @@ 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(u"".join(result))
|
return mark_safe("".join(result))
|
||||||
|
|
||||||
|
|
||||||
if settings.DBTEMPLATES_USE_CODEMIRROR:
|
if settings.DBTEMPLATES_USE_CODEMIRROR:
|
||||||
TemplateContentTextArea = CodeMirrorTextArea
|
TemplateContentTextArea = CodeMirrorTextArea
|
||||||
|
|
@ -54,26 +62,42 @@ else:
|
||||||
|
|
||||||
if settings.DBTEMPLATES_AUTO_POPULATE_CONTENT:
|
if settings.DBTEMPLATES_AUTO_POPULATE_CONTENT:
|
||||||
content_help_text = _("Leaving this empty causes Django to look for a "
|
content_help_text = _("Leaving this empty causes Django to look for a "
|
||||||
"template with the given name and populate this field with its "
|
"template with the given name and populate this "
|
||||||
"content.")
|
"field with its content.")
|
||||||
else:
|
else:
|
||||||
content_help_text = ""
|
content_help_text = ""
|
||||||
|
|
||||||
|
if settings.DBTEMPLATES_USE_CODEMIRROR and settings.DBTEMPLATES_USE_TINYMCE:
|
||||||
|
raise ImproperlyConfigured("You may use either CodeMirror or TinyMCE "
|
||||||
|
"with dbtemplates, not both. Please disable "
|
||||||
|
"one of them.")
|
||||||
|
|
||||||
|
if settings.DBTEMPLATES_USE_TINYMCE:
|
||||||
|
from tinymce.widgets import AdminTinyMCE
|
||||||
|
TemplateContentTextArea = AdminTinyMCE
|
||||||
|
elif settings.DBTEMPLATES_USE_REDACTOR:
|
||||||
|
from redactor.widgets import RedactorEditor
|
||||||
|
TemplateContentTextArea = RedactorEditor
|
||||||
|
|
||||||
|
|
||||||
class TemplateAdminForm(forms.ModelForm):
|
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({'rows': '24'}),
|
widget=TemplateContentTextArea(attrs={'rows': '24'}),
|
||||||
help_text=content_help_text, required=False)
|
help_text=content_help_text, required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Template
|
model = Template
|
||||||
|
fields = ('name', 'content', 'sites', 'creation_date', 'last_changed')
|
||||||
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
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'),
|
||||||
|
|
@ -98,7 +122,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 = ungettext(
|
message = ngettext(
|
||||||
"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)
|
||||||
|
|
@ -110,7 +134,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 = ungettext(
|
message = ngettext(
|
||||||
"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)
|
||||||
|
|
@ -123,18 +147,19 @@ 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('%s: %s' % (template.name, error))
|
errors.append(f'{template.name}: {error}')
|
||||||
if errors:
|
if errors:
|
||||||
count = len(errors)
|
count = len(errors)
|
||||||
message = ungettext(
|
message = ngettext(
|
||||||
"Template syntax check FAILED for %(names)s.",
|
"Template syntax check FAILED for %(names)s.",
|
||||||
"Template syntax check FAILED for %(count)d templates: %(names)s.",
|
"Template syntax check FAILED for "
|
||||||
|
"%(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 = ungettext(
|
message = ngettext(
|
||||||
"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})
|
||||||
|
|
@ -144,4 +169,5 @@ 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)
|
||||||
|
|
|
||||||
9
dbtemplates/apps.py
Normal file
9
dbtemplates/apps.py
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class DBTemplatesConfig(AppConfig):
|
||||||
|
name = 'dbtemplates'
|
||||||
|
verbose_name = _('Database templates')
|
||||||
|
|
||||||
|
default_auto_field = 'django.db.models.AutoField'
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
import posixpath
|
import posixpath
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from dbtemplates.utils.settings import AppSettings
|
from appconf import AppConf
|
||||||
|
|
||||||
|
|
||||||
class DbTemplatesSettings(AppSettings):
|
class DbTemplatesConf(AppConf):
|
||||||
USE_CODEMIRROR = False
|
USE_CODEMIRROR = False
|
||||||
USE_REVERSION = False
|
USE_REVERSION = False
|
||||||
|
USE_REVERSION_COMPARE = False
|
||||||
|
USE_TINYMCE = False
|
||||||
|
USE_REDACTOR = False
|
||||||
ADD_DEFAULT_SITE = True
|
ADD_DEFAULT_SITE = True
|
||||||
AUTO_POPULATE_CONTENT = True
|
AUTO_POPULATE_CONTENT = True
|
||||||
MEDIA_PREFIX = None
|
MEDIA_PREFIX = None
|
||||||
|
|
@ -15,29 +19,49 @@ class DbTemplatesSettings(AppSettings):
|
||||||
|
|
||||||
def configure_media_prefix(self, value):
|
def configure_media_prefix(self, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
base_url = getattr(self, "STATIC_URL", None)
|
base_url = getattr(settings, "STATIC_URL", None)
|
||||||
if base_url is None:
|
if base_url is None:
|
||||||
base_url = self.MEDIA_URL
|
base_url = settings.MEDIA_URL
|
||||||
value = posixpath.join(base_url, "dbtemplates/")
|
value = posixpath.join(base_url, "dbtemplates/")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def configure_cache_backend(self, value):
|
def configure_cache_backend(self, value):
|
||||||
# If we are on Django 1.3 AND using the new CACHES setting..
|
# If we are on Django 1.3 AND using the new CACHES setting..
|
||||||
if hasattr(self, "CACHES"):
|
if hasattr(settings, "CACHES"):
|
||||||
if "dbtemplates" in self.CACHES:
|
if "dbtemplates" in settings.CACHES:
|
||||||
return "dbtemplates"
|
return "dbtemplates"
|
||||||
else:
|
else:
|
||||||
return "default"
|
return "default"
|
||||||
if isinstance(value, basestring) and value.startswith("dbtemplates."):
|
if isinstance(value, str) 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.")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def configure_use_reversion(self, value):
|
def configure_use_reversion(self, value):
|
||||||
if value and 'reversion' not in self.INSTALLED_APPS:
|
if value and 'reversion' not in settings.INSTALLED_APPS:
|
||||||
raise ImproperlyConfigured("Please add 'reversion' to your "
|
raise ImproperlyConfigured("Please add 'reversion' to your "
|
||||||
"INSTALLED_APPS setting to make use of it in dbtemplates.")
|
"INSTALLED_APPS setting to make "
|
||||||
|
"use of it in dbtemplates.")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
settings = DbTemplatesSettings("DBTEMPLATES")
|
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):
|
||||||
|
if value and 'tinymce' not in settings.INSTALLED_APPS:
|
||||||
|
raise ImproperlyConfigured("Please add 'tinymce' to your "
|
||||||
|
"INSTALLED_APPS setting to make "
|
||||||
|
"use of it in dbtemplates.")
|
||||||
|
return value
|
||||||
|
|
||||||
|
def configure_use_redactor(self, value):
|
||||||
|
if value and 'redactor' not in settings.INSTALLED_APPS:
|
||||||
|
raise ImproperlyConfigured("Please add 'redactor' to your "
|
||||||
|
"INSTALLED_APPS setting to make "
|
||||||
|
"use of it in dbtemplates.")
|
||||||
|
return value
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.template import TemplateDoesNotExist
|
from django.db import router
|
||||||
|
from django.template import Origin, TemplateDoesNotExist
|
||||||
|
from django.template.loaders.base import Loader as BaseLoader
|
||||||
|
|
||||||
from dbtemplates.conf import settings
|
|
||||||
from dbtemplates.models import Template
|
from dbtemplates.models import Template
|
||||||
from dbtemplates.utils.cache import (cache, get_cache_key,
|
from dbtemplates.utils.cache import (cache, get_cache_key,
|
||||||
set_and_return, get_cache_notfound_key)
|
set_and_return, get_cache_notfound_key)
|
||||||
from django.template.loader import BaseLoader
|
|
||||||
|
|
||||||
|
|
||||||
class Loader(BaseLoader):
|
class Loader(BaseLoader):
|
||||||
|
|
@ -19,7 +19,25 @@ class Loader(BaseLoader):
|
||||||
"""
|
"""
|
||||||
is_usable = True
|
is_usable = True
|
||||||
|
|
||||||
def load_template_source(self, template_name, template_dirs=None):
|
def get_template_sources(self, template_name, template_dirs=None):
|
||||||
|
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)
|
||||||
|
db = router.db_for_read(Template, instance=template)
|
||||||
|
display_name = f'dbtemplates:{db}:{template_name}:{site.domain}'
|
||||||
|
return set_and_return(cache_key, template.content, display_name)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -33,15 +51,13 @@ class Loader(BaseLoader):
|
||||||
# in the cache indicating that queries failed, with the current
|
# in the cache indicating that queries failed, with the current
|
||||||
# timestamp.
|
# timestamp.
|
||||||
site = Site.objects.get_current()
|
site = Site.objects.get_current()
|
||||||
display_name = 'dbtemplates:%s:%s:%s' % (settings.DATABASE_ENGINE,
|
|
||||||
template_name, site.domain)
|
|
||||||
cache_key = get_cache_key(template_name)
|
cache_key = get_cache_key(template_name)
|
||||||
if cache:
|
if cache:
|
||||||
try:
|
try:
|
||||||
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:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Not found in cache, move on.
|
# Not found in cache, move on.
|
||||||
|
|
@ -51,20 +67,19 @@ 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:
|
except Exception:
|
||||||
raise TemplateDoesNotExist(template_name)
|
raise TemplateDoesNotExist(template_name)
|
||||||
|
|
||||||
# Not marked as not-found, move on...
|
# Not marked as not-found, move on...
|
||||||
|
|
||||||
try:
|
try:
|
||||||
template = Template.objects.get(name__exact=template_name,
|
return self._load_and_store_template(template_name, cache_key,
|
||||||
sites__in=[site.id])
|
site, sites__in=[site.id])
|
||||||
return set_and_return(cache_key, template.content, display_name)
|
|
||||||
except (Template.MultipleObjectsReturned, Template.DoesNotExist):
|
except (Template.MultipleObjectsReturned, Template.DoesNotExist):
|
||||||
try:
|
try:
|
||||||
template = Template.objects.get(name__exact=template_name)
|
return self._load_and_store_template(template_name, cache_key,
|
||||||
return set_and_return(cache_key, template.content, display_name)
|
site, sites__isnull=True)
|
||||||
except Template.DoesNotExist:
|
except (Template.MultipleObjectsReturned, Template.DoesNotExist):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Mark as not-found in cache.
|
# Mark as not-found in cache.
|
||||||
|
|
|
||||||
BIN
dbtemplates/locale/ru/LC_MESSAGES/django.mo
Normal file
BIN
dbtemplates/locale/ru/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
106
dbtemplates/locale/ru/LC_MESSAGES/django.po
Normal file
106
dbtemplates/locale/ru/LC_MESSAGES/django.po
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2013-07-30 14:03+0600\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\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"
|
||||||
|
|
||||||
|
#: admin.py:57
|
||||||
|
msgid ""
|
||||||
|
"Leaving this empty causes Django to look for a template with the given name "
|
||||||
|
"and populate this field with its content."
|
||||||
|
msgstr "Если вы оставите это поле незаполненным, Django будет искать шаблон с введённым именем и заполнит поле его содержимым."
|
||||||
|
|
||||||
|
#: admin.py:92
|
||||||
|
msgid "Advanced"
|
||||||
|
msgstr "Дополнительно"
|
||||||
|
|
||||||
|
#: admin.py:95
|
||||||
|
msgid "Date/time"
|
||||||
|
msgstr "Дата/время"
|
||||||
|
|
||||||
|
#: admin.py:112
|
||||||
|
#, python-format
|
||||||
|
msgid "Cache of one template successfully invalidated."
|
||||||
|
msgid_plural "Cache of %(count)d templates successfully invalidated."
|
||||||
|
msgstr[0] "Кэш для шаблона успешно очищен."
|
||||||
|
msgstr[1] "Кэш для шаблонов (%(count)d шт.) успешно очищен."
|
||||||
|
|
||||||
|
#: admin.py:116
|
||||||
|
msgid "Invalidate cache of selected templates"
|
||||||
|
msgstr "Очистить кэш для выделенных шаблонов"
|
||||||
|
|
||||||
|
#: admin.py:124
|
||||||
|
#, python-format
|
||||||
|
msgid "Cache successfully repopulated with one template."
|
||||||
|
msgid_plural "Cache successfully repopulated with %(count)d templates."
|
||||||
|
msgstr[0] "Кэш для шаблона успешно заполнен."
|
||||||
|
msgstr[1] "Кэш для шаблонов (%(count)d шт.) успешно заполнен."
|
||||||
|
|
||||||
|
#: admin.py:128
|
||||||
|
msgid "Repopulate cache with selected templates"
|
||||||
|
msgstr "Заполнить кэш для выделенных шаблонов"
|
||||||
|
|
||||||
|
#: admin.py:140
|
||||||
|
#, python-format
|
||||||
|
msgid "Template syntax check FAILED for %(names)s."
|
||||||
|
msgid_plural "Template syntax check FAILED for %(count)d templates: %(names)s."
|
||||||
|
msgstr[0] "Неверный синтаксис у шаблона %(names)s."
|
||||||
|
msgstr[1] "Неверный синтаксис у следующих шаблонов: %(names)s."
|
||||||
|
|
||||||
|
#: admin.py:148
|
||||||
|
#, python-format
|
||||||
|
msgid "Template syntax OK."
|
||||||
|
msgid_plural "Template syntax OK for %(count)d templates."
|
||||||
|
msgstr[0] "Синтаксис шаблона корректен."
|
||||||
|
msgstr[1] "Синтаксис шаблонов корректен."
|
||||||
|
|
||||||
|
#: admin.py:151
|
||||||
|
msgid "Check template syntax"
|
||||||
|
msgstr "Проверить синтаксис шаблона"
|
||||||
|
|
||||||
|
#: admin.py:155 models.py:29
|
||||||
|
msgid "sites"
|
||||||
|
msgstr "сайты"
|
||||||
|
|
||||||
|
#: models.py:26
|
||||||
|
msgid "name"
|
||||||
|
msgstr "название"
|
||||||
|
|
||||||
|
#: models.py:27
|
||||||
|
msgid "Example: 'flatpages/default.html'"
|
||||||
|
msgstr "Например: 'flatpages/default.html'"
|
||||||
|
|
||||||
|
#: models.py:28
|
||||||
|
msgid "content"
|
||||||
|
msgstr "содержимое"
|
||||||
|
|
||||||
|
#: models.py:31
|
||||||
|
msgid "creation date"
|
||||||
|
msgstr "дата создания"
|
||||||
|
|
||||||
|
#: models.py:33
|
||||||
|
msgid "last changed"
|
||||||
|
msgstr "последнее изменение"
|
||||||
|
|
||||||
|
#: models.py:41
|
||||||
|
msgid "template"
|
||||||
|
msgstr "шаблон"
|
||||||
|
|
||||||
|
#: models.py:42
|
||||||
|
msgid "templates"
|
||||||
|
msgstr "шаблоны"
|
||||||
|
|
@ -1,20 +1,19 @@
|
||||||
from django.core.management.base import CommandError, NoArgsCommand
|
from django.core.management.base import CommandError, BaseCommand
|
||||||
|
|
||||||
from dbtemplates.models import Template
|
from dbtemplates.models import Template
|
||||||
from dbtemplates.utils.template import check_template_syntax
|
from dbtemplates.utils.template import check_template_syntax
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
|
||||||
|
class Command(BaseCommand):
|
||||||
help = "Ensures templates stored in the database don't have syntax errors."
|
help = "Ensures templates stored in the database don't have syntax errors."
|
||||||
|
|
||||||
def handle_noargs(self, **options):
|
def handle(self, **options):
|
||||||
errors = []
|
errors = []
|
||||||
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('%s: %s' % (template.name, error))
|
errors.append(f'{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))
|
||||||
# NOTE: printing instead of using self.stdout.write to maintain
|
self.stdout.write('OK')
|
||||||
# Django 1.2 compatibility
|
|
||||||
print('OK')
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import sys
|
import sys
|
||||||
from optparse import make_option
|
from django.core.management.base import CommandError, BaseCommand
|
||||||
|
|
||||||
from django.core.management.base import CommandError, NoArgsCommand
|
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
|
|
||||||
from dbtemplates.models import Template
|
from dbtemplates.models import Template
|
||||||
|
|
@ -26,13 +24,15 @@ TEMPLATES = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
class Command(BaseCommand):
|
||||||
help = "Creates the default error templates as database template objects."
|
help = "Creates the default error templates as database template objects."
|
||||||
option_list = NoArgsCommand.option_list + (
|
|
||||||
make_option("-f", "--force", action="store_true", dest="force",
|
|
||||||
default=False, help="overwrite existing database templates"),)
|
|
||||||
|
|
||||||
def handle_noargs(self, **options):
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
"-f", "--force", action="store_true", dest="force",
|
||||||
|
default=False, help="overwrite existing database templates")
|
||||||
|
|
||||||
|
def handle(self, **options):
|
||||||
force = options.get('force')
|
force = options.get('force')
|
||||||
try:
|
try:
|
||||||
site = Site.objects.get_current()
|
site = Site.objects.get_current()
|
||||||
|
|
@ -43,7 +43,7 @@ class Command(NoArgsCommand):
|
||||||
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="%s.html" % error_code)
|
name=f"{error_code}.html")
|
||||||
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,111 +1,153 @@
|
||||||
import os
|
import os
|
||||||
import codecs
|
|
||||||
from optparse import make_option
|
|
||||||
|
|
||||||
from django.contrib.sites.models import Site
|
|
||||||
from django.core.management.base import CommandError, NoArgsCommand
|
|
||||||
from django.template.loaders.app_directories import app_template_dirs
|
|
||||||
|
|
||||||
from dbtemplates.conf import settings
|
|
||||||
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")
|
||||||
|
|
||||||
class Command(NoArgsCommand):
|
DIRS = []
|
||||||
|
for engine in _engine_list():
|
||||||
|
DIRS.extend(engine.dirs)
|
||||||
|
DIRS = tuple(DIRS)
|
||||||
|
app_template_dirs = get_app_template_dirs("templates")
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
help = "Syncs file system templates with the database bidirectionally."
|
help = "Syncs file system templates with the database bidirectionally."
|
||||||
option_list = NoArgsCommand.option_list + (
|
|
||||||
make_option("-e", "--ext", dest="ext", action="store", default="html",
|
|
||||||
help="extension of the files you want to sync with the database "
|
|
||||||
"[default: %default]"),
|
|
||||||
make_option("-f", "--force", action="store_true", dest="force",
|
|
||||||
default=False, help="overwrite existing database templates"),
|
|
||||||
make_option("-o", "--overwrite", action="store", dest="overwrite",
|
|
||||||
default='0', help="'0' - ask always, '1' - overwrite database "
|
|
||||||
"templates from template files, '2' - overwrite template "
|
|
||||||
"files from database templates"),
|
|
||||||
make_option("-a", "--app-first", action="store_true", dest="app_first",
|
|
||||||
default=False, help="look for templates in applications "
|
|
||||||
"directories before project templates"),
|
|
||||||
make_option("-d", "--delete", action="store_true", dest="delete",
|
|
||||||
default=False, help="Delete templates after syncing"))
|
|
||||||
|
|
||||||
def handle_noargs(self, **options):
|
def add_arguments(self, parser):
|
||||||
extension = options.get('ext')
|
parser.add_argument(
|
||||||
force = options.get('force')
|
"-e",
|
||||||
overwrite = options.get('overwrite')
|
"--ext",
|
||||||
app_first = options.get('app_first')
|
dest="ext",
|
||||||
delete = options.get('delete')
|
action="store",
|
||||||
|
default="html",
|
||||||
|
help="extension of the files you want to "
|
||||||
|
"sync with the database [default: %(default)s]",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-f",
|
||||||
|
"--force",
|
||||||
|
action="store_true",
|
||||||
|
dest="force",
|
||||||
|
default=False,
|
||||||
|
help="overwrite existing database templates",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-o",
|
||||||
|
"--overwrite",
|
||||||
|
action="store",
|
||||||
|
dest="overwrite",
|
||||||
|
default="0",
|
||||||
|
help="'0' - ask always, '1' - overwrite database "
|
||||||
|
"templates from template files, '2' - overwrite "
|
||||||
|
"template files from database templates",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-a",
|
||||||
|
"--app-first",
|
||||||
|
action="store_true",
|
||||||
|
dest="app_first",
|
||||||
|
default=False,
|
||||||
|
help="look for templates in applications "
|
||||||
|
"directories before project templates",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-d",
|
||||||
|
"--delete",
|
||||||
|
action="store_true",
|
||||||
|
dest="delete",
|
||||||
|
default=False,
|
||||||
|
help="Delete templates after syncing",
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle(self, **options):
|
||||||
|
extension = options.get("ext")
|
||||||
|
force = options.get("force")
|
||||||
|
overwrite = options.get("overwrite")
|
||||||
|
app_first = options.get("app_first")
|
||||||
|
delete = options.get("delete")
|
||||||
|
|
||||||
if not extension.startswith("."):
|
if not extension.startswith("."):
|
||||||
extension = ".%s" % extension
|
extension = f".{extension}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
site = Site.objects.get_current()
|
site = Site.objects.get_current()
|
||||||
except:
|
except Exception:
|
||||||
raise CommandError("Please make sure to have the sites contrib "
|
raise CommandError(
|
||||||
"app installed and setup with a site object")
|
"Please make sure to have the sites contrib "
|
||||||
|
"app installed and setup with a site object"
|
||||||
if not type(settings.TEMPLATE_DIRS) in (tuple, list):
|
)
|
||||||
raise CommandError("Please make sure settings.TEMPLATE_DIRS is a "
|
|
||||||
"list or tuple.")
|
|
||||||
|
|
||||||
if app_first:
|
if app_first:
|
||||||
tpl_dirs = app_template_dirs + settings.TEMPLATE_DIRS
|
tpl_dirs = app_template_dirs + DIRS
|
||||||
else:
|
else:
|
||||||
tpl_dirs = settings.TEMPLATE_DIRS + app_template_dirs
|
tpl_dirs = DIRS + app_template_dirs
|
||||||
templatedirs = [d for d in tpl_dirs if os.path.isdir(d)]
|
templatedirs = [str(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 [f for f in filenames
|
for f in [
|
||||||
if f.endswith(extension) and not f.startswith(".")]:
|
f
|
||||||
|
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(templatedir)[1]
|
name = path.split(str(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:
|
||||||
confirm = raw_input(
|
confirm = input(
|
||||||
"\nA '%s' template doesn't exist in the "
|
"\nA '%s' template doesn't exist in the "
|
||||||
"database.\nCreate it with '%s'?"
|
"database.\nCreate it with '%s'?"
|
||||||
" (y/[n]): """ % (name, path))
|
" (y/[n]): "
|
||||||
if force or confirm.lower().startswith('y'):
|
"" % (name, path)
|
||||||
t = Template(name=name,
|
)
|
||||||
content=codecs.open(path, "r").read())
|
if force or confirm.lower().startswith("y"):
|
||||||
|
with open(path, encoding="utf-8") as f:
|
||||||
|
t = Template(name=name, content=f.read())
|
||||||
t.save()
|
t.save()
|
||||||
t.sites.add(site)
|
t.sites.add(site)
|
||||||
else:
|
else:
|
||||||
while 1:
|
while True:
|
||||||
if overwrite == ALWAYS_ASK:
|
if overwrite == ALWAYS_ASK:
|
||||||
confirm = raw_input(
|
_i = (
|
||||||
"\n%s exists in the database.\n"
|
"\n%(template)s exists in the database.\n"
|
||||||
"(1) Overwrite %s with '%s'\n"
|
"(1) Overwrite %(template)s with '%(path)s'\n" # noqa
|
||||||
"(2) Overwrite '%s' with %s\n"
|
"(2) Overwrite '%(path)s' with %(template)s\n" # noqa
|
||||||
"Type 1 or 2 or press <Enter> to skip: "
|
"Type 1 or 2 or press <Enter> to skip: "
|
||||||
% (t.__repr__(),
|
% {"template": t.__repr__(), "path": path}
|
||||||
t.__repr__(), path,
|
)
|
||||||
path, t.__repr__()))
|
|
||||||
|
confirm = input(_i)
|
||||||
else:
|
else:
|
||||||
confirm = overwrite
|
confirm = overwrite
|
||||||
if confirm in ('', FILES_TO_DATABASE, DATABASE_TO_FILES):
|
if confirm in (
|
||||||
|
"",
|
||||||
|
FILES_TO_DATABASE,
|
||||||
|
DATABASE_TO_FILES,
|
||||||
|
):
|
||||||
if confirm == FILES_TO_DATABASE:
|
if confirm == FILES_TO_DATABASE:
|
||||||
t.content = codecs.open(path, 'r').read()
|
with open(path, encoding="utf-8") as f:
|
||||||
t.save()
|
t.content = f.read()
|
||||||
t.sites.add(site)
|
t.save()
|
||||||
|
t.sites.add(site)
|
||||||
if delete:
|
if delete:
|
||||||
try:
|
try:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
except OSError:
|
except OSError:
|
||||||
raise CommandError(
|
raise CommandError(
|
||||||
u"Couldn't delete %s" % path)
|
f"Couldn't delete {path}"
|
||||||
|
)
|
||||||
elif confirm == DATABASE_TO_FILES:
|
elif confirm == DATABASE_TO_FILES:
|
||||||
try:
|
with open(path, "w", encoding="utf-8") as f: # noqa
|
||||||
f = codecs.open(path, 'w')
|
|
||||||
f.write(t.content)
|
f.write(t.content)
|
||||||
finally:
|
|
||||||
f.close()
|
|
||||||
if delete:
|
if delete:
|
||||||
t.delete()
|
t.delete()
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,73 @@
|
||||||
# encoding: utf-8
|
import django
|
||||||
import datetime
|
import django.utils.timezone
|
||||||
from south.db import db
|
from django.db import migrations, models
|
||||||
from south.v2 import SchemaMigration
|
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
|
||||||
|
|
||||||
def forwards(self, orm):
|
|
||||||
|
|
||||||
# Adding model 'Template'
|
|
||||||
db.create_table('django_template', (
|
|
||||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
|
||||||
('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=100)),
|
|
||||||
('content', self.gf('django.db.models.fields.TextField')(blank=True)),
|
|
||||||
('creation_date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
|
|
||||||
('last_changed', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
|
|
||||||
))
|
|
||||||
db.send_create_signal('dbtemplates', ['Template'])
|
|
||||||
|
|
||||||
# Adding M2M table for field sites on 'Template'
|
|
||||||
db.create_table('django_template_sites', (
|
|
||||||
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
|
|
||||||
('template', models.ForeignKey(orm['dbtemplates.template'], null=False)),
|
|
||||||
('site', models.ForeignKey(orm['sites.site'], null=False))
|
|
||||||
))
|
|
||||||
db.create_unique('django_template_sites', ['template_id', 'site_id'])
|
|
||||||
|
|
||||||
|
|
||||||
def backwards(self, orm):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
# Deleting model 'Template'
|
|
||||||
db.delete_table('django_template')
|
|
||||||
|
|
||||||
# Removing M2M table for field sites on 'Template'
|
dependencies = [
|
||||||
db.delete_table('django_template_sites')
|
("sites", "0001_initial"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
models = {
|
migrations.CreateModel(
|
||||||
'dbtemplates.template': {
|
name="Template",
|
||||||
'Meta': {'ordering': "('name',)", 'object_name': 'Template', 'db_table': "'django_template'"},
|
fields=[
|
||||||
'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
(
|
||||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
"id",
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
models.AutoField(
|
||||||
'last_changed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
verbose_name="ID",
|
||||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
|
serialize=False,
|
||||||
'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sites.Site']", 'symmetrical': 'False'})
|
auto_created=True,
|
||||||
},
|
primary_key=True,
|
||||||
'sites.site': {
|
),
|
||||||
'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
|
),
|
||||||
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
(
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
"name",
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
models.CharField(
|
||||||
}
|
help_text="Example: 'flatpages/default.html'",
|
||||||
}
|
max_length=100,
|
||||||
|
verbose_name="name",
|
||||||
complete_apps = ['dbtemplates']
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"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={
|
||||||
|
"ordering": ("name",),
|
||||||
|
"db_table": "django_template",
|
||||||
|
"verbose_name": "template",
|
||||||
|
"verbose_name_plural": "templates",
|
||||||
|
},
|
||||||
|
bases=(models.Model,),
|
||||||
|
managers=[
|
||||||
|
("objects", django.db.models.manager.Manager()),
|
||||||
|
(
|
||||||
|
"on_site",
|
||||||
|
django.contrib.sites.managers.CurrentSiteManager("sites"),
|
||||||
|
), # noqa
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
# encoding: utf-8
|
|
||||||
import datetime
|
|
||||||
from south.db import db
|
|
||||||
from south.v2 import SchemaMigration
|
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
class Migration(SchemaMigration):
|
|
||||||
|
|
||||||
def forwards(self, orm):
|
|
||||||
|
|
||||||
# Removing unique constraint on 'Template', fields ['name']
|
|
||||||
db.delete_unique('django_template', ['name'])
|
|
||||||
|
|
||||||
|
|
||||||
def backwards(self, orm):
|
|
||||||
|
|
||||||
# Adding unique constraint on 'Template', fields ['name']
|
|
||||||
db.create_unique('django_template', ['name'])
|
|
||||||
|
|
||||||
|
|
||||||
models = {
|
|
||||||
'dbtemplates.template': {
|
|
||||||
'Meta': {'ordering': "('name',)", 'object_name': 'Template', 'db_table': "'django_template'"},
|
|
||||||
'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
|
||||||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
||||||
'last_changed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
|
||||||
'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sites.Site']", 'symmetrical': 'False'})
|
|
||||||
},
|
|
||||||
'sites.site': {
|
|
||||||
'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
|
|
||||||
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
|
||||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
|
||||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
complete_apps = ['dbtemplates']
|
|
||||||
|
|
@ -1,33 +1,32 @@
|
||||||
# -*- coding: utf-8 -*-
|
from dbtemplates.conf import settings
|
||||||
from datetime import datetime
|
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 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 gettext_lazy as _
|
||||||
|
|
||||||
from django.contrib.sites.models import Site
|
|
||||||
from django.contrib.sites.managers import CurrentSiteManager
|
|
||||||
|
|
||||||
from dbtemplates.conf import settings
|
|
||||||
from dbtemplates.utils.cache import add_template_to_cache, remove_cached_template
|
|
||||||
from dbtemplates.utils.template import get_template_source
|
|
||||||
|
|
||||||
|
|
||||||
class Template(models.Model):
|
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.AutoField(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=_('sites'),
|
||||||
blank=True, null=True)
|
blank=True)
|
||||||
creation_date = models.DateTimeField(_('creation date'),
|
creation_date = models.DateTimeField(_('creation date'), auto_now_add=True)
|
||||||
default=datetime.now)
|
last_changed = models.DateTimeField(_('last changed'), auto_now=True)
|
||||||
last_changed = models.DateTimeField(_('last changed'),
|
|
||||||
default=datetime.now)
|
|
||||||
|
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
on_site = CurrentSiteManager('sites')
|
on_site = CurrentSiteManager('sites')
|
||||||
|
|
@ -38,7 +37,7 @@ class Template(models.Model):
|
||||||
verbose_name_plural = _('templates')
|
verbose_name_plural = _('templates')
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def populate(self, name=None):
|
def populate(self, name=None):
|
||||||
|
|
@ -56,12 +55,11 @@ class Template(models.Model):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
self.last_changed = datetime.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(Template, self).save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def add_default_site(instance, **kwargs):
|
def add_default_site(instance, **kwargs):
|
||||||
|
|
|
||||||
175
dbtemplates/test_cases.py
Normal file
175
dbtemplates/test_cases.py
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
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
|
||||||
|
from django.core.management import call_command
|
||||||
|
from django.template import loader, TemplateDoesNotExist
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from django.contrib.sites.models import Site
|
||||||
|
|
||||||
|
from dbtemplates.conf import settings
|
||||||
|
from dbtemplates.models import Template
|
||||||
|
from dbtemplates.utils.cache import get_cache_backend, get_cache_key
|
||||||
|
from dbtemplates.utils.template import (get_template_source,
|
||||||
|
check_template_syntax)
|
||||||
|
from dbtemplates.management.commands.sync_templates import (FILES_TO_DATABASE,
|
||||||
|
DATABASE_TO_FILES)
|
||||||
|
|
||||||
|
|
||||||
|
class DbTemplatesTestCase(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.old_TEMPLATES = settings.TEMPLATES
|
||||||
|
if 'dbtemplates.loader.Loader' not in settings.TEMPLATES:
|
||||||
|
loader.template_source_loaders = None
|
||||||
|
settings.TEMPLATES = list(settings.TEMPLATES) + [
|
||||||
|
'dbtemplates.loader.Loader'
|
||||||
|
]
|
||||||
|
|
||||||
|
self.site1, created1 = Site.objects.get_or_create(
|
||||||
|
domain="example.com", name="example.com")
|
||||||
|
self.site2, created2 = Site.objects.get_or_create(
|
||||||
|
domain="example.org", name="example.org")
|
||||||
|
self.t1, _ = Template.objects.get_or_create(
|
||||||
|
name='base.html', content='base')
|
||||||
|
self.t2, _ = Template.objects.get_or_create(
|
||||||
|
name='sub.html', content='sub')
|
||||||
|
self.t2.sites.add(self.site2)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
loader.template_source_loaders = None
|
||||||
|
settings.TEMPLATES = self.old_TEMPLATES
|
||||||
|
|
||||||
|
def test_basics(self):
|
||||||
|
self.assertEqual(list(self.t1.sites.all()), [self.site1])
|
||||||
|
self.assertTrue("base" in self.t1.content)
|
||||||
|
self.assertEqual(list(Template.objects.filter(sites=self.site1)),
|
||||||
|
[self.t1, self.t2])
|
||||||
|
self.assertEqual(list(self.t2.sites.all()), [self.site1, self.site2])
|
||||||
|
|
||||||
|
def test_empty_sites(self):
|
||||||
|
old_add_default_site = settings.DBTEMPLATES_ADD_DEFAULT_SITE
|
||||||
|
try:
|
||||||
|
settings.DBTEMPLATES_ADD_DEFAULT_SITE = False
|
||||||
|
self.t3 = Template.objects.create(
|
||||||
|
name='footer.html', content='footer')
|
||||||
|
self.assertEqual(list(self.t3.sites.all()), [])
|
||||||
|
finally:
|
||||||
|
settings.DBTEMPLATES_ADD_DEFAULT_SITE = old_add_default_site
|
||||||
|
|
||||||
|
def test_load_templates_sites(self):
|
||||||
|
old_add_default_site = settings.DBTEMPLATES_ADD_DEFAULT_SITE
|
||||||
|
old_site_id = django_settings.SITE_ID
|
||||||
|
try:
|
||||||
|
settings.DBTEMPLATES_ADD_DEFAULT_SITE = False
|
||||||
|
t_site1 = Template.objects.create(
|
||||||
|
name='copyright.html', content='(c) example.com')
|
||||||
|
t_site1.sites.add(self.site1)
|
||||||
|
t_site2 = Template.objects.create(
|
||||||
|
name='copyright.html', content='(c) example.org')
|
||||||
|
t_site2.sites.add(self.site2)
|
||||||
|
|
||||||
|
django_settings.SITE_ID = Site.objects.create(
|
||||||
|
domain="example.net", name="example.net").id
|
||||||
|
Site.objects.clear_cache()
|
||||||
|
|
||||||
|
self.assertRaises(TemplateDoesNotExist,
|
||||||
|
loader.get_template, "copyright.html")
|
||||||
|
finally:
|
||||||
|
django_settings.SITE_ID = old_site_id
|
||||||
|
settings.DBTEMPLATES_ADD_DEFAULT_SITE = old_add_default_site
|
||||||
|
|
||||||
|
def test_load_templates(self):
|
||||||
|
result = loader.get_template("base.html").render()
|
||||||
|
self.assertEqual(result, 'base')
|
||||||
|
result2 = loader.get_template("sub.html").render()
|
||||||
|
self.assertEqual(result2, 'sub')
|
||||||
|
|
||||||
|
def test_error_templates_creation(self):
|
||||||
|
call_command('create_error_templates', force=True, verbosity=0)
|
||||||
|
self.assertEqual(list(Template.objects.filter(sites=self.site1)),
|
||||||
|
list(Template.objects.filter()))
|
||||||
|
self.assertTrue(Template.objects.filter(name='404.html').exists())
|
||||||
|
|
||||||
|
def test_automatic_sync(self):
|
||||||
|
admin_base_template = get_template_source('admin/base.html')
|
||||||
|
template = Template.objects.create(name='admin/base.html')
|
||||||
|
self.assertEqual(admin_base_template, template.content)
|
||||||
|
|
||||||
|
def test_sync_templates(self):
|
||||||
|
old_template_dirs = settings.TEMPLATES[0].get('DIRS', [])
|
||||||
|
temp_template_dir = tempfile.mkdtemp('dbtemplates')
|
||||||
|
temp_template_path = os.path.join(temp_template_dir, 'temp_test.html')
|
||||||
|
temp_template = open(temp_template_path, 'w', encoding='utf-8')
|
||||||
|
try:
|
||||||
|
temp_template.write('temp test')
|
||||||
|
settings.TEMPLATES[0]['DIRS'] = (temp_template_dir,)
|
||||||
|
# these works well if is not settings patched at runtime
|
||||||
|
# for supporting django < 1.7 tests we must patch dirs in runtime
|
||||||
|
from dbtemplates.management.commands import sync_templates
|
||||||
|
sync_templates.DIRS = settings.TEMPLATES[0]['DIRS']
|
||||||
|
|
||||||
|
self.assertFalse(
|
||||||
|
Template.objects.filter(name='temp_test.html').exists())
|
||||||
|
call_command('sync_templates', force=True,
|
||||||
|
verbosity=0, overwrite=FILES_TO_DATABASE)
|
||||||
|
self.assertTrue(
|
||||||
|
Template.objects.filter(name='temp_test.html').exists())
|
||||||
|
|
||||||
|
t = Template.objects.get(name='temp_test.html')
|
||||||
|
t.content = 'temp test modified'
|
||||||
|
t.save()
|
||||||
|
call_command('sync_templates', force=True,
|
||||||
|
verbosity=0, overwrite=DATABASE_TO_FILES)
|
||||||
|
self.assertEqual('temp test modified',
|
||||||
|
open(temp_template_path,
|
||||||
|
encoding='utf-8').read())
|
||||||
|
|
||||||
|
call_command('sync_templates', force=True, verbosity=0,
|
||||||
|
delete=True, overwrite=DATABASE_TO_FILES)
|
||||||
|
self.assertTrue(os.path.exists(temp_template_path))
|
||||||
|
self.assertFalse(
|
||||||
|
Template.objects.filter(name='temp_test.html').exists())
|
||||||
|
finally:
|
||||||
|
temp_template.close()
|
||||||
|
settings.TEMPLATES[0]['DIRS'] = old_template_dirs
|
||||||
|
shutil.rmtree(temp_template_dir)
|
||||||
|
|
||||||
|
def test_get_cache(self):
|
||||||
|
self.assertTrue(isinstance(get_cache_backend(), BaseCache))
|
||||||
|
|
||||||
|
def test_check_template_syntax(self):
|
||||||
|
bad_template, _ = Template.objects.get_or_create(
|
||||||
|
name='bad.html', content='{% if foo %}Bar')
|
||||||
|
good_template, _ = Template.objects.get_or_create(
|
||||||
|
name='good.html', content='{% if foo %}Bar{% endif %}')
|
||||||
|
self.assertFalse(check_template_syntax(bad_template)[0])
|
||||||
|
self.assertTrue(check_template_syntax(good_template)[0])
|
||||||
|
|
||||||
|
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')
|
||||||
51
dbtemplates/test_settings.py
Normal file
51
dbtemplates/test_settings.py
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
DBTEMPLATES_CACHE_BACKEND = 'dummy://'
|
||||||
|
|
||||||
|
DATABASE_ENGINE = 'sqlite3'
|
||||||
|
# SQLite does not support removing unique constraints (see #28)
|
||||||
|
SOUTH_TESTS_MIGRATE = False
|
||||||
|
|
||||||
|
SITE_ID = 1
|
||||||
|
|
||||||
|
SECRET_KEY = 'something-something'
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': ':memory:',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sites',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'dbtemplates',
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = (
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
)
|
||||||
|
|
||||||
|
TEMPLATE_LOADERS = (
|
||||||
|
'django.template.loaders.filesystem.Loader',
|
||||||
|
'django.template.loaders.app_directories.Loader',
|
||||||
|
'dbtemplates.loader.Loader',
|
||||||
|
)
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'OPTIONS': {
|
||||||
|
'loaders': TEMPLATE_LOADERS,
|
||||||
|
'context_processors': [
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
from __future__ import with_statement
|
|
||||||
import codecs
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
from django.core.cache.backends.base import BaseCache
|
|
||||||
from django.core.management import call_command
|
|
||||||
from django.template import loader, Context
|
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
from django.contrib.sites.models import Site
|
|
||||||
|
|
||||||
from dbtemplates.conf import settings
|
|
||||||
from dbtemplates.models import Template
|
|
||||||
from dbtemplates.utils.cache import get_cache_backend
|
|
||||||
from dbtemplates.utils.template import (get_template_source,
|
|
||||||
check_template_syntax)
|
|
||||||
from dbtemplates.management.commands.sync_templates import (FILES_TO_DATABASE,
|
|
||||||
DATABASE_TO_FILES)
|
|
||||||
|
|
||||||
class DbTemplatesTestCase(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.site1, created1 = Site.objects.get_or_create(
|
|
||||||
domain="example.com", name="example.com")
|
|
||||||
self.site2, created2 = Site.objects.get_or_create(
|
|
||||||
domain="example.org", name="example.org")
|
|
||||||
self.t1, _ = Template.objects.get_or_create(
|
|
||||||
name='base.html', content='base')
|
|
||||||
self.t2, _ = Template.objects.get_or_create(
|
|
||||||
name='sub.html', content='sub')
|
|
||||||
self.t2.sites.add(self.site2)
|
|
||||||
|
|
||||||
def test_basiscs(self):
|
|
||||||
self.assertEqual(list(self.t1.sites.all()), [self.site1])
|
|
||||||
self.assertTrue("base" in self.t1.content)
|
|
||||||
self.assertEqual(list(Template.objects.filter(sites=self.site1)),
|
|
||||||
[self.t1, self.t2])
|
|
||||||
self.assertEqual(list(self.t2.sites.all()), [self.site1, self.site2])
|
|
||||||
|
|
||||||
def test_empty_sites(self):
|
|
||||||
old_add_default_site = settings.DBTEMPLATES_ADD_DEFAULT_SITE
|
|
||||||
try:
|
|
||||||
settings.DBTEMPLATES_ADD_DEFAULT_SITE = False
|
|
||||||
self.t3 = Template.objects.create(
|
|
||||||
name='footer.html', content='footer')
|
|
||||||
self.assertEqual(list(self.t3.sites.all()), [])
|
|
||||||
finally:
|
|
||||||
settings.DBTEMPLATES_ADD_DEFAULT_SITE = old_add_default_site
|
|
||||||
|
|
||||||
def test_load_templates(self):
|
|
||||||
result = loader.get_template("base.html").render(Context({}))
|
|
||||||
self.assertEqual(result, 'base')
|
|
||||||
result2 = loader.get_template("sub.html").render(Context({}))
|
|
||||||
self.assertEqual(result2, 'sub')
|
|
||||||
|
|
||||||
def test_error_templates_creation(self):
|
|
||||||
call_command('create_error_templates', force=True, verbosity=0)
|
|
||||||
self.assertEqual(list(Template.objects.filter(sites=self.site1)),
|
|
||||||
list(Template.objects.filter()))
|
|
||||||
self.assertTrue(Template.objects.filter(name='404.html').exists())
|
|
||||||
|
|
||||||
def test_automatic_sync(self):
|
|
||||||
admin_base_template = get_template_source('admin/base.html')
|
|
||||||
template = Template.objects.create(name='admin/base.html')
|
|
||||||
self.assertEqual(admin_base_template, template.content)
|
|
||||||
|
|
||||||
def test_sync_templates(self):
|
|
||||||
old_template_dirs = settings.TEMPLATE_DIRS
|
|
||||||
temp_template_dir = tempfile.mkdtemp('dbtemplates')
|
|
||||||
last_path_part = temp_template_dir.split('/')[-1]
|
|
||||||
temp_template_path = os.path.join(temp_template_dir, 'temp_test.html')
|
|
||||||
temp_template = codecs.open(temp_template_path, 'w')
|
|
||||||
try:
|
|
||||||
temp_template.write('temp test')
|
|
||||||
settings.TEMPLATE_DIRS = (temp_template_dir,)
|
|
||||||
self.assertFalse(Template.objects.filter(name='temp_test.html').exists())
|
|
||||||
call_command('sync_templates',
|
|
||||||
force=True, verbosity=0, overwrite=FILES_TO_DATABASE)
|
|
||||||
self.assertTrue(Template.objects.filter(name='temp_test.html').exists())
|
|
||||||
|
|
||||||
t = Template.objects.get(name='temp_test.html')
|
|
||||||
t.content = 'temp test modified'
|
|
||||||
t.save()
|
|
||||||
call_command('sync_templates',
|
|
||||||
force=True, verbosity=0, overwrite=DATABASE_TO_FILES)
|
|
||||||
self.assertTrue('modified' in codecs.open(temp_template_path).read())
|
|
||||||
|
|
||||||
call_command('sync_templates',
|
|
||||||
force=True, verbosity=0, delete=True, overwrite=DATABASE_TO_FILES)
|
|
||||||
self.assertTrue(os.path.exists(temp_template_path))
|
|
||||||
self.assertFalse(Template.objects.filter(name='temp_test.html').exists())
|
|
||||||
finally:
|
|
||||||
temp_template.close()
|
|
||||||
settings.TEMPLATE_DIRS = old_template_dirs
|
|
||||||
shutil.rmtree(temp_template_dir)
|
|
||||||
|
|
||||||
def test_get_cache(self):
|
|
||||||
self.assertTrue(isinstance(get_cache_backend(), BaseCache))
|
|
||||||
|
|
||||||
def test_check_template_syntax(self):
|
|
||||||
bad_template, _ = Template.objects.get_or_create(
|
|
||||||
name='bad.html', content='{% if foo %}Bar')
|
|
||||||
good_template, _ = Template.objects.get_or_create(
|
|
||||||
name='good.html', content='{% if foo %}Bar{% endif %}')
|
|
||||||
self.assertFalse(check_template_syntax(bad_template)[0])
|
|
||||||
self.assertTrue(check_template_syntax(good_template)[0])
|
|
||||||
|
|
@ -1,23 +1,34 @@
|
||||||
from django.core.cache import get_cache
|
|
||||||
|
|
||||||
from django.contrib.sites.models import Site
|
|
||||||
|
|
||||||
from dbtemplates.conf import settings
|
from dbtemplates.conf import settings
|
||||||
|
from django.contrib.sites.models import Site
|
||||||
|
from django.core import signals
|
||||||
|
from django.template.defaultfilters import slugify
|
||||||
|
|
||||||
|
|
||||||
def get_cache_backend():
|
def get_cache_backend():
|
||||||
return get_cache(settings.DBTEMPLATES_CACHE_BACKEND)
|
"""
|
||||||
|
Compatibilty wrapper for getting Django's cache backend instance
|
||||||
|
"""
|
||||||
|
from django.core.cache import caches
|
||||||
|
cache = caches.create_connection(settings.DBTEMPLATES_CACHE_BACKEND)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
# cache.close is a no-op
|
||||||
|
signals.request_finished.connect(cache.close)
|
||||||
|
return cache
|
||||||
|
|
||||||
|
|
||||||
cache = get_cache_backend()
|
cache = get_cache_backend()
|
||||||
|
|
||||||
|
|
||||||
def get_cache_key(name):
|
def get_cache_key(name, site=None):
|
||||||
current_site = Site.objects.get_current()
|
if site is None:
|
||||||
return 'dbtemplates::%s::%s' % (name, current_site.pk)
|
site = Site.objects.get_current()
|
||||||
|
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):
|
||||||
|
|
@ -47,4 +58,5 @@ 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.
|
||||||
"""
|
"""
|
||||||
cache.delete(get_cache_key(instance.name))
|
for site in instance.sites.all():
|
||||||
|
cache.delete(get_cache_key(instance.name, site=site))
|
||||||
|
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
from inspect import getmembers
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
|
|
||||||
class AppSettings(object):
|
|
||||||
"""
|
|
||||||
An app setting object to be used for handling app setting defaults
|
|
||||||
gracefully and providing a nice API for them. Say you have an app
|
|
||||||
called ``myapp`` and want to define a few defaults, and refer to the
|
|
||||||
defaults easily in the apps code. Add a ``settings.py`` to your app::
|
|
||||||
|
|
||||||
from path.to.utils import AppSettings
|
|
||||||
|
|
||||||
class MyAppSettings(AppSettings):
|
|
||||||
SETTING_1 = "one"
|
|
||||||
SETTING_2 = (
|
|
||||||
"two",
|
|
||||||
)
|
|
||||||
|
|
||||||
Then initialize the setting with the correct prefix in the location of
|
|
||||||
of your choice, e.g. ``conf.py`` of the app module::
|
|
||||||
|
|
||||||
settings = MyAppSettings(prefix="MYAPP")
|
|
||||||
|
|
||||||
The ``MyAppSettings`` instance will automatically look at Django's
|
|
||||||
global setting to determine each of the settings and respect the
|
|
||||||
provided ``prefix``. E.g. adding this to your site's ``settings.py``
|
|
||||||
will set the ``SETTING_1`` setting accordingly::
|
|
||||||
|
|
||||||
MYAPP_SETTING_1 = "uno"
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
Instead of using ``from django.conf import settings`` as you would
|
|
||||||
usually do, you can switch to using your apps own settings module
|
|
||||||
to access the app settings::
|
|
||||||
|
|
||||||
from myapp.conf import settings
|
|
||||||
|
|
||||||
print myapp_settings.MYAPP_SETTING_1
|
|
||||||
|
|
||||||
``AppSettings`` instances also work as pass-throughs for other
|
|
||||||
global settings that aren't related to the app. For example the
|
|
||||||
following code is perfectly valid::
|
|
||||||
|
|
||||||
from myapp.conf import settings
|
|
||||||
|
|
||||||
if "myapp" in settings.INSTALLED_APPS:
|
|
||||||
print "yay, myapp is installed!"
|
|
||||||
|
|
||||||
Custom handling
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Each of the settings can be individually configured with callbacks.
|
|
||||||
For example, in case a value of a setting depends on other settings
|
|
||||||
or other dependencies. The following example sets one setting to a
|
|
||||||
different value depending on a global setting::
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
class MyCustomAppSettings(AppSettings):
|
|
||||||
ENABLED = True
|
|
||||||
|
|
||||||
def configure_enabled(self, value):
|
|
||||||
return value and not self.DEBUG
|
|
||||||
|
|
||||||
custom_settings = MyCustomAppSettings("MYAPP")
|
|
||||||
|
|
||||||
The value of ``custom_settings.MYAPP_ENABLED`` will vary depending on the
|
|
||||||
value of the global ``DEBUG`` setting.
|
|
||||||
|
|
||||||
Each of the app settings can be customized by providing
|
|
||||||
a method ``configure_<lower_setting_name>`` that takes the default
|
|
||||||
value as defined in the class attributes as the only parameter.
|
|
||||||
The method needs to return the value to be use for the setting in
|
|
||||||
question.
|
|
||||||
"""
|
|
||||||
def __dir__(self):
|
|
||||||
return sorted(list(set(self.__dict__.keys() + dir(settings))))
|
|
||||||
|
|
||||||
__members__ = lambda self: self.__dir__()
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
if name.startswith(self._prefix):
|
|
||||||
raise AttributeError("%r object has no attribute %r" %
|
|
||||||
(self.__class__.__name__, name))
|
|
||||||
return getattr(settings, name)
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
|
||||||
super(AppSettings, self).__setattr__(name, value)
|
|
||||||
if name in dir(settings):
|
|
||||||
setattr(settings, name, value)
|
|
||||||
|
|
||||||
def __init__(self, prefix):
|
|
||||||
super(AppSettings, self).__setattr__('_prefix', prefix)
|
|
||||||
for setting, class_value in getmembers(self.__class__):
|
|
||||||
if setting == setting.upper():
|
|
||||||
prefixed = "%s_%s" % (prefix.upper(), setting.upper())
|
|
||||||
configured_value = getattr(settings, prefixed, class_value)
|
|
||||||
callback_name = "configure_%s" % setting.lower()
|
|
||||||
callback = getattr(self, callback_name, None)
|
|
||||||
if callable(callback):
|
|
||||||
configured_value = callback(configured_value)
|
|
||||||
delattr(self.__class__, setting)
|
|
||||||
setattr(self, prefixed, configured_value)
|
|
||||||
|
|
@ -1,24 +1,13 @@
|
||||||
from django import VERSION
|
|
||||||
from django.template import (Template, TemplateDoesNotExist,
|
from django.template import (Template, TemplateDoesNotExist,
|
||||||
TemplateSyntaxError)
|
TemplateSyntaxError)
|
||||||
from django.utils.importlib import import_module
|
|
||||||
|
|
||||||
|
|
||||||
def get_loaders():
|
def get_loaders():
|
||||||
from django.template.loader import template_source_loaders
|
from django.template.loader import _engine_list
|
||||||
if template_source_loaders is None:
|
loaders = []
|
||||||
try:
|
for engine in _engine_list():
|
||||||
from django.template.loader import (
|
loaders.extend(engine.engine.template_loaders)
|
||||||
find_template as finder_func)
|
return loaders
|
||||||
except ImportError:
|
|
||||||
from django.template.loader import (
|
|
||||||
find_template_source as finder_func)
|
|
||||||
try:
|
|
||||||
source, name = finder_func('test')
|
|
||||||
except TemplateDoesNotExist:
|
|
||||||
pass
|
|
||||||
from django.template.loader import template_source_loaders
|
|
||||||
return template_source_loaders or []
|
|
||||||
|
|
||||||
|
|
||||||
def get_template_source(name):
|
def get_template_source(name):
|
||||||
|
|
@ -27,33 +16,18 @@ 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
|
||||||
module = import_module(loader.__module__)
|
for origin in loader.get_template_sources(name):
|
||||||
load_template_source = getattr(module, 'load_template_source', None)
|
try:
|
||||||
if load_template_source is None:
|
source = loader.get_contents(origin)
|
||||||
load_template_source = loader.load_template_source
|
except (NotImplementedError, TemplateDoesNotExist):
|
||||||
try:
|
continue
|
||||||
source, origin = load_template_source(name)
|
|
||||||
if source:
|
if source:
|
||||||
return source
|
return source
|
||||||
except NotImplementedError:
|
|
||||||
pass
|
|
||||||
except TemplateDoesNotExist:
|
|
||||||
pass
|
|
||||||
if source is None and VERSION[:2] < (1, 2):
|
|
||||||
# Django supported template source extraction still :/
|
|
||||||
try:
|
|
||||||
from django.template.loader import find_template_source
|
|
||||||
template, origin = find_template_source(name, None)
|
|
||||||
if not hasattr(template, 'render'):
|
|
||||||
return template
|
|
||||||
except (ImportError, TemplateDoesNotExist):
|
|
||||||
pass
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def check_template_syntax(template):
|
def check_template_syntax(template):
|
||||||
try:
|
try:
|
||||||
Template(template.content)
|
Template(template.content)
|
||||||
except TemplateSyntaxError, e:
|
except TemplateSyntaxError as e:
|
||||||
return (False, e)
|
return (False, e)
|
||||||
return (True, None)
|
return (True, None)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ To enable one of them you need to specify a setting called
|
||||||
.. note::
|
.. note::
|
||||||
Starting in version 1.0 ``dbtemplates`` allows you also to set the new
|
Starting in version 1.0 ``dbtemplates`` allows you also to set the new
|
||||||
dict-based ``CACHES`` setting, which was introduced in Django 1.3.
|
dict-based ``CACHES`` setting, which was introduced in Django 1.3.
|
||||||
|
|
||||||
All you have to do is to provide a new entry in the ``CACHES`` dict
|
All you have to do is to provide a new entry in the ``CACHES`` dict
|
||||||
named ``'dbtemplates'``, e.g.::
|
named ``'dbtemplates'``, e.g.::
|
||||||
|
|
||||||
|
|
@ -61,8 +61,17 @@ 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://github.com/etianen/django-reversion/wiki/getting-started
|
.. _django-reversion's documentation: https://django-reversion.readthedocs.io/en/latest/
|
||||||
|
.. _django-reversion-compare: https://github.com/jedie/django-reversion-compare
|
||||||
|
|
||||||
|
|
||||||
.. _commands:
|
.. _commands:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,133 @@
|
||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
1.2 (08-15-11)
|
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::
|
||||||
|
|
||||||
|
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)
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This is a backwards-incompatible release!
|
||||||
|
|
||||||
|
* Moved maintenance to the `Jazzband <https://jazzband.co/>`_
|
||||||
|
|
||||||
|
* Dropped support for Python 2.6
|
||||||
|
|
||||||
|
* Added support for Python 3.4 and 3.5
|
||||||
|
|
||||||
|
* Dropped support for Django < 1.8
|
||||||
|
|
||||||
|
* Removed South migrations. Please use Django's native migration system instead
|
||||||
|
|
||||||
|
* Removed the example project since it's out-of-date quickly
|
||||||
|
|
||||||
|
v1.3.2 (2015-06-15)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
* support for Django 1.8 (not full, but usable)
|
||||||
|
* support for RedactorJS
|
||||||
|
|
||||||
|
thanks for contrib - @eculver, @kmooney, @volksman
|
||||||
|
|
||||||
|
v1.3.1 (2012-05-23)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
* Minor release to move away from nose again and use own
|
||||||
|
`django-discover-runner`_.
|
||||||
|
|
||||||
|
.. _`django-discover-runner`: http://pypi.python.org/pypi/django-discover-runner
|
||||||
|
|
||||||
|
v1.3 (2012-05-07)
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
* Dropped support for Django < 1.3 **backwards incompatible**
|
||||||
|
|
||||||
|
* Dropped using versiontools in favor of home made solution.
|
||||||
|
|
||||||
|
* Added optional support for TinyMCE editor instead of the CodeMirror
|
||||||
|
editor (just enable ``DBTEMPLATES_USE_TINYMCE``).
|
||||||
|
|
||||||
|
* Fixed compatibility to Django 1.4's handling of the ``DATABASES``
|
||||||
|
setting. Should also respect database routers now.
|
||||||
|
|
||||||
|
* Fixed an issue of the cache key generation in combination with
|
||||||
|
memcache's inability to stomach spaces.
|
||||||
|
|
||||||
|
* Moved test runner to use nose_ and a hosted CI project at Travis_:
|
||||||
|
http://travis-ci.org/jazzband/django-dbtemplates
|
||||||
|
|
||||||
|
.. _nose: https://nose.readthedocs.io/
|
||||||
|
.. _Travis: http://travis-ci.org
|
||||||
|
|
||||||
|
v1.2.1 (2011-09-07)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
* Fixed a wrong use of the non-lazy localization tools.
|
||||||
|
|
||||||
|
* Fixed bugs in the documentation.
|
||||||
|
|
||||||
|
* Make use of django-appconf and versiontools.
|
||||||
|
|
||||||
|
v1.2 (2011-08-15)
|
||||||
|
-----------------
|
||||||
|
|
||||||
* Refactored the template loader to be even more cache effective.
|
* Refactored the template loader to be even more cache effective.
|
||||||
|
|
||||||
* Added ``check_template_syntax`` management command and admin action
|
* Added ``check_template_syntax`` management command and admin action
|
||||||
to make sure the saved templates are valid Django templates.
|
to make sure the saved templates are valid Django templates.
|
||||||
|
|
||||||
1.1.1 (07-08-11)
|
v1.1.1 (2011-07-08)
|
||||||
----------------
|
-------------------
|
||||||
|
|
||||||
* Fixed bug in cache loading (again).
|
* Fixed bug in cache loading (again).
|
||||||
|
|
||||||
|
|
@ -23,8 +140,8 @@ Changelog
|
||||||
(``'dbtemplates.loader.Loader'``) and **not** the previosly included
|
(``'dbtemplates.loader.Loader'``) and **not** the previosly included
|
||||||
function that ended with ``load_template_source``.
|
function that ended with ``load_template_source``.
|
||||||
|
|
||||||
1.1 (07-06-11)
|
v1.1 (2011-07-06)
|
||||||
--------------
|
-----------------
|
||||||
|
|
||||||
* **BACKWARDS-INCOMPATIBLE** Requires Django 1.2 or higher.
|
* **BACKWARDS-INCOMPATIBLE** Requires Django 1.2 or higher.
|
||||||
For previous Django versions use an older versions of ``dbtemplates``,
|
For previous Django versions use an older versions of ``dbtemplates``,
|
||||||
|
|
@ -65,13 +182,13 @@ Changelog
|
||||||
|
|
||||||
* Fixed issue with cache settings handling.
|
* Fixed issue with cache settings handling.
|
||||||
|
|
||||||
1.0.1 (04-14-11)
|
v1.0.1 (2011-04-14)
|
||||||
----------------
|
-------------------
|
||||||
|
|
||||||
* Minor bugfixes with regard to the new cache handling.
|
* Minor bugfixes with regard to the new cache handling.
|
||||||
|
|
||||||
1.0 (04-11-11)
|
v1.0 (2011-04-11)
|
||||||
--------------
|
-----------------
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
This is the first stable release of django-dbtemplates which comes with a
|
This is the first stable release of django-dbtemplates which comes with a
|
||||||
|
|
@ -94,46 +211,46 @@ Changelog
|
||||||
|
|
||||||
* Use ReadTheDocs for documentation hosting.
|
* Use ReadTheDocs for documentation hosting.
|
||||||
|
|
||||||
0.8.0 (11-07-10)
|
v0.8.0 (2010-11-07)
|
||||||
----------------
|
-------------------
|
||||||
|
|
||||||
* Added Finnish translation (by jholster)
|
* Added Finnish translation (by jholster)
|
||||||
|
|
||||||
* Added --overwrite and --app-first options to sync_templates command (by Alex Kamedov).
|
* Added --overwrite and --app-first options to sync_templates command (by Alex Kamedov).
|
||||||
|
|
||||||
0.7.4 (09-23-10)
|
v0.7.4 (2010-09-23)
|
||||||
----------------
|
-------------------
|
||||||
|
|
||||||
* Fixed tests.
|
* Fixed tests.
|
||||||
|
|
||||||
0.7.3 (09-21-10)
|
v0.7.3 (2010-09-21)
|
||||||
----------------
|
-------------------
|
||||||
|
|
||||||
* Added ``DBTEMPLATES_AUTO_POPULATE_CONTENT`` setting to be able to disable
|
* Added ``DBTEMPLATES_AUTO_POPULATE_CONTENT`` setting to be able to disable
|
||||||
to auto-populating of template content.
|
to auto-populating of template content.
|
||||||
|
|
||||||
* Fixed cosmetic issue in admin with collapsable fields.
|
* Fixed cosmetic issue in admin with collapsable fields.
|
||||||
|
|
||||||
0.7.2 (09-04-10)
|
v0.7.2 (2010-09-04)
|
||||||
----------------
|
-------------------
|
||||||
|
|
||||||
* Moved to Github again. Sigh.
|
* Moved to Github again. Sigh.
|
||||||
|
|
||||||
0.7.1 (07-07-10)
|
v0.7.1 (2010-07-07)
|
||||||
----------------
|
-------------------
|
||||||
|
|
||||||
* Fixed problem with the CodeMirror textarea, which wasn't completely
|
* Fixed problem with the CodeMirror textarea, which wasn't completely
|
||||||
disabled before.
|
disabled before.
|
||||||
|
|
||||||
* Fixed problem with the ``DBTEMPLATES_MEDIA_PREFIX`` setting, which defaults
|
* Fixed problem with the ``DBTEMPLATES_MEDIA_PREFIX`` setting, which defaults
|
||||||
now to ``os.path.join(settings.MEDIA_ROOT, 'dbtemplates')`` now.
|
now to ``os.path.join(settings.MEDIA_ROOT, 'dbtemplates')`` now.
|
||||||
|
|
||||||
In other words, if you don't specify a ``DBTEMPLATES_MEDIA_PREFIX`` setting
|
In other words, if you don't specify a ``DBTEMPLATES_MEDIA_PREFIX`` setting
|
||||||
and have the CodeMirror textarea enabled, dbtemplates will look in a
|
and have the CodeMirror textarea enabled, dbtemplates will look in a
|
||||||
subdirectory of your site's ``MEDIA_ROOT`` for the CodeMirror media files.
|
subdirectory of your site's ``MEDIA_ROOT`` for the CodeMirror media files.
|
||||||
|
|
||||||
0.7.0 (06-24-10)
|
v0.7.0 (2010-06-24)
|
||||||
----------------
|
-------------------
|
||||||
|
|
||||||
* Added CodeMirror_-based syntax highlighting textarea, based on the amaxing
|
* Added CodeMirror_-based syntax highlighting textarea, based on the amaxing
|
||||||
work_ by `Nic Pottier`_. Set the ``DBTEMPLATES_USE_CODEMIRROR`` setting
|
work_ by `Nic Pottier`_. Set the ``DBTEMPLATES_USE_CODEMIRROR`` setting
|
||||||
|
|
@ -154,14 +271,14 @@ Changelog
|
||||||
.. _work: https://gist.github.com/368758/86bcafe53c438e2e2a0e3442c3b30f2c6011fbba
|
.. _work: https://gist.github.com/368758/86bcafe53c438e2e2a0e3442c3b30f2c6011fbba
|
||||||
.. _`Nic Pottier`: http://github.com/nicpottier
|
.. _`Nic Pottier`: http://github.com/nicpottier
|
||||||
|
|
||||||
0.6.1 (10-19-09):
|
v0.6.1 (2009-10-19)
|
||||||
-----------------
|
-------------------
|
||||||
|
|
||||||
* Fixed issue with default site of a template, added ability to disable
|
* Fixed issue with default site of a template, added ability to disable
|
||||||
default site (``DBTEMPLATES_ADD_DEFAULT_SITE``).
|
default site (``DBTEMPLATES_ADD_DEFAULT_SITE``).
|
||||||
|
|
||||||
0.6.0 (10-09-09):
|
v0.6.0 (2009-10-09)
|
||||||
-----------------
|
-------------------
|
||||||
|
|
||||||
* Updated and added locales (Danish, Brazilian Portuguese)
|
* Updated and added locales (Danish, Brazilian Portuguese)
|
||||||
|
|
||||||
|
|
@ -171,8 +288,8 @@ Changelog
|
||||||
|
|
||||||
* Added Sphinx documentation
|
* Added Sphinx documentation
|
||||||
|
|
||||||
0.5.7
|
v0.5.7
|
||||||
-----
|
------
|
||||||
|
|
||||||
* Updates to the docs
|
* Updates to the docs
|
||||||
|
|
||||||
|
|
@ -186,8 +303,8 @@ Changelog
|
||||||
|
|
||||||
* fixed bug in ``create_error_template`` command.
|
* fixed bug in ``create_error_template`` command.
|
||||||
|
|
||||||
0.5.4
|
v0.5.4
|
||||||
-----
|
------
|
||||||
|
|
||||||
* Made loader and cache backends site-aware.
|
* Made loader and cache backends site-aware.
|
||||||
|
|
||||||
|
|
@ -199,16 +316,16 @@ Changelog
|
||||||
* Template is now saved explicitly to backend if not existent in cache
|
* Template is now saved explicitly to backend if not existent in cache
|
||||||
(e.g. if deleted manually or invalidated).
|
(e.g. if deleted manually or invalidated).
|
||||||
|
|
||||||
0.5.3
|
v0.5.3
|
||||||
-----
|
------
|
||||||
|
|
||||||
* Removed automatic creation of 404.html and 500.html templates and added a
|
* Removed automatic creation of 404.html and 50v0.html templates and added a
|
||||||
new management command for those cases called ``create_error_templates``
|
new management command for those cases called ``create_error_templates``
|
||||||
|
|
||||||
* Also reverted move to Bitbucket
|
* Also reverted move to Bitbucket
|
||||||
|
|
||||||
0.5.2
|
v0.5.2
|
||||||
-----
|
------
|
||||||
|
|
||||||
* Fixed a problem with ``django.contrib.sites`` when its table hasn't been
|
* Fixed a problem with ``django.contrib.sites`` when its table hasn't been
|
||||||
populated yet on initialization of dbtemplates. Thanks for the report,
|
populated yet on initialization of dbtemplates. Thanks for the report,
|
||||||
|
|
@ -216,8 +333,8 @@ Changelog
|
||||||
|
|
||||||
* Added an example Django project and docs for it
|
* Added an example Django project and docs for it
|
||||||
|
|
||||||
0.5.1
|
v0.5.1
|
||||||
-----
|
------
|
||||||
|
|
||||||
* Removed unneeded code that registered the model with reversion.
|
* Removed unneeded code that registered the model with reversion.
|
||||||
|
|
||||||
|
|
@ -228,8 +345,8 @@ Changelog
|
||||||
* Removed legacy ``sync_templates.py`` script, use ``django-admin.py
|
* Removed legacy ``sync_templates.py`` script, use ``django-admin.py
|
||||||
sync_templates`` from now on.
|
sync_templates`` from now on.
|
||||||
|
|
||||||
0.5.0
|
v0.5.0
|
||||||
-----
|
------
|
||||||
|
|
||||||
* Added support for `django-reversion`_
|
* Added support for `django-reversion`_
|
||||||
|
|
||||||
|
|
@ -237,69 +354,69 @@ Changelog
|
||||||
empty by using Django's other template loaders
|
empty by using Django's other template loaders
|
||||||
|
|
||||||
* added caching backend system with two default backends:
|
* added caching backend system with two default backends:
|
||||||
|
|
||||||
* ``FileSystemBackend``
|
* ``FileSystemBackend``
|
||||||
* ``DjangoCacheBackend``
|
* ``DjangoCacheBackend``
|
||||||
|
|
||||||
More about it in the `blog post`_ and in the docs.
|
More about it in the `blog post`_ and in the docs.
|
||||||
|
|
||||||
.. _django-reversion: http://code.google.com/p/django-reversion/
|
.. _django-reversion: http://code.google.com/p/django-reversion/
|
||||||
.. _blog post: http://jannisleidel.com/2008/11/updates-to-django-dbtemplates-and-half-assed-promise/
|
.. _blog post: http://jannisleidel.com/2008/11/updates-to-django-dbtemplates-and-half-assed-promise/
|
||||||
|
|
||||||
0.4.7
|
v0.4.7
|
||||||
-----
|
------
|
||||||
|
|
||||||
* Minor bugfix
|
* Minor bugfix
|
||||||
|
|
||||||
0.4.6
|
v0.4.6
|
||||||
-----
|
------
|
||||||
|
|
||||||
* Minor doc change and PyPI support
|
* Minor doc change and PyPI support
|
||||||
|
|
||||||
0.4.5
|
v0.4.5
|
||||||
-----
|
------
|
||||||
|
|
||||||
* fixed the --force option of the sync_templates command
|
* fixed the --force option of the sync_templates command
|
||||||
|
|
||||||
0.4.4
|
v0.4.4
|
||||||
-----
|
------
|
||||||
|
|
||||||
* fixed error in custom model save() after changes in Django `r8670`_.
|
* fixed error in custom model save() after changes in Django `r8670`_.
|
||||||
|
|
||||||
.. _r8670: http://code.djangoproject.com/changeset/8670
|
.. _r8670: http://code.djangoproject.com/changeset/8670
|
||||||
|
|
||||||
0.4.3
|
v0.4.3
|
||||||
-----
|
------
|
||||||
|
|
||||||
* removed oldforms code
|
* removed oldforms code
|
||||||
|
|
||||||
0.4.2
|
v0.4.2
|
||||||
-----
|
------
|
||||||
|
|
||||||
* added Hebrew translation (by mkriheli)
|
* added Hebrew translation (by mkriheli)
|
||||||
|
|
||||||
0.4.1
|
v0.4.1
|
||||||
-----
|
------
|
||||||
|
|
||||||
* added French (by Roland Frederic) and German locale
|
* added French (by Roland Frederic) and German locale
|
||||||
|
|
||||||
0.4.0
|
v0.4.0
|
||||||
-----
|
------
|
||||||
|
|
||||||
* adds better support for newforms-admin
|
* adds better support for newforms-admin
|
||||||
|
|
||||||
* don't forget to load the dbtemplates.admin, e.g. by using
|
* don't forget to load the dbtemplates.admin, e.g. by using
|
||||||
django.contrib.admin.autodiscover() in you urls.py
|
django.contrib.admin.autodiscover() in you urls.py
|
||||||
|
|
||||||
0.3.1
|
v0.3.1
|
||||||
-----
|
------
|
||||||
|
|
||||||
* adds a new management command *sync_templates* for bidirectional syncing
|
* adds a new management command *sync_templates* for bidirectional syncing
|
||||||
between filesystem and database (backwards-compatible) and
|
between filesystem and database (backwards-compatible) and
|
||||||
FilesystemCaching (thanks, Arne Brodowski!)
|
FilesystemCaching (thanks, Arne Brodowski!)
|
||||||
|
|
||||||
0.2.5
|
v0.2.5
|
||||||
-----
|
------
|
||||||
|
|
||||||
* adds support for newforms-admin
|
* adds support for newforms-admin
|
||||||
|
|
||||||
|
|
@ -308,4 +425,4 @@ Support
|
||||||
|
|
||||||
Please leave your questions and messages on the designated site:
|
Please leave your questions and messages on the designated site:
|
||||||
|
|
||||||
http://github.com/jezdez/django-dbtemplates/issues/
|
http://github.com/jazzband/django-dbtemplates/issues/
|
||||||
|
|
|
||||||
22
docs/conf.py
22
docs/conf.py
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- 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.
|
||||||
|
|
@ -16,7 +15,7 @@ import sys, os
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
#sys.path.append(os.path.abspath('.'))
|
sys.path.append(os.path.abspath('.'))
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -37,17 +36,22 @@ source_suffix = '.txt'
|
||||||
master_doc = 'index'
|
master_doc = 'index'
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'django-dbtemplates'
|
project = 'django-dbtemplates'
|
||||||
copyright = u'2007-2011, Jannis Leidel and contributors'
|
copyright = '2007-2019, 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
|
||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '1.2'
|
try:
|
||||||
# The full version, including alpha/beta/rc tags.
|
from dbtemplates import __version__
|
||||||
release = '1.2'
|
# The short X.Y version.
|
||||||
|
version = '.'.join(__version__.split('.')[:2])
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = __version__
|
||||||
|
except ImportError:
|
||||||
|
version = release = 'dev'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
|
|
@ -172,8 +176,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', u'django-dbtemplates Documentation',
|
('index', 'django-dbtemplates.tex', 'django-dbtemplates Documentation',
|
||||||
u'Jannis Leidel and contributors', 'manual'),
|
'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
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ It also features optional support for :ref:`versioned storage <versioned>`
|
||||||
and :ref:`django-admin command <commands>`, integrates with Django's
|
and :ref:`django-admin command <commands>`, integrates with Django's
|
||||||
:ref:`caching system <caching>` and the :ref:`admin actions <admin_actions>`.
|
:ref:`caching system <caching>` and the :ref:`admin actions <admin_actions>`.
|
||||||
|
|
||||||
Please see http://django-dbtemplates.readthedocs.org/ for more details.
|
Please see https://django-dbtemplates.readthedocs.io/ for more details.
|
||||||
|
|
||||||
The source code and issue tracker can be found on Github: https://github.com/jezdez/django-dbtemplates
|
The source code and issue tracker can be found on Github: https://github.com/jazzband/django-dbtemplates
|
||||||
|
|
||||||
.. _template loader: http://docs.djangoproject.com/en/dev/ref/templates/api/#loading-templates
|
.. _template loader: http://docs.djangoproject.com/en/dev/ref/templates/api/#loading-templates
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,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 django-dbtemplates``.
|
Python Package Index by running ``pip install django-dbtemplates``.
|
||||||
2. Follow the instructions in the INSTALL file
|
2. Edit the settings.py of your Django site:
|
||||||
3. Edit the settings.py of your Django site:
|
|
||||||
|
|
||||||
* Add ``dbtemplates`` to the ``INSTALLED_APPS`` setting
|
* Add ``dbtemplates`` to the ``INSTALLED_APPS`` setting
|
||||||
|
|
||||||
|
|
@ -24,28 +23,54 @@ Setup
|
||||||
'dbtemplates',
|
'dbtemplates',
|
||||||
)
|
)
|
||||||
|
|
||||||
* Add ``dbtemplates.loader.Loader`` to the ``TEMPLATE_LOADERS`` list
|
* Add ``dbtemplates.loader.Loader`` to the ``TEMPLATES.OPTIONS.loaders`` list
|
||||||
in the settings.py of your Django project.
|
in the settings.py of your Django project.
|
||||||
|
|
||||||
It should look something like this::
|
It should look something like this::
|
||||||
|
|
||||||
TEMPLATE_LOADERS = (
|
TEMPLATES = [
|
||||||
'django.template.loaders.filesystem.Loader',
|
{
|
||||||
'django.template.loaders.app_directories.Loader',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'dbtemplates.loader.Loader',
|
'DIRS': [ # your template dirs here
|
||||||
)
|
],
|
||||||
|
'APP_DIRS': False,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.template.context_processors.debug',
|
||||||
|
'django.template.context_processors.i18n',
|
||||||
|
'django.template.context_processors.media',
|
||||||
|
'django.template.context_processors.static',
|
||||||
|
'django.template.context_processors.tz',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
],
|
||||||
|
'loaders': [
|
||||||
|
'django.template.loaders.filesystem.Loader',
|
||||||
|
'django.template.loaders.app_directories.Loader',
|
||||||
|
'dbtemplates.loader.Loader',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
4. Sync your database ``python manage.py syncdb``
|
The order of ``TEMPLATES.OPTIONS.loaders`` is important. In the former
|
||||||
5. Restart your Django server
|
example, templates from the database will be used as a fallback (ie. when
|
||||||
|
the template does not exists in other locations). If you want the template
|
||||||
|
from the database to be used to override templates in other locations,
|
||||||
|
put ``dbtemplates.loader.Loader`` at the beginning of ``loaders``.
|
||||||
|
|
||||||
.. _Git repository: http://github.com/jezdez/django-dbtemplates/
|
3. Sync your database ``python manage.py migrate``
|
||||||
|
4. Restart your Django server
|
||||||
|
|
||||||
|
.. _Git repository: https://github.com/jazzband/django-dbtemplates/
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
=====
|
=====
|
||||||
|
|
||||||
Creating database templates is pretty simple: Just open the admin interface
|
Creating database templates is pretty simple: Just open the admin interface
|
||||||
of your Django-based site in your browser and click on "Templates" in the
|
of your Django-based site in your browser and click on "Templates" in the
|
||||||
"Dbtemplates" section.
|
"Database templates" section.
|
||||||
|
|
||||||
There you only need to fill in the ``name`` field with the identifier, Django
|
There you only need to fill in the ``name`` field with the identifier, Django
|
||||||
is supposed to use while searching for templates, e.g.
|
is supposed to use while searching for templates, e.g.
|
||||||
|
|
@ -59,44 +84,3 @@ other template loaders. For example, if you have a template called
|
||||||
contents in the database, you just need to leave the content field empty to
|
contents in the database, you just need to leave the content field empty to
|
||||||
automatically populate it. That's especially useful if you don't want to
|
automatically populate it. That's especially useful if you don't want to
|
||||||
copy and paste its content manually to the textarea.
|
copy and paste its content manually to the textarea.
|
||||||
|
|
||||||
Example
|
|
||||||
=======
|
|
||||||
|
|
||||||
``dbtemplates`` comes with an example Django project that let's you try it
|
|
||||||
out. The example uses Django's own `flatpages app`_ to enable you to create
|
|
||||||
a simple page using ``dbtemplates``. Flat pages are a perfect fit to
|
|
||||||
dbtemplates since they come prepackaged and are simple to use.
|
|
||||||
|
|
||||||
Here is how it works:
|
|
||||||
|
|
||||||
1. Open your command line and change to the ``example`` directory in the
|
|
||||||
directory with the extracted source distribution.
|
|
||||||
2. Run ``python manage.py syncdb`` and follow the instructions.
|
|
||||||
3. Run ``python manage.py runserver`` and open your favorite browser with the
|
|
||||||
address http://127.0.0.1:8000/admin/.
|
|
||||||
4. Next add a new `Template` object in the ``dbtemplates`` section and use
|
|
||||||
``flatpages/default.html`` as the value for the ``name`` field. For the
|
|
||||||
``content`` field use this example::
|
|
||||||
|
|
||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
|
|
||||||
"http://www.w3.org/TR/REC-html40/loose.dtd">
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>{{ flatpage.title }}</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
{{ flatpage.content }}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
5. Return to the home screen of the admin interface and add a new flat page.
|
|
||||||
Use ``/`` (yep, just a forward slash) and whatever ``title`` and
|
|
||||||
``content`` you prefer. Please make sure you select the default site
|
|
||||||
``example.com`` before you save the flat page.
|
|
||||||
6. Visit http://127.0.0.1:8000/ and see the flat page you just created
|
|
||||||
rendered with the ``flatpages/default.html`` template provided by
|
|
||||||
``dbtemplates``.
|
|
||||||
|
|
||||||
.. _flatpages app: http://docs.djangoproject.com/en/dev/ref/contrib/flatpages/
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ Settings
|
||||||
``DBTEMPLATES_ADD_DEFAULT_SITE``
|
``DBTEMPLATES_ADD_DEFAULT_SITE``
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
``dbtemplates`` adds the current site (``settings.SITE_ID``) to the database
|
``dbtemplates`` adds the current site (``settings.SITE_ID``) to the database
|
||||||
template when it is created by default. You can disable this feature by
|
template when it is created by default. You can disable this feature by
|
||||||
setting ``DBTEMPLATES_ADD_DEFAULT_SITE`` to ``False``.
|
setting ``DBTEMPLATES_ADD_DEFAULT_SITE`` to ``False``.
|
||||||
|
|
||||||
``DBTEMPLATES_AUTO_POPULATE_CONTENT``
|
``DBTEMPLATES_AUTO_POPULATE_CONTENT``
|
||||||
|
|
@ -28,11 +28,24 @@ The dotted Python path to the cache backend class. See
|
||||||
A boolean, if enabled triggers the use of the CodeMirror based editor.
|
A boolean, if enabled triggers the use of the CodeMirror based editor.
|
||||||
Set to ``False`` by default.
|
Set to ``False`` by default.
|
||||||
|
|
||||||
|
``DBTEMPLATES_USE_TINYMCE``
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
.. versionadded:: 1.3
|
||||||
|
|
||||||
|
A boolean, if enabled triggers the use of the TinyMCE based editor.
|
||||||
|
Set to ``False`` by default.
|
||||||
|
|
||||||
``DBTEMPLATES_USE_REVERSION``
|
``DBTEMPLATES_USE_REVERSION``
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
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,15 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
import os, sys
|
|
||||||
from django.core.management import execute_manager
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath('./..'))
|
|
||||||
|
|
||||||
try:
|
|
||||||
import settings # Assumed to be in the same directory.
|
|
||||||
except ImportError:
|
|
||||||
import sys
|
|
||||||
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
execute_manager(settings)
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
south
|
|
||||||
django>=1.3
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
# Django settings for example project.
|
|
||||||
import os, posixpath
|
|
||||||
|
|
||||||
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
|
|
||||||
|
|
||||||
DEBUG = True
|
|
||||||
TEMPLATE_DEBUG = DEBUG
|
|
||||||
|
|
||||||
ADMINS = (
|
|
||||||
# ('Your Name', 'your_email@domain.com'),
|
|
||||||
)
|
|
||||||
|
|
||||||
MANAGERS = ADMINS
|
|
||||||
|
|
||||||
DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
|
|
||||||
DATABASE_NAME = 'example.db' # Or path to database file if using sqlite3.
|
|
||||||
DATABASE_USER = '' # Not used with sqlite3.
|
|
||||||
DATABASE_PASSWORD = '' # Not used with sqlite3.
|
|
||||||
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
|
|
||||||
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
|
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
"default": {
|
|
||||||
"ENGINE": "django.db.backends.sqlite3",
|
|
||||||
"NAME": DATABASE_NAME,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Local time zone for this installation. Choices can be found here:
|
|
||||||
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
|
||||||
# although not all choices may be available on all operating systems.
|
|
||||||
# If running in a Windows environment this must be set to the same as your
|
|
||||||
# system time zone.
|
|
||||||
TIME_ZONE = 'America/Chicago'
|
|
||||||
|
|
||||||
# Language code for this installation. All choices can be found here:
|
|
||||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
|
||||||
|
|
||||||
SITE_ID = 1
|
|
||||||
|
|
||||||
# If you set this to False, Django will make some optimizations so as not
|
|
||||||
# to load the internationalization machinery.
|
|
||||||
USE_I18N = True
|
|
||||||
|
|
||||||
# Absolute path to the directory that holds media.
|
|
||||||
# Example: "/home/media/media.lawrence.com/"
|
|
||||||
MEDIA_ROOT = os.path.join(PROJECT_ROOT, "site_media", "media")
|
|
||||||
|
|
||||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
|
||||||
# trailing slash if there is a path component (optional in other cases).
|
|
||||||
# Examples: "http://media.lawrence.com", "http://example.com/media/"
|
|
||||||
MEDIA_URL = "/site_media/media/"
|
|
||||||
|
|
||||||
# Absolute path to the directory that holds static files like app media.
|
|
||||||
# Example: "/home/media/media.lawrence.com/apps/"
|
|
||||||
STATIC_ROOT = os.path.join(PROJECT_ROOT, "site_media", "static")
|
|
||||||
|
|
||||||
# URL that handles the static files like app media.
|
|
||||||
# Example: "http://media.lawrence.com"
|
|
||||||
STATIC_URL = "/site_media/static/"
|
|
||||||
|
|
||||||
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
|
|
||||||
# trailing slash.
|
|
||||||
# Examples: "http://foo.com/media/", "/media/".
|
|
||||||
ADMIN_MEDIA_PREFIX = posixpath.join(STATIC_URL, "admin/")
|
|
||||||
|
|
||||||
# Make this unique, and don't share it with anybody.
|
|
||||||
SECRET_KEY = 'e-%(1e1f8ar2v)_8d!%-75a2ag(w(ht*l%n-wts5$li!5=97)8'
|
|
||||||
|
|
||||||
# List of callables that know how to import templates from various sources.
|
|
||||||
TEMPLATE_LOADERS = (
|
|
||||||
'django.template.loaders.filesystem.Loader',
|
|
||||||
'django.template.loaders.app_directories.Loader',
|
|
||||||
'django.template.loaders.eggs.Loader',
|
|
||||||
'dbtemplates.loader.Loader',
|
|
||||||
)
|
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = (
|
|
||||||
'django.middleware.common.CommonMiddleware',
|
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
||||||
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
|
|
||||||
)
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'example.urls'
|
|
||||||
|
|
||||||
TEMPLATE_DIRS = (
|
|
||||||
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
|
||||||
# Always use forward slashes, even on Windows.
|
|
||||||
# Don't forget to use absolute paths, not relative paths.
|
|
||||||
)
|
|
||||||
|
|
||||||
INSTALLED_APPS = (
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sessions',
|
|
||||||
'django.contrib.sites',
|
|
||||||
'django.contrib.admin',
|
|
||||||
'django.contrib.flatpages',
|
|
||||||
'django.contrib.staticfiles',
|
|
||||||
'dbtemplates',
|
|
||||||
'south',
|
|
||||||
#'reversion',
|
|
||||||
)
|
|
||||||
|
|
||||||
# Uncomment the following two settings to use the file system cache backend.
|
|
||||||
# It will cache in the directory "cache" inside the example project directory.
|
|
||||||
DBTEMPLATES_CACHE_BACKEND = "locmem://"
|
|
||||||
|
|
||||||
DBTEMPLATES_MEDIA_PREFIX = posixpath.join(STATIC_URL, "dbtemplates/")
|
|
||||||
DBTEMPLATES_USE_CODEMIRROR = True
|
|
||||||
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
from django.conf import settings
|
|
||||||
from django.conf.urls.defaults import patterns, include
|
|
||||||
|
|
||||||
# Uncomment the next two lines to enable the admin:
|
|
||||||
from django.contrib import admin
|
|
||||||
admin.autodiscover()
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
|
||||||
# Example:
|
|
||||||
# (r'^example/', include('example.foo.urls')),
|
|
||||||
|
|
||||||
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
|
|
||||||
# to INSTALLED_APPS to enable admin documentation:
|
|
||||||
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
|
||||||
|
|
||||||
# Uncomment the next line to enable the admin:
|
|
||||||
(r'^admin/', include(admin.site.urls)),
|
|
||||||
)
|
|
||||||
51
pyproject.toml
Normal file
51
pyproject.toml
Normal 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
1
requirements/docs.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
django
|
||||||
2
requirements/tests.txt
Normal file
2
requirements/tests.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
flake8
|
||||||
|
coverage
|
||||||
37
runtests.py
37
runtests.py
|
|
@ -1,37 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
if not settings.configured:
|
|
||||||
settings.configure(
|
|
||||||
DBTEMPLATES_CACHE_BACKEND = 'dummy://',
|
|
||||||
DATABASE_ENGINE='sqlite3',
|
|
||||||
SITE_ID=1,
|
|
||||||
INSTALLED_APPS=[
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.sites',
|
|
||||||
'django.contrib.admin',
|
|
||||||
'dbtemplates',
|
|
||||||
],
|
|
||||||
TEMPLATE_LOADERS = (
|
|
||||||
'django.template.loaders.filesystem.Loader',
|
|
||||||
'django.template.loaders.app_directories.Loader',
|
|
||||||
'dbtemplates.loader.Loader',
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
from django.test.simple import run_tests
|
|
||||||
|
|
||||||
|
|
||||||
def runtests(*test_args):
|
|
||||||
if not test_args:
|
|
||||||
test_args = ['dbtemplates']
|
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'dbtemplates'))
|
|
||||||
failures = run_tests(test_args, verbosity=1, interactive=True)
|
|
||||||
sys.exit(failures)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
runtests(*sys.argv[1:])
|
|
||||||
13
setup.cfg
13
setup.cfg
|
|
@ -1,13 +0,0 @@
|
||||||
[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
|
|
||||||
33
setup.py
33
setup.py
|
|
@ -1,33 +0,0 @@
|
||||||
from setuptools import setup, find_packages
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name='django-dbtemplates',
|
|
||||||
version=__import__('dbtemplates').__version__,
|
|
||||||
description='Template loader for templates stored in the database',
|
|
||||||
long_description=open('README.rst').read(),
|
|
||||||
author='Jannis Leidel',
|
|
||||||
author_email='jannis@leidel.info',
|
|
||||||
url='http://django-dbtemplates.readthedocs.org/',
|
|
||||||
packages=find_packages(exclude=['example']),
|
|
||||||
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.5',
|
|
||||||
'Programming Language :: Python :: 2.6',
|
|
||||||
'Programming Language :: Python :: 2.7',
|
|
||||||
'Framework :: Django',
|
|
||||||
],
|
|
||||||
)
|
|
||||||
86
tox.ini
86
tox.ini
|
|
@ -1,41 +1,61 @@
|
||||||
[tox]
|
[tox]
|
||||||
downloadcache = .tox/_download/
|
minversion = 4.0
|
||||||
distribute = False
|
|
||||||
envlist =
|
envlist =
|
||||||
py25-1.2.X, py26-1.2.X, py27-1.2.X,
|
flake8
|
||||||
py25-1.3.X, py26-1.3.X, py27-1.3.X
|
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.8: py38
|
||||||
|
3.9: py39
|
||||||
|
3.10: py310
|
||||||
|
3.10: py310, flake8
|
||||||
|
3.11: py311
|
||||||
|
3.12: py312
|
||||||
|
3.13: py313
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
|
skipsdist = true
|
||||||
|
package = editable
|
||||||
|
basepython =
|
||||||
|
py38: python3.8
|
||||||
|
py39: python3.9
|
||||||
|
py310: python3.10
|
||||||
|
py311: python3.11
|
||||||
|
py312: python3.12
|
||||||
|
py313: python3.13
|
||||||
|
setenv =
|
||||||
|
DJANGO_SETTINGS_MODULE = dbtemplates.test_settings
|
||||||
|
deps =
|
||||||
|
-r requirements/tests.txt
|
||||||
|
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 =
|
commands =
|
||||||
python runtests.py
|
python --version
|
||||||
|
python -c "import django ; print(django.VERSION)"
|
||||||
|
coverage run --branch --parallel-mode {envbindir}/django-admin test -v2 {posargs:dbtemplates}
|
||||||
|
|
||||||
[testenv:py25-1.2.X]
|
[testenv:coverage]
|
||||||
basepython = python2.5
|
basepython = python3.10
|
||||||
deps =
|
deps = coverage
|
||||||
django==1.2.5
|
commands =
|
||||||
|
coverage combine
|
||||||
|
coverage report
|
||||||
|
coverage xml
|
||||||
|
|
||||||
[testenv:py26-1.2.X]
|
[testenv:flake8]
|
||||||
basepython = python2.6
|
basepython = python3.10
|
||||||
deps =
|
commands = flake8 dbtemplates
|
||||||
django==1.2.5
|
deps = flake8
|
||||||
|
|
||||||
[testenv:py27-1.2.X]
|
[flake8]
|
||||||
basepython = python2.7
|
exclude=.tox
|
||||||
deps =
|
ignore=E501,E127,E128,E124
|
||||||
django==1.2.5
|
|
||||||
|
|
||||||
|
|
||||||
[testenv:py25-1.3.X]
|
|
||||||
basepython = python2.5
|
|
||||||
deps =
|
|
||||||
django==1.3
|
|
||||||
|
|
||||||
[testenv:py26-1.3.X]
|
|
||||||
basepython = python2.6
|
|
||||||
deps =
|
|
||||||
django==1.3
|
|
||||||
|
|
||||||
[testenv:py27-1.3.X]
|
|
||||||
basepython = python2.7
|
|
||||||
deps =
|
|
||||||
django==1.3
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue