[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
This commit is contained in:
pre-commit-ci[bot] 2021-12-05 14:34:46 +00:00
parent 3bfe1ad131
commit 5961d6b80f
74 changed files with 1079 additions and 926 deletions

View file

@ -199,4 +199,3 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,23 +1,19 @@
__version_info__ = {
'major': 1,
'minor': 8,
'micro': 0,
'releaselevel': 'final',
'serial': 1
}
__version_info__ = {"major": 1, "minor": 8, "micro": 0, "releaselevel": "final", "serial": 1}
def get_version(short=False):
assert __version_info__['releaselevel'] in ('alpha', 'beta', 'final')
vers = ["%(major)i.%(minor)i" % __version_info__, ]
if __version_info__['micro'] and not short:
assert __version_info__["releaselevel"] in ("alpha", "beta", "final")
vers = [
"%(major)i.%(minor)i" % __version_info__,
]
if __version_info__["micro"] and not short:
vers.append(".%(micro)i" % __version_info__)
if __version_info__['releaselevel'] != 'final' and not short:
vers.append('%s%i' % (__version_info__['releaselevel'][0], __version_info__['serial']))
return ''.join(vers)
if __version_info__["releaselevel"] != "final" and not short:
vers.append("%s%i" % (__version_info__["releaselevel"][0], __version_info__["serial"]))
return "".join(vers)
__version__ = get_version()
default_app_config = 'categories.apps.CategoriesConfig'
default_app_config = "categories.apps.CategoriesConfig"

View file

@ -1,17 +1,17 @@
from django.contrib import admin
from django import forms
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from .base import CategoryBaseAdmin, CategoryBaseAdminForm
from .genericcollection import GenericCollectionTabularInline
from .settings import RELATION_MODELS, JAVASCRIPT_URL, REGISTER_ADMIN
from .models import Category
from .base import CategoryBaseAdminForm, CategoryBaseAdmin
from .settings import MODEL_REGISTRY
from .settings import JAVASCRIPT_URL, MODEL_REGISTRY, REGISTER_ADMIN, RELATION_MODELS
class NullTreeNodeChoiceField(forms.ModelChoiceField):
"""A ModelChoiceField for tree nodes."""
def __init__(self, level_indicator='---', *args, **kwargs):
def __init__(self, level_indicator="---", *args, **kwargs):
self.level_indicator = level_indicator
super(NullTreeNodeChoiceField, self).__init__(*args, **kwargs)
@ -20,7 +20,7 @@ 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:
@ -33,38 +33,43 @@ if RELATION_MODELS:
class CategoryAdminForm(CategoryBaseAdminForm):
class Meta:
model = Category
fields = '__all__'
fields = "__all__"
def clean_alternate_title(self):
if self.instance is None or not self.cleaned_data['alternate_title']:
return self.cleaned_data['name']
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"]
class CategoryAdmin(CategoryBaseAdmin):
form = CategoryAdminForm
list_display = ('name', 'alternate_title', 'active')
list_display = ("name", "alternate_title", "active")
fieldsets = (
(None, {
'fields': ('parent', 'name', 'thumbnail', 'active')
}),
(_('Meta Data'), {
'fields': ('alternate_title', 'alternate_url', 'description',
'meta_keywords', 'meta_extra'),
'classes': ('collapse',),
}),
(_('Advanced'), {
'fields': ('order', 'slug'),
'classes': ('collapse',),
}),
(None, {"fields": ("parent", "name", "thumbnail", "active")}),
(
_("Meta Data"),
{
"fields": ("alternate_title", "alternate_url", "description", "meta_keywords", "meta_extra"),
"classes": ("collapse",),
},
),
(
_("Advanced"),
{
"fields": ("order", "slug"),
"classes": ("collapse",),
},
),
)
if RELATION_MODELS:
inlines = [InlineCategoryRelation, ]
inlines = [
InlineCategoryRelation,
]
class Media:
js = (JAVASCRIPT_URL + 'genericcollections.js',)
js = (JAVASCRIPT_URL + "genericcollections.js",)
if REGISTER_ADMIN:
@ -72,18 +77,21 @@ if REGISTER_ADMIN:
for model, modeladmin in list(admin.site._registry.items()):
if model in list(MODEL_REGISTRY.values()) and modeladmin.fieldsets:
fieldsets = getattr(modeladmin, 'fieldsets', ())
fields = [cat.split('.')[2] for cat in MODEL_REGISTRY if MODEL_REGISTRY[cat] == model]
fieldsets = getattr(modeladmin, "fieldsets", ())
fields = [cat.split(".")[2] for cat in MODEL_REGISTRY if MODEL_REGISTRY[cat] == model]
# check each field to see if already defined
for cat in fields:
for k, v in fieldsets:
if cat in v['fields']:
if cat in v["fields"]:
fields.remove(cat)
# if there are any fields left, add them under the categories fieldset
if len(fields) > 0:
admin.site.unregister(model)
admin.site.register(model, type('newadmin', (modeladmin.__class__,), {
'fieldsets': fieldsets + (('Categories', {
'fields': fields
}),)
}))
admin.site.register(
model,
type(
"newadmin",
(modeladmin.__class__,),
{"fieldsets": fieldsets + (("Categories", {"fields": fields}),)},
),
)

View file

@ -2,16 +2,18 @@ from django.apps import AppConfig
class CategoriesConfig(AppConfig):
name = 'categories'
name = "categories"
verbose_name = "Categories"
def __init__(self, *args, **kwargs):
super(CategoriesConfig, self).__init__(*args, **kwargs)
from django.db.models.signals import class_prepared
class_prepared.connect(handle_class_prepared)
def ready(self):
from django.db.models.signals import post_migrate
from .migration import migrate_app
post_migrate.connect(migrate_app)
@ -21,17 +23,18 @@ def handle_class_prepared(sender, **kwargs):
"""
See if this class needs registering of fields
"""
from .settings import M2M_REGISTRY, FK_REGISTRY
from .registration import registry
from .settings import FK_REGISTRY, M2M_REGISTRY
sender_app = sender._meta.app_label
sender_name = sender._meta.model_name
for key, val in list(FK_REGISTRY.items()):
app_name, model_name = key.split('.')
app_name, model_name = key.split(".")
if app_name == sender_app and sender_name == model_name:
registry.register_model(app_name, sender, 'ForeignKey', val)
registry.register_model(app_name, sender, "ForeignKey", val)
for key, val in list(M2M_REGISTRY.items()):
app_name, model_name = key.split('.')
app_name, model_name = key.split(".")
if app_name == sender_app and sender_name == model_name:
registry.register_model(app_name, sender, 'ManyToManyField', val)
registry.register_model(app_name, sender, "ManyToManyField", val)

View file

@ -4,25 +4,23 @@ with customizable metadata and its own name space.
"""
import sys
from django import forms
from django.contrib import admin
from django.db import models
from django import forms
from django.utils.encoding import force_text
from mptt.models import MPTTModel
from django.utils.translation import ugettext_lazy as _
from mptt.fields import TreeForeignKey
from mptt.managers import TreeManager
from mptt.models import MPTTModel
from slugify import slugify
from .editor.tree_editor import TreeEditor
from .settings import ALLOW_SLUG_CHANGE, SLUG_TRANSLITERATOR
from django.utils.translation import ugettext_lazy as _
if sys.version_info[0] < 3: # Remove this after dropping support of Python 2
from django.utils.encoding import python_2_unicode_compatible
else:
def python_2_unicode_compatible(x):
return x
@ -31,6 +29,7 @@ class CategoryManager(models.Manager):
"""
A manager that adds an "active()" method for all active categories
"""
def active(self):
"""
Only categories that are active
@ -44,17 +43,18 @@ 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',
"self",
on_delete=models.CASCADE,
blank=True,
null=True,
related_name='children',
verbose_name=_('parent'),
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'))
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()
@ -78,23 +78,28 @@ class CategoryBase(MPTTModel):
def __str__(self):
ancestors = self.get_ancestors()
return ' > '.join([force_text(i.name) for i in ancestors] + [self.name, ])
return " > ".join(
[force_text(i.name) for i in ancestors]
+ [
self.name,
]
)
class Meta:
abstract = True
unique_together = ('parent', 'name')
ordering = ('tree_id', 'lft')
unique_together = ("parent", "name")
ordering = ("tree_id", "lft")
class MPTTMeta:
order_insertion_by = 'name'
order_insertion_by = "name"
class CategoryBaseAdminForm(forms.ModelForm):
def clean_slug(self):
if not self.cleaned_data.get('slug', None):
if not self.cleaned_data.get("slug", None):
if self.instance is None or not ALLOW_SLUG_CHANGE:
self.cleaned_data['slug'] = slugify(SLUG_TRANSLITERATOR(self.cleaned_data['name']))
return self.cleaned_data['slug'][:50]
self.cleaned_data["slug"] = slugify(SLUG_TRANSLITERATOR(self.cleaned_data["name"]))
return self.cleaned_data["slug"][:50]
def clean(self):
@ -107,72 +112,71 @@ class CategoryBaseAdminForm(forms.ModelForm):
# Validate slug is valid in that level
kwargs = {}
if self.cleaned_data.get('parent', None) is None:
kwargs['parent__isnull'] = True
if self.cleaned_data.get("parent", None) is None:
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]
if self.cleaned_data['slug'] in this_level_slugs:
raise forms.ValidationError(_('The slug must be unique among '
'the items at its level.'))
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
]
if self.cleaned_data["slug"] in this_level_slugs:
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
# its parent.
if self.cleaned_data.get('parent', None) is None or self.instance.id is None:
if self.cleaned_data.get("parent", None) is None or self.instance.id is None:
return self.cleaned_data
if self.instance.pk:
decendant_ids = self.instance.get_descendants().values_list('id', flat=True)
decendant_ids = self.instance.get_descendants().values_list("id", flat=True)
else:
decendant_ids = []
if self.cleaned_data['parent'].id == self.instance.id:
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."))
if self.cleaned_data["parent"].id == self.instance.id:
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."))
return self.cleaned_data
class CategoryBaseAdmin(TreeEditor, admin.ModelAdmin):
form = CategoryBaseAdminForm
list_display = ('name', 'active')
search_fields = ('name',)
prepopulated_fields = {'slug': ('name',)}
list_display = ("name", "active")
search_fields = ("name",)
prepopulated_fields = {"slug": ("name",)}
actions = ['activate', 'deactivate']
actions = ["activate", "deactivate"]
def get_actions(self, request):
actions = super(CategoryBaseAdmin, self).get_actions(request)
if 'delete_selected' in actions:
del actions['delete_selected']
if "delete_selected" in actions:
del actions["delete_selected"]
return actions
def deactivate(self, request, queryset):
"""
Set active to False for selected items
"""
selected_cats = self.model.objects.filter(
pk__in=[int(x) for x in request.POST.getlist('_selected_action')])
selected_cats = self.model.objects.filter(pk__in=[int(x) for x in request.POST.getlist("_selected_action")])
for item in selected_cats:
if item.active:
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):
"""
Set active to True for selected items
"""
selected_cats = self.model.objects.filter(
pk__in=[int(x) for x in request.POST.getlist('_selected_action')])
selected_cats = self.model.objects.filter(pk__in=[int(x) for x in request.POST.getlist("_selected_action")])
for item in selected_cats:
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")

View file

@ -1,13 +1,13 @@
from django.conf import settings
import django
from django.conf import settings
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)
STATIC_URL = getattr(settings, "STATIC_URL", settings.MEDIA_URL)
if STATIC_URL is None:
STATIC_URL = settings.MEDIA_URL
MEDIA_PATH = getattr(settings, 'EDITOR_MEDIA_PATH', '%seditor/' % STATIC_URL)
MEDIA_PATH = getattr(settings, "EDITOR_MEDIA_PATH", "%seditor/" % STATIC_URL)
TREE_INITIAL_STATE = getattr(settings, 'EDITOR_TREE_INITIAL_STATE', 'collapsed')
TREE_INITIAL_STATE = getattr(settings, "EDITOR_TREE_INITIAL_STATE", "collapsed")
IS_GRAPPELLI_INSTALLED = 'grappelli' in settings.INSTALLED_APPS
IS_GRAPPELLI_INSTALLED = "grappelli" in settings.INSTALLED_APPS

View file

