diff --git a/categories/admin.py b/categories/admin.py index 7a4b0ce..70c096e 100644 --- a/categories/admin.py +++ b/categories/admin.py @@ -78,12 +78,12 @@ class CategoryAdminForm(forms.ModelForm): class CategoryAdmin(TreeEditor, admin.ModelAdmin): form = CategoryAdminForm - list_display = ('name', 'alternate_title', ) + list_display = ('name', 'alternate_title', 'active') search_fields = ('name', 'alternate_title', ) prepopulated_fields = {'slug': ('name',)} fieldsets = ( (None, { - 'fields': ('parent', 'name', 'thumbnail') + 'fields': ('parent', 'name', 'thumbnail', 'active') }), ('Meta Data', { 'fields': ('alternate_title', 'alternate_url', 'description', @@ -95,6 +95,29 @@ class CategoryAdmin(TreeEditor, admin.ModelAdmin): 'classes': ('collapse',), }), ) + + actions = ['activate', 'deactivate'] + + def deactivate(self, request, queryset): + """ + Set active to False for selected items + """ + for item in queryset: + if item.active: + item.active = False + item.save() + deactivate.short_description = "Deactivate selected categories and their children" + + def activate(self, request, queryset): + """ + Set active to True for selected items + """ + for item in queryset: + if not item.active: + item.active = True + item.save() + activate.short_description = "Activate selected categories and their children" + if RELATION_MODELS: inlines = [InlineCategoryRelation,] diff --git a/categories/migrations/0007_auto__add_field_category_active.py b/categories/migrations/0007_auto__add_field_category_active.py new file mode 100644 index 0000000..55975ff --- /dev/null +++ b/categories/migrations/0007_auto__add_field_category_active.py @@ -0,0 +1,60 @@ +# 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 field 'Category.active' + db.add_column('categories_category', 'active', self.gf('django.db.models.fields.BooleanField')(default=True), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'Category.active' + db.delete_column('categories_category', 'active') + + + models = { + 'categories.category': { + 'Meta': {'ordering': "('tree_id', 'lft')", 'unique_together': "(('parent', 'name'),)", 'object_name': 'Category'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'alternate_title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}), + 'alternate_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'meta_extra': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + 'meta_keywords': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['categories.Category']"}), + 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), + 'thumbnail': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'thumbnail_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'thumbnail_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}) + }, + 'categories.categoryrelation': { + 'Meta': {'object_name': 'CategoryRelation'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'relation_type': ('django.db.models.fields.CharField', [], {'max_length': "'200'", 'null': 'True', 'blank': 'True'}), + 'story': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['categories.Category']"}) + }, + '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'}), + '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'}) + } + } + + complete_apps = ['categories'] diff --git a/categories/models.py b/categories/models.py index d14b901..8ff4c19 100644 --- a/categories/models.py +++ b/categories/models.py @@ -14,6 +14,16 @@ from settings import (RELATION_MODELS, RELATIONS, THUMBNAIL_UPLOAD_PATH, STORAGE = get_storage_class(THUMBNAIL_STORAGE) +class CategoryManager(models.Manager): + """ + A manager that adds an "active()" method for all active categories + """ + def active(self): + """ + Only categories that are active + """ + return self.get_query_set().filter(active=True) + class Category(MPTTModel): parent = models.ForeignKey('self', blank=True, @@ -49,6 +59,9 @@ class Category(MPTTModel): blank=True, default="", help_text="(Advanced) Any additional HTML to be placed verbatim in the <head>") + active = models.BooleanField(default=True) + + objects = CategoryManager() @property def short_title(self): @@ -88,6 +101,10 @@ class Category(MPTTModel): self.thumbnail_width = width self.thumbnail_height = height + for item in self.get_descendants(): + if item.active != self.active: + item.active = self.active + item.save() super(Category, self).save(*args, **kwargs) class Meta: diff --git a/categories/tests/manager.py b/categories/tests/manager.py new file mode 100644 index 0000000..f8c0524 --- /dev/null +++ b/categories/tests/manager.py @@ -0,0 +1,23 @@ +# test active returns only active items +import unittest, os +from categories.models import Category +from categories.management.commands.import_categories import Command +from django.core.management.base import CommandError + +class CategoryManagerTest(unittest.TestCase): + def setUp(self): + pass + + def testActive(self): + """ + Should raise an exception. + """ + all_count = Category.objects.all().count() + self.assertEqual(Category.objects.active().count(), all_count) + + cat1 = Category.objects.get(name='Category 1') + cat1.active = False + cat1.save() + + active_count = all_count - cat1.get_descendants(True).count() + self.assertEqual(Category.objects.active().count(), active_count)