Merge branch '1.2'

This commit is contained in:
Corey Oordt 2013-03-20 06:59:27 -05:00
commit 0bb8af6351
23 changed files with 437 additions and 158 deletions

View file

@ -12,4 +12,7 @@ recursive-include doc_src *.rst *.txt *.png *.css *.html *.js
include doc_src/Makefile
include doc_src/make.bat
recursive-include categories/locale *.mo *.po
recursive-include categories/editor/locale *.mo *.po
prune example

View file

@ -13,6 +13,15 @@ Django Categories grew out of our need to provide a basic hierarchical taxonomy
As a news site, our stories, photos, and other content get divided into "sections" and we wanted all the apps to use the same set of sections. As our needs grew, the Django Categories grew in the functionality it gave to category handling within web pages.
New in 1.2
==========
* Support for Django 1.5
* Dropped support for Django 1.2
* Dropped caching within the app
* Removed the old settings compatibility layer. *Must use new dictionary-based settings!*
New in 1.1
==========

View file

@ -1,7 +1,7 @@
__version_info__ = {
'major': 1,
'minor': 1,
'micro': 4,
'minor': 2,
'micro': 0,
'releaselevel': 'final',
'serial': 1
}
@ -25,5 +25,5 @@ try:
register_m2m)
_process_registry(settings.FK_REGISTRY, register_fk)
_process_registry(settings.M2M_REGISTRY, register_m2m)
except ImportError:
except:
pass

View file