@ -1,29 +1,30 @@
import django
from django.contrib.admin.templatetags.admin_list import _boolean_icon, result_headers
from django.contrib.admin.utils import lookup_field
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.template import Library
from django.contrib.admin.templatetags.admin_list import result_headers, _boolean_icon
from django.contrib.admin.utils import lookup_field
from categories.editor.utils import 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, escapejs, format_html
from django.utils.encoding import force_text, smart_text
from django.utils.html import conditional_escape, escape, escapejs, format_html
from django.utils.safestring import mark_safe
from categories.editor import settings
from categories.editor.utils import display_for_field
register = Library()
TREE_LIST_RESULTS_TEMPLATE = 'admin/editor/tree_list_results.html'
TREE_LIST_RESULTS_TEMPLATE = "admin/editor/tree_list_results.html"
if settings.IS_GRAPPELLI_INSTALLED:
TREE_LIST_RESULTS_TEMPLATE = 'admin/editor/grappelli_tree_list_results.html'
TREE_LIST_RESULTS_TEMPLATE = "admin/editor/grappelli_tree_list_results.html"
def get_empty_value_display(cl):
if hasattr(cl.model_admin, 'get_empty_value_display'):
if hasattr(cl.model_admin, "get_empty_value_display"):
return cl.model_admin.get_empty_value_display()
else:
# Django < 1.9
from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
return EMPTY_CHANGELIST_VALUE
@ -34,7 +35,7 @@ def items_for_tree_result(cl, result, form):
first = True
pk = cl.lookup_opts.pk.attname
for field_name in cl.list_display:
row_class = ''
row_class = ""
try:
f, attr, value = lookup_field(field_name, result, cl.model_admin)
except (AttributeError, ObjectDoesNotExist):
@ -42,10 +43,10 @@ def items_for_tree_result(cl, result, form):
else:
if f is None:
if django.VERSION[0] == 1 and django.VERSION[1] == 4:
if field_name == 'action_checkbox':
if field_name == "action_checkbox":
row_class = ' class="action-checkbox disclosure"'
allow_tags = getattr(attr, 'allow_tags', False)
boolean = getattr(attr, 'boolean', False)
allow_tags = getattr(attr, "allow_tags", False)
boolean = getattr(attr, "boolean", False)
if boolean:
allow_tags = True
result_repr = _boolean_icon(value)
@ -60,16 +61,16 @@ def items_for_tree_result(cl, result, form):
else:
if value is None:
result_repr = get_empty_value_display(cl)
if hasattr(f, 'rel') and isinstance(f.rel, models.ManyToOneRel):
if hasattr(f, "rel") and isinstance(f.rel, models.ManyToOneRel):
result_repr = escape(getattr(result, f.name))
else:
result_repr = display_for_field(value, f, '')
result_repr = display_for_field(value, f, "")
if isinstance(f, models.DateField) or isinstance(f, models.TimeField):
row_class = ' class="nowrap"'
if first:
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
try:
f, attr, checkbox_value = lookup_field('action_checkbox', result, cl.model_admin)
f, attr, checkbox_value = lookup_field("action_checkbox", result, cl.model_admin)
if row_class:
row_class = "%s%s" % (row_class[:-1], ' disclosure"')
else:
@ -77,14 +78,14 @@ def items_for_tree_result(cl, result, form):
except (AttributeError, ObjectDoesNotExist):
pass
if force_text(result_repr) == '':
result_repr = mark_safe('&nbsp;')
if force_text(result_repr) == "":
result_repr = mark_safe("&nbsp;")
# If list_display_links not defined, add the link tag to the first field
if (first and not cl.list_display_links) or field_name in cl.list_display_links:
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
table_tag = 'td' # {True:'th', False:'td'}[first]
table_tag = "td" # {True:'th', False:'td'}[first]
else:
table_tag = {True: 'th', False: 'td'}[first]
table_tag = {True: "th", False: "td"}[first]
url = cl.url_for_result(result)
# Convert the pk to something that can be used in Javascript.
@ -104,9 +105,14 @@ def items_for_tree_result(cl, result, form):
row_class,
url,
format_html(
' onclick="opener.dismissRelatedLookupPopup(window, '
'&#39;{}&#39;); return false;"', result_id
) if cl.is_popup else '', result_repr, table_tag)
' onclick="opener.dismissRelatedLookupPopup(window, ' '&#39;{}&#39;); return false;"',
result_id,
)
if cl.is_popup
else "",
result_repr,
table_tag,
)
)
else:
@ -118,9 +124,9 @@ def items_for_tree_result(cl, result, form):
result_repr = mark_safe(force_text(bf.errors) + force_text(bf))
else:
result_repr = conditional_escape(result_repr)
yield mark_safe(smart_text('<td%s>%s</td>' % (row_class, result_repr)))
yield mark_safe(smart_text("<td%s>%s</td>" % (row_class, result_repr)))
if form and not form[cl.model._meta.pk.name].is_hidden:
yield mark_safe(smart_text('<td>%s</td>' % force_text(form[cl.model._meta.pk.name])))
yield mark_safe(smart_text("<td>%s</td>" % force_text(form[cl.model._meta.pk.name])))
class TreeList(list):
@ -131,7 +137,7 @@ def tree_results(cl):
if cl.formset:
for res, form in zip(cl.result_list, cl.formset.forms):
result = TreeList(items_for_tree_result(cl, res, form))
if hasattr(res, 'pk'):
if hasattr(res, "pk"):
result.pk = res.pk
if res.parent:
result.parent_pk = res.parent.pk
@ -141,7 +147,7 @@ def tree_results(cl):
else:
for res in cl.result_list:
result = TreeList(items_for_tree_result(cl, res, None))
if hasattr(res, 'pk'):
if hasattr(res, "pk"):
result.pk = res.pk
if res.parent:
result.parent_pk = res.parent.pk
@ -155,14 +161,12 @@ 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[0] == 1 and django.VERSION[1] > 2:
from django.contrib.admin.templatetags.admin_list import result_hidden_fields
result['result_hidden_fields'] = list(result_hidden_fields(cl))
result["result_hidden_fields"] = list(result_hidden_fields(cl))
return result

View file

@ -1,12 +1,11 @@
from django.contrib import admin
from django.db.models.query import QuerySet
from django.contrib.admin.views.main import ChangeList
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _
from django.contrib.admin.options import IncorrectLookupParameters
from django.shortcuts import render
import django
from django.contrib import admin
from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.views.main import ChangeList
from django.db.models.query import QuerySet
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils.translation import ugettext_lazy as _
from . import settings
@ -24,6 +23,7 @@ class TreeEditorQuerySet(QuerySet):
in the result set, so the resulting tree display actually
makes sense.
"""
def iterator(self):
qs = self
# Reaching into the bowels of query sets to find out whether the qs is
@ -36,9 +36,9 @@ class TreeEditorQuerySet(QuerySet):
# this cuts down the number of queries considerably since all ancestors
# 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():
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:
ancestor_id_list = p.get_ancestors().values_list('id', flat=True)
ancestor_id_list = p.get_ancestors().values_list("id", flat=True)
include_pages.update(ancestor_id_list)
if include_pages:
@ -68,18 +68,18 @@ class TreeEditorQuerySet(QuerySet):
class TreeChangeList(ChangeList):
def _get_default_ordering(self):
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
return '', '' # ('tree_id', 'lft')
return "", "" # ('tree_id', 'lft')
else:
return []
def get_ordering(self, request=None, queryset=None):
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
return '', '' # ('tree_id', 'lft')
return "", "" # ('tree_id', 'lft')
else:
return []
def get_queryset(self, *args, **kwargs):
qs = super(TreeChangeList, self).get_queryset(*args, **kwargs).order_by('tree_id', 'lft')
qs = super(TreeChangeList, self).get_queryset(*args, **kwargs).order_by("tree_id", "lft")
return qs
@ -88,7 +88,7 @@ class TreeEditor(admin.ModelAdmin):
list_max_show_all = 200 # new in django 1.4
class Media:
css = {'all': (settings.MEDIA_PATH + "jquery.treeTable.css", )}
css = {"all": (settings.MEDIA_PATH + "jquery.treeTable.css",)}
js = []
js.extend((settings.MEDIA_PATH + "jquery.treeTable.js",))
@ -98,8 +98,8 @@ class TreeEditor(admin.ModelAdmin):
self.list_display = list(self.list_display)
if 'action_checkbox' in self.list_display:
self.list_display.remove('action_checkbox')
if "action_checkbox" in self.list_display:
self.list_display.remove("action_checkbox")
opts = self.model._meta
@ -108,9 +108,9 @@ class TreeEditor(admin.ModelAdmin):
grappelli_prefix = "grappelli_"
self.change_list_template = [
'admin/%s/%s/editor/%stree_editor.html' % (opts.app_label, opts.object_name.lower(), grappelli_prefix),
'admin/%s/editor/%stree_editor.html' % (opts.app_label, grappelli_prefix),
'admin/editor/%stree_editor.html' % grappelli_prefix,
"admin/%s/%s/editor/%stree_editor.html" % (opts.app_label, opts.object_name.lower(), grappelli_prefix),
"admin/%s/editor/%stree_editor.html" % (opts.app_label, grappelli_prefix),
"admin/editor/%stree_editor.html" % grappelli_prefix,
]
def get_changelist(self, request, **kwargs):
@ -125,6 +125,7 @@ class TreeEditor(admin.ModelAdmin):
from django.core.exceptions import PermissionDenied
from django.utils.encoding import force_text
from django.utils.translation import ungettext
opts = self.model._meta
app_label = opts.app_label
if not self.has_change_permission(request, None):
@ -137,31 +138,56 @@ class TreeEditor(admin.ModelAdmin):
list_display = list(self.list_display)
if not actions:
try:
list_display.remove('action_checkbox')
list_display.remove("action_checkbox")
except ValueError:
pass
try:
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
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)
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,
)
elif django.VERSION[0] == 1 or (django.VERSION[0] == 2 and django.VERSION[1] < 1):
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,
self.list_editable, self)
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,
self.list_editable,
self,
)
else:
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,
self.list_editable, self, self.sortable_by)
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,
self.list_editable,
self,
self.sortable_by,
)
cl = TreeChangeList(*params)
except IncorrectLookupParameters:
# Wacky lookup parameters were given, so redirect to the main
@ -170,15 +196,13 @@ class TreeEditor(admin.ModelAdmin):
# the 'invalid=1' parameter was already in the query string, something
# is screwed up with the database, so display an error page.
if ERROR_FLAG in list(request.GET.keys()):
return render(
request,
'admin/invalid_setup.html', {'title': _('Database error')})
return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1')
return render(request, "admin/invalid_setup.html", {"title": _("Database error")})
return HttpResponseRedirect(request.path + "?" + ERROR_FLAG + "=1")
# If the request was POSTed, this might be a bulk action or a bulk edit.
# Try to look up an action first, but if this isn't an action the POST
# will fall through to the bulk edit check, below.
if actions and request.method == 'POST':
if actions and request.method == "POST":
response = self.response_action(request, queryset=cl.get_queryset())
if response:
return response
@ -191,9 +215,7 @@ class TreeEditor(admin.ModelAdmin):
# Handle POSTed bulk-edit data.
if request.method == "POST" and self.list_editable:
FormSet = self.get_changelist_formset(request)
formset = cl.formset = FormSet(
request.POST, request.FILES, queryset=cl.result_list
)
formset = cl.formset = FormSet(request.POST, request.FILES, queryset=cl.result_list)
if formset.is_valid():
changecount = 0
for form in formset.forms:
@ -213,9 +235,8 @@ class TreeEditor(admin.ModelAdmin):
msg = ungettext(
"%(count)s %(name)s was changed successfully.",
"%(count)s %(name)s were changed successfully.",
changecount) % {'count': changecount,
'name': name,
'obj': force_text(obj)}
changecount,
) % {"count": changecount, "name": name, "obj": force_text(obj)}
self.message_user(request, msg)
return HttpResponseRedirect(request.get_full_path())
@ -234,40 +255,47 @@ class TreeEditor(admin.ModelAdmin):
# Build the action form and populate it with available actions.
if actions:
action_form = self.action_form(auto_id=None)
action_form.fields['action'].choices = self.get_action_choices(request)
action_form.fields["action"].choices = self.get_action_choices(request)
else:
action_form = None
context = {
'title': cl.title,
'is_popup': cl.is_popup,
'cl': cl,
'media': media,
'has_add_permission': self.has_add_permission(request),
'app_label': app_label,
'action_form': action_form,
'actions_on_top': self.actions_on_top,
'actions_on_bottom': self.actions_on_bottom,
"title": cl.title,
"is_popup": cl.is_popup,
"cl": cl,
"media": media,
"has_add_permission": self.has_add_permission(request),
"app_label": app_label,
"action_form": action_form,
"actions_on_top": self.actions_on_top,
"actions_on_bottom": self.actions_on_bottom,
}
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
context['root_path'] = self.admin_site.root_path
context["root_path"] = self.admin_site.root_path
elif django.VERSION[0] == 1 or (django.VERSION[0] == 2 and django.VERSION[1] < 1):
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),
'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)},
'selection_note_all': selection_note_all % {'total_count': cl.result_count},
})
context.update(
{
"module_name": force_text(opts.verbose_name_plural),
"selection_note": _("0 of %(cnt)s selected") % {"cnt": len(cl.result_list)},
"selection_note_all": selection_note_all % {"total_count": cl.result_count},
}
)
else:
context['opts'] = self.model._meta
context["opts"] = self.model._meta
context.update(extra_context or {})
return render(request, self.change_list_template or [
'admin/%s/%s/change_list.html' % (app_label, opts.object_name.lower()),
'admin/%s/change_list.html' % app_label,
'admin/change_list.html'
], context=context)
return render(
request,
self.change_list_template
or [
"admin/%s/%s/change_list.html" % (app_label, opts.object_name.lower()),
"admin/%s/change_list.html" % app_label,
"admin/change_list.html",
],
context=context,
)
def changelist_view(self, request, extra_context=None, *args, **kwargs):
"""
@ -275,8 +303,8 @@ class TreeEditor(admin.ModelAdmin):
change list/actions page.
"""
extra_context = extra_context or {}
extra_context['EDITOR_MEDIA_PATH'] = settings.MEDIA_PATH
extra_context['EDITOR_TREE_INITIAL_STATE'] = settings.TREE_INITIAL_STATE
extra_context["EDITOR_MEDIA_PATH"] = settings.MEDIA_PATH
extra_context["EDITOR_TREE_INITIAL_STATE"] = settings.TREE_INITIAL_STATE
# FIXME
return self.old_changelist_view(request, extra_context)

View file

@ -4,21 +4,24 @@ from django.db.models import ForeignKey, ManyToManyField
class CategoryM2MField(ManyToManyField):
def __init__(self, **kwargs):
from .models import Category
if 'to' in kwargs:
kwargs.pop('to')
if "to" in kwargs:
kwargs.pop("to")
super(CategoryM2MField, self).__init__(to=Category, **kwargs)
class CategoryFKField(ForeignKey):
def __init__(self, **kwargs):
from .models import Category
if 'to' in kwargs:
kwargs.pop('to')
if "to" in kwargs:
kwargs.pop("to")
super(CategoryFKField, self).__init__(to=Category, **kwargs)
try:
from south.modelsinspector import add_introspection_rules
add_introspection_rules([], [r"^categories\.fields\.CategoryFKField"])
add_introspection_rules([], [r"^categories\.fields\.CategoryM2MField"])
except ImportError:

View file

@ -1,7 +1,8 @@
import json
from django.contrib import admin
from django.contrib.contenttypes.models import ContentType
from django.urls import reverse, NoReverseMatch
import json
from django.urls import NoReverseMatch, reverse
class GenericCollectionInlineModelAdmin(admin.options.InlineModelAdmin):
@ -9,7 +10,7 @@ class GenericCollectionInlineModelAdmin(admin.options.InlineModelAdmin):
ct_fk_field = "object_id"
def get_content_types(self):
ctypes = ContentType.objects.all().order_by('id').values_list('id', 'app_label', 'model')
ctypes = ContentType.objects.all().order_by("id").values_list("id", "app_label", "model")
elements = {}
for x, y, z in ctypes:
try:
@ -25,12 +26,12 @@ class GenericCollectionInlineModelAdmin(admin.options.InlineModelAdmin):
return result
class Media:
js = ('contentrelations/js/genericlookup.js', )
js = ("contentrelations/js/genericlookup.js",)
class GenericCollectionTabularInline(GenericCollectionInlineModelAdmin):
template = 'admin/edit_inline/gen_coll_tabular.html'
template = "admin/edit_inline/gen_coll_tabular.html"
class GenericCollectionStackedInline(GenericCollectionInlineModelAdmin):
template = 'admin/edit_inline/gen_coll_stacked.html'
template = "admin/edit_inline/gen_coll_stacked.html"

View file

@ -181,4 +181,3 @@ msgstr "Cancella?"
#: templates/admin/edit_inline/gen_coll_tabular.html:24
msgid "View on site"
msgstr "Vedi sul sito"

View file

@ -5,13 +5,14 @@ class Command(BaseCommand):
"""
Alter one or more models' tables with the registered attributes
"""
help = "Alter the tables for all registered models, or just specified models"
args = "[appname ...]"
can_import_settings = True
requires_system_checks = False
def add_arguments(self, parser):
parser.add_argument('app_names', nargs='*')
parser.add_argument("app_names", nargs="*")
def handle(self, *args, **options):
"""
@ -20,8 +21,9 @@ class Command(BaseCommand):
from categories.migration import migrate_app
from categories.settings import MODEL_REGISTRY
if options['app_names']:
for app in options['app_names']:
if options["app_names"]:
for app in options["app_names"]:
migrate_app(None, app)
else:
for app in MODEL_REGISTRY:

