mirror of
https://github.com/jazzband/django-categories.git
synced 2026-03-16 22:30:24 +00:00
Made a bunch of flake8 fixes and also added flake8 testing to Travis and Tox
This commit is contained in:
parent
75b1496d90
commit
64686cdaa8
37 changed files with 162 additions and 148 deletions
|
|
@ -5,6 +5,7 @@ branches:
|
|||
- master
|
||||
|
||||
env:
|
||||
- TOXENV=py27-lint
|
||||
- TOXENV=py27-django18
|
||||
- TOXENV=py27-django19
|
||||
- TOXENV=py34-django18
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ __version__ = get_version()
|
|||
|
||||
default_app_config = 'categories.apps.CategoriesConfig'
|
||||
|
||||
|
||||
def register():
|
||||
from categories import settings
|
||||
from categories.registration import (_process_registry, registry)
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ class NullTreeNodeChoiceField(forms.ModelChoiceField):
|
|||
Creates labels which represent the tree level of each node when
|
||||
generating option labels.
|
||||
"""
|
||||
return '%s %s' % (self.level_indicator * getattr(
|
||||
obj, obj._mptt_meta.level_attr), obj)
|
||||
return '%s %s' % (self.level_indicator * getattr(obj, obj._mptt_meta.level_attr), obj)
|
||||
|
||||
if RELATION_MODELS:
|
||||
from .models import CategoryRelation
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ class CategoryAdmin(CategoryBaseAdmin):
|
|||
}),
|
||||
(_('Meta Data'), {
|
||||
'fields': ('alternate_title', 'alternate_url', 'description',
|
||||
'meta_keywords', 'meta_extra'),
|
||||
'meta_keywords', 'meta_extra'),
|
||||
'classes': ('collapse',),
|
||||
}),
|
||||
(_('Advanced'), {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ class CategoryBase(MPTTModel):
|
|||
This base model includes the absolute bare bones fields and methods. One
|
||||
could simply subclass this model and do nothing else and it should work.
|
||||
"""
|
||||
parent = TreeForeignKey('self',
|
||||
parent = TreeForeignKey(
|
||||
'self',
|
||||
blank=True,
|
||||
null=True,
|
||||
related_name='children',
|
||||
|
|
@ -99,9 +100,7 @@ class CategoryBaseAdminForm(forms.ModelForm):
|
|||
kwargs['parent__isnull'] = True
|
||||
else:
|
||||
kwargs['parent__pk'] = int(self.cleaned_data['parent'].id)
|
||||
this_level_slugs = [c['slug'] for c in opts.model.objects.filter(
|
||||
**kwargs).values('id', 'slug'
|
||||
) if c['id'] != self.instance.id]
|
||||
this_level_slugs = [c['slug'] for c in opts.model.objects.filter(**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.'))
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
# Placeholder for Django
|
||||
# Placeholder for Django
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import django
|
|||
DJANGO10_COMPAT = django.VERSION[0] < 1 or (django.VERSION[0] == 1 and django.VERSION[1] < 1)
|
||||
|
||||
STATIC_URL = getattr(settings, 'STATIC_URL', settings.MEDIA_URL)
|
||||
if STATIC_URL == None:
|
||||
if STATIC_URL is None:
|
||||
STATIC_URL = settings.MEDIA_URL
|
||||
MEDIA_PATH = getattr(settings, 'EDITOR_MEDIA_PATH', '%seditor/' % STATIC_URL)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ from django.db import models
|
|||
from django.template import Library
|
||||
from django.contrib.admin.templatetags.admin_list import result_headers, _boolean_icon
|
||||
try:
|
||||
from django.contrib.admin.utils import lookup_field, display_for_field, label_for_field
|
||||
from django.contrib.admin.utils import lookup_field, display_for_field
|
||||
except ImportError:
|
||||
from categories.editor.utils import lookup_field, display_for_field, label_for_field
|
||||
from categories.editor.utils import lookup_field, display_for_field
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils.encoding import smart_text, force_text
|
||||
from django.utils.html import escape, conditional_escape
|
||||
|
|
@ -99,10 +99,12 @@ def items_for_tree_result(cl, result, form):
|
|||
result_id = repr(force_text(value))[1:]
|
||||
first = False
|
||||
if django.VERSION[1] < 4:
|
||||
yield mark_safe('<%s%s>%s<a href="%s"%s>%s</a></%s>' % \
|
||||
yield mark_safe(
|
||||
'<%s%s>%s<a href="%s"%s>%s</a></%s>' %
|
||||
(table_tag, row_class, checkbox_value, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), conditional_escape(result_repr), table_tag))
|
||||
else:
|
||||
yield mark_safe('<%s%s><a href="%s"%s>%s</a></%s>' % \
|
||||
yield mark_safe(
|
||||
'<%s%s><a href="%s"%s>%s</a></%s>' %
|
||||
(table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), conditional_escape(result_repr), table_tag))
|
||||
|
||||
else:
|
||||
|
|
@ -151,9 +153,11 @@ def result_tree_list(cl):
|
|||
Displays the headers and data list together
|
||||
"""
|
||||
import django
|
||||
result = {'cl': cl,
|
||||
'result_headers': list(result_headers(cl)),
|
||||
'results': list(tree_results(cl))}
|
||||
result = {
|
||||
'cl': cl,
|
||||
'result_headers': list(result_headers(cl)),
|
||||
'results': list(tree_results(cl))
|
||||
}
|
||||
if django.VERSION[1] > 2:
|
||||
from django.contrib.admin.templatetags.admin_list import result_hidden_fields
|
||||
result['result_hidden_fields'] = list(result_hidden_fields(cl))
|
||||
|
|
|
|||
|
|
@ -38,8 +38,7 @@ class TreeEditorQuerySet(QuerySet):
|
|||
# will already be in include_pages when they are checked, thus not
|
||||
# trigger additional queries.
|
||||
for p in super(TreeEditorQuerySet, self.order_by('rght')).iterator():
|
||||
if p.parent_id and p.parent_id not in include_pages and \
|
||||
p.id not in include_pages:
|
||||
if p.parent_id and p.parent_id not in include_pages and p.id not in include_pages:
|
||||
ancestor_id_list = p.get_ancestors().values_list('id', flat=True)
|
||||
include_pages.update(ancestor_id_list)
|
||||
|
||||
|
|
@ -145,12 +144,14 @@ class TreeEditor(admin.ModelAdmin):
|
|||
|
||||
try:
|
||||
if django.VERSION[1] < 4:
|
||||
params = (request, self.model, list_display,
|
||||
params = (
|
||||
request, self.model, list_display,
|
||||
self.list_display_links, self.list_filter, self.date_hierarchy,
|
||||
self.search_fields, self.list_select_related,
|
||||
self.list_per_page, self.list_editable, self)
|
||||
else:
|
||||
params = (request, self.model, list_display,
|
||||
params = (
|
||||
request, self.model, list_display,
|
||||
self.list_display_links, self.list_filter, self.date_hierarchy,
|
||||
self.search_fields, self.list_select_related,
|
||||
self.list_per_page, self.list_max_show_all,
|
||||
|
|
@ -244,8 +245,7 @@ class TreeEditor(admin.ModelAdmin):
|
|||
if django.VERSION[1] < 4:
|
||||
context['root_path'] = self.admin_site.root_path
|
||||
else:
|
||||
selection_note_all = ungettext('%(total_count)s selected',
|
||||
'All %(total_count)s selected', cl.result_count)
|
||||
selection_note_all = ungettext('%(total_count)s selected', 'All %(total_count)s selected', cl.result_count)
|
||||
|
||||
context.update({
|
||||
'module_name': force_text(opts.verbose_name_plural),
|
||||
|
|
|
|||
|
|
@ -24,8 +24,7 @@ def lookup_field(name, obj, model_admin=None):
|
|||
if isinstance(name, collections.Callable):
|
||||
attr = name
|
||||
value = attr(obj)
|
||||
elif (model_admin is not None and hasattr(model_admin, name) and
|
||||
not name == '__str__' and not name == '__unicode__'):
|
||||
elif (model_admin is not None and hasattr(model_admin, name) and not name == '__str__' and not name == '__unicode__'):
|
||||
attr = getattr(model_admin, name)
|
||||
value = attr(obj)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from django.contrib import admin
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
|
||||
class GenericCollectionInlineModelAdmin(admin.options.InlineModelAdmin):
|
||||
ct_field = "content_type"
|
||||
ct_fk_field = "object_id"
|
||||
|
|
@ -17,6 +18,7 @@ class GenericCollectionInlineModelAdmin(admin.options.InlineModelAdmin):
|
|||
result.ct_fk_field = self.ct_fk_field
|
||||
return result
|
||||
|
||||
|
||||
class GenericCollectionTabularInline(GenericCollectionInlineModelAdmin):
|
||||
template = 'admin/edit_inline/gen_coll_tabular.html'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
try:
|
||||
from south.db import db
|
||||
from django.db.models.signals import post_syncdb
|
||||
from categories.migration import migrate_app
|
||||
from south.db import db # noqa
|
||||
from django.db.models.signals import post_syncdb # noqa
|
||||
from categories.migration import migrate_app # noqa
|
||||
|
||||
post_syncdb.connect(migrate_app)
|
||||
except ImportError:
|
||||
|
|
|
|||
|
|
@ -14,11 +14,6 @@ class Command(BaseCommand):
|
|||
"""
|
||||
Alter the tables
|
||||
"""
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
try:
|
||||
from south.db import db
|
||||
except ImportError:
|
||||
raise ImproperlyConfigured("South must be installed for this command to work")
|
||||
|
||||
from categories.migration import migrate_app
|
||||
from categories.settings import MODEL_REGISTRY
|
||||
|
|
|
|||
|
|
@ -14,10 +14,6 @@ class Command(BaseCommand):
|
|||
"""
|
||||
Alter the tables
|
||||
"""
|
||||
try:
|
||||
from south.db import db
|
||||
except ImportError:
|
||||
raise ImproperlyConfigured("South must be installed for this command to work")
|
||||
|
||||
from categories.migration import drop_field
|
||||
if len(args) != 3:
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class Command(BaseCommand):
|
|||
cat = Category(
|
||||
name=string.strip(),
|
||||
slug=slugify(SLUG_TRANSLITERATOR(string.strip()))[:49],
|
||||
#parent=parent,
|
||||
# arent=parent,
|
||||
order=order
|
||||
)
|
||||
cat._tree_manager.insert_node(cat, parent, 'last-child', True)
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ from django.core.files.storage import get_storage_class
|
|||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .settings import (RELATION_MODELS, RELATIONS, THUMBNAIL_UPLOAD_PATH,
|
||||
THUMBNAIL_STORAGE)
|
||||
from .settings import (RELATION_MODELS, RELATIONS, THUMBNAIL_UPLOAD_PATH, THUMBNAIL_STORAGE)
|
||||
|
||||
from .base import CategoryBase
|
||||
|
||||
|
|
@ -133,7 +132,8 @@ class CategoryRelation(models.Model):
|
|||
ContentType, limit_choices_to=CATEGORY_RELATION_LIMITS, verbose_name=_('content type'))
|
||||
object_id = models.PositiveIntegerField(verbose_name=_('object id'))
|
||||
content_object = GenericForeignKey('content_type', 'object_id')
|
||||
relation_type = models.CharField(verbose_name=_('relation type'),
|
||||
relation_type = models.CharField(
|
||||
verbose_name=_('relation type'),
|
||||
max_length=200,
|
||||
blank=True,
|
||||
null=True,
|
||||
|
|
@ -145,7 +145,7 @@ class CategoryRelation(models.Model):
|
|||
return "CategoryRelation"
|
||||
|
||||
try:
|
||||
from south.db import db # South is required for migrating. Need to check for it
|
||||
from south.db import db # noqa, South is required for migrating. Need to check for it
|
||||
from django.db.models.signals import post_syncdb
|
||||
from categories.migration import migrate_app
|
||||
post_syncdb.connect(migrate_app)
|
||||
|
|
|
|||
|
|
@ -35,8 +35,7 @@ class Registry(object):
|
|||
if isinstance(field_definitions, str):
|
||||
field_definitions = [field_definitions]
|
||||
elif not isinstance(field_definitions, collections.Iterable):
|
||||
raise ImproperlyConfigured(_('Field configuration for %(app)s should '
|
||||
'be a string or iterable') % {'app': app})
|
||||
raise ImproperlyConfigured(_('Field configuration for %(app)s should be a string or iterable') % {'app': app})
|
||||
|
||||
if field_type not in ('ForeignKey', 'ManyToManyField'):
|
||||
raise ImproperlyConfigured(_('`field_type` must be either `"ForeignKey"` or `"ManyToManyField"`.'))
|
||||
|
|
@ -73,8 +72,8 @@ class Registry(object):
|
|||
continue
|
||||
extra_params.update(fld)
|
||||
else:
|
||||
raise ImproperlyConfigured(_("%(settings)s doesn't recognize the "
|
||||
"value of %(app)s.%(model)s") % {
|
||||
raise ImproperlyConfigured(
|
||||
_("%(settings)s doesn't recognize the value of %(app)s.%(model)s") % {
|
||||
'settings': 'CATEGORY_SETTINGS',
|
||||
'app': app,
|
||||
'model': model_name})
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import collections
|
|||
|
||||
DEFAULT_SETTINGS = {
|
||||
'ALLOW_SLUG_CHANGE': False,
|
||||
'RELATION_MODELS': [],
|
||||
'M2M_REGISTRY': {},
|
||||
'FK_REGISTRY': {},
|
||||
'THUMBNAIL_UPLOAD_PATH': 'uploads/categories/thumbnails',
|
||||
|
|
@ -37,7 +36,7 @@ else:
|
|||
# Add all the keys/values to the module's namespace
|
||||
globals().update(DEFAULT_SETTINGS)
|
||||
|
||||
RELATIONS = [Q(app_label=al, model=m) for al, m in [x.split('.') for x in RELATION_MODELS]]
|
||||
RELATIONS = [Q(app_label=al, model=m) for al, m in [x.split('.') for x in DEFAULT_SETTINGS['RELATION_MODELS']]]
|
||||
|
||||
# The field registry keeps track of the individual fields created.
|
||||
# {'app.model.field': Field(**extra_params)}
|
||||
|
|
|
|||
|
|
@ -54,8 +54,7 @@ def get_category(category_string, model=Category):
|
|||
if len(cat_list) == 0:
|
||||
return None
|
||||
try:
|
||||
categories = model_class.objects.filter(name=cat_list[-1],
|
||||
level=len(cat_list) - 1)
|
||||
categories = model_class.objects.filter(name=cat_list[-1], level=len(cat_list) - 1)
|
||||
if len(cat_list) == 1 and len(categories) > 1:
|
||||
return None
|
||||
# If there is only one, use it. If there is more than one, check
|
||||
|
|
@ -265,8 +264,7 @@ def get_top_level_categories(parser, token):
|
|||
return TopLevelCategoriesNode(varname, model)
|
||||
|
||||
|
||||
def get_latest_objects_by_category(category, app_label, model_name, set_name,
|
||||
date_field='pub_date', num=15):
|
||||
def get_latest_objects_by_category(category, app_label, model_name, set_name, date_field='pub_date', num=15):
|
||||
m = apps.get_model(app_label, model_name)
|
||||
if not isinstance(category, CategoryBase):
|
||||
category = Category.objects.get(slug=str(category))
|
||||
|
|
@ -304,8 +302,7 @@ class LatestObjectsNode(Node):
|
|||
date_field = resolve(self.date_field, context)
|
||||
num = resolve(self.num, context)
|
||||
|
||||
result = get_latest_objects_by_category(category, app_label, model_name,
|
||||
set_name, date_field, num)
|
||||
result = get_latest_objects_by_category(category, app_label, model_name, set_name, date_field, num)
|
||||
context[self.var_name] = result
|
||||
|
||||
return ''
|
||||
|
|
@ -339,8 +336,7 @@ def do_get_latest_objects_by_category(parser, token):
|
|||
num = FilterExpression(bits[6], parser)
|
||||
else:
|
||||
num = FilterExpression(None, parser)
|
||||
return LatestObjectsNode(var_name, category, app_label, model_name, set_name,
|
||||
date_field, num)
|
||||
return LatestObjectsNode(var_name, category, app_label, model_name, set_name, date_field, num)
|
||||
|
||||
register.tag("get_latest_objects_by_category", do_get_latest_objects_by_category)
|
||||
|
||||
|
|
@ -369,8 +365,7 @@ def tree_queryset(value):
|
|||
# will already be in include_pages when they are checked, thus not
|
||||
# trigger additional queries.
|
||||
for p in qs2.order_by('rght').iterator():
|
||||
if p.parent_id and p.parent_id not in include_pages and \
|
||||
p.id not in include_pages:
|
||||
if p.parent_id and p.parent_id not in include_pages and p.id not in include_pages:
|
||||
ancestor_id_list = p.get_ancestors().values_list('id', flat=True)
|
||||
include_pages.update(ancestor_id_list)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@ from categories.models import Category
|
|||
from categories.management.commands.import_categories import Command
|
||||
from django.core.management.base import CommandError
|
||||
|
||||
from categories.registration import _process_registry, registry
|
||||
|
||||
|
||||
@override_settings(INSTALLED_APPS=(app for app in settings.INSTALLED_APPS if app != 'django.contrib.flatpages'))
|
||||
class CategoryImportTest(TestCase):
|
||||
|
|
|
|||
|
|
@ -54,5 +54,3 @@ class Categorym2mTest(TestCase):
|
|||
_process_registry(M2M_REGISTRY, registry.register_m2m)
|
||||
from django.contrib.flatpages.models import FlatPage
|
||||
self.assertTrue('category' in [f.name for f in FlatPage()._meta.get_fields()])
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -35,27 +35,31 @@ class CategoryTagsTest(TestCase):
|
|||
|
||||
# display_drilldown_as_ul
|
||||
expected_resp = '<ul><li><a href="/categories/">Top</a><ul><li><a href="/categories/world/">World</a><ul><li><strong>Worldbeat</strong><ul><li><a href="/categories/world/worldbeat/afrobeat/">Afrobeat</a></li></ul></li></ul></li></ul></li></ul>'
|
||||
resp = self.render_template('{% load category_tags %}'
|
||||
resp = self.render_template(
|
||||
'{% load category_tags %}'
|
||||
'{% display_drilldown_as_ul "/World/Worldbeat" "categories.category" %}')
|
||||
resp = re.sub(r'\n$', "", resp)
|
||||
self.assertEqual(resp, expected_resp)
|
||||
|
||||
# breadcrumbs
|
||||
expected_resp = '<a href="/categories/world/">World</a> > Worldbeat'
|
||||
resp = self.render_template('{% load category_tags %}'
|
||||
resp = self.render_template(
|
||||
'{% load category_tags %}'
|
||||
'{% breadcrumbs "/World/Worldbeat" " > " "categories.category" %}')
|
||||
self.assertEqual(resp, expected_resp)
|
||||
|
||||
# get_top_level_categories
|
||||
expected_resp = 'Avant-garde|Blues|Country|Easy listening|Electronic|Hip hop/Rap music|Jazz|Latin|Modern folk|Pop|Reggae|Rhythm and blues|Rock|World|'
|
||||
resp = self.render_template('{% load category_tags %}'
|
||||
resp = self.render_template(
|
||||
'{% load category_tags %}'
|
||||
'{% get_top_level_categories using "categories.category" as varname %}'
|
||||
'{% for item in varname %}{{ item }}|{% endfor %}')
|
||||
self.assertEqual(resp, expected_resp)
|
||||
|
||||
# get_category_drilldown
|
||||
expected_resp = "World|World > Worldbeat|"
|
||||
resp = self.render_template('{% load category_tags %}'
|
||||
resp = self.render_template(
|
||||
'{% load category_tags %}'
|
||||
'{% get_category_drilldown "/World" using "categories.category" as var %}'
|
||||
'{% for item in var %}{{ item }}|{% endfor %}')
|
||||
self.assertEqual(resp, expected_resp)
|
||||
|
|
@ -63,7 +67,8 @@ class CategoryTagsTest(TestCase):
|
|||
# recursetree
|
||||
expected_resp = '<ul><li>Country<ul><li>Country pop<ul><li>Urban Cowboy</li></ul></li></ul></li><li>World<ul><li>Worldbeat<ul></ul></li></ul></li></ul>'
|
||||
ctxt = {'nodes': Category.objects.filter(name__in=("Worldbeat", "Urban Cowboy"))}
|
||||
resp = self.render_template('{% load category_tags %}'
|
||||
resp = self.render_template(
|
||||
'{% load category_tags %}'
|
||||
'<ul>{% recursetree nodes|tree_queryset %}<li>{{ node.name }}'
|
||||
'{% if not node.is_leaf_node %}<ul>{{ children }}'
|
||||
'</ul>{% endif %}</li>{% endrecursetree %}</ul>', ctxt)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,8 @@
|
|||
from django.conf.urls import url
|
||||
from django.views.generic import ListView
|
||||
from .models import Category
|
||||
from . import views
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -17,16 +17,17 @@ except ImportError:
|
|||
from .models import Category
|
||||
|
||||
|
||||
def category_detail(request, path,
|
||||
template_name='categories/category_detail.html', extra_context={}):
|
||||
def category_detail(request, path, template_name='categories/category_detail.html', extra_context={}):
|
||||
path_items = path.strip('/').split('/')
|
||||
if len(path_items) >= 2:
|
||||
category = get_object_or_404(Category,
|
||||
category = get_object_or_404(
|
||||
Category,
|
||||
slug__iexact=path_items[-1],
|
||||
level=len(path_items) - 1,
|
||||
parent__slug__iexact=path_items[-2])
|
||||
else:
|
||||
category = get_object_or_404(Category,
|
||||
category = get_object_or_404(
|
||||
Category,
|
||||
slug__iexact=path_items[-1],
|
||||
level=len(path_items) - 1)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
from categories.models import CategoryBase
|
||||
|
||||
|
||||
class SimpleCategory(CategoryBase):
|
||||
"""
|
||||
A simple of catgorizing example
|
||||
"""
|
||||
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = 'simple categories'
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ from categories.admin import CategoryBaseAdmin
|
|||
|
||||
from .models import SimpleCategory
|
||||
|
||||
|
||||
class SimpleCategoryAdmin(CategoryBaseAdmin):
|
||||
pass
|
||||
|
||||
admin.site.register(SimpleCategory, SimpleCategoryAdmin)
|
||||
admin.site.register(SimpleCategory, SimpleCategoryAdmin)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
from categories import models, settings
|
||||
from categories.base import CategoryBase
|
||||
|
||||
|
||||
class Category(CategoryBase):
|
||||
thumbnail = models.FileField(
|
||||
upload_to=THUMBNAIL_UPLOAD_PATH,
|
||||
upload_to=settings.THUMBNAIL_UPLOAD_PATH,
|
||||
null=True, blank=True,
|
||||
storage=STORAGE(),)
|
||||
storage=settings.THUMBNAIL_STORAGE,)
|
||||
thumbnail_width = models.IntegerField(blank=True, null=True)
|
||||
thumbnail_height = models.IntegerField(blank=True, null=True)
|
||||
order = models.IntegerField(default=0)
|
||||
|
|
@ -12,8 +16,8 @@ class Category(CategoryBase):
|
|||
max_length=100,
|
||||
help_text="An alternative title to use on pages with this category.")
|
||||
alternate_url = models.CharField(
|
||||
blank=True,
|
||||
max_length=200,
|
||||
blank=True,
|
||||
max_length=200,
|
||||
help_text="An alternative URL to use instead of the one derived from "
|
||||
"the category hierarchy.")
|
||||
description = models.TextField(blank=True, null=True)
|
||||
|
|
@ -26,4 +30,4 @@ class Category(CategoryBase):
|
|||
blank=True,
|
||||
default="",
|
||||
help_text="(Advanced) Any additional HTML to be placed verbatim "
|
||||
"in the <head>")
|
||||
"in the <head>")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
from categories.models import Category
|
||||
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.thumbnail:
|
||||
from django.core.files.images import get_image_dimensions
|
||||
|
|
@ -8,8 +11,8 @@ def save(self, *args, **kwargs):
|
|||
width, height = get_image_dimensions(self.thumbnail.file, close=True)
|
||||
else:
|
||||
width, height = None, None
|
||||
|
||||
|
||||
self.thumbnail_width = width
|
||||
self.thumbnail_height = height
|
||||
|
||||
super(Category, self).save(*args, **kwargs)
|
||||
|
||||
super(Category, self).save(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
from categories.base import CategoryBase
|
||||
|
||||
|
||||
class Meta(CategoryBase.Meta):
|
||||
verbose_name_plural = 'categories'
|
||||
|
||||
|
||||
class MPTTMeta:
|
||||
order_insertion_by = ('order', 'name')
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
from categories.base import CategoryBaseAdminForm
|
||||
from categories.models import Category
|
||||
|
||||
|
||||
class CategoryAdminForm(CategoryBaseAdminForm):
|
||||
class Meta:
|
||||
model = Category
|
||||
|
||||
|
||||
def clean_alternate_title(self):
|
||||
if self.instance is None or not self.cleaned_data['alternate_title']:
|
||||
return self.cleaned_data['name']
|
||||
else:
|
||||
return self.cleaned_data['alternate_title']
|
||||
return self.cleaned_data['alternate_title']
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
from categories.admin import CategoryAdminForm
|
||||
from categories.base import CategoryBaseAdmin
|
||||
|
||||
|
||||
class CategoryAdmin(CategoryBaseAdmin):
|
||||
form = CategoryAdminForm
|
||||
list_display = ('name', 'alternate_title', 'active')
|
||||
|
|
@ -6,12 +10,12 @@ class CategoryAdmin(CategoryBaseAdmin):
|
|||
'fields': ('parent', 'name', 'thumbnail', 'active')
|
||||
}),
|
||||
('Meta Data', {
|
||||
'fields': ('alternate_title', 'alternate_url', 'description',
|
||||
'meta_keywords', 'meta_extra'),
|
||||
'fields': ('alternate_title', 'alternate_url', 'description',
|
||||
'meta_keywords', 'meta_extra'),
|
||||
'classes': ('collapse',),
|
||||
}),
|
||||
('Advanced', {
|
||||
'fields': ('order', 'slug'),
|
||||
'classes': ('collapse',),
|
||||
}),
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@
|
|||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
import sys
|
||||
import os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
|
|
@ -19,7 +20,7 @@ import sys, os
|
|||
sys.path.append(os.path.abspath('..'))
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings'
|
||||
|
||||
import categories
|
||||
import categories # noqa
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ templates_path = ['_templates']
|
|||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8'
|
||||
# ource_encoding = 'utf-8'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
|
@ -54,40 +55,40 @@ release = categories.get_version()
|
|||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
# anguage = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# oday = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
# oday_fmt = '%B %d, %Y'
|
||||
|
||||
# List of documents that shouldn't be included in the build.
|
||||
#unused_docs = []
|
||||
# nused_docs = []
|
||||
|
||||
# List of directories, relative to source directory, that shouldn't be searched
|
||||
# for source files.
|
||||
exclude_trees = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
# efault_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
# dd_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
# dd_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
# how_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
# odindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
|
@ -99,26 +100,26 @@ html_theme = 'default'
|
|||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
# tml_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = []
|
||||
# tml_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
# tml_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
# tml_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
# tml_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
# tml_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
|
|
@ -127,38 +128,38 @@ html_static_path = ['_static']
|
|||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
# tml_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
# tml_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
# tml_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
# tml_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_use_modindex = True
|
||||
# tml_use_modindex = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
# tml_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
# tml_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
# tml_show_sourcelink = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
# tml_use_opensearch = ''
|
||||
|
||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = ''
|
||||
# tml_file_suffix = ''
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'DjangoCategoriesdoc'
|
||||
|
|
@ -167,31 +168,30 @@ htmlhelp_basename = 'DjangoCategoriesdoc'
|
|||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
# atex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
# atex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'DjangoCategories.tex', 'Django Categories Documentation',
|
||||
'CoreyOordt', 'manual'),
|
||||
('index', 'DjangoCategories.tex', 'Django Categories Documentation', 'CoreyOordt', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
# atex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
# atex_use_parts = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
# atex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
# atex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_use_modindex = True
|
||||
# atex_use_modindex = True
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from django.contrib import admin
|
|||
|
||||
from categories.admin import CategoryBaseAdmin, CategoryBaseAdminForm
|
||||
|
||||
|
||||
class SimpleTextAdmin(admin.ModelAdmin):
|
||||
fieldsets = (
|
||||
(None, {
|
||||
|
|
@ -16,8 +17,9 @@ class SimpleCategoryAdminForm(CategoryBaseAdminForm):
|
|||
model = SimpleCategory
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class SimpleCategoryAdmin(CategoryBaseAdmin):
|
||||
form = SimpleCategoryAdminForm
|
||||
|
||||
admin.site.register(SimpleText, SimpleTextAdmin)
|
||||
admin.site.register(SimpleCategory, SimpleCategoryAdmin)
|
||||
admin.site.register(SimpleCategory, SimpleCategoryAdmin)
|
||||
|
|
|
|||
|
|
@ -2,15 +2,16 @@ from django.db import models
|
|||
|
||||
from categories.base import CategoryBase
|
||||
|
||||
|
||||
class SimpleText(models.Model):
|
||||
"""
|
||||
(SimpleText description)
|
||||
"""
|
||||
|
||||
name = models.CharField(max_length=255)
|
||||
name = models.CharField(max_length=255)
|
||||
description = models.TextField(blank=True)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
updated = models.DateTimeField(auto_now=True)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
updated = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = 'Simple Text'
|
||||
|
|
@ -27,13 +28,14 @@ class SimpleText(models.Model):
|
|||
def get_absolute_url(self):
|
||||
return ('simpletext_detail_view_name', [str(self.id)])
|
||||
|
||||
|
||||
class SimpleCategory(CategoryBase):
|
||||
"""A Test of catgorizing"""
|
||||
class Meta:
|
||||
verbose_name_plural = 'simple categories'
|
||||
|
||||
|
||||
#import categories
|
||||
# mport categories
|
||||
|
||||
#categories.register_fk(SimpleText, 'primary_category', {'related_name':'simpletext_primary_set'})
|
||||
#categories.register_m2m(SimpleText, 'cats', )
|
||||
# ategories.register_fk(SimpleText, 'primary_category', {'related_name':'simpletext_primary_set'})
|
||||
# ategories.register_m2m(SimpleText, 'cats', )
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ Replace these with more appropriate tests for your application.
|
|||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
|
|
@ -20,4 +21,3 @@ Another way to test that 1 + 1 is equal to 2.
|
|||
>>> 1 + 1 == 2
|
||||
True
|
||||
"""}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
from django.conf.urls import include, url
|
||||
import os
|
||||
|
||||
# Uncomment the next two lines to enable the admin:
|
||||
from django.conf.urls import include, url
|
||||
from django.contrib import admin
|
||||
from django.views.static import serve
|
||||
|
||||
admin.autodiscover()
|
||||
|
||||
|
||||
import os
|
||||
|
||||
ROOT_PATH = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
urlpatterns = (
|
||||
|
|
@ -22,7 +20,7 @@ urlpatterns = (
|
|||
# Uncomment the next line to enable the admin:
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
url(r'^categories/', include('categories.urls')),
|
||||
#(r'^cats/', include('categories.urls')),
|
||||
# r'^cats/', include('categories.urls')),
|
||||
|
||||
url(r'^static/categories/(?P<path>.*)$', serve,
|
||||
{'document_root': ROOT_PATH + '/categories/media/categories/'}),
|
||||
|
|
@ -31,7 +29,6 @@ urlpatterns = (
|
|||
# {'document_root': ROOT_PATH + '/editor/media/editor/',
|
||||
# 'show_indexes':True}),
|
||||
|
||||
url(r'^static/(?P<path>.*)$', serve,
|
||||
{'document_root': os.path.join(ROOT_PATH, 'example', 'static')}),
|
||||
url(r'^static/(?P<path>.*)$', serve, {'document_root': os.path.join(ROOT_PATH, 'example', 'static')}),
|
||||
|
||||
)
|
||||
|
|
|
|||
4
setup.py
4
setup.py
|
|
@ -25,6 +25,6 @@ setup(
|
|||
classifiers=[
|
||||
'Framework :: Django',
|
||||
],
|
||||
install_requires = reqs,
|
||||
dependency_links = []
|
||||
install_requires=reqs,
|
||||
dependency_links=[]
|
||||
)
|
||||
|
|
|
|||
8
tox.ini
8
tox.ini
|
|
@ -1,5 +1,6 @@
|
|||
[tox]
|
||||
envlist =
|
||||
py27-lint
|
||||
py27-django{18,19},
|
||||
py34-django{18,19},
|
||||
py35-django{18,19},
|
||||
|
|
@ -12,3 +13,10 @@ deps=
|
|||
|
||||
commands=
|
||||
{toxinidir}/example/manage.py test --settings='settings-testing' categories
|
||||
|
||||
[testenv:py27-lint]
|
||||
deps=
|
||||
flake8
|
||||
|
||||
commands=
|
||||
flake8 . --ignore=E501 --exclude=categories/south_migrations/
|
||||
|
|
|
|||
Loading…
Reference in a new issue