@ -1,5 +1,6 @@
from django.contrib import admin
from django import forms
from django.utils.translation import ugettext_lazy as _
from .genericcollection import GenericCollectionTabularInline
from .settings import RELATION_MODELS, JAVASCRIPT_URL, REGISTER_ADMIN
@ -46,12 +47,12 @@ class CategoryAdmin(CategoryBaseAdmin):
(None, {
'fields': ('parent', 'name', 'thumbnail', 'active')
}),
('Meta Data', {
(_('Meta Data'), {
'fields': ('alternate_title', 'alternate_url', 'description',
'meta_keywords', 'meta_extra'),
'classes': ('collapse',),
}),
('Advanced', {
(_('Advanced'), {
'fields': ('order', 'slug'),
'classes': ('collapse',),
}),

View file

@ -8,6 +8,7 @@ from django.db import models
from django import forms
from django.template.defaultfilters import slugify
from django.utils.encoding import force_unicode
from django.utils.translation import ugettext as _
from mptt.models import MPTTModel
from mptt.fields import TreeForeignKey
@ -36,11 +37,11 @@ class CategoryBase(MPTTModel):
parent = TreeForeignKey('self',
blank=True,
null=True,
related_name="children",
verbose_name='Parent')
name = models.CharField(max_length=100)
slug = models.SlugField()
active = models.BooleanField(default=True)
related_name='children',
verbose_name=_('parent'))
name = models.CharField(max_length=100, verbose_name=_('name'))
slug = models.SlugField(verbose_name=_('slug'))
active = models.BooleanField(default=True, verbose_name=_('active'))
objects = CategoryManager()
tree = TreeManager()
@ -100,8 +101,8 @@ class CategoryBaseAdminForm(forms.ModelForm):
**kwargs).values('id', 'slug'
) if c['id'] != self.instance.id]
if self.cleaned_data['slug'] in this_level_slugs:
raise forms.ValidationError("The slug must be unique among "
"the items at its level.")
raise forms.ValidationError(_('The slug must be unique among '
'the items at its level.'))
# Validate Category Parent
# Make sure the category doesn't set itself or any of its children as
@ -110,11 +111,11 @@ class CategoryBaseAdminForm(forms.ModelForm):
if self.cleaned_data.get('parent', None) is None or self.instance.id is None:
return self.cleaned_data
elif self.cleaned_data['parent'].id == self.instance.id:
raise forms.ValidationError("You can't set the parent of the "
"item to itself.")
raise forms.ValidationError(_("You can't set the parent of the "
"item to itself."))
elif self.cleaned_data['parent'].id in decendant_ids:
raise forms.ValidationError("You can't set the parent of the "
"item to a descendant.")
raise forms.ValidationError(_("You can't set the parent of the "
"item to a descendant."))
return self.cleaned_data
@ -144,7 +145,7 @@ class CategoryBaseAdmin(TreeEditor, admin.ModelAdmin):
item.active = False
item.save()
item.children.all().update(active=False)
deactivate.short_description = "Deactivate selected categories and their children"
deactivate.short_description = _('Deactivate selected categories and their children')
def activate(self, request, queryset):
"""
@ -157,4 +158,4 @@ class CategoryBaseAdmin(TreeEditor, admin.ModelAdmin):
item.active = True
item.save()
item.children.all().update(active=True)
activate.short_description = "Activate selected categories and their children"
activate.short_description = _('Activate selected categories and their children')

Binary file not shown.

View file

@ -0,0 +1,40 @@
# German translation of django-categories
# Copyright (C) 2012 winniehell
# This file is distributed under the same license as django-categories.
# winniehell <git@winniehell.de>, 2012.
msgid ""
msgstr ""
"Project-Id-Version: django-categories 1.1.4\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-09-02 12:39+0200\n"
"PO-Revision-Date: 2012-09-01 02:38+0200\n"
"Last-Translator: winniehell <git@winniehell.de>\n"
"Language-Team: winniehell <git@winniehell.de>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
#: tree_editor.py:167
msgid "Database error"
msgstr ""
#: tree_editor.py:206
#, python-format
msgid "%(count)s %(name)s was changed successfully."
msgid_plural "%(count)s %(name)s were changed successfully."
msgstr[0] ""
msgstr[1] ""
#: tree_editor.py:247
#, python-format
msgid "%(total_count)s selected"
msgid_plural "All %(total_count)s selected"
msgstr[0] ""
msgstr[1] ""
#: tree_editor.py:252
#, python-format
msgid "0 of %(cnt)s selected"
msgstr ""

View file

@ -8,10 +8,19 @@
<table cellspacing="0" id="result_list">
<thead>
<tr>
{% for header in result_headers %}<th{{ header.class_attrib }}>
{% if header.sortable %}<a href="{{ header.url }}">{% endif %}
{{ header.text|capfirst }}
{% if header.sortable %}</a>{% endif %}</th>{% endfor %}
{% for header in result_headers %}<th scope="col" {{ header.class_attrib }}>
{% if header.sortable %}
{% if header.sort_priority > 0 %}
<div class="sortoptions">
<a class="sortremove" href="{{ header.url_remove }}" title="{% trans "Remove from sorting" %}"></a>
{% if num_sorted_fields > 1 %}<span class="sortpriority" title="{% blocktrans with priority_number=header.sort_priority %}Sorting priority: {{ priority_number }}{% endblocktrans %}">{{ header.sort_priority }}</span>{% endif %}
<a href="{{ header.url_toggle }}" class="toggle {% if header.ascending %}ascending{% else %}descending{% endif %}" title="{% trans "Toggle sorting" %}"></a>
</div>
{% endif %}
{% endif %}
<div class="text">{% if header.sortable %}<a href="{{ header.url_primary }}">{{ header.text|capfirst }}</a>{% else %}<span>{{ header.text|capfirst }}</span>{% endif %}</div>
<div class="clear"></div>
</th>{% endfor %}
</tr>
</thead>
<tbody>

Binary file not shown.

View file

@ -0,0 +1,154 @@
# German translation of django-categories
# Copyright (C) 2012 winniehell
# This file is distributed under the same license as django-categories.
# winniehell <git@winniehell.de>, 2012.
msgid ""
msgstr ""
"Project-Id-Version: django-categories 1.1.4\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-09-02 15:59+0200\n"
"PO-Revision-Date: 2012-09-01 02:38+0200\n"
"Last-Translator: winniehell <git@winniehell.de>\n"
"Language-Team: winniehell <git@winniehell.de>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
#: admin.py:50
msgid "Meta Data"
msgstr "Meta-Daten"
#: admin.py:55
msgid "Advanced"
msgstr "Erweitert"
#: base.py:41
msgid "parent"
msgstr "oberkategorie"
#: base.py:42
msgid "name"
msgstr "name"
#: base.py:43
msgid "slug"
msgstr "slug"
#: base.py:44
msgid "active"
msgstr "aktiv"
#: base.py:99
msgid "The slug must be unique among the items at its level."
msgstr "Der Slug muss eindeutig innerhalb der Kategorien einer Ebene sein."
#: base.py:109
msgid "You can't set the parent of the item to itself."
msgstr "Eine Kategorie kann nicht Oberkategorie von sich selbst sein."
#: base.py:112
msgid "You can't set the parent of the item to a descendant."
msgstr "Die Oberkategorie kann keine untergeordnete Kategorie sein."
#: base.py:143
msgid "Deactivate selected categories and their children"
msgstr "Markierte Kategorien und ihre Unterkategorien deaktivieren"
#: base.py:156
msgid "Activate selected categories and their children"
msgstr "Markierte Kategorien und ihre Unterkategorien aktivieren"
#: migration.py:21 migration.py:91
msgid "%(dependency) must be installed for this command to work"
msgstr ""
"%(dependency) muss installiert sein, damit dieses Kommando funktioniert"
#: migration.py:43
msgid "Added ForeignKey %(field_name) to %(model_name)"
msgstr "Fremdschlüssel %(field_name) zu %(model_name) hinzugefügt"
#: migration.py:49
msgid "ForeignKey %(field_name) to %(model_name) already exists"
msgstr "Fremdschlüssel %(field_name) zu %(model_name) existiert bereits"
#: migration.py:66
msgid "Added Many2Many table between %(model_name) and %(category_table)"
msgstr "M:N-Beziehung zwischen %(model_name) und %(category_table) hinzugefügt"
#: migration.py:72
msgid ""
"Many2Many table between %(model_name) and %(category_table) already exists"
msgstr ""
"M:N-Beziehung zwischen %(model_name) und %(category_table) existiert bereits"
#: migration.py:98
msgid "Dropping ForeignKey %(field_name) from %(model_name)"
msgstr "Entferne Fremdschlüssel %(field_name) zu %(model_name)"
#: migration.py:109
msgid "Dropping Many2Many table between %(model_name) and %(category_table)"
msgstr "Entferne M:N-Beziehung zwischen %(model_name) und %(category_table)"
#: models.py:91 models.py:122
msgid "category"
msgstr "kategorie"
#: models.py:92
msgid "categories"
msgstr "kategorien"
#: models.py:124
msgid "content type"
msgstr "content type"
#: models.py:125
msgid "object id"
msgstr "objekt-ID"
#: models.py:127
msgid "relation type"
msgstr "relationstyp"
#: models.py:131
msgid "A generic text field to tag a relation, like 'leadphoto'."
msgstr ""
"Ein generisches Textfeld um eine Relation zu bezeichnen, z.B. 'leadphoto'"
#: registration.py:45
#, python-format
msgid "%(key) is not a model"
msgstr "%(key) ist kein Model"
#: registration.py:54 registration.py:62
msgid "%(settings) doesn't recognize the value of %(key)"
msgstr "%(settings) erkennt den Wert von %(key) nicht"
#: settings.py:33
msgid "%(transliterator) must be a callable or a string."
msgstr "%(transliterator) muss callable oder string sein"
#: settings.py:39
#, python-format
msgid "%(deprecated_setting) is deprecated; use %(replacement)s instead."
msgstr ""
"%(deprecated_setting) ist veraltet und wurde durch %(replacement)s ersetzt."
#: views.py:73
msgid "Category detail view %(view) must be called with a %(path_field)."
msgstr ""
"Kategorie-Detailansicht %(view) muss ein %(path_field) übergeben bekommen."
#: views.py:80
#, python-format
msgid "No %(verbose_name)s found matching the query"
msgstr "Es wurde kein passendes Objekt für %(verbose_name)s gefunden"
#: templates/admin/edit_inline/gen_coll_tabular.html:13
msgid "Delete?"
msgstr "Löschen?"
#: templates/admin/edit_inline/gen_coll_tabular.html:24
msgid "View on site"
msgstr "Anzeigen"

View file

@ -1,5 +1,6 @@
from django.db import models, DatabaseError
from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import ugettext_lazy as _
def migrate_app(sender, app, created_models, verbosity, *args, **kwargs):
@ -17,7 +18,8 @@ def migrate_app(sender, app, created_models, verbosity, *args, **kwargs):
try:
from south.db import db
except ImportError:
raise ImproperlyConfigured("South must be installed for this command to work")
raise ImproperlyConfigured(_('%(dependency) must be installed for this command to work') %
{'dependency' : 'South'})
# pull the information from the registry
app_name = app.__name__.split('.')[-2]
@ -38,17 +40,19 @@ def migrate_app(sender, app, created_models, verbosity, *args, **kwargs):
db.add_column(table_name, field_name, FIELD_REGISTRY[fld], keep_default=False)
db.commit_transaction()
if verbosity:
print "Added ForeignKey %s to %s" % (field_name, model_name)
print (_('Added ForeignKey %(field_name) to %(model_name)') %
{'field_name' : field_name, 'model_name' : model_name})
except DatabaseError, e:
db.rollback_transaction()
if "already exists" in str(e):
if verbosity > 1:
print "ForeignKey %s to %s already exists" % (field_name, model_name)
print (_('ForeignKey %(field_name) to %(model_name) already exists') %
{'field_name' : field_name, 'model_name' : model_name})
else:
sys.stderr = org_stderror
raise e
elif isinstance(FIELD_REGISTRY[fld], CategoryM2MField):
table_name = "%s_%s" % (mdl._meta.db_table, 'categories')
table_name = '%s_%s' % (mdl._meta.db_table, 'categories')
try:
db.start_transaction()
db.create_table(table_name, (
@ -59,12 +63,14 @@ def migrate_app(sender, app, created_models, verbosity, *args, **kwargs):
db.create_unique(table_name, ['%s_id' % model_name, 'category_id'])
db.commit_transaction()
if verbosity:
print "Added Many2Many table between %s and %s" % (model_name, 'category')
print (_('Added Many2Many table between %(model_name) and %(category_table)') %
{'model_name' : model_name, 'category_table' : 'category'})
except DatabaseError, e:
db.rollback_transaction()
if "already exists" in str(e):
if verbosity > 1:
print "Many2Many table between %s and %s already exists" % (model_name, 'category')
print (_('Many2Many table between %(model_name) and %(category_table) already exists') %
{'model_name' : model_name, 'category_table' : 'category'})
else:
sys.stderr = org_stderror
raise e
@ -82,13 +88,15 @@ def drop_field(app_name, model_name, field_name):
try:
from south.db import db
except ImportError:
raise ImproperlyConfigured("South must be installed for this command to work")
raise ImproperlyConfigured(_('%(dependency) must be installed for this command to work') %
{'dependency' : 'South'})
mdl = models.get_model(app_name, model_name)
fld = "%s.%s.%s" % (app_name, model_name, field_name)
fld = '%s.%s.%s' % (app_name, model_name, field_name)
if isinstance(FIELD_REGISTRY[fld], CategoryFKField):
print "Dropping ForeignKey %s from %s" % (field_name, model_name)
print (_('Dropping ForeignKey %(field_name) from %(model_name)') %
{'field_name' : field_name, 'model_name' : model_name})
try:
db.start_transaction()
table_name = mdl._meta.db_table
@ -98,8 +106,8 @@ def drop_field(app_name, model_name, field_name):
db.rollback_transaction()
raise e
elif isinstance(FIELD_REGISTRY[fld], CategoryM2MField):
print "Dropping Many2Many table between %s and %s" % (model_name, 'category')
table_name = "%s_%s" % (mdl._meta.db_table, 'categories')
print (_('Dropping Many2Many table between %(model_name) and %(category_table)') %
{'model_name' : model_name, 'category_table' : 'category'})
try:
db.start_transaction()
db.delete_table(table_name, cascade=False)

View file

@ -88,7 +88,8 @@ class Category(CategoryBase):
super(Category, self).save(*args, **kwargs)
class Meta(CategoryBase.Meta):
verbose_name_plural = 'categories'
verbose_name = _('category')
verbose_name_plural = _('categories')
class MPTTMeta:
order_insertion_by = ('order', 'name')
@ -118,12 +119,12 @@ class CategoryRelationManager(models.Manager):
class CategoryRelation(models.Model):
"""Related category item"""
category = models.ForeignKey(Category)
category = models.ForeignKey(Category, verbose_name=_('category'))
content_type = models.ForeignKey(
ContentType, limit_choices_to=CATEGORY_RELATION_LIMITS)
object_id = models.PositiveIntegerField()
ContentType, limit_choices_to=CATEGORY_RELATION_LIMITS, verbose_name=_('content type'))
object_id = models.PositiveIntegerField(verbose_name=_('object id'))
content_object = generic.GenericForeignKey('content_type', 'object_id')
relation_type = models.CharField(_("Relation Type"),
relation_type = models.CharField(verbose_name=_('relation type'),
max_length="200",
blank=True,
null=True,

View file

@ -42,7 +42,7 @@ def _process_registry(registry, call_func):
for key, value in registry.items():
model = get_model(*key.split('.'))
if model is None:
raise ImproperlyConfigured('%s is not a model' % key)
raise ImproperlyConfigured(_('%(key) is not a model') % {'key' : key})
if isinstance(value, (tuple, list)):
for item in value:
if isinstance(item, basestring):
@ -51,11 +51,13 @@ def _process_registry(registry, call_func):
field_name = item.pop('name')
call_func(model, field_name, extra_params=item)
else:
raise ImproperlyConfigured("CATEGORY_SETTINGS doesn't recognize the value of %s" % key)
raise ImproperlyConfigured(_("%(settings) doesn't recognize the value of %(key)") %
{'settings' : 'CATEGORY_SETTINGS', 'key' : key})
elif isinstance(value, basestring):
call_func(model, value)
elif isinstance(value, dict):
field_name = value.pop('name')
call_func(model, field_name, extra_params=value)
else:
raise ImproperlyConfigured("CATEGORY_SETTINGS doesn't recognize the value of %s" % key)
raise ImproperlyConfigured(_("%(settings) doesn't recognize the value of %(key)") %
{'settings' : 'CATEGORY_SETTINGS', 'key' : key})

View file

@ -1,11 +1,9 @@
import warnings
from django.conf import settings
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
DEFAULT_SETTINGS = {
'ALLOW_SLUG_CHANGE': False,
'CACHE_VIEW_LENGTH': 600,
'RELATION_MODELS': [],
'M2M_REGISTRY': {},
'FK_REGISTRY': {},
@ -29,27 +27,11 @@ if DEFAULT_SETTINGS['SLUG_TRANSLITERATOR']:
DEFAULT_SETTINGS['SLUG_TRANSLITERATOR'] = getattr(module, bits[-1])
else:
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured("SLUG_TRANSLITERATOR must be a callable or a string.")
raise ImproperlyConfigured(_('%(transliterator) must be a callable or a string.') %
{'transliterator': 'SLUG_TRANSLITERATOR'})
else:
DEFAULT_SETTINGS['SLUG_TRANSLITERATOR'] = lambda x: x
ERR_MSG = "settings.%s is deprecated; use settings.CATEGORIES_SETTINGS instead."
if hasattr(settings, 'CATEGORIES_ALLOW_SLUG_CHANGE'):
warnings.warn(ERR_MSG % 'CATEGORIES_ALLOW_SLUG_CHANGE', DeprecationWarning)
DEFAULT_SETTINGS["ALLOW_SLUG_CHANGE"] = getattr(settings, 'CATEGORIES_ALLOW_SLUG_CHANGE')
if hasattr(settings, 'CATEGORIES_CACHE_VIEW_LENGTH'):
warnings.warn(ERR_MSG % "CATEGORIES_CACHE_VIEW_LENGTH", DeprecationWarning)
DEFAULT_SETTINGS["CACHE_VIEW_LENGTH"] = getattr(settings, 'CATEGORIES_CACHE_VIEW_LENGTH')
if hasattr(settings, 'CATEGORIES_THUMBNAIL_UPLOAD_PATH'):
warnings.warn(ERR_MSG % "CATEGORIES_THUMBNAIL_UPLOAD_PATH", DeprecationWarning)
DEFAULT_SETTINGS["THUMBNAIL_UPLOAD_PATH"] = getattr(settings, 'CATEGORIES_THUMBNAIL_UPLOAD_PATH')
if hasattr(settings, 'CATEGORIES_RELATION_MODELS'):
warnings.warn(ERR_MSG % "CATEGORIES_RELATION_MODELS", DeprecationWarning)
DEFAULT_SETTINGS["RELATION_MODELS"] = getattr(settings, 'CATEGORIES_RELATION_MODELS')
# Add all the keys/values to the module's namespace
globals().update(DEFAULT_SETTINGS)

View file

@ -1,5 +1,5 @@
{% load category_tags %}{% spaceless %}
<ul><li><a href="{% url categories_tree_list %}">Top</a>
<ul><li><a href="{% url 'categories_tree_list' %}">Top</a>
{% for node,structure in path|tree_info %}
{% if structure.new_level %}<ul><li>
{% else %}</li><li>

View file

@ -1,13 +1,23 @@
from django.conf.urls.defaults import *
from django.conf.urls import patterns, url
from .models import Category
try:
from django.views.generic import DetailView, ListView
except ImportError:
try:
from cbv import DetailView, ListView
except ImportError:
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured("For older versions of Django, you need django-cbv.")
categorytree_dict = {
'queryset': Category.objects.filter(level=0)
}
urlpatterns = patterns('django.views.generic.list_detail',
urlpatterns = patterns('',
url(
r'^$', 'object_list', categorytree_dict, name='categories_tree_list'
r'^$', ListView.as_view(**categorytree_dict), name='categories_tree_list'
),
)

View file

@ -1,17 +1,22 @@
import django
from django.shortcuts import get_object_or_404
from django.core.exceptions import ObjectDoesNotExist
from django.template import RequestContext
from django.http import HttpResponse, Http404
from django.views.decorators.cache import cache_page
from django.template.loader import select_template
from django.utils.translation import ugettext_lazy as _
try:
from django.views.generic import DetailView, ListView
except ImportError:
try:
from cbv import DetailView, ListView
except ImportError:
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured("For older versions of Django, you need django-cbv.")
from .models import Category
from .settings import CACHE_VIEW_LENGTH
@cache_page(CACHE_VIEW_LENGTH)
def category_detail(request, path,
template_name='categories/category_detail.html', extra_context={}):
path_items = path.strip('/').split('/')
@ -51,59 +56,78 @@ def get_category_for_path(path, queryset=Category.objects.all()):
level=len(path_items) - 1)
return queryset.get()
try:
import cbv
HAS_CBV = True
except ImportError:
HAS_CBV = False
if ((django.VERSION[0] >= 1 and django.VERSION[1] >= 3) or HAS_CBV):
if HAS_CBV:
from cbv import DetailView, ListView
else:
from django.views.generic import DetailView, ListView
class CategoryDetailView(DetailView):
model = Category
path_field = 'path'
class CategoryDetailView(DetailView):
def get_object(self, **kwargs):
if self.path_field not in self.kwargs:
raise AttributeError(u"Category detail view %s must be called with "
u"a %s." % self.__class__.__name__, self.path_field)
if self.queryset is None:
queryset = self.get_queryset()
try:
return get_category_for_path(self.kwargs[self.path_field])
except ObjectDoesNotExist:
raise Http404(_(u"No %(verbose_name)s found matching the query") %
{'verbose_name': queryset.model._meta.verbose_name})
model = Category
path_field = 'path'
def get_template_names(self):
names = []
path_items = self.kwargs[self.path_field].strip('/').split('/')
while path_items:
names.append('categories/%s.html' % '_'.join(path_items))
path_items.pop()
names.extend(super(CategoryDetailView, self).get_template_names())
return names
def get_object(self, **kwargs):
if self.path_field not in self.kwargs:
raise AttributeError(u"Category detail view %s must be called with "
u"a %s." % self.__class__.__name__, self.path_field)
if self.queryset is None:
queryset = self.get_queryset()
try:
return get_category_for_path(self.kwargs[self.path_field])
except ObjectDoesNotExist:
raise Http404(_(u"No %(verbose_name)s found matching the query") %
{'verbose_name': queryset.model._meta.verbose_name})
def get_template_names(self):
names = []
class CategoryRelatedDetail(DetailView):
path_field = 'category_path'
object_name_field = None
def get_object(self, **kwargs):
queryset = super(CategoryRelatedDetail, self).get_queryset()
category = get_category_for_path(self.kwargs[self.path_field])
return queryset.get(category=category, slug=self.kwargs[self.slug_field])
def get_template_names(self):
names = []
opts = self.object._meta
path_items = self.kwargs[self.path_field].strip('/').split('/')
if self.object_name_field:
path_items.append(getattr(self.object, self.object_name_field))
while path_items:
names.append('%s/category_%s_%s%s.html' % (
opts.app_label,
'_'.join(path_items),
opts.object_name.lower(),
self.template_name_suffix)
)
path_items.pop()
names.append('%s/category_%s%s.html' % (
opts.app_label,
opts.object_name.lower(),
self.template_name_suffix)
)
names.extend(super(CategoryRelatedDetail, self).get_template_names())
return names
class CategoryRelatedList(ListView):
path_field = 'category_path'
def get_queryset(self):
queryset = super(CategoryRelatedList, self).get_queryset()
category = get_category_for_path(self.kwargs['category_path'])
return queryset.filter(category=category)
def get_template_names(self):
names = []
if hasattr(self.object_list, 'model'):
opts = self.object_list.model._meta
path_items = self.kwargs[self.path_field].strip('/').split('/')
while path_items:
names.append('categories/%s.html' % '_'.join(path_items))
path_items.pop()
names.extend(super(CategoryDetailView, self).get_template_names())
return names
class CategoryRelatedDetail(DetailView):
path_field = 'category_path'
object_name_field = None
def get_object(self, **kwargs):
queryset = super(CategoryRelatedDetail, self).get_queryset()
category = get_category_for_path(self.kwargs[self.path_field])
return queryset.get(category=category, slug=self.kwargs[self.slug_field])
def get_template_names(self):
names = []
opts = self.object._meta
path_items = self.kwargs[self.path_field].strip('/').split('/')
if self.object_name_field:
path_items.append(getattr(self.object, self.object_name_field))
while path_items:
names.append('%s/category_%s_%s%s.html' % (
opts.app_label,
@ -117,34 +141,5 @@ if ((django.VERSION[0] >= 1 and django.VERSION[1] >= 3) or HAS_CBV):
opts.object_name.lower(),
self.template_name_suffix)
)
names.extend(super(CategoryRelatedDetail, self).get_template_names())
return names
class CategoryRelatedList(ListView):
path_field = 'category_path'
def get_queryset(self):
queryset = super(CategoryRelatedList, self).get_queryset()
category = get_category_for_path(self.kwargs['category_path'])
return queryset.filter(category=category)
def get_template_names(self):
names = []
if hasattr(self.object_list, 'model'):
opts = self.object_list.model._meta
path_items = self.kwargs[self.path_field].strip('/').split('/')
while path_items:
names.append('%s/category_%s_%s%s.html' % (
opts.app_label,
'_'.join(path_items),
opts.object_name.lower(),
self.template_name_suffix)
)
path_items.pop()
names.append('%s/category_%s%s.html' % (
opts.app_label,
opts.object_name.lower(),
self.template_name_suffix)
)
names.extend(super(CategoryRelatedList, self).get_template_names())
return names
names.extend(super(CategoryRelatedList, self).get_template_names())
return names

View file

@ -0,0 +1,49 @@
.. _admin_settings:
==============================
Adding the fields to the Admin
==============================
By default, Django Categories adds the fields you configure to the model's Admin class. If your ModelAdmin class does not use the ``fieldsets`` attribute, the configured category fields are simply appended to the bottom the fields. If your ModelAdmin uses the ``fieldsets`` attribute, a new fieldset named ``Categories``, containing all the configured fields is appended to the fieldsets. You can override or alter this behavior with the :ref:`ADMIN_FIELDSETS` setting.
ADMIN_FIELDSETS allows you to:
* Prevent Django Categories from adding the fields or fieldsets to a model's ModelAdmin class.
* Change the name of the fieldset (from the default: "Categories")
* Change the placement of the fieldset (from the default: bottom)
Preventing fields in the admin class
====================================
If you don't want Django Categories to add any fields to the admin class, simply use the following format::
CATEGORIES_SETTINGS = {
'ADMIN_FIELDSETS': [
'app.model': None,
]
}
Changing the name of the field set
==================================
To rename the field set, use the following format::
CATEGORIES_SETTINGS = {
'ADMIN_FIELDSETS': [
'app.model': 'Taxonomy',
]
}
Putting the field set exactly where you want it
===============================================
For complete control over the field set, use the following format::
CATEGORIES_SETTINGS = {
'ADMIN_FIELDSETS': [
'app.model': {
'name': 'Categories',
'index': 0
},
]
}

View file

@ -31,6 +31,7 @@ Contents
usage
registering_models
adding_the_fields
admin_settings
custom_categories
reference/index

View file

@ -13,7 +13,7 @@ The ``CATEGORIES_SETTINGS`` dictionary is where you can override the default set
The default settings are:
.. code-block:: python
CATEGORIES_SETTINGS = {
'ALLOW_SLUG_CHANGE': False,
'CACHE_VIEW_LENGTH': 0,
@ -23,6 +23,7 @@ The default settings are:
'THUMBNAIL_UPLOAD_PATH': 'uploads/categories/thumbnails',
'THUMBNAIL_STORAGE': settings.DEFAULT_FILE_STORAGE,
'SLUG_TRANSLITERATOR': lambda x: x,
'ADMIN_FIELDSETS': {}
}
@ -119,3 +120,12 @@ JAVASCRIPT_URL
**Default:** ``STATIC_URL or MEDIA_URL + 'js/'``
**Description:** Allows for customization of javascript placement.
.. _ADMIN_FIELDSETS:
ADMIN_FIELDSETS
===============
**Default:** ``{}``
**Description:** Allows for selective customization of the default behavior of adding the fields to the admin class. See :ref:`admin_settings` for more information.

View file

@ -123,7 +123,7 @@ CATEGORIES_SETTINGS = {
},
}
if django.VERSION[1] == 4:
if django.VERSION[1] >= 4:
from settings14 import *
if django.VERSION[1] == 3:
from settings13 import *

View file

@ -3,9 +3,13 @@ from django.contrib import admin
from categories.admin import CategoryBaseAdmin, CategoryBaseAdminForm
# class SimpleTextAdmin(admin.ModelAdmin):
# filter_horizontal = ['cats',]
#
class SimpleTextAdmin(admin.ModelAdmin):
fieldsets = (
(None, {
'fields': ('name', 'description', )
}),
)
class SimpleCategoryAdminForm(CategoryBaseAdminForm):
class Meta:
@ -13,6 +17,6 @@ class SimpleCategoryAdminForm(CategoryBaseAdminForm):
class SimpleCategoryAdmin(CategoryBaseAdmin):
form = SimpleCategoryAdminForm
admin.site.register(SimpleText) #, SimpleTextAdmin)
admin.site.register(SimpleText, SimpleTextAdmin)
admin.site.register(SimpleCategory, SimpleCategoryAdmin)

View file

@ -1,4 +1,4 @@
from django.conf.urls.defaults import *
from django.conf.urls import patterns, include
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
@ -13,7 +13,7 @@ urlpatterns = patterns('',
# Example:
# (r'^sample/', include('sample.foo.urls')),
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
# to INSTALLED_APPS to enable admin documentation:
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
@ -32,4 +32,4 @@ urlpatterns = patterns('',
(r'^static/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': os.path.join(ROOT_PATH, 'example', 'static')}),
)
)