View file

@ -1,27 +1,28 @@
from django.core.management.base import BaseCommand
from django.core.management.base import CommandError
from django.core.management.base import BaseCommand, CommandError
class Command(BaseCommand):
"""
Alter one or more models' tables with the registered attributes
"""
help = "Drop the given field from the given model's table"
args = "appname modelname fieldname"
can_import_settings = True
requires_system_checks = False
def add_arguments(self, parser):
parser.add_argument('app_name')
parser.add_argument('model_name')
parser.add_argument('field_name')
parser.add_argument("app_name")
parser.add_argument("model_name")
parser.add_argument("field_name")
def handle(self, *args, **options):
"""
Alter the tables
"""
from categories.migration import drop_field
if 'app_name' not in options or 'model_name' not in options or 'field_name' not in options:
if "app_name" not in options or "model_name" not in options or "field_name" not in options:
raise CommandError("You must specify an Application name, a Model name and a Field name")
drop_field(options['app_name'], options['model_name'], options['field_name'])
drop_field(options["app_name"], options["model_name"], options["field_name"])

View file

@ -1,6 +1,5 @@
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from slugify import slugify
from categories.models import Category
@ -10,7 +9,9 @@ from categories.settings import SLUG_TRANSLITERATOR
class Command(BaseCommand):
"""Import category trees from a file."""
help = "Imports category tree(s) from a file. Sub categories must be indented by the same multiple of spaces or tabs."
help = (
"Imports category tree(s) from a file. Sub categories must be indented by the same multiple of spaces or tabs."
)
args = "file_path [file_path ...]"
def get_indent(self, string):
@ -19,13 +20,13 @@ class Command(BaseCommand):
"""
indent_amt = 0
if string[0] == '\t':
return '\t'
if string[0] == "\t":
return "\t"
for char in string:
if char == ' ':
if char == " ":
indent_amt += 1
else:
return ' ' * indent_amt
return " " * indent_amt
@transaction.atomic
def make_category(self, string, parent=None, order=1):
@ -36,9 +37,9 @@ class Command(BaseCommand):
name=string.strip(),
slug=slugify(SLUG_TRANSLITERATOR(string.strip()))[:49],
# arent=parent,
order=order
order=order,
)
cat._tree_manager.insert_node(cat, parent, 'last-child', True)
cat._tree_manager.insert_node(cat, parent, "last-child", True)
cat.save()
if parent:
parent.rght = cat.rght + 1
@ -49,10 +50,10 @@ class Command(BaseCommand):
"""
Do the work of parsing each line
"""
indent = ''
indent = ""
level = 0
if lines[0][0] == ' ' or lines[0][0] == '\t':
if lines[0][0] == " " or lines[0][0] == "\t":
raise CommandError("The first line in the file cannot start with a space or tab.")
# This keeps track of the current parents at a given level
@ -61,8 +62,8 @@ class Command(BaseCommand):
for line in lines:
if len(line) == 0:
continue
if line[0] == ' ' or line[0] == '\t':
if indent == '':
if line[0] == " " or line[0] == "\t":
if indent == "":
indent = self.get_indent(line)
elif not line[0] in indent:
raise CommandError("You can't mix spaces and tabs for indents")
@ -83,7 +84,7 @@ class Command(BaseCommand):
if not os.path.isfile(file_path):
print("File %s not found." % file_path)
continue
f = open(file_path, 'r')
f = open(file_path, "r")
data = f.readlines()
f.close()

View file

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
from django.db import connection, transaction
from django.apps import apps
from django.db import connection, transaction
from django.db.utils import ProgrammingError
@ -25,7 +24,7 @@ def field_exists(app_name, model_name, field_name):
# Return True if the many to many table exists
field = model._meta.get_field(field_name)
if hasattr(field, 'm2m_db_table'):
if hasattr(field, "m2m_db_table"):
m2m_table_name = field.m2m_db_table()
m2m_field_info = connection.introspection.get_table_description(cursor, m2m_table_name)
if m2m_field_info:
@ -50,9 +49,10 @@ def migrate_app(sender, *args, **kwargs):
Migrate all models of this app registered
"""
from .registration import registry
if 'app_config' not in kwargs:
if "app_config" not in kwargs:
return
app_config = kwargs['app_config']
app_config = kwargs["app_config"]
app_name = app_config.label
@ -60,7 +60,7 @@ def migrate_app(sender, *args, **kwargs):
sid = transaction.savepoint()
for fld in fields:
model_name, field_name = fld.split('.')[1:]
model_name, field_name = fld.split(".")[1:]
if field_exists(app_name, model_name, field_name):
continue
model = app_config.get_model(model_name)

View file

@ -1,59 +1,123 @@
# -*- coding: utf-8 -*-
from django.db import models, migrations
import django.core.files.storage
import mptt.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0001_initial'),
("contenttypes", "0001_initial"),
]
operations = [
migrations.CreateModel(
name='Category',
name="Category",
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=100, verbose_name='name')),
('slug', models.SlugField(verbose_name='slug')),
('active', models.BooleanField(default=True, verbose_name='active')),
('thumbnail', models.FileField(storage=django.core.files.storage.FileSystemStorage(), null=True, upload_to='uploads/categories/thumbnails', blank=True)),
('thumbnail_width', models.IntegerField(null=True, blank=True)),
('thumbnail_height', models.IntegerField(null=True, blank=True)),
('order', models.IntegerField(default=0)),
('alternate_title', models.CharField(default='', help_text='An alternative title to use on pages with this category.', max_length=100, blank=True)),
('alternate_url', models.CharField(help_text='An alternative URL to use instead of the one derived from the category hierarchy.', max_length=200, blank=True)),
('description', models.TextField(null=True, blank=True)),
('meta_keywords', models.CharField(default='', help_text='Comma-separated keywords for search engines.', max_length=255, blank=True)),
('meta_extra', models.TextField(default='', help_text='(Advanced) Any additional HTML to be placed verbatim in the &lt;head&gt;', blank=True)),
('lft', models.PositiveIntegerField(editable=False, db_index=True)),
('rght', models.PositiveIntegerField(editable=False, db_index=True)),
('tree_id', models.PositiveIntegerField(editable=False, db_index=True)),
('level', models.PositiveIntegerField(editable=False, db_index=True)),
('parent', mptt.fields.TreeForeignKey(related_name='children', verbose_name='parent', blank=True, to='categories.Category', on_delete=models.CASCADE, null=True)),
("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)),
("name", models.CharField(max_length=100, verbose_name="name")),
("slug", models.SlugField(verbose_name="slug")),
("active", models.BooleanField(default=True, verbose_name="active")),
(
"thumbnail",
models.FileField(
storage=django.core.files.storage.FileSystemStorage(),
null=True,
upload_to="uploads/categories/thumbnails",
blank=True,
),
),
("thumbnail_width", models.IntegerField(null=True, blank=True)),
("thumbnail_height", models.IntegerField(null=True, blank=True)),
("order", models.IntegerField(default=0)),
(
"alternate_title",
models.CharField(
default="",
help_text="An alternative title to use on pages with this category.",
max_length=100,
blank=True,
),
),
(
"alternate_url",
models.CharField(
help_text="An alternative URL to use instead of the one derived from the category hierarchy.",
max_length=200,
blank=True,
),
),
("description", models.TextField(null=True, blank=True)),
(
"meta_keywords",
models.CharField(
default="",
help_text="Comma-separated keywords for search engines.",
max_length=255,
blank=True,
),
),
(
"meta_extra",
models.TextField(
default="",
help_text="(Advanced) Any additional HTML to be placed verbatim in the &lt;head&gt;",
blank=True,
),
),
("lft", models.PositiveIntegerField(editable=False, db_index=True)),
("rght", models.PositiveIntegerField(editable=False, db_index=True)),
("tree_id", models.PositiveIntegerField(editable=False, db_index=True)),
("level", models.PositiveIntegerField(editable=False, db_index=True)),
(
"parent",
mptt.fields.TreeForeignKey(
related_name="children",
verbose_name="parent",
blank=True,
to="categories.Category",
on_delete=models.CASCADE,
null=True,
),
),
],
options={
'ordering': ('tree_id', 'lft'),
'abstract': False,
'verbose_name': 'category',
'verbose_name_plural': 'categories',
"ordering": ("tree_id", "lft"),
"abstract": False,
"verbose_name": "category",
"verbose_name_plural": "categories",
},
),
migrations.CreateModel(
name='CategoryRelation',
name="CategoryRelation",
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('object_id', models.PositiveIntegerField(verbose_name='object id')),
('relation_type', models.CharField(help_text="A generic text field to tag a relation, like 'leadphoto'.", max_length='200', null=True, verbose_name='relation type', blank=True)),
('category', models.ForeignKey(verbose_name='category', to='categories.Category', on_delete=models.CASCADE)),
('content_type', models.ForeignKey(verbose_name='content type', to='contenttypes.ContentType', on_delete=models.CASCADE)),
("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)),
("object_id", models.PositiveIntegerField(verbose_name="object id")),
(
"relation_type",
models.CharField(
help_text="A generic text field to tag a relation, like 'leadphoto'.",
max_length="200",
null=True,
verbose_name="relation type",
blank=True,
),
),
(
"category",
models.ForeignKey(verbose_name="category", to="categories.Category", on_delete=models.CASCADE),
),
(
"content_type",
models.ForeignKey(
verbose_name="content type", to="contenttypes.ContentType", on_delete=models.CASCADE
),
),
],
),
migrations.AlterUniqueTogether(
name='category',
unique_together=set([('parent', 'name')]),
name="category",
unique_together=set([("parent", "name")]),
),
]

View file

