From 5b9151093820f2e4413e6c25a3e1f89ee6356c65 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Fri, 14 Aug 2015 09:41:04 +0200 Subject: [PATCH] Change LatestPostsPlugin.tags field type to use taggit-autosugges --- README.rst | 1 + djangocms_blog/cms_plugins.py | 2 + djangocms_blog/forms.py | 1 - .../migrations/0008_auto_20150814_0831.py | 21 ++ .../0009_latestpostsplugin_tags_new.py | 55 +++++ djangocms_blog/models.py | 10 +- .../south_migrations/0012_move_tags.py | 233 ++++++++++++++++++ djangocms_blog/south_migrations/0013_auto.py | 201 +++++++++++++++ tests/test_models.py | 9 +- 9 files changed, 525 insertions(+), 8 deletions(-) create mode 100644 djangocms_blog/migrations/0008_auto_20150814_0831.py create mode 100644 djangocms_blog/migrations/0009_latestpostsplugin_tags_new.py create mode 100644 djangocms_blog/south_migrations/0012_move_tags.py create mode 100644 djangocms_blog/south_migrations/0013_auto.py diff --git a/README.rst b/README.rst index d08541f..a85cadd 100644 --- a/README.rst +++ b/README.rst @@ -65,6 +65,7 @@ Add ``djangocms_blog`` and its dependencies to INSTALLED_APPS:: 'parler', 'taggit', 'taggit_autosuggest', + 'taggit_labels', 'meta', 'meta_mixin', 'admin_enhancer', diff --git a/djangocms_blog/cms_plugins.py b/djangocms_blog/cms_plugins.py index 0af4eff..c25b8c3 100644 --- a/djangocms_blog/cms_plugins.py +++ b/djangocms_blog/cms_plugins.py @@ -23,6 +23,7 @@ class BlogLatestEntriesPlugin(BlogPlugin): model = LatestPostsPlugin form = LatestEntriesForm filter_horizontal = ('categories',) + fields = ('latest_posts', 'tags', 'categories') cache = False def render(self, context, instance, placeholder): @@ -41,6 +42,7 @@ class BlogLatestEntriesPluginCached(BlogPlugin): model = LatestPostsPlugin form = LatestEntriesForm filter_horizontal = ('categories',) + fields = ('latest_posts', 'tags', 'categories') def render(self, context, instance, placeholder): context = super(BlogLatestEntriesPluginCached, self).render(context, instance, placeholder) diff --git a/djangocms_blog/forms.py b/djangocms_blog/forms.py index b203bd7..4dd5226 100644 --- a/djangocms_blog/forms.py +++ b/djangocms_blog/forms.py @@ -5,7 +5,6 @@ from taggit_autosuggest.widgets import TagAutoSuggest class LatestEntriesForm(forms.ModelForm): - def __init__(self, *args, **kwargs): super(LatestEntriesForm, self).__init__(*args, **kwargs) self.fields['tags'].widget = TagAutoSuggest('taggit.Tag') diff --git a/djangocms_blog/migrations/0008_auto_20150814_0831.py b/djangocms_blog/migrations/0008_auto_20150814_0831.py new file mode 100644 index 0000000..8978a1f --- /dev/null +++ b/djangocms_blog/migrations/0008_auto_20150814_0831.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import djangocms_text_ckeditor.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('djangocms_blog', '0007_auto_20150719_0933'), + ] + + operations = [ + migrations.AlterField( + model_name='posttranslation', + name='abstract', + field=djangocms_text_ckeditor.fields.HTMLField(default=b'', verbose_name='abstract', blank=True), + preserve_default=True, + ), + ] diff --git a/djangocms_blog/migrations/0009_latestpostsplugin_tags_new.py b/djangocms_blog/migrations/0009_latestpostsplugin_tags_new.py new file mode 100644 index 0000000..53860ec --- /dev/null +++ b/djangocms_blog/migrations/0009_latestpostsplugin_tags_new.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.contrib.contenttypes.models import ContentType +import taggit_autosuggest.managers + + +def migrate_tags(apps, schema_editor): + LatestPostsPlugin = apps.get_model('djangocms_blog', 'LatestPostsPlugin') + Tag = apps.get_model('taggit', 'Tag') + TaggedItem = apps.get_model('taggit', 'TaggedItem') + plugin_content_type = ContentType.objects.get_for_model(LatestPostsPlugin) + for tag in Tag.objects.all(): + for plugin in tag.latestpostsplugin_set.all(): + if not TaggedItem.objects.filter(tag=tag, object_id=plugin.pk, content_type_id=plugin_content_type.pk).exists(): + TaggedItem.objects.create(tag=tag, object_id=plugin.pk, content_type_id=plugin_content_type.pk) + + +def migrate_tags_reverse(apps, schema_editor): + LatestPostsPlugin = apps.get_model('djangocms_blog', 'LatestPostsPlugin') + Tag = apps.get_model('taggit', 'Tag') + TaggedItem = apps.get_model('taggit', 'TaggedItem') + plugin_content_type = ContentType.objects.get_for_model(LatestPostsPlugin) + for tagged in TaggedItem.objects.filter(content_type_id=plugin_content_type.pk): + post = LatestPostsPlugin.objects.get(pk=tagged.object_id) + post.tags.add(tagged.tag) + tagged.delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ('taggit', '__latest__'), + ('djangocms_blog', '0008_auto_20150814_0831'), + ] + + operations = [ + migrations.AddField( + model_name='latestpostsplugin', + name='tags_new', + field=taggit_autosuggest.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='Show only the blog articles tagged with chosen tags.', verbose_name='filter by tag'), + preserve_default=True, + ), + migrations.RunPython(migrate_tags, migrate_tags_reverse), + migrations.RemoveField( + model_name='latestpostsplugin', + name='tags', + ), + migrations.RenameField( + model_name='latestpostsplugin', + old_name='tags_new', + new_name='tags', + ), + ] diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index fcb8a71..24b7314 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -247,8 +247,9 @@ class LatestPostsPlugin(BasePostPlugin): latest_posts = models.IntegerField(_(u'articles'), default=get_setting('LATEST_POSTS'), help_text=_(u'The number of latests articles to be displayed.')) - tags = models.ManyToManyField('taggit.Tag', blank=True, verbose_name=_(u'filter by tag'), - help_text=_(u'Show only the blog articles tagged with chosen tags.')) + tags = TaggableManager(_(u'filter by tag'), blank=True, + help_text=_(u'Show only the blog articles tagged with chosen tags.'), + related_name='djangocms_blog_latest_post') categories = models.ManyToManyField('djangocms_blog.BlogCategory', blank=True, verbose_name=_(u'filter by category'), help_text=_(u'Show only the blog articles tagged with chosen categories.')) @@ -256,7 +257,8 @@ class LatestPostsPlugin(BasePostPlugin): return _(u'%s latest articles by tag') % self.latest_posts def copy_relations(self, oldinstance): - self.tags = oldinstance.tags.all() + for tag in oldinstance.tags.all(): + self.tags.add(tag) def get_posts(self, request): posts = self.post_queryset(request) @@ -264,7 +266,7 @@ class LatestPostsPlugin(BasePostPlugin): posts = posts.filter(tags__in=list(self.tags.all())) if self.categories.exists(): posts = posts.filter(categories__in=list(self.categories.all())) - return posts[:self.latest_posts] + return posts.distinct()[:self.latest_posts] class AuthorEntriesPlugin(BasePostPlugin): diff --git a/djangocms_blog/south_migrations/0012_move_tags.py b/djangocms_blog/south_migrations/0012_move_tags.py new file mode 100644 index 0000000..bcf9def --- /dev/null +++ b/djangocms_blog/south_migrations/0012_move_tags.py @@ -0,0 +1,233 @@ +# -*- coding: utf-8 -*- +from django.contrib.contenttypes.models import ContentType +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + "Write your forwards methods here." + LatestPostsPlugin = orm['djangocms_blog.latestpostsplugin'] + Tag = orm['taggit.tag'] + TaggedItem = orm['taggit.taggeditem'] + try: + plugin_content_type = ContentType.objects.get(app_label=LatestPostsPlugin._meta.app_label, + model=LatestPostsPlugin._meta.model_name) + for tag in Tag.objects.all(): + for plugin in tag.latestpostsplugin_set.all(): + if not TaggedItem.objects.filter(tag=tag, object_id=plugin.pk, content_type_id=plugin_content_type.pk).exists(): + orm['taggit.taggeditem'].objects.create( + tag=tag, + content_type_id=plugin_content_type.pk, + object_id=plugin.pk + ) + except ContentType.DoesNotExist: + # no content type means no data, we can skip data migration + pass + + + def backwards(self, orm): + "Write your backwards methods here." + LatestPostsPlugin = orm['djangocms_blog.latestpostsplugin'] + Tag = orm['taggit.tag'] + TaggedItem = orm['taggit.taggeditem'] + plugin_content_type = ContentType.objects.get_for_model(LatestPostsPlugin) + for tagged in TaggedItem.objects.filter(content_type_id=plugin_content_type.pk): + post = LatestPostsPlugin.objects.get(pk=tagged.object_id) + post.tags.add(tagged.tag) + tagged.delete() + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'cms.cmsplugin': { + 'Meta': {'object_name': 'CMSPlugin'}, + 'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'depth': ('django.db.models.fields.PositiveIntegerField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + 'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}), + 'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + 'cms.placeholder': { + 'Meta': {'object_name': 'Placeholder'}, + 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slot': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}) + }, + u'cmsplugin_filer_image.thumbnailoption': { + 'Meta': {'ordering': "(u'width', u'height')", 'object_name': 'ThumbnailOption'}, + 'crop': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'height': ('django.db.models.fields.IntegerField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'upscale': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'width': ('django.db.models.fields.IntegerField', [], {}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'djangocms_blog.authorentriesplugin': { + 'Meta': {'object_name': 'AuthorEntriesPlugin'}, + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'}), + u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), + 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) + }, + u'djangocms_blog.blogcategory': { + 'Meta': {'object_name': 'BlogCategory'}, + 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['djangocms_blog.BlogCategory']", 'null': 'True', 'blank': 'True'}) + }, + u'djangocms_blog.blogcategorytranslation': { + 'Meta': {'unique_together': "[('language_code', 'slug'), (u'language_code', u'master')]", 'object_name': 'BlogCategoryTranslation', 'db_table': "u'djangocms_blog_blogcategory_translation'"}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.BlogCategory']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}) + }, + u'djangocms_blog.latestpostsplugin': { + 'Meta': {'object_name': 'LatestPostsPlugin'}, + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['djangocms_blog.BlogCategory']", 'symmetrical': 'False', 'blank': 'True'}), + u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), + 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['taggit.Tag']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'djangocms_blog.post': { + 'Meta': {'ordering': "('-date_published', '-date_created')", 'object_name': 'Post'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'djangocms_blog_post_author'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'blog_posts'", 'symmetrical': 'False', 'to': u"orm['djangocms_blog.BlogCategory']"}), + 'content': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'post_content'", 'null': 'True', 'to': "orm['cms.Placeholder']"}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'date_published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_published_end': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'enable_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_image': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'djangocms_blog_post_image'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['filer.Image']"}), + 'main_image_full': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'djangocms_blog_post_full'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['cmsplugin_filer_image.ThumbnailOption']"}), + 'main_image_thumbnail': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'djangocms_blog_post_thumbnail'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['cmsplugin_filer_image.ThumbnailOption']"}), + 'publish': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['sites.Site']", 'null': 'True', 'blank': 'True'}) + }, + u'djangocms_blog.posttranslation': { + 'Meta': {'unique_together': "[('language_code', 'slug'), (u'language_code', u'master')]", 'object_name': 'PostTranslation', 'db_table': "u'djangocms_blog_post_translation'"}, + 'abstract': ('djangocms_text_ckeditor.fields.HTMLField', [], {'default': "''", 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.Post']"}), + 'meta_description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'meta_keywords': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'meta_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'blank': 'True'}), + 'post_text': ('djangocms_text_ckeditor.fields.HTMLField', [], {'default': "''", 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + u'filer.file': { + 'Meta': {'object_name': 'File'}, + '_file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'folder': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'all_files'", 'null': 'True', 'to': u"orm['filer.Folder']"}), + 'has_all_mandatory_data': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), + 'original_filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'owned_files'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'polymorphic_filer.file_set+'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}), + 'sha1': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '40', 'blank': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + u'filer.folder': { + 'Meta': {'ordering': "(u'name',)", 'unique_together': "((u'parent', u'name'),)", 'object_name': 'Folder'}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + u'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + u'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'filer_owned_folders'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'children'", 'null': 'True', 'to': u"orm['filer.Folder']"}), + u'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + u'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'filer.image': { + 'Meta': {'object_name': 'Image'}, + '_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + '_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'author': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'date_taken': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'default_alt_text': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'default_caption': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + u'file_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['filer.File']", 'unique': 'True', 'primary_key': 'True'}), + 'must_always_publish_author_credit': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'must_always_publish_copyright': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'subject_location': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '64', 'null': 'True', 'blank': 'True'}) + }, + u'sites.site': { + 'Meta': {'ordering': "(u'domain',)", 'object_name': 'Site', 'db_table': "u'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'}) + }, + u'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_tagged_items'", 'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_items'", 'to': u"orm['taggit.Tag']"}) + } + } + + complete_apps = ['djangocms_blog'] + symmetrical = True diff --git a/djangocms_blog/south_migrations/0013_auto.py b/djangocms_blog/south_migrations/0013_auto.py new file mode 100644 index 0000000..c1c4d16 --- /dev/null +++ b/djangocms_blog/south_migrations/0013_auto.py @@ -0,0 +1,201 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Removing M2M table for field tags on 'LatestPostsPlugin' + db.delete_table(db.shorten_name(u'djangocms_blog_latestpostsplugin_tags')) + + + def backwards(self, orm): + # Adding M2M table for field tags on 'LatestPostsPlugin' + m2m_table_name = db.shorten_name(u'djangocms_blog_latestpostsplugin_tags') + db.create_table(m2m_table_name, ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('latestpostsplugin', models.ForeignKey(orm[u'djangocms_blog.latestpostsplugin'], null=False)), + ('tag', models.ForeignKey(orm[u'taggit.tag'], null=False)) + )) + db.create_unique(m2m_table_name, ['latestpostsplugin_id', 'tag_id']) + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'cms.cmsplugin': { + 'Meta': {'object_name': 'CMSPlugin'}, + 'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'depth': ('django.db.models.fields.PositiveIntegerField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + 'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}), + 'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + 'cms.placeholder': { + 'Meta': {'object_name': 'Placeholder'}, + 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slot': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}) + }, + u'cmsplugin_filer_image.thumbnailoption': { + 'Meta': {'ordering': "(u'width', u'height')", 'object_name': 'ThumbnailOption'}, + 'crop': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'height': ('django.db.models.fields.IntegerField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'upscale': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'width': ('django.db.models.fields.IntegerField', [], {}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'djangocms_blog.authorentriesplugin': { + 'Meta': {'object_name': 'AuthorEntriesPlugin'}, + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'}), + u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), + 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) + }, + u'djangocms_blog.blogcategory': { + 'Meta': {'object_name': 'BlogCategory'}, + 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['djangocms_blog.BlogCategory']", 'null': 'True', 'blank': 'True'}) + }, + u'djangocms_blog.blogcategorytranslation': { + 'Meta': {'unique_together': "[('language_code', 'slug'), (u'language_code', u'master')]", 'object_name': 'BlogCategoryTranslation', 'db_table': "u'djangocms_blog_blogcategory_translation'"}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.BlogCategory']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}) + }, + u'djangocms_blog.latestpostsplugin': { + 'Meta': {'object_name': 'LatestPostsPlugin'}, + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['djangocms_blog.BlogCategory']", 'symmetrical': 'False', 'blank': 'True'}), + u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), + 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) + }, + u'djangocms_blog.post': { + 'Meta': {'ordering': "('-date_published', '-date_created')", 'object_name': 'Post'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'djangocms_blog_post_author'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'blog_posts'", 'symmetrical': 'False', 'to': u"orm['djangocms_blog.BlogCategory']"}), + 'content': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'post_content'", 'null': 'True', 'to': "orm['cms.Placeholder']"}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'date_published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_published_end': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'enable_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_image': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'djangocms_blog_post_image'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['filer.Image']"}), + 'main_image_full': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'djangocms_blog_post_full'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['cmsplugin_filer_image.ThumbnailOption']"}), + 'main_image_thumbnail': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'djangocms_blog_post_thumbnail'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['cmsplugin_filer_image.ThumbnailOption']"}), + 'publish': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['sites.Site']", 'null': 'True', 'blank': 'True'}) + }, + u'djangocms_blog.posttranslation': { + 'Meta': {'unique_together': "[('language_code', 'slug'), (u'language_code', u'master')]", 'object_name': 'PostTranslation', 'db_table': "u'djangocms_blog_post_translation'"}, + 'abstract': ('djangocms_text_ckeditor.fields.HTMLField', [], {'default': "''", 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.Post']"}), + 'meta_description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'meta_keywords': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'meta_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'blank': 'True'}), + 'post_text': ('djangocms_text_ckeditor.fields.HTMLField', [], {'default': "''", 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + u'filer.file': { + 'Meta': {'object_name': 'File'}, + '_file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'folder': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'all_files'", 'null': 'True', 'to': u"orm['filer.Folder']"}), + 'has_all_mandatory_data': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), + 'original_filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'owned_files'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'polymorphic_filer.file_set+'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}), + 'sha1': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '40', 'blank': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + u'filer.folder': { + 'Meta': {'ordering': "(u'name',)", 'unique_together': "((u'parent', u'name'),)", 'object_name': 'Folder'}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + u'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + u'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'filer_owned_folders'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'children'", 'null': 'True', 'to': u"orm['filer.Folder']"}), + u'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + u'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'filer.image': { + 'Meta': {'object_name': 'Image'}, + '_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + '_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'author': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'date_taken': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'default_alt_text': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'default_caption': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + u'file_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['filer.File']", 'unique': 'True', 'primary_key': 'True'}), + 'must_always_publish_author_credit': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'must_always_publish_copyright': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'subject_location': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '64', 'null': 'True', 'blank': 'True'}) + }, + u'sites.site': { + 'Meta': {'ordering': "(u'domain',)", 'object_name': 'Site', 'db_table': "u'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + } + } + + complete_apps = ['djangocms_blog'] \ No newline at end of file diff --git a/tests/test_models.py b/tests/test_models.py index 636a19e..3a1d0ee 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -286,16 +286,19 @@ class ModelsTest(BaseTest): def test_copy_plugin_latest(self): post1 = self._get_post(self.data['en'][0]) post2 = self._get_post(self.data['en'][1]) - tag = Tag.objects.create(name='tag 1') + tag1 = Tag.objects.create(name='tag 1') + tag2 = Tag.objects.create(name='tag 2') plugin = add_plugin(post1.content, 'BlogLatestEntriesPlugin', language='en') - plugin.tags.add(tag) + plugin.tags.add(tag1) + plugin.tags.add(tag2) if CMS_30: plugins = list(post1.content.cmsplugin_set.filter(language='en').order_by('tree_id', 'level', 'position')) else: plugins = list(post1.content.cmsplugin_set.filter(language='en').order_by('path', 'depth', 'position')) copy_plugins_to(plugins, post2.content) new = downcast_plugins(post2.content.cmsplugin_set.all()) - self.assertEqual(set(new[0].tags.all()), set([tag])) + self.assertEqual(set(new[0].tags.all()), set([tag1, tag2])) + self.assertEqual(set(new[0].tags.all()), set(plugin.tags.all())) def test_plugin_author(self): post1 = self._get_post(self.data['en'][0])