Skip to content
This repository has been archived by the owner on Dec 9, 2019. It is now read-only.

Commit

Permalink
Experimental support for sites
Browse files Browse the repository at this point in the history
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
zerok committed Oct 8, 2011
1 parent 2a9b115 commit c15545f
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 10 deletions.
46 changes: 46 additions & 0 deletions flatblocks/migrations/0002_sitesupport.py
Original file line number Diff line number Diff line change
@@ -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']
48 changes: 47 additions & 1 deletion flatblocks/models.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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,)
Expand All @@ -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 change: 1 addition & 0 deletions flatblocks/templates/flatblocks/edit.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<h1>Edit "{{ flatblock.slug }}"</h1>

<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save" />
</form>
12 changes: 9 additions & 3 deletions flatblocks/templatetags/flatblock_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down
47 changes: 42 additions & 5 deletions flatblocks/tests.py
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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 %}')
Expand Down
2 changes: 1 addition & 1 deletion flatblocks/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions test_project/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit c15545f

Please sign in to comment.