@ -1,27 +1,32 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-02-17 11:11
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.manager
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('categories', '0001_initial'),
("categories", "0001_initial"),
]
operations = [
migrations.AlterModelManagers(
name='category',
name="category",
managers=[
('tree', django.db.models.manager.Manager()),
("tree", django.db.models.manager.Manager()),
],
),
migrations.AlterField(
model_name='categoryrelation',
name='relation_type',
field=models.CharField(blank=True, help_text="A generic text field to tag a relation, like 'leadphoto'.", max_length=200, null=True, verbose_name='relation type'),
model_name="categoryrelation",
name="relation_type",
field=models.CharField(
blank=True,
help_text="A generic text field to tag a relation, like 'leadphoto'.",
max_length=200,
null=True,
verbose_name="relation type",
),
),
]

View file

@ -1,35 +1,44 @@
# Generated by Django 3.0.4 on 2020-03-06 10:50
from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('categories', '0002_auto_20170217_1111'),
("contenttypes", "0002_remove_content_type_name"),
("categories", "0002_auto_20170217_1111"),
]
operations = [
migrations.AlterField(
model_name='category',
name='level',
model_name="category",
name="level",
field=models.PositiveIntegerField(editable=False),
),
migrations.AlterField(
model_name='category',
name='lft',
model_name="category",
name="lft",
field=models.PositiveIntegerField(editable=False),
),
migrations.AlterField(
model_name='category',
name='rght',
model_name="category",
name="rght",
field=models.PositiveIntegerField(editable=False),
),
migrations.AlterField(
model_name='categoryrelation',
name='content_type',
field=models.ForeignKey(limit_choices_to=models.Q(models.Q(('app_label', 'simpletext'), ('model', 'simpletext')), models.Q(('app_label', 'flatpages'), ('model', 'flatpage')), _connector='OR'), on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', verbose_name='content type'),
model_name="categoryrelation",
name="content_type",
field=models.ForeignKey(
limit_choices_to=models.Q(
models.Q(("app_label", "simpletext"), ("model", "simpletext")),
models.Q(("app_label", "flatpages"), ("model", "flatpage")),
_connector="OR",
),
on_delete=django.db.models.deletion.CASCADE,
to="contenttypes.ContentType",
verbose_name="content type",
),
),
]

View file

@ -1,20 +1,22 @@
# Generated by Django 3.0.6 on 2020-05-17 18:32
from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('categories', '0003_auto_20200306_1050'),
("contenttypes", "0002_remove_content_type_name"),
("categories", "0003_auto_20200306_1050"),
]
operations = [
migrations.AlterField(
model_name='categoryrelation',
name='content_type',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', verbose_name='content type'),
model_name="categoryrelation",
name="content_type",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="contenttypes.ContentType", verbose_name="content type"
),
),
]

View file

@ -1,20 +1,26 @@
from django.core.files.images import get_image_dimensions
from django.urls import reverse
from django.db import models
from django.utils.encoding import force_text
from django.contrib.contenttypes.models import ContentType
from functools import reduce
from django.contrib.contenttypes.models import ContentType
from django.core.files.images import get_image_dimensions
from django.db import models
from django.urls import reverse
from django.utils.encoding import force_text
try:
from django.contrib.contenttypes.fields import GenericForeignKey
except ImportError:
from django.contrib.contenttypes.generic import GenericForeignKey
from django.core.files.storage import get_storage_class
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 .base import CategoryBase
from .settings import (
RELATION_MODELS,
RELATIONS,
THUMBNAIL_STORAGE,
THUMBNAIL_UPLOAD_PATH,
)
STORAGE = get_storage_class(THUMBNAIL_STORAGE)
@ -22,32 +28,28 @@ STORAGE = get_storage_class(THUMBNAIL_STORAGE)
class Category(CategoryBase):
thumbnail = models.FileField(
upload_to=THUMBNAIL_UPLOAD_PATH,
null=True, blank=True,
storage=STORAGE(),)
null=True,
blank=True,
storage=STORAGE(),
)
thumbnail_width = models.IntegerField(blank=True, null=True)
thumbnail_height = models.IntegerField(blank=True, null=True)
order = models.IntegerField(default=0)
alternate_title = models.CharField(
blank=True,
default="",
max_length=100,
help_text="An alternative title to use on pages with this category.")
blank=True, default="", max_length=100, help_text="An alternative title to use on pages with this category."
)
alternate_url = models.CharField(
blank=True,
max_length=200,
help_text="An alternative URL to use instead of the one derived from "
"the category hierarchy.")
help_text="An alternative URL to use instead of the one derived from " "the category hierarchy.",
)
description = models.TextField(blank=True, null=True)
meta_keywords = models.CharField(
blank=True,
default="",
max_length=255,
help_text="Comma-separated keywords for search engines.")
blank=True, default="", max_length=255, help_text="Comma-separated keywords for search engines."
)
meta_extra = models.TextField(
blank=True,
default="",
help_text="(Advanced) Any additional HTML to be placed verbatim "
"in the &lt;head&gt;")
blank=True, default="", help_text="(Advanced) Any additional HTML to be placed verbatim " "in the &lt;head&gt;"
)
@property
def short_title(self):
@ -60,19 +62,21 @@ class Category(CategoryBase):
if self.alternate_url:
return self.alternate_url
try:
prefix = reverse('categories_tree_list')
prefix = reverse("categories_tree_list")
except NoReverseMatch:
prefix = '/'
ancestors = list(self.get_ancestors()) + [self, ]
return prefix + '/'.join([force_text(i.slug) for i in ancestors]) + '/'
prefix = "/"
ancestors = list(self.get_ancestors()) + [
self,
]
return prefix + "/".join([force_text(i.slug) for i in ancestors]) + "/"
if RELATION_MODELS:
def get_related_content_type(self, content_type):
"""
Get all related items of the specified content type
"""
return self.categoryrelation_set.filter(
content_type__name=content_type)
return self.categoryrelation_set.filter(content_type__name=content_type)
def get_relation_type(self, relation_type):
"""
@ -92,11 +96,11 @@ class Category(CategoryBase):
super(Category, self).save(*args, **kwargs)
class Meta(CategoryBase.Meta):
verbose_name = _('category')
verbose_name_plural = _('categories')
verbose_name = _("category")
verbose_name_plural = _("categories")
class MPTTMeta:
order_insertion_by = ('order', 'name')
order_insertion_by = ("order", "name")
if RELATIONS:
@ -123,17 +127,23 @@ class CategoryRelationManager(models.Manager):
class CategoryRelation(models.Model):
"""Related category item"""
category = models.ForeignKey(Category, verbose_name=_('category'), on_delete=models.CASCADE)
category = models.ForeignKey(Category, verbose_name=_("category"), on_delete=models.CASCADE)
content_type = models.ForeignKey(
ContentType, on_delete=models.CASCADE, 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')
ContentType,
on_delete=models.CASCADE,
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'),
verbose_name=_("relation type"),
max_length=200,
blank=True,
null=True,
help_text=_("A generic text field to tag a relation, like 'leadphoto'."))
help_text=_("A generic text field to tag a relation, like 'leadphoto'."),
)
objects = CategoryRelationManager()

View file

@ -1,17 +1,17 @@
"""
These functions handle the adding of fields to other models
"""
from django.db.models import ForeignKey, ManyToManyField, CASCADE
from django.core.exceptions import FieldDoesNotExist
from . import fields
from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured
from django.db.models import CASCADE, ForeignKey, ManyToManyField
# from settings import self._field_registry, self._model_registry
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ImproperlyConfigured
from . import fields
FIELD_TYPES = {
'ForeignKey': ForeignKey,
'ManyToManyField': ManyToManyField,
"ForeignKey": ForeignKey,
"ManyToManyField": ManyToManyField,
}
@ -28,17 +28,20 @@ class Registry(object):
field_definitions: a string, tuple or list of field configurations
field_type: either 'ForeignKey' or 'ManyToManyField'
"""
from django.apps import apps
import collections
from django.apps import apps
app_label = app
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'):
if field_type not in ("ForeignKey", "ManyToManyField"):
raise ImproperlyConfigured(_('`field_type` must be either `"ForeignKey"` or `"ManyToManyField"`.'))
try:
@ -55,30 +58,31 @@ class Registry(object):
if model not in self._model_registry[app_label]:
self._model_registry[app_label].append(model)
except LookupError:
raise ImproperlyConfigured('Model "%(model)s" doesn\'t exist in app "%(app)s".' % {'model': model_name, 'app': app})
raise ImproperlyConfigured(
'Model "%(model)s" doesn\'t exist in app "%(app)s".' % {"model": model_name, "app": app}
)
if not isinstance(field_definitions, (tuple, list)):
field_definitions = [field_definitions]
for fld in field_definitions:
extra_params = {'to': 'categories.Category', 'blank': True}
if field_type != 'ManyToManyField':
extra_params['on_delete'] = CASCADE
extra_params['null'] = True
extra_params = {"to": "categories.Category", "blank": True}
if field_type != "ManyToManyField":
extra_params["on_delete"] = CASCADE
extra_params["null"] = True
if isinstance(fld, str):
field_name = fld
elif isinstance(fld, dict):
if 'name' in fld:
field_name = fld.pop('name')
if "name" in fld:
field_name = fld.pop("name")
else:
continue
extra_params.update(fld)
else:
raise ImproperlyConfigured(
_("%(settings)s doesn't recognize the value of %(app)s.%(model)s") % {
'settings': 'CATEGORY_SETTINGS',
'app': app,
'model': model_name})
_("%(settings)s doesn't recognize the value of %(app)s.%(model)s")
% {"settings": "CATEGORY_SETTINGS", "app": app, "model": model_name}
)
registry_name = ".".join([app_label, model_name.lower(), field_name])
if registry_name in self._field_registry:
continue
@ -89,10 +93,10 @@ class Registry(object):
self._field_registry[registry_name] = FIELD_TYPES[field_type](**extra_params)
self._field_registry[registry_name].contribute_to_class(model, field_name)
def register_m2m(self, model, field_name='categories', extra_params={}):
def register_m2m(self, model, field_name="categories", extra_params={}):
return self._register(model, field_name, extra_params, fields.CategoryM2MField)
def register_fk(self, model, field_name='category', extra_params={}):
def register_fk(self, model, field_name="category", extra_params={}):
return self._register(model, field_name, extra_params, fields.CategoryFKField)
def _register(self, model, field_name, extra_params={}, field=fields.CategoryFKField):
@ -120,28 +124,32 @@ def _process_registry(registry, call_func):
"""
Given a dictionary, and a registration function, process the registry
"""
from django.core.exceptions import ImproperlyConfigured
from django.apps import apps
from django.core.exceptions import ImproperlyConfigured
for key, value in list(registry.items()):
model = apps.get_model(*key.split('.'))
model = apps.get_model(*key.split("."))
if model is None:
raise ImproperlyConfigured(_('%(key)s is not a model') % {'key': key})
raise ImproperlyConfigured(_("%(key)s is not a model") % {"key": key})
if isinstance(value, (tuple, list)):
for item in value:
if isinstance(item, str):
call_func(model, item)
elif isinstance(item, dict):
field_name = item.pop('name')
field_name = item.pop("name")
call_func(model, field_name, extra_params=item)
else:
raise ImproperlyConfigured(_("%(settings)s doesn't recognize the value of %(key)s") %
{'settings': 'CATEGORY_SETTINGS', 'key': key})
raise ImproperlyConfigured(
_("%(settings)s doesn't recognize the value of %(key)s")
% {"settings": "CATEGORY_SETTINGS", "key": key}
)
elif isinstance(value, str):
call_func(model, value)
elif isinstance(value, dict):
field_name = value.pop('name')
field_name = value.pop("name")
call_func(model, field_name, extra_params=value)
else:
raise ImproperlyConfigured(_("%(settings)s doesn't recognize the value of %(key)s") %
{'settings': 'CATEGORY_SETTINGS', 'key': key})
raise ImproperlyConfigured(
_("%(settings)s doesn't recognize the value of %(key)s")
% {"settings": "CATEGORY_SETTINGS", "key": key}
)

View file

