From 863e3edaa3a0c4e1a2351ed471cb019088714b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sever=20B=C4=83ne=C5=9Fiu?= Date: Wed, 5 Oct 2011 12:06:54 +0300 Subject: [PATCH 1/8] Made template name unique and sites required. --- .../0003_auto__add_unique_template_name.py | 39 +++++++++++++++++++ dbtemplates/models.py | 4 +- 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 dbtemplates/migrations/0003_auto__add_unique_template_name.py diff --git a/dbtemplates/migrations/0003_auto__add_unique_template_name.py b/dbtemplates/migrations/0003_auto__add_unique_template_name.py new file mode 100644 index 0000000..3da4ba0 --- /dev/null +++ b/dbtemplates/migrations/0003_auto__add_unique_template_name.py @@ -0,0 +1,39 @@ +# 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): + + # Adding unique constraint on 'Template', fields ['name'] + db.create_unique('django_template', ['name']) + + + def backwards(self, orm): + + # Removing unique constraint on 'Template', fields ['name'] + db.delete_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', [], {'unique': 'True', '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'] diff --git a/dbtemplates/models.py b/dbtemplates/models.py index c8bb3d6..aeef50e 100644 --- a/dbtemplates/models.py +++ b/dbtemplates/models.py @@ -19,11 +19,11 @@ class Template(models.Model): Defines a template model for use with the database template loader. The field ``name`` is the equivalent to the filename of a static template. """ - name = models.CharField(_('name'), max_length=100, + name = models.CharField(_('name'), max_length=100, null=False, unique=True, help_text=_("Example: 'flatpages/default.html'")) content = models.TextField(_('content'), blank=True) sites = models.ManyToManyField(Site, verbose_name=_(u'sites'), - blank=True, null=True) + blank=False, null=False) creation_date = models.DateTimeField(_('creation date'), default=datetime.now) last_changed = models.DateTimeField(_('last changed'), From 6b9704c0dd27f8d321425cb3ee69e5523d8b650d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sever=20B=C4=83ne=C5=9Fiu?= Date: Fri, 7 Oct 2011 15:39:43 +0300 Subject: [PATCH 2/8] Added own version in order to fix dependencies. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index be12e1f..54a9651 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ read = lambda filepath: codecs.open(filepath, 'r', 'utf-8').read() setup( name='django-dbtemplates', - version=':versiontools:dbtemplates:', + version=':versiontools:dbtemplates:pbs', description='Template loader for templates stored in the database', long_description=read(path.join(path.dirname(__file__), 'README.rst')), author='Jannis Leidel', From 720992c4153b07e49db6f05b1f985fdf8a9c9d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sever=20B=C4=83ne=C5=9Fiu?= Date: Fri, 7 Oct 2011 15:44:19 +0300 Subject: [PATCH 3/8] Errr. --- dbtemplates/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dbtemplates/__init__.py b/dbtemplates/__init__.py index e16f115..22a6019 100644 --- a/dbtemplates/__init__.py +++ b/dbtemplates/__init__.py @@ -1,2 +1,2 @@ # following PEP 386, versiontools will pick it up -__version__ = (1, 2, 1, "final", 0) +__version__ = (1, 2, 1, "pbs", 0) diff --git a/setup.py b/setup.py index 54a9651..f785abc 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ read = lambda filepath: codecs.open(filepath, 'r', 'utf-8').read() setup( name='django-dbtemplates', - version=':versiontools:dbtemplates:pbs', + version=':versiontools:dbtemplates', description='Template loader for templates stored in the database', long_description=read(path.join(path.dirname(__file__), 'README.rst')), author='Jannis Leidel', From fc7da2a8a558cb53cf2ee65c3fa4a22bf8af3898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sever=20B=C4=83ne=C5=9Fiu?= Date: Fri, 7 Oct 2011 15:48:28 +0300 Subject: [PATCH 4/8] Override the version like a man. --- dbtemplates/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dbtemplates/__init__.py b/dbtemplates/__init__.py index 22a6019..e16f115 100644 --- a/dbtemplates/__init__.py +++ b/dbtemplates/__init__.py @@ -1,2 +1,2 @@ # following PEP 386, versiontools will pick it up -__version__ = (1, 2, 1, "pbs", 0) +__version__ = (1, 2, 1, "final", 0) diff --git a/setup.py b/setup.py index f785abc..dfee0fc 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ read = lambda filepath: codecs.open(filepath, 'r', 'utf-8').read() setup( name='django-dbtemplates', - version=':versiontools:dbtemplates', + version='1.2.1pbs', description='Template loader for templates stored in the database', long_description=read(path.join(path.dirname(__file__), 'README.rst')), author='Jannis Leidel', From 30a470145f0f196412441c1c951b3d46b8b4da48 Mon Sep 17 00:00:00 2001 From: Bogdan Hodorog Date: Wed, 19 Sep 2012 11:03:13 +0300 Subject: [PATCH 5/8] UNICORN-5222 pull from remotes/jezdez/tags/1.3 --- .gitignore | 4 +- .hgignore | 9 --- .hgtags | 24 -------- .travis.yml | 20 +++++++ AUTHORS | 4 ++ INSTALL | 17 ------ LICENSE | 2 +- MANIFEST.in | 1 - README.rst | 4 ++ dbtemplates/__init__.py | 4 +- dbtemplates/admin.py | 11 +++- dbtemplates/conf.py | 7 +++ dbtemplates/loader.py | 21 ++++--- .../commands/check_template_syntax.py | 1 + .../management/commands/sync_templates.py | 6 +- dbtemplates/migrations/0001_initial.py | 6 +- .../0002_auto__del_unique_template_name.py | 7 +-- dbtemplates/models.py | 14 +++-- dbtemplates/{tests.py => test_cases.py} | 60 +++++++++++++++---- dbtemplates/test_settings.py | 15 +++++ dbtemplates/utils/cache.py | 3 +- dbtemplates/utils/template.py | 8 +-- docs/changelog.txt | 22 +++++++ docs/conf.py | 15 +++-- docs/overview.txt | 7 ++- docs/settings.txt | 12 +++- requirements/tests.txt | 3 + setup.cfg | 4 ++ setup.py | 24 ++++++-- tox.ini | 51 ---------------- 30 files changed, 221 insertions(+), 165 deletions(-) delete mode 100644 .hgignore delete mode 100644 .hgtags create mode 100644 .travis.yml delete mode 100644 INSTALL rename dbtemplates/{tests.py => test_cases.py} (64%) create mode 100644 requirements/tests.txt delete mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index cb3f83e..9d5809f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,4 @@ example/example.db docs/_build .tox/ *.egg/ -pep8.txt -coverage.xml -.coverage \ No newline at end of file +.coverage diff --git a/.hgignore b/.hgignore deleted file mode 100644 index 508637e..0000000 --- a/.hgignore +++ /dev/null @@ -1,9 +0,0 @@ -syntax: glob -*.pyc -.*.swp -MANIFEST -build -dist -django_dbtemplates.egg-info/ -example/example.db -docs/_build \ No newline at end of file diff --git a/.hgtags b/.hgtags deleted file mode 100644 index c9e6f4c..0000000 --- a/.hgtags +++ /dev/null @@ -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 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..29545e8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ +language: python +python: +# - "2.5" + - "2.6" + - "2.7" +install: + - pip install . + - pip install -r requirements/tests.txt Django==$DJANGO +before_script: + - export PIP_USE_MIRRORS=true + - export DJANGO_SETTINGS_MODULE=dbtemplates.test_settings + - flake8 dbtemplates --ignore=E501 +script: + - django-admin.py test dbtemplates +env: + - DJANGO=1.3.1 + - DJANGO=1.4 +branches: + only: + - develop diff --git a/AUTHORS b/AUTHORS index 612aac1..9dd4105 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,10 +5,14 @@ Alexander Artemenko Arne Brodowski David Paccoud Diego Búrigo Zacarão +Dmitry Falk Jannis Leidel +Jure Cuhalev Jason Mayfield Kevin Mooney +Mark Stahler Matt Dorn Oliver George +Selwin Ong Stephan Peijnik , ANEXIA Internetdienstleistungs GmbH, http://www.anexia.at/ Zhang Kun diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 2e5bc7d..0000000 --- a/INSTALL +++ /dev/null @@ -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/ \ No newline at end of file diff --git a/LICENSE b/LICENSE index 0d17aff..00c83fd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2007-2011, Jannis Leidel and contributors +Copyright (c) 2007-2012, Jannis Leidel and contributors All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/MANIFEST.in b/MANIFEST.in index 8d620eb..9852006 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,3 @@ -include INSTALL include LICENSE include AUTHORS include README.rst diff --git a/README.rst b/README.rst index 3dd74ec..e0e9977 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,10 @@ django-dbtemplates ================== +.. image:: https://secure.travis-ci.org/jezdez/django-dbtemplates.png?branch=develop + :alt: Build Status + :target: http://travis-ci.org/jezdez/django-dbtemplates + ``dbtemplates`` is a Django app that consists of two parts: 1. It allows you to store templates in your database diff --git a/dbtemplates/__init__.py b/dbtemplates/__init__.py index e16f115..6524f6c 100644 --- a/dbtemplates/__init__.py +++ b/dbtemplates/__init__.py @@ -1,2 +1,2 @@ -# following PEP 386, versiontools will pick it up -__version__ = (1, 2, 1, "final", 0) +# following PEP 386 +__version__ = "1.3" diff --git a/dbtemplates/admin.py b/dbtemplates/admin.py index efdfbcb..9505d26 100644 --- a/dbtemplates/admin.py +++ b/dbtemplates/admin.py @@ -1,6 +1,7 @@ import posixpath from django import forms from django.contrib import admin +from django.core.exceptions import ImproperlyConfigured from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.safestring import mark_safe @@ -14,7 +15,7 @@ from dbtemplates.utils.template import check_template_syntax if settings.DBTEMPLATES_USE_REVERSION: from reversion.admin import VersionAdmin as TemplateModelAdmin else: - from django.contrib.admin import ModelAdmin as TemplateModelAdmin + from django.contrib.admin import ModelAdmin as TemplateModelAdmin # noqa class CodeMirrorTextArea(forms.Textarea): @@ -59,6 +60,14 @@ if settings.DBTEMPLATES_AUTO_POPULATE_CONTENT: else: 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 + class TemplateAdminForm(forms.ModelForm): """ diff --git a/dbtemplates/conf.py b/dbtemplates/conf.py index e1075a2..0bcf4ec 100644 --- a/dbtemplates/conf.py +++ b/dbtemplates/conf.py @@ -9,6 +9,7 @@ from appconf import AppConf class DbTemplatesConf(AppConf): USE_CODEMIRROR = False USE_REVERSION = False + USE_TINYMCE = False ADD_DEFAULT_SITE = True AUTO_POPULATE_CONTENT = True MEDIA_PREFIX = None @@ -40,3 +41,9 @@ class DbTemplatesConf(AppConf): raise ImproperlyConfigured("Please add 'reversion' 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 diff --git a/dbtemplates/loader.py b/dbtemplates/loader.py index 24e5010..a467305 100644 --- a/dbtemplates/loader.py +++ b/dbtemplates/loader.py @@ -1,7 +1,7 @@ from django.contrib.sites.models import Site +from django.db import router from django.template import TemplateDoesNotExist -from dbtemplates.conf import settings from dbtemplates.models import Template from dbtemplates.utils.cache import (cache, get_cache_key, set_and_return, get_cache_notfound_key) @@ -19,6 +19,12 @@ class Loader(BaseLoader): """ is_usable = True + 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 = 'dbtemplates:%s:%s:%s' % (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: # * Try to find the template in the cache. If found, return it. @@ -33,8 +39,6 @@ class Loader(BaseLoader): # in the cache indicating that queries failed, with the current # timestamp. 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) if cache: try: @@ -57,14 +61,13 @@ class Loader(BaseLoader): # Not marked as not-found, move on... try: - template = Template.objects.get(name__exact=template_name, - sites__in=[site.id]) - return set_and_return(cache_key, template.content, display_name) + return self.load_and_store_template(template_name, cache_key, + site, sites__in=[site.id]) except (Template.MultipleObjectsReturned, Template.DoesNotExist): try: - template = Template.objects.get(name__exact=template_name) - return set_and_return(cache_key, template.content, display_name) - except Template.DoesNotExist: + return self.load_and_store_template(template_name, cache_key, + site, sites__in=[]) + except (Template.MultipleObjectsReturned, Template.DoesNotExist): pass # Mark as not-found in cache. diff --git a/dbtemplates/management/commands/check_template_syntax.py b/dbtemplates/management/commands/check_template_syntax.py index 16d4ca2..214f531 100644 --- a/dbtemplates/management/commands/check_template_syntax.py +++ b/dbtemplates/management/commands/check_template_syntax.py @@ -3,6 +3,7 @@ from django.core.management.base import CommandError, NoArgsCommand from dbtemplates.models import Template from dbtemplates.utils.template import check_template_syntax + class Command(NoArgsCommand): help = "Ensures templates stored in the database don't have syntax errors." diff --git a/dbtemplates/management/commands/sync_templates.py b/dbtemplates/management/commands/sync_templates.py index ca73d7f..cffa7db 100644 --- a/dbtemplates/management/commands/sync_templates.py +++ b/dbtemplates/management/commands/sync_templates.py @@ -11,6 +11,7 @@ from dbtemplates.models import Template ALWAYS_ASK, FILES_TO_DATABASE, DATABASE_TO_FILES = ('0', '1', '2') + class Command(NoArgsCommand): help = "Syncs file system templates with the database bidirectionally." option_list = NoArgsCommand.option_list + ( @@ -89,7 +90,8 @@ class Command(NoArgsCommand): path, t.__repr__())) else: 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: t.content = codecs.open(path, 'r').read() t.save() @@ -101,8 +103,8 @@ class Command(NoArgsCommand): raise CommandError( u"Couldn't delete %s" % path) elif confirm == DATABASE_TO_FILES: + f = codecs.open(path, 'w', 'utf-8') try: - f = codecs.open(path, 'w') f.write(t.content) finally: f.close() diff --git a/dbtemplates/migrations/0001_initial.py b/dbtemplates/migrations/0001_initial.py index a5192af..0e08603 100644 --- a/dbtemplates/migrations/0001_initial.py +++ b/dbtemplates/migrations/0001_initial.py @@ -4,10 +4,11 @@ from south.db import db 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)), @@ -26,16 +27,13 @@ class Migration(SchemaMigration): )) db.create_unique('django_template_sites', ['template_id', 'site_id']) - def backwards(self, orm): - # Deleting model 'Template' db.delete_table('django_template') # Removing M2M table for field sites on 'Template' db.delete_table('django_template_sites') - models = { 'dbtemplates.template': { 'Meta': {'ordering': "('name',)", 'object_name': 'Template', 'db_table': "'django_template'"}, diff --git a/dbtemplates/migrations/0002_auto__del_unique_template_name.py b/dbtemplates/migrations/0002_auto__del_unique_template_name.py index 17a8a6e..418a3ab 100644 --- a/dbtemplates/migrations/0002_auto__del_unique_template_name.py +++ b/dbtemplates/migrations/0002_auto__del_unique_template_name.py @@ -1,23 +1,18 @@ # 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'"}, diff --git a/dbtemplates/models.py b/dbtemplates/models.py index aeef50e..808d223 100644 --- a/dbtemplates/models.py +++ b/dbtemplates/models.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from datetime import datetime - from django.db import models from django.db.models import signals from django.template import TemplateDoesNotExist @@ -13,6 +11,12 @@ 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 +try: + from django.utils.timezone import now +except ImportError: + from datetime import datetime + now = datetime.now + class Template(models.Model): """ @@ -25,9 +29,9 @@ class Template(models.Model): sites = models.ManyToManyField(Site, verbose_name=_(u'sites'), blank=False, null=False) creation_date = models.DateTimeField(_('creation date'), - default=datetime.now) + default=now) last_changed = models.DateTimeField(_('last changed'), - default=datetime.now) + default=now) objects = models.Manager() on_site = CurrentSiteManager('sites') @@ -56,7 +60,7 @@ class Template(models.Model): pass def save(self, *args, **kwargs): - self.last_changed = datetime.now() + self.last_changed = now() # If content is empty look for a template with the given name and # populate the template instance with its content. if settings.DBTEMPLATES_AUTO_POPULATE_CONTENT and not self.content: diff --git a/dbtemplates/tests.py b/dbtemplates/test_cases.py similarity index 64% rename from dbtemplates/tests.py rename to dbtemplates/test_cases.py index 2e52587..42b2a31 100644 --- a/dbtemplates/tests.py +++ b/dbtemplates/test_cases.py @@ -1,26 +1,33 @@ -from __future__ import with_statement import codecs import os import shutil import tempfile +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, Context +from django.template import loader, Context, 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 +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_template_loaders = settings.TEMPLATE_LOADERS + if 'dbtemplates.loader.Loader' not in settings.TEMPLATE_LOADERS: + loader.template_source_loaders = None + settings.TEMPLATE_LOADERS = (list(settings.TEMPLATE_LOADERS) + + ['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( @@ -31,6 +38,10 @@ class DbTemplatesTestCase(TestCase): name='sub.html', content='sub') self.t2.sites.add(self.site2) + def tearDown(self): + loader.template_source_loaders = None + settings.TEMPLATE_LOADERS = self.old_template_loaders + def test_basiscs(self): self.assertEqual(list(self.t1.sites.all()), [self.site1]) self.assertTrue("base" in self.t1.content) @@ -48,6 +59,28 @@ class DbTemplatesTestCase(TestCase): 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(Context({})) self.assertEqual(result, 'base') @@ -68,28 +101,31 @@ class DbTemplatesTestCase(TestCase): 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()) + 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()) + 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()) + self.assertTrue( + 'modified' in codecs.open(temp_template_path).read()) - call_command('sync_templates', - force=True, verbosity=0, delete=True, overwrite=DATABASE_TO_FILES) + 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()) + self.assertFalse( + Template.objects.filter(name='temp_test.html').exists()) finally: temp_template.close() settings.TEMPLATE_DIRS = old_template_dirs @@ -105,3 +141,7 @@ class DbTemplatesTestCase(TestCase): 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') diff --git a/dbtemplates/test_settings.py b/dbtemplates/test_settings.py index 4b46789..6ce4617 100644 --- a/dbtemplates/test_settings.py +++ b/dbtemplates/test_settings.py @@ -1,14 +1,27 @@ 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.admin', + 'django.contrib.auth', 'dbtemplates', + 'django_nose', ] TEMPLATE_LOADERS = ( @@ -16,3 +29,5 @@ TEMPLATE_LOADERS = ( 'django.template.loaders.app_directories.Loader', 'dbtemplates.loader.Loader', ) + +TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' diff --git a/dbtemplates/utils/cache.py b/dbtemplates/utils/cache.py index 299647c..13a48d7 100644 --- a/dbtemplates/utils/cache.py +++ b/dbtemplates/utils/cache.py @@ -1,6 +1,7 @@ from django.core.cache import get_cache from django.contrib.sites.models import Site +from django.template.defaultfilters import slugify from dbtemplates.conf import settings @@ -13,7 +14,7 @@ cache = get_cache_backend() def get_cache_key(name): current_site = Site.objects.get_current() - return 'dbtemplates::%s::%s' % (name, current_site.pk) + return 'dbtemplates::%s::%s' % (slugify(name), current_site.pk) def get_cache_notfound_key(name): diff --git a/dbtemplates/utils/template.py b/dbtemplates/utils/template.py index 2113237..679a7d9 100644 --- a/dbtemplates/utils/template.py +++ b/dbtemplates/utils/template.py @@ -8,13 +8,11 @@ def get_loaders(): from django.template.loader import template_source_loaders if template_source_loaders is None: try: - from django.template.loader import ( - find_template as finder_func) + from django.template.loader import find_template as finder except ImportError: - from django.template.loader import ( - find_template_source as finder_func) + from django.template.loader import find_template_source as finder # noqa try: - source, name = finder_func('test') + source, name = finder('test') except TemplateDoesNotExist: pass from django.template.loader import template_source_loaders diff --git a/docs/changelog.txt b/docs/changelog.txt index 9271c6e..b324766 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -1,6 +1,28 @@ Changelog ========= +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/jezdez/django-dbtemplates + +.. _nose: http://nose.rtfd.org/ +.. _Travis: http://travis-ci.org + v1.2.1 (2011-09-07) ------------------- diff --git a/docs/conf.py b/docs/conf.py index 98092ec..0bccac6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,7 +16,7 @@ import sys, os # 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 # 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 ----------------------------------------------------- @@ -38,16 +38,21 @@ master_doc = 'index' # General information about the project. project = u'django-dbtemplates' -copyright = u'2007-2011, Jannis Leidel and contributors' +copyright = u'2007-2012, Jannis Leidel and contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '1.2' -# The full version, including alpha/beta/rc tags. -release = '1.2.1' +try: + from dbtemplates import __version__ + # 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 # for a list of supported languages. diff --git a/docs/overview.txt b/docs/overview.txt index c1229f4..8d67121 100644 --- a/docs/overview.txt +++ b/docs/overview.txt @@ -2,7 +2,7 @@ Setup ===== 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 3. Edit the settings.py of your Django site: @@ -35,6 +35,11 @@ Setup 'dbtemplates.loader.Loader', ) + Order of TEMPLATE_LOADERS is important. In the former example, templates from database + will be used as a fallback (ie. when template does not exists in other locations). + If you want template from database to be used to override templates in other locations, + put ``dbtemplates.loader.Loader`` at beginning of ``TEMPLATE_LOADERS`` settting. + 4. Sync your database ``python manage.py syncdb`` 5. Restart your Django server diff --git a/docs/settings.txt b/docs/settings.txt index 3494c14..45b5bbe 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -4,8 +4,8 @@ Settings ``DBTEMPLATES_ADD_DEFAULT_SITE`` -------------------------------- -``dbtemplates`` adds the current site (``settings.SITE_ID``) to the database -template when it is created by default. You can disable this feature by +``dbtemplates`` adds the current site (``settings.SITE_ID``) to the database +template when it is created by default. You can disable this feature by setting ``DBTEMPLATES_ADD_DEFAULT_SITE`` to ``False``. ``DBTEMPLATES_AUTO_POPULATE_CONTENT`` @@ -28,6 +28,14 @@ The dotted Python path to the cache backend class. See A boolean, if enabled triggers the use of the CodeMirror based editor. 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`` ----------------------------- diff --git a/requirements/tests.txt b/requirements/tests.txt new file mode 100644 index 0000000..68eba17 --- /dev/null +++ b/requirements/tests.txt @@ -0,0 +1,3 @@ +flake8 +django-nose +coverage diff --git a/setup.cfg b/setup.cfg index cf6fb59..78ce9b8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,3 +11,7 @@ upload-dir = docs/_build/html [upload_sphinx] upload-dir = docs/_build/html + +[nosetests] +with-coverage=1 +cover-package=dbtemplates diff --git a/setup.py b/setup.py index dfee0fc..778878f 100644 --- a/setup.py +++ b/setup.py @@ -1,20 +1,33 @@ +import os +import re import codecs -from os import path from setuptools import setup, find_packages -read = lambda filepath: codecs.open(filepath, 'r', 'utf-8').read() + +def read(*parts): + return codecs.open(os.path.join(os.path.dirname(__file__), *parts)).read() + + +def find_version(*file_paths): + version_file = read(*file_paths) + version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", + version_file, re.M) + if version_match: + return version_match.group(1) + raise RuntimeError("Unable to find version string.") + setup( name='django-dbtemplates', - version='1.2.1pbs', + version=find_version('dbtemplates', '__init__.py'), description='Template loader for templates stored in the database', - long_description=read(path.join(path.dirname(__file__), 'README.rst')), + long_description=read('README.rst'), author='Jannis Leidel', author_email='jannis@leidel.info', url='http://django-dbtemplates.readthedocs.org/', packages=find_packages(exclude=['example']), zip_safe=False, - package_data = { + package_data={ 'dbtemplates': [ 'locale/*/LC_MESSAGES/*', 'static/dbtemplates/css/*.css', @@ -35,5 +48,4 @@ setup( 'Framework :: Django', ], install_requires=['django-appconf >= 0.4'], - setup_requires=['versiontools >= 1.5'], ) diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 66f3dc6..0000000 --- a/tox.ini +++ /dev/null @@ -1,51 +0,0 @@ -[tox] -distribute = false -envlist = - py25-1.2.X, py26-1.2.X, py27-1.2.X, - py25-1.3.X, py26-1.3.X, py27-1.3.X - -[testenv] -downloadcache = {toxworkdir}/_download/ -commands = - {envbindir}/coverage erase - {envbindir}/coverage run --branch --source=dbtemplates {envbindir}/django-admin.py test {posargs:dbtemplates} --settings=dbtemplates.test_settings - {envbindir}/coverage report --omit=*test* - {envbindir}/coverage html --omit=*test* -d {envtmpdir} - echo "Type the following to open the coverage report: python -m webbrowser -t file://{envtmpdir}/index.html" - -[testenv:py25-1.2.X] -basepython = python2.5 -deps = - coverage - django==1.2.5 - -[testenv:py26-1.2.X] -basepython = python2.6 -deps = - coverage - django==1.2.5 - -[testenv:py27-1.2.X] -basepython = python2.7 -deps = - coverage - django==1.2.5 - - -[testenv:py25-1.3.X] -basepython = python2.5 -deps = - coverage - django==1.3 - -[testenv:py26-1.3.X] -basepython = python2.6 -deps = - coverage - django==1.3 - -[testenv:py27-1.3.X] -basepython = python2.7 -deps = - coverage - django==1.3 From f22ca2c7e02bb70cdd7d5ddb34142d0ab6c5357a Mon Sep 17 00:00:00 2001 From: Bogdan Hodorog Date: Fri, 21 Sep 2012 18:35:53 +0300 Subject: [PATCH 6/8] TSK5222 Properly bump the version --- dbtemplates/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbtemplates/__init__.py b/dbtemplates/__init__.py index 6524f6c..c55ebfa 100644 --- a/dbtemplates/__init__.py +++ b/dbtemplates/__init__.py @@ -1,2 +1,2 @@ # following PEP 386 -__version__ = "1.3" +__version__ = "1.3pbs" From e559006f0555cfbd64c97328788294f33bad4ed7 Mon Sep 17 00:00:00 2001 From: Alex Cucu Date: Mon, 8 Oct 2012 11:39:23 +0300 Subject: [PATCH 7/8] When changing template-site assignments, invalidate cached templates that belonged to sites that got unassigned --- dbtemplates/models.py | 5 ++++- dbtemplates/utils/cache.py | 34 +++++++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/dbtemplates/models.py b/dbtemplates/models.py index 808d223..b59f7b3 100644 --- a/dbtemplates/models.py +++ b/dbtemplates/models.py @@ -8,7 +8,8 @@ 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.cache import (add_template_to_cache, remove_cached_template, + invalidate_cache_for_sites) from dbtemplates.utils.template import get_template_source try: @@ -84,3 +85,5 @@ def add_default_site(instance, **kwargs): signals.post_save.connect(add_default_site, sender=Template) signals.post_save.connect(add_template_to_cache, sender=Template) signals.pre_delete.connect(remove_cached_template, sender=Template) +signals.m2m_changed.connect(invalidate_cache_for_sites, + sender=Template.sites.through) diff --git a/dbtemplates/utils/cache.py b/dbtemplates/utils/cache.py index 13a48d7..f534c92 100644 --- a/dbtemplates/utils/cache.py +++ b/dbtemplates/utils/cache.py @@ -12,13 +12,14 @@ def get_cache_backend(): cache = get_cache_backend() -def get_cache_key(name): - current_site = Site.objects.get_current() - return 'dbtemplates::%s::%s' % (slugify(name), current_site.pk) +def get_cache_key(name, site_pk=None): + if site_pk is None: + site_pk = Site.objects.get_current().pk + return 'dbtemplates::%s::%s' % (slugify(name), site_pk) -def get_cache_notfound_key(name): - return get_cache_key(name) + '::notfound' +def get_cache_notfound_key(name, site_pk=None): + return get_cache_key(name, site_pk) + '::notfound' def remove_notfound_key(instance): @@ -43,6 +44,29 @@ def add_template_to_cache(instance, **kwargs): cache.set(get_cache_key(instance.name), instance.content) +def invalidate_cache_for_sites(sender, instance, action, reverse, + model, pk_set, **kwargs): + if action != 'post_add': + return + if isinstance(instance, Site): + # model is dbtemplates.models.Template + site = instance + for template in model.objects.all(): + if template.pk in pk_set: + cache.delete(get_cache_notfound_key(template.name, site.pk)) + else: + cache.delete(get_cache_key(template.name, site.pk)) + else: + # instance is of type dbtempaltes.models.Template + # model is django.contrib.sites.models.Site + template = instance + for site in model.objects.all(): + if site.pk in pk_set: + cache.delete(get_cache_notfound_key(template.name, site.pk)) + else: + cache.delete(get_cache_key(template.name, site.pk)) + + def remove_cached_template(instance, **kwargs): """ Called via Django's signals to remove cached templates, if the template From 4e480adf95f4fbb02b0b74fa43823a7097c52dd2 Mon Sep 17 00:00:00 2001 From: Amalia Gherasim Date: Thu, 11 Oct 2012 18:18:52 +0300 Subject: [PATCH 8/8] Bumped version --- dbtemplates/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbtemplates/__init__.py b/dbtemplates/__init__.py index c55ebfa..56e88fa 100644 --- a/dbtemplates/__init__.py +++ b/dbtemplates/__init__.py @@ -1,2 +1,2 @@ # following PEP 386 -__version__ = "1.3pbs" +__version__ = "1.4pbs"