Permalink
Browse files

Experimental support for sites

This removes the constraint on slugs since they should be enforced o site
basis. This is done right now using the m2m_changed signal and model
validation.
  • Loading branch information...
1 parent 2a9b115 commit c15545fa6b9f35a2eba053aab96b49fb92dd7ed3 @zerok committed Oct 8, 2011
@@ -0,0 +1,46 @@
+# -*- coding: 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 'FlatBlock', fields ['slug']
+ db.delete_unique('flatblocks_flatblock', ['slug'])
+
+ # Adding M2M table for field sites on 'FlatBlock'
+ db.create_table('flatblocks_flatblock_sites', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('flatblock', models.ForeignKey(orm['flatblocks.flatblock'], null=False)),
+ ('site', models.ForeignKey(orm['sites.site'], null=False))
+ ))
+ db.create_unique('flatblocks_flatblock_sites', ['flatblock_id', 'site_id'])
+
+ def backwards(self, orm):
+ # Removing M2M table for field sites on 'FlatBlock'
+ db.delete_table('flatblocks_flatblock_sites')
+
+ # Adding unique constraint on 'FlatBlock', fields ['slug']
+ db.create_unique('flatblocks_flatblock', ['slug'])
+
+ models = {
+ 'flatblocks.flatblock': {
+ 'Meta': {'object_name': 'FlatBlock'},
+ 'content': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'header': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sites.Site']", 'symmetrical': 'False'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ '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 = ['flatblocks']
View
@@ -1,6 +1,11 @@
from django.db import models
+from django.db.utils import IntegrityError
+from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache
+from django.core.exceptions import ValidationError
+from django.contrib.sites.models import Site
+from django.contrib.sites.managers import CurrentSiteManager
from flatblocks.settings import CACHE_PREFIX
@@ -11,13 +16,17 @@ class FlatBlock(models.Model):
basically a piece of content with a given name (slug) and an optional
title (header) which you can, for example, use in a sidebar of a website.
"""
- slug = models.CharField(max_length=255, unique=True,
+ slug = models.CharField(max_length=255,
verbose_name=_('Slug'),
help_text=_("A unique name used for reference in the templates"))
header = models.CharField(blank=True, null=True, max_length=255,
verbose_name=_('Header'),
help_text=_("An optional header for this content"))
content = models.TextField(verbose_name=_('Content'), blank=True, null=True)
+ sites = models.ManyToManyField(Site)
+
+ objects = models.Manager()
+ site_objects = CurrentSiteManager()
def __unicode__(self):
return u"%s" % (self.slug,)
@@ -27,6 +36,43 @@ def save(self, *args, **kwargs):
# Now also invalidate the cache used in the templatetag
cache.delete('%s%s' % (CACHE_PREFIX, self.slug, ))
+ def validate_unique(self, exclude=None):
+ """
+ Validates the uniqueness of the slug with respect to the associated sites.
+ """
+ super(FlatBlock, self).validate_unique(exclude)
+ if not self.pk or (exclude and 'sites' in exclude):
+ return
+ validate_site_uniqueness(self)
+
class Meta:
verbose_name = _('Flat block')
verbose_name_plural = _('Flat blocks')
+
+def validate_site_uniqueness(block, site_pks=None):
+ """
+ Enforces that slug and site are unique.
+ """
+ if site_pks is None:
+ site_pks = [site.pk for site in block.sites.all()]
+ for site_pk in site_pks:
+ for other in FlatBlock.objects.filter(slug=block.slug).filter(sites=site_pk):
+ if other != block:
+ raise ValidationError, "slug is not unique for the selected sites"
+
+@receiver(models.signals.m2m_changed, sender=FlatBlock.sites.through)
+def check_m2m_add(sender, **kwargs):
+ instance = kwargs.get('instance')
+ action = kwargs.get('action')
+ pk_set = kwargs.get('pk_set')
+ reverse = kwargs.get('reverse', False)
+ site_pks = pk_set
+ block = instance # If done normally
+ if reverse:
+ block = kwargs.get('model').objects.get(pk=pk_set.__iter__().next())
+ site_pks = [instance.pk]
+ if action == 'pre_add':
+ try:
+ validate_site_uniqueness(block, site_pks)
+ except ValidationError, e:
+ raise IntegrityError, e
@@ -1,6 +1,7 @@
<h1>Edit "{{ flatblock.slug }}"</h1>
<form method="post">
+ {% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save" />
</form>
@@ -44,6 +44,7 @@
from django import template
from django.template import loader
from django.db import models
+from django.contrib.sites.models import Site
from django.core.cache import cache
from flatblocks import settings
@@ -159,12 +160,17 @@ def render(self, context):
# This behaviour can be configured using the
# FLATBLOCKS_AUTOCREATE_STATIC_BLOCKS setting
if self.is_variable or not settings.AUTOCREATE_STATIC_BLOCKS:
- flatblock = FlatBlock.objects.get(slug=real_slug)
+ flatblock = FlatBlock.site_objects.get(slug=real_slug)
else:
- flatblock, _ = FlatBlock.objects.get_or_create(
+ flatblock, created = FlatBlock.site_objects.get_or_create(
slug=real_slug,
- defaults = {'content': real_slug}
+ defaults = {
+ 'content': real_slug,
+ }
)
+ if created:
+ flatblock.sites.add(Site.objects.get_current())
+ flatblock.save()
if self.cache_time != 0:
if self.cache_time is None or self.cache_time == 'None':
logger.debug("Caching %s for the cache's default timeout"
View
@@ -1,23 +1,48 @@
from django import template
from django.test import TestCase
+from django.conf import settings as global_settings
from django.core.cache import cache
from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
from django import db
from flatblocks.models import FlatBlock
from flatblocks import settings
-class BasicTests(TestCase):
+class SiteTestCase(TestCase):
+ @classmethod
+ def setUpClass(cls):
+ cls.site_1 = Site(name='site1', domain='site1.com')
+ cls.site_1.save()
+ cls.site_2 = Site(name='site2', domain='site2.com')
+ cls.site_2.save()
+ global_settings.SITE_ID = cls.site_1.pk
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.site_1.delete()
+ cls.site_2.delete()
+
+ def create_block_for_site1(self, **kwargs):
+ return self.__class__.site_1.flatblock_set.create(**kwargs)
+
+ def create_block_for_site2(self, **kwargs):
+ return self.__class__.site_2.flatblock_set.create(**kwargs)
+
+class BasicTests(SiteTestCase):
urls = 'flatblocks.urls'
def setUp(self):
- self.testblock = FlatBlock.objects.create(
+ self.testblock = self.create_block_for_site1(
slug='block',
header='HEADER',
- content='CONTENT'
+ content='CONTENT',
)
self.admin = User.objects.create_superuser('admin', 'admin@localhost', 'adminpwd')
+
+ def tearDown(self):
+ self.testblock.delete()
def testURLConf(self):
# We have to support two different APIs here (1.1 and 1.2)
@@ -49,15 +74,27 @@ def testSaveKwargs(self):
block = FlatBlock.objects.get(slug='block')
self.assertRaises(db.IntegrityError, block.save, force_insert=True)
+ def testSiteUniqueness(self):
+ """
+ Make sure that there can only be one flatblock with a slug for a site.
+ """
+ self.create_block_for_site1(slug='unique1').full_clean()
+ self.create_block_for_site2(slug='unique1').full_clean()
+ with self.assertRaises(Exception) as cm:
+ self.create_block_for_site1(slug='unique1').full_clean()
-class TagTests(TestCase):
+
+class TagTests(SiteTestCase):
def setUp(self):
- self.testblock = FlatBlock.objects.create(
+ self.testblock = self.create_block_for_site1(
slug='block',
header='HEADER',
content='CONTENT'
)
+ def tearDown(self):
+ self.testblock.delete()
+
def testLoadingTaglib(self):
"""Tests if the taglib defined in this app can be loaded"""
tpl = template.Template('{% load flatblock_tags %}')
View
@@ -37,7 +37,7 @@ def my_perm_check(request, flatblock):
If everything is alright with the permissions, simply return True.
"""
- flatblock = get_object_or_404(FlatBlock, pk=pk)
+ flatblock = get_object_or_404(FlatBlock.site_objects.all(), pk=pk)
if permission_check is not None:
permcheck_result = permission_check(request, flatblock)
if permcheck_result is False:
View
@@ -11,11 +11,13 @@
TEMPLATE_DEBUG=True
DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = '/tmp/flatblocks.db'
+SITE_ID = 1
INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.admin',
'django.contrib.contenttypes',
'django.contrib.sessions',
+ 'django.contrib.sites',
'flatblocks',
]
try:

0 comments on commit c15545f

Please sign in to comment.