@ -1,42 +1,46 @@
import collections
from django.conf import settings
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
import collections
DEFAULT_SETTINGS = {
'ALLOW_SLUG_CHANGE': False,
'M2M_REGISTRY': {},
'FK_REGISTRY': {},
'THUMBNAIL_UPLOAD_PATH': 'uploads/categories/thumbnails',
'THUMBNAIL_STORAGE': settings.DEFAULT_FILE_STORAGE,
'JAVASCRIPT_URL': getattr(settings, 'STATIC_URL', settings.MEDIA_URL) + 'js/',
'SLUG_TRANSLITERATOR': '',
'REGISTER_ADMIN': True,
'RELATION_MODELS': [],
"ALLOW_SLUG_CHANGE": False,
"M2M_REGISTRY": {},
"FK_REGISTRY": {},
"THUMBNAIL_UPLOAD_PATH": "uploads/categories/thumbnails",
"THUMBNAIL_STORAGE": settings.DEFAULT_FILE_STORAGE,
"JAVASCRIPT_URL": getattr(settings, "STATIC_URL", settings.MEDIA_URL) + "js/",
"SLUG_TRANSLITERATOR": "",
"REGISTER_ADMIN": True,
"RELATION_MODELS": [],
}
DEFAULT_SETTINGS.update(getattr(settings, 'CATEGORIES_SETTINGS', {}))
DEFAULT_SETTINGS.update(getattr(settings, "CATEGORIES_SETTINGS", {}))
if DEFAULT_SETTINGS['SLUG_TRANSLITERATOR']:
if isinstance(DEFAULT_SETTINGS['SLUG_TRANSLITERATOR'], collections.Callable):
if DEFAULT_SETTINGS["SLUG_TRANSLITERATOR"]:
if isinstance(DEFAULT_SETTINGS["SLUG_TRANSLITERATOR"], collections.Callable):
pass
elif isinstance(DEFAULT_SETTINGS['SLUG_TRANSLITERATOR'], str):
elif isinstance(DEFAULT_SETTINGS["SLUG_TRANSLITERATOR"], str):
from django.utils.importlib import import_module
bits = DEFAULT_SETTINGS['SLUG_TRANSLITERATOR'].split(".")
bits = DEFAULT_SETTINGS["SLUG_TRANSLITERATOR"].split(".")
module = import_module(".".join(bits[:-1]))
DEFAULT_SETTINGS['SLUG_TRANSLITERATOR'] = getattr(module, bits[-1])
DEFAULT_SETTINGS["SLUG_TRANSLITERATOR"] = getattr(module, bits[-1])
else:
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured(_('%(transliterator) must be a callable or a string.') %
{'transliterator': 'SLUG_TRANSLITERATOR'})
raise ImproperlyConfigured(
_("%(transliterator) must be a callable or a string.") % {"transliterator": "SLUG_TRANSLITERATOR"}
)
else:
DEFAULT_SETTINGS['SLUG_TRANSLITERATOR'] = lambda x: x
DEFAULT_SETTINGS["SLUG_TRANSLITERATOR"] = lambda x: x
# 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 DEFAULT_SETTINGS['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)}

View file

@ -1,13 +1,18 @@
from django import template
from django.apps import apps
from django.template import (Node, TemplateSyntaxError, VariableDoesNotExist)
from django.template import Node, TemplateSyntaxError, VariableDoesNotExist
from django.template.base import FilterExpression
from mptt.templatetags.mptt_tags import (
RecurseTreeNode,
full_tree_for_model,
tree_info,
tree_path,
)
from mptt.utils import drilldown_tree_for_node
from six import string_types
from categories.base import CategoryBase
from categories.models import Category
from mptt.utils import drilldown_tree_for_node
from mptt.templatetags.mptt_tags import (tree_path, tree_info, RecurseTreeNode,
full_tree_for_model)
register = template.Library()
@ -48,9 +53,9 @@ def get_category(category_string, model=Category):
"""
model_class = get_cat_model(model)
category = str(category_string).strip("'\"")
category = category.strip('/')
category = category.strip("/")
cat_list = category.split('/')
cat_list = category.split("/")
if len(cat_list) == 0:
return None
try:
@ -88,7 +93,7 @@ class CategoryDrillDownNode(template.Node):
context[self.varname] = []
except Exception:
context[self.varname] = []
return ''
return ""
@register.tag
@ -114,30 +119,32 @@ def get_category_drilldown(parser, token):
Grandparent, Parent, Child 1, Child 2, Child n
"""
bits = token.split_contents()
error_str = '%(tagname)s tag should be in the format {%% %(tagname)s ' \
'"category name" [using "app.Model"] as varname %%} or ' \
'{%% %(tagname)s category_obj as varname %%}.'
error_str = (
"%(tagname)s tag should be in the format {%% %(tagname)s "
'"category name" [using "app.Model"] as varname %%} or '
"{%% %(tagname)s category_obj as varname %%}."
)
if len(bits) == 4:
if bits[2] != 'as':
raise template.TemplateSyntaxError(error_str % {'tagname': bits[0]})
if bits[2] == 'as':
if bits[2] != "as":
raise template.TemplateSyntaxError(error_str % {"tagname": bits[0]})
if bits[2] == "as":
varname = bits[3].strip("'\"")
model = "categories.category"
if len(bits) == 6:
if bits[2] not in ('using', 'as') or bits[4] not in ('using', 'as'):
raise template.TemplateSyntaxError(error_str % {'tagname': bits[0]})
if bits[2] == 'as':
if bits[2] not in ("using", "as") or bits[4] not in ("using", "as"):
raise template.TemplateSyntaxError(error_str % {"tagname": bits[0]})
if bits[2] == "as":
varname = bits[3].strip("'\"")
model = bits[5].strip("'\"")
if bits[2] == 'using':
if bits[2] == "using":
varname = bits[5].strip("'\"")
model = bits[3].strip("'\"")
category = FilterExpression(bits[1], parser)
return CategoryDrillDownNode(category, varname, model)
@register.inclusion_tag('categories/breadcrumbs.html')
def breadcrumbs(category_string, separator=' > ', using='categories.category'):
@register.inclusion_tag("categories/breadcrumbs.html")
def breadcrumbs(category_string, separator=" > ", using="categories.category"):
"""
{% breadcrumbs category separator="::" using="categories.category" %}
@ -146,11 +153,11 @@ def breadcrumbs(category_string, separator=' > ', using='categories.category'):
"""
cat = get_category(category_string, using)
return {'category': cat, 'separator': separator}
return {"category": cat, "separator": separator}
@register.inclusion_tag('categories/ul_tree.html')
def display_drilldown_as_ul(category, using='categories.Category'):
@register.inclusion_tag("categories/ul_tree.html")
def display_drilldown_as_ul(category, using="categories.Category"):
"""
Render the category with ancestors and children using the
``categories/ul_tree.html`` template.
@ -185,13 +192,13 @@ def display_drilldown_as_ul(category, using='categories.Category'):
"""
cat = get_category(category, using)
if cat is None:
return {'category': cat, 'path': []}
return {"category": cat, "path": []}
else:
return {'category': cat, 'path': drilldown_tree_for_node(cat)}
return {"category": cat, "path": drilldown_tree_for_node(cat)}
@register.inclusion_tag('categories/ul_tree.html')
def display_path_as_ul(category, using='categories.Category'):
@register.inclusion_tag("categories/ul_tree.html")
def display_path_as_ul(category, using="categories.Category"):
"""
Render the category with ancestors, but no children using the
``categories/ul_tree.html`` template.
@ -219,7 +226,7 @@ def display_path_as_ul(category, using='categories.Category'):
else:
cat = get_category(category)
return {'category': cat, 'path': cat.get_ancestors() or []}
return {"category": cat, "path": cat.get_ancestors() or []}
class TopLevelCategoriesNode(template.Node):
@ -229,8 +236,8 @@ class TopLevelCategoriesNode(template.Node):
def render(self, context):
model = get_cat_model(self.model)
context[self.varname] = model.objects.filter(parent=None).order_by('name')
return ''
context[self.varname] = model.objects.filter(parent=None).order_by("name")
return ""
@register.tag
@ -247,14 +254,14 @@ def get_top_level_categories(parser, token):
bits = token.split_contents()
usage = 'Usage: {%% %s [using "app.Model"] as <variable> %%}' % bits[0]
if len(bits) == 3:
if bits[1] != 'as':
if bits[1] != "as":
raise template.TemplateSyntaxError(usage)
varname = bits[2]
model = "categories.category"
elif len(bits) == 5:
if bits[1] not in ('as', 'using') and bits[3] not in ('as', 'using'):
if bits[1] not in ("as", "using") and bits[3] not in ("as", "using"):
raise template.TemplateSyntaxError(usage)
if bits[1] == 'using':
if bits[1] == "using":
model = bits[2].strip("'\"")
varname = bits[4].strip("'\"")
else:
@ -264,22 +271,21 @@ 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))
children = category.children.all()
ids = []
for cat in list(children) + [category]:
if hasattr(cat, '%s_set' % set_name):
ids.extend([x.pk for x in getattr(cat, '%s_set' % set_name).all()[:num]])
if hasattr(cat, "%s_set" % set_name):
ids.extend([x.pk for x in getattr(cat, "%s_set" % set_name).all()[:num]])
return m.objects.filter(pk__in=ids).order_by('-%s' % date_field)[:num]
return m.objects.filter(pk__in=ids).order_by("-%s" % date_field)[:num]
class LatestObjectsNode(Node):
def __init__(self, var_name, category, app_label, model_name, set_name,
date_field='pub_date', num=15):
def __init__(self, var_name, category, app_label, model_name, set_name, date_field="pub_date", num=15):
"""
Get latest objects of app_label.model_name
"""
@ -305,7 +311,7 @@ class LatestObjectsNode(Node):
result = get_latest_objects_by_category(category, app_label, model_name, set_name, date_field, num)
context[self.var_name] = result
return ''
return ""
def do_get_latest_objects_by_category(parser, token):
@ -317,7 +323,7 @@ def do_get_latest_objects_by_category(parser, token):
proper_form = "{% get_latest_objects_by_category category app_name model_name set_name [date_field] [number] as [var_name] %}"
bits = token.split_contents()
if bits[-2] != 'as':
if bits[-2] != "as":
raise TemplateSyntaxError("%s tag shoud be in the form: %s" % (bits[0], proper_form))
if len(bits) < 7:
raise TemplateSyntaxError("%s tag shoud be in the form: %s" % (bits[0], proper_form))
@ -328,11 +334,11 @@ def do_get_latest_objects_by_category(parser, token):
model_name = FilterExpression(bits[3], parser)
set_name = FilterExpression(bits[4], parser)
var_name = bits[-1]
if bits[5] != 'as':
if bits[5] != "as":
date_field = FilterExpression(bits[5], parser)
else:
date_field = FilterExpression(None, parser)
if bits[6] != 'as':
if bits[6] != "as":
num = FilterExpression(bits[6], parser)
else:
num = FilterExpression(None, parser)
@ -348,8 +354,10 @@ def tree_queryset(value):
Converts a normal queryset from an MPTT model to include all the ancestors
so a filtered subset of items can be formatted correctly
"""
from django.db.models.query import QuerySet
from copy import deepcopy
from django.db.models.query import QuerySet
if not isinstance(value, QuerySet):
return value
@ -365,9 +373,9 @@ def tree_queryset(value):
# this cuts down the number of queries considerably since all ancestors
# will already be in include_pages when they are checked, thus not
# trigger additional queries.
for p in qs2.order_by('rght').iterator():
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:
ancestor_id_list = p.get_ancestors().values_list('id', flat=True)
ancestor_id_list = p.get_ancestors().values_list("id", flat=True)
include_pages.update(ancestor_id_list)
if include_pages:
@ -400,10 +408,10 @@ def recursetree(parser, token):
"""
bits = token.contents.split()
if len(bits) != 2:
raise template.TemplateSyntaxError('%s tag requires a queryset' % bits[0])
raise template.TemplateSyntaxError("%s tag requires a queryset" % bits[0])
queryset_var = FilterExpression(bits[1], parser)
template_nodes = parser.parse(('endrecursetree',))
template_nodes = parser.parse(("endrecursetree",))
parser.delete_first_token()
return RecurseTreeNode(template_nodes, queryset_var)

View file

@ -1,68 +1,68 @@
# -*- coding: utf-8 -*-
from django.contrib.auth.models import User
from django.urls import reverse
from django.test import Client, TestCase
from django.urls import reverse
from django.utils.encoding import smart_text
from categories.models import Category
class TestCategoryAdmin(TestCase):
def setUp(self):
self.client = Client()
def test_adding_parent_and_child(self):
User.objects.create_superuser('testuser', 'testuser@example.com', 'password')
self.client.login(username='testuser', password='password')
url = reverse('admin:categories_category_add')
User.objects.create_superuser("testuser", "testuser@example.com", "password")
self.client.login(username="testuser", password="password")
url = reverse("admin:categories_category_add")
data = {
'parent': '',
'name': smart_text('Parent Catégory'),
'thumbnail': '',
'filename': '',
'active': 'on',
'alternate_title': '',
'alternate_url': '',
'description': '',
'meta_keywords': '',
'meta_extra': '',
'order': 0,
'slug': 'parent',
'_save': '_save',
'categoryrelation_set-TOTAL_FORMS': '0',
'categoryrelation_set-INITIAL_FORMS': '0',
'categoryrelation_set-MIN_NUM_FORMS': '1000',
'categoryrelation_set-MAX_NUM_FORMS': '1000',
"parent": "",
"name": smart_text("Parent Catégory"),
"thumbnail": "",
"filename": "",
"active": "on",
"alternate_title": "",
"alternate_url": "",
"description": "",
"meta_keywords": "",
"meta_extra": "",
"order": 0,
"slug": "parent",
"_save": "_save",
"categoryrelation_set-TOTAL_FORMS": "0",
"categoryrelation_set-INITIAL_FORMS": "0",
"categoryrelation_set-MIN_NUM_FORMS": "1000",
"categoryrelation_set-MAX_NUM_FORMS": "1000",
}
resp = self.client.post(url, data=data)
self.assertEqual(resp.status_code, 302)
self.assertEqual(1, Category.objects.count())
# update parent
data.update({'name': smart_text('Parent Catégory (Changed)')})
resp = self.client.post(reverse('admin:categories_category_change', args=(1,)), data=data)
data.update({"name": smart_text("Parent Catégory (Changed)")})
resp = self.client.post(reverse("admin:categories_category_change", args=(1,)), data=data)
self.assertEqual(resp.status_code, 302)
self.assertEqual(1, Category.objects.count())
# add a child
data.update({
'parent': '1',
'name': smart_text('Child Catégory'),
'slug': smart_text('child-category'),
})
data.update(
{
"parent": "1",
"name": smart_text("Child Catégory"),
"slug": smart_text("child-category"),
}
)
resp = self.client.post(url, data=data)
self.assertEqual(resp.status_code, 302)
self.assertEqual(2, Category.objects.count())
# update child
data.update({'name': 'Child (Changed)'})
resp = self.client.post(reverse('admin:categories_category_change', args=(2,)), data=data)
data.update({"name": "Child (Changed)"})
resp = self.client.post(reverse("admin:categories_category_change", args=(2,)), data=data)
self.assertEqual(resp.status_code, 302)
self.assertEqual(2, Category.objects.count())
# test the admin list view
url = reverse('admin:categories_category_changelist')
url = reverse("admin:categories_category_changelist")
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)

View file

@ -4,20 +4,21 @@
import os
from django.conf import settings
from django.test import TestCase, override_settings
from categories.models import Category
from categories.management.commands.import_categories import Command
from django.core.management.base import CommandError
from django.test import TestCase, override_settings
from categories.management.commands.import_categories import Command
from categories.models import Category
@override_settings(INSTALLED_APPS=(app for app in settings.INSTALLED_APPS if app != 'django.contrib.flatpages'))
@override_settings(INSTALLED_APPS=(app for app in settings.INSTALLED_APPS if app != "django.contrib.flatpages"))
class CategoryImportTest(TestCase):
def setUp(self):
pass
def _import_file(self, filename):
root_cats = ['Category 1', 'Category 2', 'Category 3']
testfile = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'fixtures', filename))
root_cats = ["Category 1", "Category 2", "Category 3"]
testfile = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), "fixtures", filename))
cmd = Command()
cmd.handle(testfile)
roots = Category.tree.root_nodes()
@ -26,31 +27,31 @@ class CategoryImportTest(TestCase):
for item in roots:
assert item.name in root_cats
cat2 = Category.objects.get(name='Category 2')
cat2 = Category.objects.get(name="Category 2")
cat21 = cat2.children.all()[0]
self.assertEqual(cat21.name, 'Category 2-1')
self.assertEqual(cat21.name, "Category 2-1")
cat211 = cat21.children.all()[0]
self.assertEqual(cat211.name, 'Category 2-1-1')
self.assertEqual(cat211.name, "Category 2-1-1")
def testImportSpaceDelimited(self):
Category.objects.all().delete()
self._import_file('test_category_spaces.txt')
self._import_file("test_category_spaces.txt")
items = Category.objects.all()
self.assertEqual(items[0].name, 'Category 1')
self.assertEqual(items[1].name, 'Category 1-1')
self.assertEqual(items[2].name, 'Category 1-2')
self.assertEqual(items[0].name, "Category 1")
self.assertEqual(items[1].name, "Category 1-1")
self.assertEqual(items[2].name, "Category 1-2")
def testImportTabDelimited(self):
Category.objects.all().delete()
self._import_file('test_category_tabs.txt')
self._import_file("test_category_tabs.txt")
items = Category.objects.all()
self.assertEqual(items[0].name, 'Category 1')
self.assertEqual(items[1].name, 'Category 1-1')
self.assertEqual(items[2].name, 'Category 1-2')
self.assertEqual(items[0].name, "Category 1")
self.assertEqual(items[1].name, "Category 1-1")
self.assertEqual(items[2].name, "Category 1-2")
def testMixingTabsSpaces(self):
"""

View file

@ -1,10 +1,11 @@
# test active returns only active items
from django.test import TestCase
from categories.models import Category
class CategoryManagerTest(TestCase):
fixtures = ['categories.json']
fixtures = ["categories.json"]
def setUp(self):
pass
@ -16,7 +17,7 @@ class CategoryManagerTest(TestCase):
all_count = Category.objects.all().count()
self.assertEqual(Category.objects.active().count(), all_count)
cat1 = Category.objects.get(name='Category 1')
cat1 = Category.objects.get(name="Category 1")
cat1.active = False
cat1.save()

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.core import management
from django.core.management.base import CommandError
from django.db import connection
@ -6,7 +5,6 @@ from django.test import TestCase
class TestMgmtCommands(TestCase):
@classmethod
def setUpClass(cls):
connection.disable_constraint_checking()
@ -18,13 +16,13 @@ class TestMgmtCommands(TestCase):
connection.enable_constraint_checking()
def test_add_category_fields(self):
management.call_command('add_category_fields', verbosity=0)
management.call_command("add_category_fields", verbosity=0)
def test_add_category_fields_app(self):
management.call_command('add_category_fields', 'flatpages', verbosity=0)
management.call_command("add_category_fields", "flatpages", verbosity=0)
def test_drop_category_field(self):
management.call_command('drop_category_field', 'flatpages', 'flatpage', 'category', verbosity=0)
management.call_command("drop_category_field", "flatpages", "flatpage", "category", verbosity=0)
def test_drop_category_field_error(self):
self.assertRaises(CommandError, management.call_command, 'drop_category_field', verbosity=0)
self.assertRaises(CommandError, management.call_command, "drop_category_field", verbosity=0)

View file

@ -2,20 +2,19 @@ import os
from django.core.files import File
from django.core.files.uploadedfile import UploadedFile
from django.test import TestCase
from categories.models import Category
from django.test import TestCase
class TestCategoryThumbnail(TestCase):
def test_thumbnail(self):
file_name = 'test_image.jpg'
file_name = "test_image.jpg"
with open(os.path.join(os.path.dirname(__file__), file_name), 'rb') as f:
test_image = UploadedFile(File(f), content_type='image/jpeg')
category = Category.objects.create(name='Test Category', slug='test-category', thumbnail=test_image)
with open(os.path.join(os.path.dirname(__file__), file_name), "rb") as f:
test_image = UploadedFile(File(f), content_type="image/jpeg")
category = Category.objects.create(name="Test Category", slug="test-category", thumbnail=test_image)
self.assertEqual(category.pk, 1)
self.assertEqual(category.thumbnail_width, 640)
self.assertEqual(category.thumbnail_height, 480)

View file

@ -14,43 +14,39 @@ class CategoryRegistrationTest(TestCase):
"""
def test_foreignkey_string(self):
FK_REGISTRY = {
'flatpages.flatpage': 'category'
}
FK_REGISTRY = {"flatpages.flatpage": "category"}
_process_registry(FK_REGISTRY, registry.register_fk)
from django.contrib.flatpages.models import FlatPage
self.assertTrue('category' in [f.name for f in FlatPage()._meta.get_fields()])
self.assertTrue("category" in [f.name for f in FlatPage()._meta.get_fields()])
def test_foreignkey_dict(self):
FK_REGISTRY = {
'flatpages.flatpage': {'name': 'category'}
}
FK_REGISTRY = {"flatpages.flatpage": {"name": "category"}}
_process_registry(FK_REGISTRY, registry.register_fk)
from django.contrib.flatpages.models import FlatPage
self.assertTrue('category' in [f.name for f in FlatPage()._meta.get_fields()])
self.assertTrue("category" in [f.name for f in FlatPage()._meta.get_fields()])
def test_foreignkey_list(self):
FK_REGISTRY = {
'flatpages.flatpage': (
{'name': 'category', 'related_name': 'cats'},
)
}
FK_REGISTRY = {"flatpages.flatpage": ({"name": "category", "related_name": "cats"},)}
_process_registry(FK_REGISTRY, registry.register_fk)
from django.contrib.flatpages.models import FlatPage
self.assertTrue('category' in [f.name for f in FlatPage()._meta.get_fields()])
self.assertTrue("category" in [f.name for f in FlatPage()._meta.get_fields()])
if django.VERSION[1] >= 7:
def test_new_foreignkey_string(self):
registry.register_model('flatpages', 'flatpage', 'ForeignKey', 'category')
registry.register_model("flatpages", "flatpage", "ForeignKey", "category")
from django.contrib.flatpages.models import FlatPage
self.assertTrue('category' in [f.name for f in FlatPage()._meta.get_fields()])
self.assertTrue("category" in [f.name for f in FlatPage()._meta.get_fields()])
class Categorym2mTest(TestCase):
def test_m2m_string(self):
M2M_REGISTRY = {
'flatpages.flatpage': 'categories'
}
M2M_REGISTRY = {"flatpages.flatpage": "categories"}
_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()])
self.assertTrue("category" in [f.name for f in FlatPage()._meta.get_fields()])

View file

@ -1,13 +1,14 @@
from django.test import TestCase
from django import template
import re
from django import template
from django.test import TestCase
from categories.models import Category
class CategoryTagsTest(TestCase):
fixtures = ['musicgenres.json']
fixtures = ["musicgenres.json"]
def render_template(self, template_string, context={}):
"""
@ -21,7 +22,9 @@ class CategoryTagsTest(TestCase):
"""
Ensure that get_category raises an exception if there aren't enough arguments.
"""
self.assertRaises(template.TemplateSyntaxError, self.render_template, '{% load category_tags %}{% get_category %}')
self.assertRaises(
template.TemplateSyntaxError, self.render_template, "{% load category_tags %}{% get_category %}"
)
def testBasicUsage(self):
"""
@ -30,46 +33,50 @@ class CategoryTagsTest(TestCase):
# display_path_as_ul
rock_resp = '<ul><li><a href="/categories/">Top</a></li></ul>'
resp = self.render_template('{% load category_tags %}{% display_path_as_ul "/Rock" %}')
resp = re.sub(r'\n$', "", resp)
resp = re.sub(r"\n$", "", resp)
self.assertEqual(resp, rock_resp)
# 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 %}'
'{% display_drilldown_as_ul "/World/Worldbeat" "categories.category" %}')
resp = re.sub(r'\n$', "", resp)
"{% 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> &gt; Worldbeat'
resp = self.render_template(
'{% load category_tags %}'
'{% breadcrumbs "/World/Worldbeat" " &gt; " "categories.category" %}')
"{% load category_tags %}" '{% breadcrumbs "/World/Worldbeat" " &gt; " "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|'
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 %}'
"{% load category_tags %}"
'{% get_top_level_categories using "categories.category" as varname %}'
'{% for item in varname %}{{ item }}|{% endfor %}')
"{% for item in varname %}{{ item }}|{% endfor %}"
)
self.assertEqual(resp, expected_resp)
# get_category_drilldown
expected_resp = "World|World &gt; Worldbeat|"
resp = self.render_template(
'{% load category_tags %}'
"{% load category_tags %}"
'{% get_category_drilldown "/World" using "categories.category" as var %}'
'{% for item in var %}{{ item }}|{% endfor %}')
"{% for item in var %}{{ item }}|{% endfor %}"
)
self.assertEqual(resp, expected_resp)
# 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"))}
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 %}'
'<ul>{% recursetree nodes|tree_queryset %}<li>{{ node.name }}'
'{% if not node.is_leaf_node %}<ul>{{ children }}'
'</ul>{% endif %}</li>{% endrecursetree %}</ul>', ctxt)
"{% 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,
)
self.assertEqual(resp, expected_resp)

View file

@ -1,8 +1,9 @@
from django.http import Http404
from django.contrib.auth.models import AnonymousUser
from django.test import Client, TestCase, RequestFactory
from categories.models import Category, CategoryRelation
from django.http import Http404
from django.test import Client, RequestFactory, TestCase
from categories import views
from categories.models import Category, CategoryRelation
class MyCategoryRelationView(views.CategoryRelatedDetail):
@ -10,16 +11,18 @@ class MyCategoryRelationView(views.CategoryRelatedDetail):
class TestCategoryViews(TestCase):
fixtures = ['musicgenres.json', ]
fixtures = [
"musicgenres.json",
]
def setUp(self):
self.client = Client()
self.factory = RequestFactory()
def test_category_detail(self):
cat0 = Category.objects.get(slug='country', level=0)
cat1 = cat0.children.get(slug='country-pop')
cat2 = Category.objects.get(slug='urban-cowboy')
cat0 = Category.objects.get(slug="country", level=0)
cat1 = cat0.children.get(slug="country-pop")
cat2 = Category.objects.get(slug="urban-cowboy")
url = cat0.get_absolute_url()
response = self.client.get(url)
self.assertEquals(response.status_code, 200)
@ -33,51 +36,48 @@ class TestCategoryViews(TestCase):
self.assertEquals(response.status_code, 404)
def test_get_category_for_path(self):
cat0 = Category.objects.get(slug='country', level=0)
cat1 = cat0.children.get(slug='country-pop')
cat2 = Category.objects.get(slug='urban-cowboy')
cat0 = Category.objects.get(slug="country", level=0)
cat1 = cat0.children.get(slug="country-pop")
cat2 = Category.objects.get(slug="urban-cowboy")
result = views.get_category_for_path('/country/country-pop/urban-cowboy/')
result = views.get_category_for_path("/country/country-pop/urban-cowboy/")
self.assertEquals(result, cat2)
result = views.get_category_for_path('/country/country-pop/')
result = views.get_category_for_path("/country/country-pop/")
self.assertEquals(result, cat1)
result = views.get_category_for_path('/country/')
result = views.get_category_for_path("/country/")
self.assertEquals(result, cat0)
def test_categorydetailview(self):
request = self.factory.get('')
request = self.factory.get("")
request.user = AnonymousUser()
self.assertRaises(AttributeError, views.CategoryDetailView.as_view(), request)
request = self.factory.get('')
request = self.factory.get("")
request.user = AnonymousUser()
response = views.CategoryDetailView.as_view()(request, path='/country/country-pop/urban-cowboy/')
response = views.CategoryDetailView.as_view()(request, path="/country/country-pop/urban-cowboy/")
self.assertEquals(response.status_code, 200)
request = self.factory.get('')
request = self.factory.get("")
request.user = AnonymousUser()
self.assertRaises(Http404, views.CategoryDetailView.as_view(), request, path='/country/country-pop/foo/')
self.assertRaises(Http404, views.CategoryDetailView.as_view(), request, path="/country/country-pop/foo/")
def test_categoryrelateddetailview(self):
from simpletext.models import SimpleText
stext = SimpleText.objects.create(
name='Test',
description='test description'
)
cat = Category.objects.get(slug='urban-cowboy')
cat_rel = CategoryRelation.objects.create( # NOQA
category=cat,
content_object=stext
)
request = self.factory.get('')
stext = SimpleText.objects.create(name="Test", description="test description")
cat = Category.objects.get(slug="urban-cowboy")
cat_rel = CategoryRelation.objects.create(category=cat, content_object=stext) # NOQA
request = self.factory.get("")
request.user = AnonymousUser()
self.assertRaises(AttributeError, MyCategoryRelationView.as_view(), request)
request = self.factory.get('')
request = self.factory.get("")
request.user = AnonymousUser()
response = MyCategoryRelationView.as_view()(request, category_path='/country/country-pop/urban-cowboy/')
response = MyCategoryRelationView.as_view()(request, category_path="/country/country-pop/urban-cowboy/")
self.assertEquals(response.status_code, 200)
request = self.factory.get('')
request = self.factory.get("")
request.user = AnonymousUser()
self.assertRaises(Http404, MyCategoryRelationView.as_view(), request, category_path='/country/country-pop/foo/')
self.assertRaises(
Http404, MyCategoryRelationView.as_view(), request, category_path="/country/country-pop/foo/"
)

View file

@ -1,19 +1,11 @@
from django.conf.urls import url
from django.views.generic import ListView
from .models import Category
from . import views
from .models import Category
categorytree_dict = {"queryset": Category.objects.filter(level=0)}
categorytree_dict = {
'queryset': Category.objects.filter(level=0)
}
urlpatterns = (url(r"^$", ListView.as_view(**categorytree_dict), name="categories_tree_list"),)
urlpatterns = (
url(
r'^$', ListView.as_view(**categorytree_dict), name='categories_tree_list'
),
)
urlpatterns += (
url(r'^(?P<path>.+)/$', views.category_detail, name='categories_category'),
)
urlpatterns += (url(r"^(?P<path>.+)/$", views.category_detail, name="categories_category"),)

View file

@ -1,5 +1,5 @@
from django.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404
from django.http import HttpResponse, Http404
from django.template.loader import select_template
from django.utils.translation import ugettext_lazy as _
from django.views.generic import DetailView, ListView
@ -7,139 +7,126 @@ from django.views.generic import DetailView, ListView
from .models import Category
def category_detail(request, path, template_name='categories/category_detail.html', extra_context={}):
path_items = path.strip('/').split('/')
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,
slug__iexact=path_items[-1],
level=len(path_items) - 1,
parent__slug__iexact=path_items[-2])
Category, slug__iexact=path_items[-1], level=len(path_items) - 1, parent__slug__iexact=path_items[-2]
)
else:
category = get_object_or_404(
Category,
slug__iexact=path_items[-1],
level=len(path_items) - 1)
category = get_object_or_404(Category, slug__iexact=path_items[-1], level=len(path_items) - 1)
templates = []
while path_items:
templates.append('categories/%s.html' % '_'.join(path_items))
templates.append("categories/%s.html" % "_".join(path_items))
path_items.pop()
templates.append(template_name)
context = {'category': category}
context = {"category": category}
if extra_context:
context.update(extra_context)
return HttpResponse(select_template(templates).render(context))
def get_category_for_path(path, queryset=Category.objects.all()):
path_items = path.strip('/').split('/')
path_items = path.strip("/").split("/")
if len(path_items) >= 2:
queryset = queryset.filter(
slug__iexact=path_items[-1],
level=len(path_items) - 1,
parent__slug__iexact=path_items[-2])
slug__iexact=path_items[-1], level=len(path_items) - 1, parent__slug__iexact=path_items[-2]
)
else:
queryset = queryset.filter(
slug__iexact=path_items[-1],
level=len(path_items) - 1)
queryset = queryset.filter(slug__iexact=path_items[-1], level=len(path_items) - 1)
return queryset.get()
class CategoryDetailView(DetailView):
model = Category
path_field = 'path'
path_field = "path"
def get_object(self, **kwargs):
if self.path_field not in self.kwargs:
raise AttributeError("Category detail view %s must be called with "
"a %s." % (self.__class__.__name__, self.path_field))
raise AttributeError(
"Category detail view %s must be called with " "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], self.model.objects.all())
except Category.DoesNotExist:
raise Http404(_("No %(verbose_name)s found matching the query") %
{'verbose_name': queryset.model._meta.verbose_name})
raise Http404(
_("No %(verbose_name)s found matching the query") % {"verbose_name": queryset.model._meta.verbose_name}
)
def get_template_names(self):
names = []
path_items = self.kwargs[self.path_field].strip('/').split('/')
path_items = self.kwargs[self.path_field].strip("/").split("/")
while path_items:
names.append('categories/%s.html' % '_'.join(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'
path_field = "category_path"
object_name_field = None
def get_object(self, **kwargs):
if self.path_field not in self.kwargs:
raise AttributeError("Category detail view %s must be called with "
"a %s." % (self.__class__.__name__, self.path_field))
raise AttributeError(
"Category detail view %s must be called with " "a %s." % (self.__class__.__name__, self.path_field)
)
queryset = super(CategoryRelatedDetail, self).get_queryset()
try:
category = get_category_for_path(self.kwargs[self.path_field])
except Category.DoesNotExist:
raise Http404(_("No %(verbose_name)s found matching the query") %
{'verbose_name': queryset.model._meta.verbose_name})
raise Http404(
_("No %(verbose_name)s found matching the query") % {"verbose_name": queryset.model._meta.verbose_name}
)
return queryset.get(category=category)
def get_template_names(self):
names = []
opts = self.object._meta
path_items = self.kwargs[self.path_field].strip('/').split('/')
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)
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.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'
path_field = "category_path"
def get_queryset(self):
if self.path_field not in self.kwargs:
raise AttributeError("Category detail view %s must be called with "
"a %s." % (self.__class__.__name__, self.path_field))
raise AttributeError(
"Category detail view %s must be called with " "a %s." % (self.__class__.__name__, self.path_field)
)
queryset = super(CategoryRelatedList, self).get_queryset()
category = get_category_for_path(self.kwargs[self.path_field])
return queryset.filter(category=category)
def get_template_names(self):
names = []
if hasattr(self.object_list, 'model'):
if hasattr(self.object_list, "model"):
opts = self.object_list.model._meta
path_items = self.kwargs[self.path_field].strip('/').split('/')
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)
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.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

View file

@ -7,4 +7,4 @@ class SimpleCategory(CategoryBase):
"""
class Meta:
verbose_name_plural = 'simple categories'
verbose_name_plural = "simple categories"

View file

@ -5,29 +5,25 @@ from categories.base import CategoryBase
class Category(CategoryBase):
thumbnail = models.FileField(
upload_to=settings.THUMBNAIL_UPLOAD_PATH,
null=True, blank=True,
storage=settings.THUMBNAIL_STORAGE,)
null=True,
blank=True,
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)
alternate_title = models.CharField(
blank=True,
default="",
max_length=100,
help_text="An alternative title to use on pages with this category.")
blank=True, default="", max_length=100, help_text="An alternative title to use on pages with this category."
)
alternate_url = models.CharField(
blank=True,
max_length=200,
help_text="An alternative URL to use instead of the one derived from "
"the category hierarchy.")
help_text="An alternative URL to use instead of the one derived from " "the category hierarchy.",
)
description = models.TextField(blank=True, null=True)
meta_keywords = models.CharField(
blank=True,
default="",
max_length=255,
help_text="Comma-separated keywords for search engines.")
blank=True, default="", max_length=255, help_text="Comma-separated keywords for search engines."
)
meta_extra = models.TextField(
blank=True,
default="",
help_text="(Advanced) Any additional HTML to be placed verbatim "
"in the &lt;head&gt;")
blank=True, default="", help_text="(Advanced) Any additional HTML to be placed verbatim " "in the &lt;head&gt;"
)

View file

@ -3,8 +3,9 @@ from categories.models import Category
def save(self, *args, **kwargs):
if self.thumbnail:
from django.core.files.images import get_image_dimensions
import django
from django.core.files.images import get_image_dimensions
if django.VERSION[1] < 2:
width, height = get_image_dimensions(self.thumbnail.file)
else:

View file

@ -2,8 +2,8 @@ from categories.base import CategoryBase
class Meta(CategoryBase.Meta):
verbose_name_plural = 'categories'
verbose_name_plural = "categories"
class MPTTMeta:
order_insertion_by = ('order', 'name')
order_insertion_by = ("order", "name")

View file

@ -7,7 +7,7 @@ class CategoryAdminForm(CategoryBaseAdminForm):
model = Category
def clean_alternate_title(self):
if self.instance is None or not self.cleaned_data['alternate_title']:
return self.cleaned_data['name']
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"]

View file

@ -4,18 +4,21 @@ from categories.base import CategoryBaseAdmin
class CategoryAdmin(CategoryBaseAdmin):
form = CategoryAdminForm
list_display = ('name', 'alternate_title', 'active')
list_display = ("name", "alternate_title", "active")
fieldsets = (
(None, {
'fields': ('parent', 'name', 'thumbnail', 'active')
}),
('Meta Data', {
'fields': ('alternate_title', 'alternate_url', 'description',
'meta_keywords', 'meta_extra'),
'classes': ('collapse',),
}),
('Advanced', {
'fields': ('order', 'slug'),
'classes': ('collapse',),
}),
(None, {"fields": ("parent", "name", "thumbnail", "active")}),
(
"Meta Data",
{
"fields": ("alternate_title", "alternate_url", "description", "meta_keywords", "meta_extra"),
"classes": ("collapse",),
},
),
(
"Advanced",
{
"fields": ("order", "slug"),
"classes": ("collapse",),
},
),
)

View file

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
#
# Django Categories documentation build configuration file, created by
# sphinx-quickstart on Tue Oct 6 07:53:33 2009.
@ -11,14 +10,14 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
import sys
# 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
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.append(os.path.abspath('..'))
os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings'
sys.path.append(os.path.abspath(".."))
os.environ["DJANGO_SETTINGS_MODULE"] = "example.settings"
import categories # noqa
@ -32,17 +31,17 @@ extensions = []
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
source_suffix = ".rst"
# The encoding of source files.
# ource_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# General information about the project.
project = 'Django Categories'
copyright = '2010-2012, Corey Oordt'
project = "Django Categories"
copyright = "2010-2012, Corey Oordt"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@ -68,7 +67,7 @@ release = categories.get_version()
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['_build']
exclude_trees = ["_build"]
# The reST default role (used for this markup: `text`) to use for all documents.
# efault_role = None
@ -85,7 +84,7 @@ exclude_trees = ['_build']
# how_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# odindex_common_prefix = []
@ -124,7 +123,7 @@ html_theme = 'default'
# 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,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
@ -162,7 +161,7 @@ html_static_path = ['_static']
# tml_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'DjangoCategoriesdoc'
htmlhelp_basename = "DjangoCategoriesdoc"
# -- Options for LaTeX output --------------------------------------------------
@ -176,7 +175,7 @@ htmlhelp_basename = 'DjangoCategoriesdoc'
# 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

View file

@ -41,4 +41,3 @@ Indices and tables
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View file

@ -59,4 +59,3 @@ comma-separated list of feature names. The valid feature names are:
Books -> []
Sci-fi -> [u'Books']
Dystopian Futures -> [u'Books', u'Sci-fi']

View file

@ -16,108 +16,105 @@ ADMINS = (
MANAGERS = ADMINS
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'dev.db',
'USER': '',
'PASSWORD': '',
'HOST': '',
'PORT': '',
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": "dev.db",
"USER": "",
"PASSWORD": "",
"HOST": "",
"PORT": "",
}
}
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.flatpages',
'categories',
'categories.editor',
'mptt',
'simpletext',
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.sites",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.flatpages",
"categories",
"categories.editor",
"mptt",
"simpletext",
)
TIME_ZONE = 'America/Chicago'
TIME_ZONE = "America/Chicago"
LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = "en-us"
SITE_ID = 1
USE_I18N = True
MEDIA_ROOT = os.path.abspath(os.path.join(PROJ_ROOT, 'media', 'uploads'))
MEDIA_ROOT = os.path.abspath(os.path.join(PROJ_ROOT, "media", "uploads"))
MEDIA_URL = '/uploads/'
MEDIA_URL = "/uploads/"
STATIC_ROOT = os.path.abspath(os.path.join(PROJ_ROOT, 'media', 'static'))
STATIC_ROOT = os.path.abspath(os.path.join(PROJ_ROOT, "media", "static"))
STATIC_URL = '/static/'
STATIC_URL = "/static/"
STATICFILES_DIRS = ()
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
"django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
)
SECRET_KEY = 'bwq#m)-zsey-fs)0#4*o=2z(v5g!ei=zytl9t-1hesh4b&-u^d'
SECRET_KEY = "bwq#m)-zsey-fs)0#4*o=2z(v5g!ei=zytl9t-1hesh4b&-u^d"
MIDDLEWARE = (
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
)
ROOT_URLCONF = 'urls'
ROOT_URLCONF = "urls"
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'APP_DIRS': True,
'DIRS': [os.path.abspath(os.path.join(os.path.dirname(__file__), 'templates'))],
'OPTIONS': {
'debug': DEBUG,
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.contrib.messages.context_processors.messages',
"BACKEND": "django.template.backends.django.DjangoTemplates",
"APP_DIRS": True,
"DIRS": [os.path.abspath(os.path.join(os.path.dirname(__file__), "templates"))],
"OPTIONS": {
"debug": DEBUG,
"context_processors": [
"django.contrib.auth.context_processors.auth",
"django.template.context_processors.debug",
"django.template.context_processors.i18n",
"django.template.context_processors.media",
"django.template.context_processors.static",
"django.template.context_processors.tz",
"django.contrib.messages.context_processors.messages",
],
}
},
},
]
CATEGORIES_SETTINGS = {
'ALLOW_SLUG_CHANGE': True,
'RELATION_MODELS': ['simpletext.simpletext', 'flatpages.flatpage'],
'FK_REGISTRY': {
'flatpages.flatpage': (
'category',
{'on_delete': models.CASCADE}
),
'simpletext.simpletext': (
'primary_category',
{'name': 'secondary_category', 'related_name': 'simpletext_sec_cat'},
"ALLOW_SLUG_CHANGE": True,
"RELATION_MODELS": ["simpletext.simpletext", "flatpages.flatpage"],
"FK_REGISTRY": {
"flatpages.flatpage": ("category", {"on_delete": models.CASCADE}),
"simpletext.simpletext": (
"primary_category",
{"name": "secondary_category", "related_name": "simpletext_sec_cat"},
),
},
'M2M_REGISTRY': {
"M2M_REGISTRY": {
# 'simpletext.simpletext': {'name': 'categories', 'related_name': 'm2mcats'},
'flatpages.flatpage': (
{'name': 'other_categories', 'related_name': 'other_cats'},
{'name': 'more_categories', 'related_name': 'more_cats'},
"flatpages.flatpage": (
{"name": "other_categories", "related_name": "other_cats"},
{"name": "more_categories", "related_name": "more_cats"},
),
},
}
TEST_RUNNER = 'django.test.runner.DiscoverRunner'
TEST_RUNNER = "django.test.runner.DiscoverRunner"

View file

@ -1,6 +1,7 @@
# Django settings for sample project.
import os
import sys
import django
from django.db import models
@ -16,109 +17,106 @@ ADMINS = (
MANAGERS = ADMINS
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'dev.db',
'USER': '',
'PASSWORD': '',
'HOST': '',
'PORT': '',
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": "dev.db",
"USER": "",
"PASSWORD": "",
"HOST": "",
"PORT": "",
}
}
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.flatpages',
'categories',
'categories.editor',
'mptt',
'simpletext',
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.sites",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.flatpages",
"categories",
"categories.editor",
"mptt",
"simpletext",
)
TIME_ZONE = 'America/Chicago'
TIME_ZONE = "America/Chicago"
LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = "en-us"
SITE_ID = 1
USE_I18N = True
MEDIA_ROOT = os.path.abspath(os.path.join(PROJ_ROOT, 'media', 'uploads'))
MEDIA_ROOT = os.path.abspath(os.path.join(PROJ_ROOT, "media", "uploads"))
MEDIA_URL = '/uploads/'
MEDIA_URL = "/uploads/"
STATIC_ROOT = os.path.abspath(os.path.join(PROJ_ROOT, 'media', 'static'))
STATIC_ROOT = os.path.abspath(os.path.join(PROJ_ROOT, "media", "static"))
STATIC_URL = '/static/'
STATIC_URL = "/static/"
STATICFILES_DIRS = ()
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
"django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
)
SECRET_KEY = 'bwq#m)-zsey-fs)0#4*o=2z(v5g!ei=zytl9t-1hesh4b&-u^d'
SECRET_KEY = "bwq#m)-zsey-fs)0#4*o=2z(v5g!ei=zytl9t-1hesh4b&-u^d"
MIDDLEWARE = (
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
)
ROOT_URLCONF = 'urls'
ROOT_URLCONF = "urls"
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'APP_DIRS': True,
'DIRS': [os.path.abspath(os.path.join(os.path.dirname(__file__), 'templates'))],
'OPTIONS': {
'debug': DEBUG,
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.contrib.messages.context_processors.messages',
"BACKEND": "django.template.backends.django.DjangoTemplates",
"APP_DIRS": True,
"DIRS": [os.path.abspath(os.path.join(os.path.dirname(__file__), "templates"))],
"OPTIONS": {
"debug": DEBUG,
"context_processors": [
"django.contrib.auth.context_processors.auth",
"django.template.context_processors.debug",
"django.template.context_processors.i18n",
"django.template.context_processors.media",
"django.template.context_processors.static",
"django.template.context_processors.tz",
"django.contrib.messages.context_processors.messages",
],
}
},
}
]
CATEGORIES_SETTINGS = {
'ALLOW_SLUG_CHANGE': True,
'RELATION_MODELS': ['simpletext.simpletext', 'flatpages.flatpage'],
'FK_REGISTRY': {
'flatpages.flatpage': (
'category',
{'on_delete': models.CASCADE}
),
'simpletext.simpletext': (
'primary_category',
{'name': 'secondary_category', 'related_name': 'simpletext_sec_cat'},
"ALLOW_SLUG_CHANGE": True,
"RELATION_MODELS": ["simpletext.simpletext", "flatpages.flatpage"],
"FK_REGISTRY": {
"flatpages.flatpage": ("category", {"on_delete": models.CASCADE}),
"simpletext.simpletext": (
"primary_category",
{"name": "secondary_category", "related_name": "simpletext_sec_cat"},
),
},
'M2M_REGISTRY': {
'simpletext.simpletext': {'name': 'categories', 'related_name': 'm2mcats'},
'flatpages.flatpage': (
{'name': 'other_categories', 'related_name': 'other_cats'},
{'name': 'more_categories', 'related_name': 'more_cats'},
"M2M_REGISTRY": {
"simpletext.simpletext": {"name": "categories", "related_name": "m2mcats"},
"flatpages.flatpage": (
{"name": "other_categories", "related_name": "other_cats"},
{"name": "more_categories", "related_name": "more_cats"},
),
},
}
if django.VERSION[1] > 5:
TEST_RUNNER = 'django.test.runner.DiscoverRunner'
TEST_RUNNER = "django.test.runner.DiscoverRunner"

View file

@ -1,21 +1,28 @@
from .models import SimpleText, SimpleCategory
from django.contrib import admin
from categories.admin import CategoryBaseAdmin, CategoryBaseAdminForm
from .models import SimpleCategory, SimpleText
class SimpleTextAdmin(admin.ModelAdmin):
fieldsets = (
(None, {
'fields': ('name', 'description', )
}),
(
None,
{
"fields": (
"name",
"description",
)
},
),
)
class SimpleCategoryAdminForm(CategoryBaseAdminForm):
class Meta:
model = SimpleCategory
fields = '__all__'
fields = "__all__"
class SimpleCategoryAdmin(CategoryBaseAdmin):

View file

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2017-10-12 20:27
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import mptt.fields
from django.db import migrations, models
class Migration(migrations.Migration):
@ -15,35 +14,45 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
name='SimpleCategory',
name="SimpleCategory",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='name')),
('slug', models.SlugField(verbose_name='slug')),
('active', models.BooleanField(default=True, verbose_name='active')),
('lft', models.PositiveIntegerField(db_index=True, editable=False)),
('rght', models.PositiveIntegerField(db_index=True, editable=False)),
('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
('level', models.PositiveIntegerField(db_index=True, editable=False)),
('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='simpletext.SimpleCategory', verbose_name='parent')),
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("name", models.CharField(max_length=100, verbose_name="name")),
("slug", models.SlugField(verbose_name="slug")),
("active", models.BooleanField(default=True, verbose_name="active")),
("lft", models.PositiveIntegerField(db_index=True, editable=False)),
("rght", models.PositiveIntegerField(db_index=True, editable=False)),
("tree_id", models.PositiveIntegerField(db_index=True, editable=False)),
("level", models.PositiveIntegerField(db_index=True, editable=False)),
(
"parent",
mptt.fields.TreeForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="children",
to="simpletext.SimpleCategory",
verbose_name="parent",
),
),
],
options={
'verbose_name_plural': 'simple categories',
"verbose_name_plural": "simple categories",
},
),
migrations.CreateModel(
name='SimpleText',
name="SimpleText",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('description', models.TextField(blank=True)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("name", models.CharField(max_length=255)),
("description", models.TextField(blank=True)),
("created", models.DateTimeField(auto_now_add=True)),
("updated", models.DateTimeField(auto_now=True)),
],
options={
'ordering': ('-created',),
'get_latest_by': 'updated',
'verbose_name_plural': 'Simple Text',
"ordering": ("-created",),
"get_latest_by": "updated",
"verbose_name_plural": "Simple Text",
},
),
]

View file

@ -1,39 +1,46 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-12-04 07:21
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import django.db.models.manager
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('categories', '0002_auto_20170217_1111'),
('simpletext', '0001_initial'),
("categories", "0002_auto_20170217_1111"),
("simpletext", "0001_initial"),
]
operations = [
migrations.AlterModelManagers(
name='simplecategory',
name="simplecategory",
managers=[
('tree', django.db.models.manager.Manager()),
("tree", django.db.models.manager.Manager()),
],
),
migrations.AddField(
model_name='simpletext',
name='categories',
field=models.ManyToManyField(blank=True, related_name='m2mcats', to='categories.Category'),
model_name="simpletext",
name="categories",
field=models.ManyToManyField(blank=True, related_name="m2mcats", to="categories.Category"),
),
migrations.AddField(
model_name='simpletext',
name='primary_category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='categories.Category'),
model_name="simpletext",
name="primary_category",
field=models.ForeignKey(
blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="categories.Category"
),
),
migrations.AddField(
model_name='simpletext',
name='secondary_category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='simpletext_sec_cat', to='categories.Category'),
model_name="simpletext",
name="secondary_category",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="simpletext_sec_cat",
to="categories.Category",
),
),
]

View file

@ -6,23 +6,23 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('simpletext', '0002_auto_20171204_0721'),
("simpletext", "0002_auto_20171204_0721"),
]
operations = [
migrations.AlterField(
model_name='simplecategory',
name='level',
model_name="simplecategory",
name="level",
field=models.PositiveIntegerField(editable=False),
),
migrations.AlterField(
model_name='simplecategory',
name='lft',
model_name="simplecategory",
name="lft",
field=models.PositiveIntegerField(editable=False),
),
migrations.AlterField(
model_name='simplecategory',
name='rght',
model_name="simplecategory",
name="rght",
field=models.PositiveIntegerField(editable=False),
),
]

View file

@ -14,9 +14,9 @@ class SimpleText(models.Model):
updated = models.DateTimeField(auto_now=True)
class Meta:
verbose_name_plural = 'Simple Text'
ordering = ('-created',)
get_latest_by = 'updated'
verbose_name_plural = "Simple Text"
ordering = ("-created",)
get_latest_by = "updated"
def __unicode__(self):
return self.name
@ -24,16 +24,19 @@ class SimpleText(models.Model):
def get_absolute_url(self):
try:
from django.db.models import permalink
return permalink('simpletext_detail_view_name', [str(self.id)])
return permalink("simpletext_detail_view_name", [str(self.id)])
except ImportError:
from django.urls import reverse
return reverse('simpletext_detail_view_name', args=[str(self.id)])
return reverse("simpletext_detail_view_name", args=[str(self.id)])
class SimpleCategory(CategoryBase):
"""A Test of catgorizing"""
class Meta:
verbose_name_plural = 'simple categories'
verbose_name_plural = "simple categories"
# mport categories

View file

@ -16,9 +16,11 @@ class SimpleTest(TestCase):
self.assertEqual(1 + 1, 2)
__test__ = {"doctest": """
__test__ = {
"doctest": """
Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2
True
"""}
"""
}

View file

@ -12,23 +12,16 @@ ROOT_PATH = os.path.dirname(os.path.dirname(__file__))
urlpatterns = (
# Example:
# (r'^sample/', include('sample.foo.urls')),
# 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')),
# Uncomment the next line to enable the admin:
url(r'^admin/', admin.site.urls),
url(r'^categories/', include('categories.urls')),
url(r"^admin/", admin.site.urls),
url(r"^categories/", include("categories.urls")),
# r'^cats/', include('categories.urls')),
url(r'^static/categories/(?P<path>.*)$', serve,
{'document_root': ROOT_PATH + '/categories/media/categories/'}),
url(r"^static/categories/(?P<path>.*)$", serve, {"document_root": ROOT_PATH + "/categories/media/categories/"}),
# (r'^static/editor/(?P<path>.*)$', 'django.views.static.serve',
# {'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")}),
)