mirror of
https://github.com/jazzband/django-categories.git
synced 2026-03-16 22:30:24 +00:00
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
This commit is contained in:
parent
3bfe1ad131
commit
5961d6b80f
74 changed files with 1079 additions and 926 deletions
|
|
@ -199,4 +199,3 @@
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,19 @@
|
||||||
__version_info__ = {
|
__version_info__ = {"major": 1, "minor": 8, "micro": 0, "releaselevel": "final", "serial": 1}
|
||||||
'major': 1,
|
|
||||||
'minor': 8,
|
|
||||||
'micro': 0,
|
|
||||||
'releaselevel': 'final',
|
|
||||||
'serial': 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_version(short=False):
|
def get_version(short=False):
|
||||||
assert __version_info__['releaselevel'] in ('alpha', 'beta', 'final')
|
assert __version_info__["releaselevel"] in ("alpha", "beta", "final")
|
||||||
vers = ["%(major)i.%(minor)i" % __version_info__, ]
|
vers = [
|
||||||
if __version_info__['micro'] and not short:
|
"%(major)i.%(minor)i" % __version_info__,
|
||||||
|
]
|
||||||
|
if __version_info__["micro"] and not short:
|
||||||
vers.append(".%(micro)i" % __version_info__)
|
vers.append(".%(micro)i" % __version_info__)
|
||||||
if __version_info__['releaselevel'] != 'final' and not short:
|
if __version_info__["releaselevel"] != "final" and not short:
|
||||||
vers.append('%s%i' % (__version_info__['releaselevel'][0], __version_info__['serial']))
|
vers.append("%s%i" % (__version_info__["releaselevel"][0], __version_info__["serial"]))
|
||||||
return ''.join(vers)
|
return "".join(vers)
|
||||||
|
|
||||||
|
|
||||||
__version__ = get_version()
|
__version__ = get_version()
|
||||||
|
|
||||||
|
|
||||||
default_app_config = 'categories.apps.CategoriesConfig'
|
default_app_config = "categories.apps.CategoriesConfig"
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
from django.contrib import admin
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.contrib import admin
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from .base import CategoryBaseAdmin, CategoryBaseAdminForm
|
||||||
from .genericcollection import GenericCollectionTabularInline
|
from .genericcollection import GenericCollectionTabularInline
|
||||||
from .settings import RELATION_MODELS, JAVASCRIPT_URL, REGISTER_ADMIN
|
|
||||||
from .models import Category
|
from .models import Category
|
||||||
from .base import CategoryBaseAdminForm, CategoryBaseAdmin
|
from .settings import JAVASCRIPT_URL, MODEL_REGISTRY, REGISTER_ADMIN, RELATION_MODELS
|
||||||
from .settings import MODEL_REGISTRY
|
|
||||||
|
|
||||||
|
|
||||||
class NullTreeNodeChoiceField(forms.ModelChoiceField):
|
class NullTreeNodeChoiceField(forms.ModelChoiceField):
|
||||||
"""A ModelChoiceField for tree nodes."""
|
"""A ModelChoiceField for tree nodes."""
|
||||||
def __init__(self, level_indicator='---', *args, **kwargs):
|
|
||||||
|
def __init__(self, level_indicator="---", *args, **kwargs):
|
||||||
self.level_indicator = level_indicator
|
self.level_indicator = level_indicator
|
||||||
super(NullTreeNodeChoiceField, self).__init__(*args, **kwargs)
|
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
|
Creates labels which represent the tree level of each node when
|
||||||
generating option labels.
|
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:
|
if RELATION_MODELS:
|
||||||
|
|
@ -33,38 +33,43 @@ if RELATION_MODELS:
|
||||||
class CategoryAdminForm(CategoryBaseAdminForm):
|
class CategoryAdminForm(CategoryBaseAdminForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Category
|
model = Category
|
||||||
fields = '__all__'
|
fields = "__all__"
|
||||||
|
|
||||||
def clean_alternate_title(self):
|
def clean_alternate_title(self):
|
||||||
if self.instance is None or not self.cleaned_data['alternate_title']:
|
if self.instance is None or not self.cleaned_data["alternate_title"]:
|
||||||
return self.cleaned_data['name']
|
return self.cleaned_data["name"]
|
||||||
else:
|
else:
|
||||||
return self.cleaned_data['alternate_title']
|
return self.cleaned_data["alternate_title"]
|
||||||
|
|
||||||
|
|
||||||
class CategoryAdmin(CategoryBaseAdmin):
|
class CategoryAdmin(CategoryBaseAdmin):
|
||||||
form = CategoryAdminForm
|
form = CategoryAdminForm
|
||||||
list_display = ('name', 'alternate_title', 'active')
|
list_display = ("name", "alternate_title", "active")
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {
|
(None, {"fields": ("parent", "name", "thumbnail", "active")}),
|
||||||
'fields': ('parent', 'name', 'thumbnail', 'active')
|
(
|
||||||
}),
|
_("Meta Data"),
|
||||||
(_('Meta Data'), {
|
{
|
||||||
'fields': ('alternate_title', 'alternate_url', 'description',
|
"fields": ("alternate_title", "alternate_url", "description", "meta_keywords", "meta_extra"),
|
||||||
'meta_keywords', 'meta_extra'),
|
"classes": ("collapse",),
|
||||||
'classes': ('collapse',),
|
},
|
||||||
}),
|
),
|
||||||
(_('Advanced'), {
|
(
|
||||||
'fields': ('order', 'slug'),
|
_("Advanced"),
|
||||||
'classes': ('collapse',),
|
{
|
||||||
}),
|
"fields": ("order", "slug"),
|
||||||
|
"classes": ("collapse",),
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if RELATION_MODELS:
|
if RELATION_MODELS:
|
||||||
inlines = [InlineCategoryRelation, ]
|
inlines = [
|
||||||
|
InlineCategoryRelation,
|
||||||
|
]
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
js = (JAVASCRIPT_URL + 'genericcollections.js',)
|
js = (JAVASCRIPT_URL + "genericcollections.js",)
|
||||||
|
|
||||||
|
|
||||||
if REGISTER_ADMIN:
|
if REGISTER_ADMIN:
|
||||||
|
|
@ -72,18 +77,21 @@ if REGISTER_ADMIN:
|
||||||
|
|
||||||
for model, modeladmin in list(admin.site._registry.items()):
|
for model, modeladmin in list(admin.site._registry.items()):
|
||||||
if model in list(MODEL_REGISTRY.values()) and modeladmin.fieldsets:
|
if model in list(MODEL_REGISTRY.values()) and modeladmin.fieldsets:
|
||||||
fieldsets = getattr(modeladmin, 'fieldsets', ())
|
fieldsets = getattr(modeladmin, "fieldsets", ())
|
||||||
fields = [cat.split('.')[2] for cat in MODEL_REGISTRY if MODEL_REGISTRY[cat] == model]
|
fields = [cat.split(".")[2] for cat in MODEL_REGISTRY if MODEL_REGISTRY[cat] == model]
|
||||||
# check each field to see if already defined
|
# check each field to see if already defined
|
||||||
for cat in fields:
|
for cat in fields:
|
||||||
for k, v in fieldsets:
|
for k, v in fieldsets:
|
||||||
if cat in v['fields']:
|
if cat in v["fields"]:
|
||||||
fields.remove(cat)
|
fields.remove(cat)
|
||||||
# if there are any fields left, add them under the categories fieldset
|
# if there are any fields left, add them under the categories fieldset
|
||||||
if len(fields) > 0:
|
if len(fields) > 0:
|
||||||
admin.site.unregister(model)
|
admin.site.unregister(model)
|
||||||
admin.site.register(model, type('newadmin', (modeladmin.__class__,), {
|
admin.site.register(
|
||||||
'fieldsets': fieldsets + (('Categories', {
|
model,
|
||||||
'fields': fields
|
type(
|
||||||
}),)
|
"newadmin",
|
||||||
}))
|
(modeladmin.__class__,),
|
||||||
|
{"fieldsets": fieldsets + (("Categories", {"fields": fields}),)},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,18 @@ from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
class CategoriesConfig(AppConfig):
|
class CategoriesConfig(AppConfig):
|
||||||
name = 'categories'
|
name = "categories"
|
||||||
verbose_name = "Categories"
|
verbose_name = "Categories"
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(CategoriesConfig, self).__init__(*args, **kwargs)
|
super(CategoriesConfig, self).__init__(*args, **kwargs)
|
||||||
from django.db.models.signals import class_prepared
|
from django.db.models.signals import class_prepared
|
||||||
|
|
||||||
class_prepared.connect(handle_class_prepared)
|
class_prepared.connect(handle_class_prepared)
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
from django.db.models.signals import post_migrate
|
from django.db.models.signals import post_migrate
|
||||||
|
|
||||||
from .migration import migrate_app
|
from .migration import migrate_app
|
||||||
|
|
||||||
post_migrate.connect(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
|
See if this class needs registering of fields
|
||||||
"""
|
"""
|
||||||
from .settings import M2M_REGISTRY, FK_REGISTRY
|
|
||||||
from .registration import registry
|
from .registration import registry
|
||||||
|
from .settings import FK_REGISTRY, M2M_REGISTRY
|
||||||
|
|
||||||
sender_app = sender._meta.app_label
|
sender_app = sender._meta.app_label
|
||||||
sender_name = sender._meta.model_name
|
sender_name = sender._meta.model_name
|
||||||
|
|
||||||
for key, val in list(FK_REGISTRY.items()):
|
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:
|
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()):
|
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:
|
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)
|
||||||
|
|
|
||||||
|
|
@ -4,25 +4,23 @@ with customizable metadata and its own name space.
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django import forms
|
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from mptt.models import MPTTModel
|
|
||||||
from mptt.fields import TreeForeignKey
|
from mptt.fields import TreeForeignKey
|
||||||
from mptt.managers import TreeManager
|
from mptt.managers import TreeManager
|
||||||
|
from mptt.models import MPTTModel
|
||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
|
|
||||||
from .editor.tree_editor import TreeEditor
|
from .editor.tree_editor import TreeEditor
|
||||||
from .settings import ALLOW_SLUG_CHANGE, SLUG_TRANSLITERATOR
|
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
|
if sys.version_info[0] < 3: # Remove this after dropping support of Python 2
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
else:
|
else:
|
||||||
|
|
||||||
def python_2_unicode_compatible(x):
|
def python_2_unicode_compatible(x):
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
@ -31,6 +29,7 @@ class CategoryManager(models.Manager):
|
||||||
"""
|
"""
|
||||||
A manager that adds an "active()" method for all active categories
|
A manager that adds an "active()" method for all active categories
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def active(self):
|
def active(self):
|
||||||
"""
|
"""
|
||||||
Only categories that are active
|
Only categories that are active
|
||||||
|
|
@ -44,17 +43,18 @@ class CategoryBase(MPTTModel):
|
||||||
This base model includes the absolute bare bones fields and methods. One
|
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.
|
could simply subclass this model and do nothing else and it should work.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
parent = TreeForeignKey(
|
parent = TreeForeignKey(
|
||||||
'self',
|
"self",
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
related_name='children',
|
related_name="children",
|
||||||
verbose_name=_('parent'),
|
verbose_name=_("parent"),
|
||||||
)
|
)
|
||||||
name = models.CharField(max_length=100, verbose_name=_('name'))
|
name = models.CharField(max_length=100, verbose_name=_("name"))
|
||||||
slug = models.SlugField(verbose_name=_('slug'))
|
slug = models.SlugField(verbose_name=_("slug"))
|
||||||
active = models.BooleanField(default=True, verbose_name=_('active'))
|
active = models.BooleanField(default=True, verbose_name=_("active"))
|
||||||
|
|
||||||
objects = CategoryManager()
|
objects = CategoryManager()
|
||||||
tree = TreeManager()
|
tree = TreeManager()
|
||||||
|
|
@ -78,23 +78,28 @@ class CategoryBase(MPTTModel):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
ancestors = self.get_ancestors()
|
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:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
unique_together = ('parent', 'name')
|
unique_together = ("parent", "name")
|
||||||
ordering = ('tree_id', 'lft')
|
ordering = ("tree_id", "lft")
|
||||||
|
|
||||||
class MPTTMeta:
|
class MPTTMeta:
|
||||||
order_insertion_by = 'name'
|
order_insertion_by = "name"
|
||||||
|
|
||||||
|
|
||||||
class CategoryBaseAdminForm(forms.ModelForm):
|
class CategoryBaseAdminForm(forms.ModelForm):
|
||||||
def clean_slug(self):
|
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:
|
if self.instance is None or not ALLOW_SLUG_CHANGE:
|
||||||
self.cleaned_data['slug'] = slugify(SLUG_TRANSLITERATOR(self.cleaned_data['name']))
|
self.cleaned_data["slug"] = slugify(SLUG_TRANSLITERATOR(self.cleaned_data["name"]))
|
||||||
return self.cleaned_data['slug'][:50]
|
return self.cleaned_data["slug"][:50]
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
|
||||||
|
|
@ -107,72 +112,71 @@ class CategoryBaseAdminForm(forms.ModelForm):
|
||||||
|
|
||||||
# Validate slug is valid in that level
|
# Validate slug is valid in that level
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if self.cleaned_data.get('parent', None) is None:
|
if self.cleaned_data.get("parent", None) is None:
|
||||||
kwargs['parent__isnull'] = True
|
kwargs["parent__isnull"] = True
|
||||||
else:
|
else:
|
||||||
kwargs['parent__pk'] = int(self.cleaned_data['parent'].id)
|
kwargs["parent__pk"] = int(self.cleaned_data["parent"].id)
|
||||||
this_level_slugs = [c['slug'] for c in opts.model.objects.filter(**kwargs).values('id', 'slug') if c['id'] != self.instance.id]
|
this_level_slugs = [
|
||||||
if self.cleaned_data['slug'] in this_level_slugs:
|
c["slug"] for c in opts.model.objects.filter(**kwargs).values("id", "slug") if c["id"] != self.instance.id
|
||||||
raise forms.ValidationError(_('The slug must be unique among '
|
]
|
||||||
'the items at its level.'))
|
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
|
# Validate Category Parent
|
||||||
# Make sure the category doesn't set itself or any of its children as
|
# Make sure the category doesn't set itself or any of its children as
|
||||||
# its parent.
|
# 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
|
return self.cleaned_data
|
||||||
if self.instance.pk:
|
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:
|
else:
|
||||||
decendant_ids = []
|
decendant_ids = []
|
||||||
|
|
||||||
if self.cleaned_data['parent'].id == self.instance.id:
|
if self.cleaned_data["parent"].id == self.instance.id:
|
||||||
raise forms.ValidationError(_("You can't set the parent of the "
|
raise forms.ValidationError(_("You can't set the parent of the " "item to itself."))
|
||||||
"item to itself."))
|
elif self.cleaned_data["parent"].id in decendant_ids:
|
||||||
elif self.cleaned_data['parent'].id in decendant_ids:
|
raise forms.ValidationError(_("You can't set the parent of the " "item to a descendant."))
|
||||||
raise forms.ValidationError(_("You can't set the parent of the "
|
|
||||||
"item to a descendant."))
|
|
||||||
return self.cleaned_data
|
return self.cleaned_data
|
||||||
|
|
||||||
|
|
||||||
class CategoryBaseAdmin(TreeEditor, admin.ModelAdmin):
|
class CategoryBaseAdmin(TreeEditor, admin.ModelAdmin):
|
||||||
form = CategoryBaseAdminForm
|
form = CategoryBaseAdminForm
|
||||||
list_display = ('name', 'active')
|
list_display = ("name", "active")
|
||||||
search_fields = ('name',)
|
search_fields = ("name",)
|
||||||
prepopulated_fields = {'slug': ('name',)}
|
prepopulated_fields = {"slug": ("name",)}
|
||||||
|
|
||||||
actions = ['activate', 'deactivate']
|
actions = ["activate", "deactivate"]
|
||||||
|
|
||||||
def get_actions(self, request):
|
def get_actions(self, request):
|
||||||
actions = super(CategoryBaseAdmin, self).get_actions(request)
|
actions = super(CategoryBaseAdmin, self).get_actions(request)
|
||||||
if 'delete_selected' in actions:
|
if "delete_selected" in actions:
|
||||||
del actions['delete_selected']
|
del actions["delete_selected"]
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
def deactivate(self, request, queryset):
|
def deactivate(self, request, queryset):
|
||||||
"""
|
"""
|
||||||
Set active to False for selected items
|
Set active to False for selected items
|
||||||
"""
|
"""
|
||||||
selected_cats = self.model.objects.filter(
|
selected_cats = self.model.objects.filter(pk__in=[int(x) for x in request.POST.getlist("_selected_action")])
|
||||||
pk__in=[int(x) for x in request.POST.getlist('_selected_action')])
|
|
||||||
|
|
||||||
for item in selected_cats:
|
for item in selected_cats:
|
||||||
if item.active:
|
if item.active:
|
||||||
item.active = False
|
item.active = False
|
||||||
item.save()
|
item.save()
|
||||||
item.children.all().update(active=False)
|
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):
|
def activate(self, request, queryset):
|
||||||
"""
|
"""
|
||||||
Set active to True for selected items
|
Set active to True for selected items
|
||||||
"""
|
"""
|
||||||
selected_cats = self.model.objects.filter(
|
selected_cats = self.model.objects.filter(pk__in=[int(x) for x in request.POST.getlist("_selected_action")])
|
||||||
pk__in=[int(x) for x in request.POST.getlist('_selected_action')])
|
|
||||||
|
|
||||||
for item in selected_cats:
|
for item in selected_cats:
|
||||||
item.active = True
|
item.active = True
|
||||||
item.save()
|
item.save()
|
||||||
item.children.all().update(active=True)
|
item.children.all().update(active=True)
|
||||||
activate.short_description = _('Activate selected categories and their children')
|
|
||||||
|
activate.short_description = _("Activate selected categories and their children")
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
from django.conf import settings
|
|
||||||
import django
|
import django
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
DJANGO10_COMPAT = django.VERSION[0] < 1 or (django.VERSION[0] == 1 and django.VERSION[1] < 1)
|
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:
|
if STATIC_URL is None:
|
||||||
STATIC_URL = settings.MEDIA_URL
|
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
|
||||||
|
|
|
||||||
|
|
@ -66,4 +66,4 @@
|
||||||
.treeTable .ui-draggable-dragging {
|
.treeTable .ui-draggable-dragging {
|
||||||
color: #000;
|
color: #000;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -460,4 +460,4 @@
|
||||||
function parentOf(node) {
|
function parentOf(node) {
|
||||||
return $(node).parentOf();
|
return $(node).parentOf();
|
||||||
}
|
}
|
||||||
})(django.jQuery);
|
})(django.jQuery);
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,4 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
||||||
|
|
@ -36,4 +36,4 @@
|
||||||
{% if action_form and actions_on_top and cl.full_result_count %}{% admin_actions %}{% endif %}
|
{% if action_form and actions_on_top and cl.full_result_count %}{% admin_actions %}{% endif %}
|
||||||
{% result_tree_list cl %}
|
{% result_tree_list cl %}
|
||||||
{% if action_form and actions_on_bottom and cl.full_result_count %}{% admin_actions %}{% endif %}
|
{% if action_form and actions_on_bottom and cl.full_result_count %}{% admin_actions %}{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,30 @@
|
||||||
import django
|
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.db import models
|
||||||
from django.template import Library
|
from django.template import Library
|
||||||
from django.contrib.admin.templatetags.admin_list import result_headers, _boolean_icon
|
from django.utils.encoding import force_text, smart_text
|
||||||
from django.contrib.admin.utils import lookup_field
|
from django.utils.html import conditional_escape, escape, escapejs, format_html
|
||||||
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.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
from categories.editor import settings
|
from categories.editor import settings
|
||||||
|
from categories.editor.utils import display_for_field
|
||||||
|
|
||||||
register = Library()
|
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:
|
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):
|
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()
|
return cl.model_admin.get_empty_value_display()
|
||||||
else:
|
else:
|
||||||
# Django < 1.9
|
# Django < 1.9
|
||||||
from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
|
from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE
|
||||||
|
|
||||||
return EMPTY_CHANGELIST_VALUE
|
return EMPTY_CHANGELIST_VALUE
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -34,7 +35,7 @@ def items_for_tree_result(cl, result, form):
|
||||||
first = True
|
first = True
|
||||||
pk = cl.lookup_opts.pk.attname
|
pk = cl.lookup_opts.pk.attname
|
||||||
for field_name in cl.list_display:
|
for field_name in cl.list_display:
|
||||||
row_class = ''
|
row_class = ""
|
||||||
try:
|
try:
|
||||||
f, attr, value = lookup_field(field_name, result, cl.model_admin)
|
f, attr, value = lookup_field(field_name, result, cl.model_admin)
|
||||||
except (AttributeError, ObjectDoesNotExist):
|
except (AttributeError, ObjectDoesNotExist):
|
||||||
|
|
@ -42,10 +43,10 @@ def items_for_tree_result(cl, result, form):
|
||||||
else:
|
else:
|
||||||
if f is None:
|
if f is None:
|
||||||
if django.VERSION[0] == 1 and django.VERSION[1] == 4:
|
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"'
|
row_class = ' class="action-checkbox disclosure"'
|
||||||
allow_tags = getattr(attr, 'allow_tags', False)
|
allow_tags = getattr(attr, "allow_tags", False)
|
||||||
boolean = getattr(attr, 'boolean', False)
|
boolean = getattr(attr, "boolean", False)
|
||||||
if boolean:
|
if boolean:
|
||||||
allow_tags = True
|
allow_tags = True
|
||||||
result_repr = _boolean_icon(value)
|
result_repr = _boolean_icon(value)
|
||||||
|
|
@ -60,16 +61,16 @@ def items_for_tree_result(cl, result, form):
|
||||||
else:
|
else:
|
||||||
if value is None:
|
if value is None:
|
||||||
result_repr = get_empty_value_display(cl)
|
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))
|
result_repr = escape(getattr(result, f.name))
|
||||||
else:
|
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):
|
if isinstance(f, models.DateField) or isinstance(f, models.TimeField):
|
||||||
row_class = ' class="nowrap"'
|
row_class = ' class="nowrap"'
|
||||||
if first:
|
if first:
|
||||||
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
|
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
|
||||||
try:
|
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:
|
if row_class:
|
||||||
row_class = "%s%s" % (row_class[:-1], ' disclosure"')
|
row_class = "%s%s" % (row_class[:-1], ' disclosure"')
|
||||||
else:
|
else:
|
||||||
|
|
@ -77,14 +78,14 @@ def items_for_tree_result(cl, result, form):
|
||||||
except (AttributeError, ObjectDoesNotExist):
|
except (AttributeError, ObjectDoesNotExist):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if force_text(result_repr) == '':
|
if force_text(result_repr) == "":
|
||||||
result_repr = mark_safe(' ')
|
result_repr = mark_safe(" ")
|
||||||
# If list_display_links not defined, add the link tag to the first field
|
# 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 (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:
|
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:
|
else:
|
||||||
table_tag = {True: 'th', False: 'td'}[first]
|
table_tag = {True: "th", False: "td"}[first]
|
||||||
|
|
||||||
url = cl.url_for_result(result)
|
url = cl.url_for_result(result)
|
||||||
# Convert the pk to something that can be used in Javascript.
|
# 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,
|
row_class,
|
||||||
url,
|
url,
|
||||||
format_html(
|
format_html(
|
||||||
' onclick="opener.dismissRelatedLookupPopup(window, '
|
' onclick="opener.dismissRelatedLookupPopup(window, ' ''{}'); return false;"',
|
||||||
''{}'); return false;"', result_id
|
result_id,
|
||||||
) if cl.is_popup else '', result_repr, table_tag)
|
)
|
||||||
|
if cl.is_popup
|
||||||
|
else "",
|
||||||
|
result_repr,
|
||||||
|
table_tag,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
@ -118,9 +124,9 @@ def items_for_tree_result(cl, result, form):
|
||||||
result_repr = mark_safe(force_text(bf.errors) + force_text(bf))
|
result_repr = mark_safe(force_text(bf.errors) + force_text(bf))
|
||||||
else:
|
else:
|
||||||
result_repr = conditional_escape(result_repr)
|
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:
|
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):
|
class TreeList(list):
|
||||||
|
|
@ -131,7 +137,7 @@ def tree_results(cl):
|
||||||
if cl.formset:
|
if cl.formset:
|
||||||
for res, form in zip(cl.result_list, cl.formset.forms):
|
for res, form in zip(cl.result_list, cl.formset.forms):
|
||||||
result = TreeList(items_for_tree_result(cl, res, form))
|
result = TreeList(items_for_tree_result(cl, res, form))
|
||||||
if hasattr(res, 'pk'):
|
if hasattr(res, "pk"):
|
||||||
result.pk = res.pk
|
result.pk = res.pk
|
||||||
if res.parent:
|
if res.parent:
|
||||||
result.parent_pk = res.parent.pk
|
result.parent_pk = res.parent.pk
|
||||||
|
|
@ -141,7 +147,7 @@ def tree_results(cl):
|
||||||
else:
|
else:
|
||||||
for res in cl.result_list:
|
for res in cl.result_list:
|
||||||
result = TreeList(items_for_tree_result(cl, res, None))
|
result = TreeList(items_for_tree_result(cl, res, None))
|
||||||
if hasattr(res, 'pk'):
|
if hasattr(res, "pk"):
|
||||||
result.pk = res.pk
|
result.pk = res.pk
|
||||||
if res.parent:
|
if res.parent:
|
||||||
result.parent_pk = res.parent.pk
|
result.parent_pk = res.parent.pk
|
||||||
|
|
@ -155,14 +161,12 @@ def result_tree_list(cl):
|
||||||
Displays the headers and data list together
|
Displays the headers and data list together
|
||||||
"""
|
"""
|
||||||
import django
|
import django
|
||||||
result = {
|
|
||||||
'cl': cl,
|
result = {"cl": cl, "result_headers": list(result_headers(cl)), "results": list(tree_results(cl))}
|
||||||
'result_headers': list(result_headers(cl)),
|
|
||||||
'results': list(tree_results(cl))
|
|
||||||
}
|
|
||||||
if django.VERSION[0] == 1 and django.VERSION[1] > 2:
|
if django.VERSION[0] == 1 and django.VERSION[1] > 2:
|
||||||
from django.contrib.admin.templatetags.admin_list import result_hidden_fields
|
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
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
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
|
from . import settings
|
||||||
|
|
||||||
|
|
@ -24,6 +23,7 @@ class TreeEditorQuerySet(QuerySet):
|
||||||
in the result set, so the resulting tree display actually
|
in the result set, so the resulting tree display actually
|
||||||
makes sense.
|
makes sense.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def iterator(self):
|
def iterator(self):
|
||||||
qs = self
|
qs = self
|
||||||
# Reaching into the bowels of query sets to find out whether the qs is
|
# 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
|
# this cuts down the number of queries considerably since all ancestors
|
||||||
# will already be in include_pages when they are checked, thus not
|
# will already be in include_pages when they are checked, thus not
|
||||||
# trigger additional queries.
|
# 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:
|
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)
|
include_pages.update(ancestor_id_list)
|
||||||
|
|
||||||
if include_pages:
|
if include_pages:
|
||||||
|
|
@ -68,18 +68,18 @@ class TreeEditorQuerySet(QuerySet):
|
||||||
class TreeChangeList(ChangeList):
|
class TreeChangeList(ChangeList):
|
||||||
def _get_default_ordering(self):
|
def _get_default_ordering(self):
|
||||||
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
|
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
|
||||||
return '', '' # ('tree_id', 'lft')
|
return "", "" # ('tree_id', 'lft')
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_ordering(self, request=None, queryset=None):
|
def get_ordering(self, request=None, queryset=None):
|
||||||
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
|
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
|
||||||
return '', '' # ('tree_id', 'lft')
|
return "", "" # ('tree_id', 'lft')
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def get_queryset(self, *args, **kwargs):
|
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
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -88,18 +88,18 @@ class TreeEditor(admin.ModelAdmin):
|
||||||
list_max_show_all = 200 # new in django 1.4
|
list_max_show_all = 200 # new in django 1.4
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
css = {'all': (settings.MEDIA_PATH + "jquery.treeTable.css", )}
|
css = {"all": (settings.MEDIA_PATH + "jquery.treeTable.css",)}
|
||||||
js = []
|
js = []
|
||||||
|
|
||||||
js.extend((settings.MEDIA_PATH + "jquery.treeTable.js", ))
|
js.extend((settings.MEDIA_PATH + "jquery.treeTable.js",))
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(TreeEditor, self).__init__(*args, **kwargs)
|
super(TreeEditor, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.list_display = list(self.list_display)
|
self.list_display = list(self.list_display)
|
||||||
|
|
||||||
if 'action_checkbox' in self.list_display:
|
if "action_checkbox" in self.list_display:
|
||||||
self.list_display.remove('action_checkbox')
|
self.list_display.remove("action_checkbox")
|
||||||
|
|
||||||
opts = self.model._meta
|
opts = self.model._meta
|
||||||
|
|
||||||
|
|
@ -108,9 +108,9 @@ class TreeEditor(admin.ModelAdmin):
|
||||||
grappelli_prefix = "grappelli_"
|
grappelli_prefix = "grappelli_"
|
||||||
|
|
||||||
self.change_list_template = [
|
self.change_list_template = [
|
||||||
'admin/%s/%s/editor/%stree_editor.html' % (opts.app_label, opts.object_name.lower(), 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/%s/editor/%stree_editor.html" % (opts.app_label, grappelli_prefix),
|
||||||
'admin/editor/%stree_editor.html' % grappelli_prefix,
|
"admin/editor/%stree_editor.html" % grappelli_prefix,
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_changelist(self, request, **kwargs):
|
def get_changelist(self, request, **kwargs):
|
||||||
|
|
@ -125,6 +125,7 @@ class TreeEditor(admin.ModelAdmin):
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.translation import ungettext
|
from django.utils.translation import ungettext
|
||||||
|
|
||||||
opts = self.model._meta
|
opts = self.model._meta
|
||||||
app_label = opts.app_label
|
app_label = opts.app_label
|
||||||
if not self.has_change_permission(request, None):
|
if not self.has_change_permission(request, None):
|
||||||
|
|
@ -137,31 +138,56 @@ class TreeEditor(admin.ModelAdmin):
|
||||||
list_display = list(self.list_display)
|
list_display = list(self.list_display)
|
||||||
if not actions:
|
if not actions:
|
||||||
try:
|
try:
|
||||||
list_display.remove('action_checkbox')
|
list_display.remove("action_checkbox")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
|
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
|
||||||
params = (
|
params = (
|
||||||
request, self.model, list_display,
|
request,
|
||||||
self.list_display_links, self.list_filter, self.date_hierarchy,
|
self.model,
|
||||||
self.search_fields, self.list_select_related,
|
list_display,
|
||||||
self.list_per_page, self.list_editable, self)
|
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):
|
elif django.VERSION[0] == 1 or (django.VERSION[0] == 2 and django.VERSION[1] < 1):
|
||||||
params = (
|
params = (
|
||||||
request, self.model, list_display,
|
request,
|
||||||
self.list_display_links, self.list_filter, self.date_hierarchy,
|
self.model,
|
||||||
self.search_fields, self.list_select_related,
|
list_display,
|
||||||
self.list_per_page, self.list_max_show_all,
|
self.list_display_links,
|
||||||
self.list_editable, self)
|
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:
|
else:
|
||||||
params = (
|
params = (
|
||||||
request, self.model, list_display,
|
request,
|
||||||
self.list_display_links, self.list_filter, self.date_hierarchy,
|
self.model,
|
||||||
self.search_fields, self.list_select_related,
|
list_display,
|
||||||
self.list_per_page, self.list_max_show_all,
|
self.list_display_links,
|
||||||
self.list_editable, self, self.sortable_by)
|
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)
|
cl = TreeChangeList(*params)
|
||||||
except IncorrectLookupParameters:
|
except IncorrectLookupParameters:
|
||||||
# Wacky lookup parameters were given, so redirect to the main
|
# 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
|
# the 'invalid=1' parameter was already in the query string, something
|
||||||
# is screwed up with the database, so display an error page.
|
# is screwed up with the database, so display an error page.
|
||||||
if ERROR_FLAG in list(request.GET.keys()):
|
if ERROR_FLAG in list(request.GET.keys()):
|
||||||
return render(
|
return render(request, "admin/invalid_setup.html", {"title": _("Database error")})
|
||||||
request,
|
return HttpResponseRedirect(request.path + "?" + ERROR_FLAG + "=1")
|
||||||
'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.
|
# 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
|
# 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.
|
# 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())
|
response = self.response_action(request, queryset=cl.get_queryset())
|
||||||
if response:
|
if response:
|
||||||
return response
|
return response
|
||||||
|
|
@ -191,9 +215,7 @@ class TreeEditor(admin.ModelAdmin):
|
||||||
# Handle POSTed bulk-edit data.
|
# Handle POSTed bulk-edit data.
|
||||||
if request.method == "POST" and self.list_editable:
|
if request.method == "POST" and self.list_editable:
|
||||||
FormSet = self.get_changelist_formset(request)
|
FormSet = self.get_changelist_formset(request)
|
||||||
formset = cl.formset = FormSet(
|
formset = cl.formset = FormSet(request.POST, request.FILES, queryset=cl.result_list)
|
||||||
request.POST, request.FILES, queryset=cl.result_list
|
|
||||||
)
|
|
||||||
if formset.is_valid():
|
if formset.is_valid():
|
||||||
changecount = 0
|
changecount = 0
|
||||||
for form in formset.forms:
|
for form in formset.forms:
|
||||||
|
|
@ -213,9 +235,8 @@ class TreeEditor(admin.ModelAdmin):
|
||||||
msg = ungettext(
|
msg = ungettext(
|
||||||
"%(count)s %(name)s was changed successfully.",
|
"%(count)s %(name)s was changed successfully.",
|
||||||
"%(count)s %(name)s were changed successfully.",
|
"%(count)s %(name)s were changed successfully.",
|
||||||
changecount) % {'count': changecount,
|
changecount,
|
||||||
'name': name,
|
) % {"count": changecount, "name": name, "obj": force_text(obj)}
|
||||||
'obj': force_text(obj)}
|
|
||||||
self.message_user(request, msg)
|
self.message_user(request, msg)
|
||||||
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
|
|
@ -234,40 +255,47 @@ class TreeEditor(admin.ModelAdmin):
|
||||||
# Build the action form and populate it with available actions.
|
# Build the action form and populate it with available actions.
|
||||||
if actions:
|
if actions:
|
||||||
action_form = self.action_form(auto_id=None)
|
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:
|
else:
|
||||||
action_form = None
|
action_form = None
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'title': cl.title,
|
"title": cl.title,
|
||||||
'is_popup': cl.is_popup,
|
"is_popup": cl.is_popup,
|
||||||
'cl': cl,
|
"cl": cl,
|
||||||
'media': media,
|
"media": media,
|
||||||
'has_add_permission': self.has_add_permission(request),
|
"has_add_permission": self.has_add_permission(request),
|
||||||
'app_label': app_label,
|
"app_label": app_label,
|
||||||
'action_form': action_form,
|
"action_form": action_form,
|
||||||
'actions_on_top': self.actions_on_top,
|
"actions_on_top": self.actions_on_top,
|
||||||
'actions_on_bottom': self.actions_on_bottom,
|
"actions_on_bottom": self.actions_on_bottom,
|
||||||
}
|
}
|
||||||
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
|
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):
|
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({
|
context.update(
|
||||||
'module_name': force_text(opts.verbose_name_plural),
|
{
|
||||||
'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)},
|
"module_name": force_text(opts.verbose_name_plural),
|
||||||
'selection_note_all': selection_note_all % {'total_count': cl.result_count},
|
"selection_note": _("0 of %(cnt)s selected") % {"cnt": len(cl.result_list)},
|
||||||
})
|
"selection_note_all": selection_note_all % {"total_count": cl.result_count},
|
||||||
|
}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
context['opts'] = self.model._meta
|
context["opts"] = self.model._meta
|
||||||
|
|
||||||
context.update(extra_context or {})
|
context.update(extra_context or {})
|
||||||
return render(request, self.change_list_template or [
|
return render(
|
||||||
'admin/%s/%s/change_list.html' % (app_label, opts.object_name.lower()),
|
request,
|
||||||
'admin/%s/change_list.html' % app_label,
|
self.change_list_template
|
||||||
'admin/change_list.html'
|
or [
|
||||||
], context=context)
|
"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):
|
def changelist_view(self, request, extra_context=None, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -275,8 +303,8 @@ class TreeEditor(admin.ModelAdmin):
|
||||||
change list/actions page.
|
change list/actions page.
|
||||||
"""
|
"""
|
||||||
extra_context = extra_context or {}
|
extra_context = extra_context or {}
|
||||||
extra_context['EDITOR_MEDIA_PATH'] = settings.MEDIA_PATH
|
extra_context["EDITOR_MEDIA_PATH"] = settings.MEDIA_PATH
|
||||||
extra_context['EDITOR_TREE_INITIAL_STATE'] = settings.TREE_INITIAL_STATE
|
extra_context["EDITOR_TREE_INITIAL_STATE"] = settings.TREE_INITIAL_STATE
|
||||||
|
|
||||||
# FIXME
|
# FIXME
|
||||||
return self.old_changelist_view(request, extra_context)
|
return self.old_changelist_view(request, extra_context)
|
||||||
|
|
|
||||||
|
|
@ -4,21 +4,24 @@ from django.db.models import ForeignKey, ManyToManyField
|
||||||
class CategoryM2MField(ManyToManyField):
|
class CategoryM2MField(ManyToManyField):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
from .models import Category
|
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)
|
super(CategoryM2MField, self).__init__(to=Category, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class CategoryFKField(ForeignKey):
|
class CategoryFKField(ForeignKey):
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
from .models import Category
|
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)
|
super(CategoryFKField, self).__init__(to=Category, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from south.modelsinspector import add_introspection_rules
|
from south.modelsinspector import add_introspection_rules
|
||||||
|
|
||||||
add_introspection_rules([], [r"^categories\.fields\.CategoryFKField"])
|
add_introspection_rules([], [r"^categories\.fields\.CategoryFKField"])
|
||||||
add_introspection_rules([], [r"^categories\.fields\.CategoryM2MField"])
|
add_introspection_rules([], [r"^categories\.fields\.CategoryM2MField"])
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
|
||||||
|
|
@ -4414,4 +4414,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,4 @@ Category 2
|
||||||
Category 2-1
|
Category 2-1
|
||||||
Category 2-1-1
|
Category 2-1-1
|
||||||
Category 3
|
Category 3
|
||||||
Category 3-1
|
Category 3-1
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,4 @@ Category 2
|
||||||
Category 2-1
|
Category 2-1
|
||||||
Category 2-1-1
|
Category 2-1-1
|
||||||
Category 3
|
Category 3
|
||||||
Category 3-1
|
Category 3-1
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
|
import json
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.urls import reverse, NoReverseMatch
|
from django.urls import NoReverseMatch, reverse
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
class GenericCollectionInlineModelAdmin(admin.options.InlineModelAdmin):
|
class GenericCollectionInlineModelAdmin(admin.options.InlineModelAdmin):
|
||||||
|
|
@ -9,7 +10,7 @@ class GenericCollectionInlineModelAdmin(admin.options.InlineModelAdmin):
|
||||||
ct_fk_field = "object_id"
|
ct_fk_field = "object_id"
|
||||||
|
|
||||||
def get_content_types(self):
|
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 = {}
|
elements = {}
|
||||||
for x, y, z in ctypes:
|
for x, y, z in ctypes:
|
||||||
try:
|
try:
|
||||||
|
|
@ -25,12 +26,12 @@ class GenericCollectionInlineModelAdmin(admin.options.InlineModelAdmin):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
js = ('contentrelations/js/genericlookup.js', )
|
js = ("contentrelations/js/genericlookup.js",)
|
||||||
|
|
||||||
|
|
||||||
class GenericCollectionTabularInline(GenericCollectionInlineModelAdmin):
|
class GenericCollectionTabularInline(GenericCollectionInlineModelAdmin):
|
||||||
template = 'admin/edit_inline/gen_coll_tabular.html'
|
template = "admin/edit_inline/gen_coll_tabular.html"
|
||||||
|
|
||||||
|
|
||||||
class GenericCollectionStackedInline(GenericCollectionInlineModelAdmin):
|
class GenericCollectionStackedInline(GenericCollectionInlineModelAdmin):
|
||||||
template = 'admin/edit_inline/gen_coll_stacked.html'
|
template = "admin/edit_inline/gen_coll_stacked.html"
|
||||||
|
|
|
||||||
|
|
@ -181,4 +181,3 @@ msgstr "Cancella?"
|
||||||
#: templates/admin/edit_inline/gen_coll_tabular.html:24
|
#: templates/admin/edit_inline/gen_coll_tabular.html:24
|
||||||
msgid "View on site"
|
msgid "View on site"
|
||||||
msgstr "Vedi sul sito"
|
msgstr "Vedi sul sito"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,14 @@ class Command(BaseCommand):
|
||||||
"""
|
"""
|
||||||
Alter one or more models' tables with the registered attributes
|
Alter one or more models' tables with the registered attributes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
help = "Alter the tables for all registered models, or just specified models"
|
help = "Alter the tables for all registered models, or just specified models"
|
||||||
args = "[appname ...]"
|
args = "[appname ...]"
|
||||||
can_import_settings = True
|
can_import_settings = True
|
||||||
requires_system_checks = False
|
requires_system_checks = False
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument('app_names', nargs='*')
|
parser.add_argument("app_names", nargs="*")
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
"""
|
"""
|
||||||
|
|
@ -20,8 +21,9 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
from categories.migration import migrate_app
|
from categories.migration import migrate_app
|
||||||
from categories.settings import MODEL_REGISTRY
|
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)
|
migrate_app(None, app)
|
||||||
else:
|
else:
|
||||||
for app in MODEL_REGISTRY:
|
for app in MODEL_REGISTRY:
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,28 @@
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.core.management.base import CommandError
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""
|
"""
|
||||||
Alter one or more models' tables with the registered attributes
|
Alter one or more models' tables with the registered attributes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
help = "Drop the given field from the given model's table"
|
help = "Drop the given field from the given model's table"
|
||||||
args = "appname modelname fieldname"
|
args = "appname modelname fieldname"
|
||||||
can_import_settings = True
|
can_import_settings = True
|
||||||
requires_system_checks = False
|
requires_system_checks = False
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument('app_name')
|
parser.add_argument("app_name")
|
||||||
parser.add_argument('model_name')
|
parser.add_argument("model_name")
|
||||||
parser.add_argument('field_name')
|
parser.add_argument("field_name")
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
"""
|
"""
|
||||||
Alter the tables
|
Alter the tables
|
||||||
"""
|
"""
|
||||||
from categories.migration import drop_field
|
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")
|
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"])
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
|
||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
|
|
||||||
from categories.models import Category
|
from categories.models import Category
|
||||||
|
|
@ -10,7 +9,9 @@ from categories.settings import SLUG_TRANSLITERATOR
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""Import category trees from a file."""
|
"""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 ...]"
|
args = "file_path [file_path ...]"
|
||||||
|
|
||||||
def get_indent(self, string):
|
def get_indent(self, string):
|
||||||
|
|
@ -19,13 +20,13 @@ class Command(BaseCommand):
|
||||||
"""
|
"""
|
||||||
indent_amt = 0
|
indent_amt = 0
|
||||||
|
|
||||||
if string[0] == '\t':
|
if string[0] == "\t":
|
||||||
return '\t'
|
return "\t"
|
||||||
for char in string:
|
for char in string:
|
||||||
if char == ' ':
|
if char == " ":
|
||||||
indent_amt += 1
|
indent_amt += 1
|
||||||
else:
|
else:
|
||||||
return ' ' * indent_amt
|
return " " * indent_amt
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def make_category(self, string, parent=None, order=1):
|
def make_category(self, string, parent=None, order=1):
|
||||||
|
|
@ -36,9 +37,9 @@ class Command(BaseCommand):
|
||||||
name=string.strip(),
|
name=string.strip(),
|
||||||
slug=slugify(SLUG_TRANSLITERATOR(string.strip()))[:49],
|
slug=slugify(SLUG_TRANSLITERATOR(string.strip()))[:49],
|
||||||
# arent=parent,
|
# 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()
|
cat.save()
|
||||||
if parent:
|
if parent:
|
||||||
parent.rght = cat.rght + 1
|
parent.rght = cat.rght + 1
|
||||||
|
|
@ -49,10 +50,10 @@ class Command(BaseCommand):
|
||||||
"""
|
"""
|
||||||
Do the work of parsing each line
|
Do the work of parsing each line
|
||||||
"""
|
"""
|
||||||
indent = ''
|
indent = ""
|
||||||
level = 0
|
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.")
|
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
|
# This keeps track of the current parents at a given level
|
||||||
|
|
@ -61,8 +62,8 @@ class Command(BaseCommand):
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if len(line) == 0:
|
if len(line) == 0:
|
||||||
continue
|
continue
|
||||||
if line[0] == ' ' or line[0] == '\t':
|
if line[0] == " " or line[0] == "\t":
|
||||||
if indent == '':
|
if indent == "":
|
||||||
indent = self.get_indent(line)
|
indent = self.get_indent(line)
|
||||||
elif not line[0] in indent:
|
elif not line[0] in indent:
|
||||||
raise CommandError("You can't mix spaces and tabs for indents")
|
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):
|
if not os.path.isfile(file_path):
|
||||||
print("File %s not found." % file_path)
|
print("File %s not found." % file_path)
|
||||||
continue
|
continue
|
||||||
f = open(file_path, 'r')
|
f = open(file_path, "r")
|
||||||
data = f.readlines()
|
data = f.readlines()
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from django.db import connection, transaction
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
from django.db import connection, transaction
|
||||||
from django.db.utils import ProgrammingError
|
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
|
# Return True if the many to many table exists
|
||||||
field = model._meta.get_field(field_name)
|
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_table_name = field.m2m_db_table()
|
||||||
m2m_field_info = connection.introspection.get_table_description(cursor, m2m_table_name)
|
m2m_field_info = connection.introspection.get_table_description(cursor, m2m_table_name)
|
||||||
if m2m_field_info:
|
if m2m_field_info:
|
||||||
|
|
@ -50,9 +49,10 @@ def migrate_app(sender, *args, **kwargs):
|
||||||
Migrate all models of this app registered
|
Migrate all models of this app registered
|
||||||
"""
|
"""
|
||||||
from .registration import registry
|
from .registration import registry
|
||||||
if 'app_config' not in kwargs:
|
|
||||||
|
if "app_config" not in kwargs:
|
||||||
return
|
return
|
||||||
app_config = kwargs['app_config']
|
app_config = kwargs["app_config"]
|
||||||
|
|
||||||
app_name = app_config.label
|
app_name = app_config.label
|
||||||
|
|
||||||
|
|
@ -60,7 +60,7 @@ def migrate_app(sender, *args, **kwargs):
|
||||||
|
|
||||||
sid = transaction.savepoint()
|
sid = transaction.savepoint()
|
||||||
for fld in fields:
|
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):
|
if field_exists(app_name, model_name, field_name):
|
||||||
continue
|
continue
|
||||||
model = app_config.get_model(model_name)
|
model = app_config.get_model(model_name)
|
||||||
|
|
|
||||||
|
|
@ -1,59 +1,123 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from django.db import models, migrations
|
|
||||||
import django.core.files.storage
|
import django.core.files.storage
|
||||||
import mptt.fields
|
import mptt.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('contenttypes', '0001_initial'),
|
("contenttypes", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Category',
|
name="Category",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)),
|
||||||
('name', models.CharField(max_length=100, verbose_name='name')),
|
("name", models.CharField(max_length=100, verbose_name="name")),
|
||||||
('slug', models.SlugField(verbose_name='slug')),
|
("slug", models.SlugField(verbose_name="slug")),
|
||||||
('active', models.BooleanField(default=True, verbose_name='active')),
|
("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",
|
||||||
('thumbnail_height', models.IntegerField(null=True, blank=True)),
|
models.FileField(
|
||||||
('order', models.IntegerField(default=0)),
|
storage=django.core.files.storage.FileSystemStorage(),
|
||||||
('alternate_title', models.CharField(default='', help_text='An alternative title to use on pages with this category.', max_length=100, blank=True)),
|
null=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)),
|
upload_to="uploads/categories/thumbnails",
|
||||||
('description', models.TextField(null=True, blank=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 <head>', blank=True)),
|
),
|
||||||
('lft', models.PositiveIntegerField(editable=False, db_index=True)),
|
("thumbnail_width", models.IntegerField(null=True, blank=True)),
|
||||||
('rght', models.PositiveIntegerField(editable=False, db_index=True)),
|
("thumbnail_height", models.IntegerField(null=True, blank=True)),
|
||||||
('tree_id', models.PositiveIntegerField(editable=False, db_index=True)),
|
("order", models.IntegerField(default=0)),
|
||||||
('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)),
|
"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 <head>",
|
||||||
|
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={
|
options={
|
||||||
'ordering': ('tree_id', 'lft'),
|
"ordering": ("tree_id", "lft"),
|
||||||
'abstract': False,
|
"abstract": False,
|
||||||
'verbose_name': 'category',
|
"verbose_name": "category",
|
||||||
'verbose_name_plural': 'categories',
|
"verbose_name_plural": "categories",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='CategoryRelation',
|
name="CategoryRelation",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)),
|
||||||
('object_id', models.PositiveIntegerField(verbose_name='object id')),
|
("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)),
|
"relation_type",
|
||||||
('content_type', models.ForeignKey(verbose_name='content type', to='contenttypes.ContentType', on_delete=models.CASCADE)),
|
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(
|
migrations.AlterUniqueTogether(
|
||||||
name='category',
|
name="category",
|
||||||
unique_together=set([('parent', 'name')]),
|
unique_together=set([("parent", "name")]),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,32 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.5 on 2017-02-17 11:11
|
# Generated by Django 1.10.5 on 2017-02-17 11:11
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.manager
|
import django.db.models.manager
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('categories', '0001_initial'),
|
("categories", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelManagers(
|
migrations.AlterModelManagers(
|
||||||
name='category',
|
name="category",
|
||||||
managers=[
|
managers=[
|
||||||
('tree', django.db.models.manager.Manager()),
|
("tree", django.db.models.manager.Manager()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='categoryrelation',
|
model_name="categoryrelation",
|
||||||
name='relation_type',
|
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'),
|
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",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,44 @@
|
||||||
# Generated by Django 3.0.4 on 2020-03-06 10:50
|
# Generated by Django 3.0.4 on 2020-03-06 10:50
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('contenttypes', '0002_remove_content_type_name'),
|
("contenttypes", "0002_remove_content_type_name"),
|
||||||
('categories', '0002_auto_20170217_1111'),
|
("categories", "0002_auto_20170217_1111"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='category',
|
model_name="category",
|
||||||
name='level',
|
name="level",
|
||||||
field=models.PositiveIntegerField(editable=False),
|
field=models.PositiveIntegerField(editable=False),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='category',
|
model_name="category",
|
||||||
name='lft',
|
name="lft",
|
||||||
field=models.PositiveIntegerField(editable=False),
|
field=models.PositiveIntegerField(editable=False),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='category',
|
model_name="category",
|
||||||
name='rght',
|
name="rght",
|
||||||
field=models.PositiveIntegerField(editable=False),
|
field=models.PositiveIntegerField(editable=False),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='categoryrelation',
|
model_name="categoryrelation",
|
||||||
name='content_type',
|
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'),
|
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",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,22 @@
|
||||||
# Generated by Django 3.0.6 on 2020-05-17 18:32
|
# Generated by Django 3.0.6 on 2020-05-17 18:32
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('contenttypes', '0002_remove_content_type_name'),
|
("contenttypes", "0002_remove_content_type_name"),
|
||||||
('categories', '0003_auto_20200306_1050'),
|
("categories", "0003_auto_20200306_1050"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='categoryrelation',
|
model_name="categoryrelation",
|
||||||
name='content_type',
|
name="content_type",
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', verbose_name='content type'),
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE, to="contenttypes.ContentType", verbose_name="content type"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -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 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:
|
try:
|
||||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from django.contrib.contenttypes.generic import GenericForeignKey
|
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 django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from .settings import (RELATION_MODELS, RELATIONS, THUMBNAIL_UPLOAD_PATH, THUMBNAIL_STORAGE)
|
|
||||||
|
|
||||||
from .base import CategoryBase
|
from .base import CategoryBase
|
||||||
|
from .settings import (
|
||||||
|
RELATION_MODELS,
|
||||||
|
RELATIONS,
|
||||||
|
THUMBNAIL_STORAGE,
|
||||||
|
THUMBNAIL_UPLOAD_PATH,
|
||||||
|
)
|
||||||
|
|
||||||
STORAGE = get_storage_class(THUMBNAIL_STORAGE)
|
STORAGE = get_storage_class(THUMBNAIL_STORAGE)
|
||||||
|
|
||||||
|
|
@ -22,32 +28,28 @@ STORAGE = get_storage_class(THUMBNAIL_STORAGE)
|
||||||
class Category(CategoryBase):
|
class Category(CategoryBase):
|
||||||
thumbnail = models.FileField(
|
thumbnail = models.FileField(
|
||||||
upload_to=THUMBNAIL_UPLOAD_PATH,
|
upload_to=THUMBNAIL_UPLOAD_PATH,
|
||||||
null=True, blank=True,
|
null=True,
|
||||||
storage=STORAGE(),)
|
blank=True,
|
||||||
|
storage=STORAGE(),
|
||||||
|
)
|
||||||
thumbnail_width = models.IntegerField(blank=True, null=True)
|
thumbnail_width = models.IntegerField(blank=True, null=True)
|
||||||
thumbnail_height = models.IntegerField(blank=True, null=True)
|
thumbnail_height = models.IntegerField(blank=True, null=True)
|
||||||
order = models.IntegerField(default=0)
|
order = models.IntegerField(default=0)
|
||||||
alternate_title = models.CharField(
|
alternate_title = models.CharField(
|
||||||
blank=True,
|
blank=True, default="", max_length=100, help_text="An alternative title to use on pages with this category."
|
||||||
default="",
|
)
|
||||||
max_length=100,
|
|
||||||
help_text="An alternative title to use on pages with this category.")
|
|
||||||
alternate_url = models.CharField(
|
alternate_url = models.CharField(
|
||||||
blank=True,
|
blank=True,
|
||||||
max_length=200,
|
max_length=200,
|
||||||
help_text="An alternative URL to use instead of the one derived from "
|
help_text="An alternative URL to use instead of the one derived from " "the category hierarchy.",
|
||||||
"the category hierarchy.")
|
)
|
||||||
description = models.TextField(blank=True, null=True)
|
description = models.TextField(blank=True, null=True)
|
||||||
meta_keywords = models.CharField(
|
meta_keywords = models.CharField(
|
||||||
blank=True,
|
blank=True, default="", max_length=255, help_text="Comma-separated keywords for search engines."
|
||||||
default="",
|
)
|
||||||
max_length=255,
|
|
||||||
help_text="Comma-separated keywords for search engines.")
|
|
||||||
meta_extra = models.TextField(
|
meta_extra = models.TextField(
|
||||||
blank=True,
|
blank=True, default="", help_text="(Advanced) Any additional HTML to be placed verbatim " "in the <head>"
|
||||||
default="",
|
)
|
||||||
help_text="(Advanced) Any additional HTML to be placed verbatim "
|
|
||||||
"in the <head>")
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def short_title(self):
|
def short_title(self):
|
||||||
|
|
@ -60,19 +62,21 @@ class Category(CategoryBase):
|
||||||
if self.alternate_url:
|
if self.alternate_url:
|
||||||
return self.alternate_url
|
return self.alternate_url
|
||||||
try:
|
try:
|
||||||
prefix = reverse('categories_tree_list')
|
prefix = reverse("categories_tree_list")
|
||||||
except NoReverseMatch:
|
except NoReverseMatch:
|
||||||
prefix = '/'
|
prefix = "/"
|
||||||
ancestors = list(self.get_ancestors()) + [self, ]
|
ancestors = list(self.get_ancestors()) + [
|
||||||
return prefix + '/'.join([force_text(i.slug) for i in ancestors]) + '/'
|
self,
|
||||||
|
]
|
||||||
|
return prefix + "/".join([force_text(i.slug) for i in ancestors]) + "/"
|
||||||
|
|
||||||
if RELATION_MODELS:
|
if RELATION_MODELS:
|
||||||
|
|
||||||
def get_related_content_type(self, content_type):
|
def get_related_content_type(self, content_type):
|
||||||
"""
|
"""
|
||||||
Get all related items of the specified content type
|
Get all related items of the specified content type
|
||||||
"""
|
"""
|
||||||
return self.categoryrelation_set.filter(
|
return self.categoryrelation_set.filter(content_type__name=content_type)
|
||||||
content_type__name=content_type)
|
|
||||||
|
|
||||||
def get_relation_type(self, relation_type):
|
def get_relation_type(self, relation_type):
|
||||||
"""
|
"""
|
||||||
|
|
@ -92,11 +96,11 @@ class Category(CategoryBase):
|
||||||
super(Category, self).save(*args, **kwargs)
|
super(Category, self).save(*args, **kwargs)
|
||||||
|
|
||||||
class Meta(CategoryBase.Meta):
|
class Meta(CategoryBase.Meta):
|
||||||
verbose_name = _('category')
|
verbose_name = _("category")
|
||||||
verbose_name_plural = _('categories')
|
verbose_name_plural = _("categories")
|
||||||
|
|
||||||
class MPTTMeta:
|
class MPTTMeta:
|
||||||
order_insertion_by = ('order', 'name')
|
order_insertion_by = ("order", "name")
|
||||||
|
|
||||||
|
|
||||||
if RELATIONS:
|
if RELATIONS:
|
||||||
|
|
@ -123,17 +127,23 @@ class CategoryRelationManager(models.Manager):
|
||||||
|
|
||||||
class CategoryRelation(models.Model):
|
class CategoryRelation(models.Model):
|
||||||
"""Related category item"""
|
"""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(
|
content_type = models.ForeignKey(
|
||||||
ContentType, on_delete=models.CASCADE, limit_choices_to=CATEGORY_RELATION_LIMITS, verbose_name=_('content type'))
|
ContentType,
|
||||||
object_id = models.PositiveIntegerField(verbose_name=_('object id'))
|
on_delete=models.CASCADE,
|
||||||
content_object = GenericForeignKey('content_type', 'object_id')
|
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(
|
relation_type = models.CharField(
|
||||||
verbose_name=_('relation type'),
|
verbose_name=_("relation type"),
|
||||||
max_length=200,
|
max_length=200,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=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()
|
objects = CategoryRelationManager()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
"""
|
"""
|
||||||
These functions handle the adding of fields to other models
|
These functions handle the adding of fields to other models
|
||||||
"""
|
"""
|
||||||
from django.db.models import ForeignKey, ManyToManyField, CASCADE
|
from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured
|
||||||
from django.core.exceptions import FieldDoesNotExist
|
from django.db.models import CASCADE, ForeignKey, ManyToManyField
|
||||||
from . import fields
|
|
||||||
# from settings import self._field_registry, self._model_registry
|
# from settings import self._field_registry, self._model_registry
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
|
|
||||||
|
from . import fields
|
||||||
|
|
||||||
FIELD_TYPES = {
|
FIELD_TYPES = {
|
||||||
'ForeignKey': ForeignKey,
|
"ForeignKey": ForeignKey,
|
||||||
'ManyToManyField': ManyToManyField,
|
"ManyToManyField": ManyToManyField,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -28,17 +28,20 @@ class Registry(object):
|
||||||
field_definitions: a string, tuple or list of field configurations
|
field_definitions: a string, tuple or list of field configurations
|
||||||
field_type: either 'ForeignKey' or 'ManyToManyField'
|
field_type: either 'ForeignKey' or 'ManyToManyField'
|
||||||
"""
|
"""
|
||||||
from django.apps import apps
|
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
|
||||||
app_label = app
|
app_label = app
|
||||||
|
|
||||||
if isinstance(field_definitions, str):
|
if isinstance(field_definitions, str):
|
||||||
field_definitions = [field_definitions]
|
field_definitions = [field_definitions]
|
||||||
elif not isinstance(field_definitions, collections.Iterable):
|
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"`.'))
|
raise ImproperlyConfigured(_('`field_type` must be either `"ForeignKey"` or `"ManyToManyField"`.'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -55,30 +58,31 @@ class Registry(object):
|
||||||
if model not in self._model_registry[app_label]:
|
if model not in self._model_registry[app_label]:
|
||||||
self._model_registry[app_label].append(model)
|
self._model_registry[app_label].append(model)
|
||||||
except LookupError:
|
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)):
|
if not isinstance(field_definitions, (tuple, list)):
|
||||||
field_definitions = [field_definitions]
|
field_definitions = [field_definitions]
|
||||||
|
|
||||||
for fld in field_definitions:
|
for fld in field_definitions:
|
||||||
extra_params = {'to': 'categories.Category', 'blank': True}
|
extra_params = {"to": "categories.Category", "blank": True}
|
||||||
if field_type != 'ManyToManyField':
|
if field_type != "ManyToManyField":
|
||||||
extra_params['on_delete'] = CASCADE
|
extra_params["on_delete"] = CASCADE
|
||||||
extra_params['null'] = True
|
extra_params["null"] = True
|
||||||
if isinstance(fld, str):
|
if isinstance(fld, str):
|
||||||
field_name = fld
|
field_name = fld
|
||||||
elif isinstance(fld, dict):
|
elif isinstance(fld, dict):
|
||||||
if 'name' in fld:
|
if "name" in fld:
|
||||||
field_name = fld.pop('name')
|
field_name = fld.pop("name")
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
extra_params.update(fld)
|
extra_params.update(fld)
|
||||||
else:
|
else:
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
_("%(settings)s doesn't recognize the value of %(app)s.%(model)s") % {
|
_("%(settings)s doesn't recognize the value of %(app)s.%(model)s")
|
||||||
'settings': 'CATEGORY_SETTINGS',
|
% {"settings": "CATEGORY_SETTINGS", "app": app, "model": model_name}
|
||||||
'app': app,
|
)
|
||||||
'model': model_name})
|
|
||||||
registry_name = ".".join([app_label, model_name.lower(), field_name])
|
registry_name = ".".join([app_label, model_name.lower(), field_name])
|
||||||
if registry_name in self._field_registry:
|
if registry_name in self._field_registry:
|
||||||
continue
|
continue
|
||||||
|
|
@ -89,10 +93,10 @@ class Registry(object):
|
||||||
self._field_registry[registry_name] = FIELD_TYPES[field_type](**extra_params)
|
self._field_registry[registry_name] = FIELD_TYPES[field_type](**extra_params)
|
||||||
self._field_registry[registry_name].contribute_to_class(model, field_name)
|
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)
|
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)
|
return self._register(model, field_name, extra_params, fields.CategoryFKField)
|
||||||
|
|
||||||
def _register(self, model, field_name, extra_params={}, field=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
|
Given a dictionary, and a registration function, process the registry
|
||||||
"""
|
"""
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
for key, value in list(registry.items()):
|
for key, value in list(registry.items()):
|
||||||
model = apps.get_model(*key.split('.'))
|
model = apps.get_model(*key.split("."))
|
||||||
if model is None:
|
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)):
|
if isinstance(value, (tuple, list)):
|
||||||
for item in value:
|
for item in value:
|
||||||
if isinstance(item, str):
|
if isinstance(item, str):
|
||||||
call_func(model, item)
|
call_func(model, item)
|
||||||
elif isinstance(item, dict):
|
elif isinstance(item, dict):
|
||||||
field_name = item.pop('name')
|
field_name = item.pop("name")
|
||||||
call_func(model, field_name, extra_params=item)
|
call_func(model, field_name, extra_params=item)
|
||||||
else:
|
else:
|
||||||
raise ImproperlyConfigured(_("%(settings)s doesn't recognize the value of %(key)s") %
|
raise ImproperlyConfigured(
|
||||||
{'settings': 'CATEGORY_SETTINGS', 'key': key})
|
_("%(settings)s doesn't recognize the value of %(key)s")
|
||||||
|
% {"settings": "CATEGORY_SETTINGS", "key": key}
|
||||||
|
)
|
||||||
elif isinstance(value, str):
|
elif isinstance(value, str):
|
||||||
call_func(model, value)
|
call_func(model, value)
|
||||||
elif isinstance(value, dict):
|
elif isinstance(value, dict):
|
||||||
field_name = value.pop('name')
|
field_name = value.pop("name")
|
||||||
call_func(model, field_name, extra_params=value)
|
call_func(model, field_name, extra_params=value)
|
||||||
else:
|
else:
|
||||||
raise ImproperlyConfigured(_("%(settings)s doesn't recognize the value of %(key)s") %
|
raise ImproperlyConfigured(
|
||||||
{'settings': 'CATEGORY_SETTINGS', 'key': key})
|
_("%(settings)s doesn't recognize the value of %(key)s")
|
||||||
|
% {"settings": "CATEGORY_SETTINGS", "key": key}
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,46 @@
|
||||||
|
import collections
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
import collections
|
|
||||||
|
|
||||||
DEFAULT_SETTINGS = {
|
DEFAULT_SETTINGS = {
|
||||||
'ALLOW_SLUG_CHANGE': False,
|
"ALLOW_SLUG_CHANGE": False,
|
||||||
'M2M_REGISTRY': {},
|
"M2M_REGISTRY": {},
|
||||||
'FK_REGISTRY': {},
|
"FK_REGISTRY": {},
|
||||||
'THUMBNAIL_UPLOAD_PATH': 'uploads/categories/thumbnails',
|
"THUMBNAIL_UPLOAD_PATH": "uploads/categories/thumbnails",
|
||||||
'THUMBNAIL_STORAGE': settings.DEFAULT_FILE_STORAGE,
|
"THUMBNAIL_STORAGE": settings.DEFAULT_FILE_STORAGE,
|
||||||
'JAVASCRIPT_URL': getattr(settings, 'STATIC_URL', settings.MEDIA_URL) + 'js/',
|
"JAVASCRIPT_URL": getattr(settings, "STATIC_URL", settings.MEDIA_URL) + "js/",
|
||||||
'SLUG_TRANSLITERATOR': '',
|
"SLUG_TRANSLITERATOR": "",
|
||||||
'REGISTER_ADMIN': True,
|
"REGISTER_ADMIN": True,
|
||||||
'RELATION_MODELS': [],
|
"RELATION_MODELS": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT_SETTINGS.update(getattr(settings, 'CATEGORIES_SETTINGS', {}))
|
DEFAULT_SETTINGS.update(getattr(settings, "CATEGORIES_SETTINGS", {}))
|
||||||
|
|
||||||
if DEFAULT_SETTINGS['SLUG_TRANSLITERATOR']:
|
if DEFAULT_SETTINGS["SLUG_TRANSLITERATOR"]:
|
||||||
if isinstance(DEFAULT_SETTINGS['SLUG_TRANSLITERATOR'], collections.Callable):
|
if isinstance(DEFAULT_SETTINGS["SLUG_TRANSLITERATOR"], collections.Callable):
|
||||||
pass
|
pass
|
||||||
elif isinstance(DEFAULT_SETTINGS['SLUG_TRANSLITERATOR'], str):
|
elif isinstance(DEFAULT_SETTINGS["SLUG_TRANSLITERATOR"], str):
|
||||||
from django.utils.importlib import import_module
|
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]))
|
module = import_module(".".join(bits[:-1]))
|
||||||
DEFAULT_SETTINGS['SLUG_TRANSLITERATOR'] = getattr(module, bits[-1])
|
DEFAULT_SETTINGS["SLUG_TRANSLITERATOR"] = getattr(module, bits[-1])
|
||||||
else:
|
else:
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
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:
|
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
|
# Add all the keys/values to the module's namespace
|
||||||
globals().update(DEFAULT_SETTINGS)
|
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.
|
# The field registry keeps track of the individual fields created.
|
||||||
# {'app.model.field': Field(**extra_params)}
|
# {'app.model.field': Field(**extra_params)}
|
||||||
|
|
|
||||||
|
|
@ -17,4 +17,4 @@ function showGenericRelatedObjectLookupPopup(triggeringLink, ctArray) {
|
||||||
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
|
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
|
||||||
win.focus();
|
win.focus();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,4 @@
|
||||||
{% else %}<a href="{{ node.get_absolute_url }}">{{ node.name }}</a>
|
{% else %}<a href="{{ node.get_absolute_url }}">{{ node.name }}</a>
|
||||||
{% endifequal %}
|
{% endifequal %}
|
||||||
{% for level in structure.closed_levels %}</li></ul>{% endfor %}
|
{% for level in structure.closed_levels %}</li></ul>{% endfor %}
|
||||||
{% endfor %}</li></ul>
|
{% endfor %}</li></ul>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
{% spaceless %}{% for item in category.get_ancestors %}
|
{% spaceless %}{% for item in category.get_ancestors %}
|
||||||
<a href="{{ item.get_absolute_url }}">{{ item.name }}</a>{{ separator }}{% endfor %}{{ category.name }}
|
<a href="{{ item.get_absolute_url }}">{{ item.name }}</a>{{ separator }}{% endfor %}{{ category.name }}
|
||||||
{% endspaceless %}
|
{% endspaceless %}
|
||||||
|
|
|
||||||
|
|
@ -14,4 +14,4 @@
|
||||||
{% if category.parent %}<h3>Go up to <a href="{{ category.parent.get_absolute_url }}">{{ category.parent }}</a></h3>{% endif %}
|
{% if category.parent %}<h3>Go up to <a href="{{ category.parent.get_absolute_url }}">{{ category.parent }}</a></h3>{% endif %}
|
||||||
{% if category.description %}<p>{{ category.description }}</p>{% endif %}
|
{% if category.description %}<p>{{ category.description }}</p>{% endif %}
|
||||||
{% if category.children.count %}<h3>Subcategories</h3><ul>{% for child in category.children.all %}<li><a href="{{ child.get_absolute_url }}">{{ child }}</a></li>{% endfor %}</ul>{% endif %}
|
{% if category.children.count %}<h3>Subcategories</h3><ul>{% for child in category.children.all %}<li><a href="{{ child.get_absolute_url }}">{{ child }}</a></li>{% endfor %}</ul>{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,4 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Categories</h2>
|
<h2>Categories</h2>
|
||||||
<ul>{% for category in object_list %}<li><a href="{{ category.get_absolute_url }}">{{ category }}</a></li>{% endfor %}</ul>
|
<ul>{% for category in object_list %}<li><a href="{{ category.get_absolute_url }}">{{ category }}</a></li>{% endfor %}</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,4 @@
|
||||||
{% else %}<a href="{{ node.get_absolute_url }}">{{ node.name }}</a>
|
{% else %}<a href="{{ node.get_absolute_url }}">{{ node.name }}</a>
|
||||||
{% endifequal %}
|
{% endifequal %}
|
||||||
{% for level in structure.closed_levels %}</li></ul>{% endfor %}
|
{% for level in structure.closed_levels %}</li></ul>{% endfor %}
|
||||||
{% endfor %}</li></ul>{% endspaceless %}
|
{% endfor %}</li></ul>{% endspaceless %}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,18 @@
|
||||||
from django import template
|
from django import template
|
||||||
from django.apps import apps
|
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 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 six import string_types
|
||||||
|
|
||||||
from categories.base import CategoryBase
|
from categories.base import CategoryBase
|
||||||
from categories.models import Category
|
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()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
@ -48,9 +53,9 @@ def get_category(category_string, model=Category):
|
||||||
"""
|
"""
|
||||||
model_class = get_cat_model(model)
|
model_class = get_cat_model(model)
|
||||||
category = str(category_string).strip("'\"")
|
category = str(category_string).strip("'\"")
|
||||||
category = category.strip('/')
|
category = category.strip("/")
|
||||||
|
|
||||||
cat_list = category.split('/')
|
cat_list = category.split("/")
|
||||||
if len(cat_list) == 0:
|
if len(cat_list) == 0:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
|
|
@ -88,7 +93,7 @@ class CategoryDrillDownNode(template.Node):
|
||||||
context[self.varname] = []
|
context[self.varname] = []
|
||||||
except Exception:
|
except Exception:
|
||||||
context[self.varname] = []
|
context[self.varname] = []
|
||||||
return ''
|
return ""
|
||||||
|
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
|
|
@ -114,30 +119,32 @@ def get_category_drilldown(parser, token):
|
||||||
Grandparent, Parent, Child 1, Child 2, Child n
|
Grandparent, Parent, Child 1, Child 2, Child n
|
||||||
"""
|
"""
|
||||||
bits = token.split_contents()
|
bits = token.split_contents()
|
||||||
error_str = '%(tagname)s tag should be in the format {%% %(tagname)s ' \
|
error_str = (
|
||||||
'"category name" [using "app.Model"] as varname %%} or ' \
|
"%(tagname)s tag should be in the format {%% %(tagname)s "
|
||||||
'{%% %(tagname)s category_obj as varname %%}.'
|
'"category name" [using "app.Model"] as varname %%} or '
|
||||||
|
"{%% %(tagname)s category_obj as varname %%}."
|
||||||
|
)
|
||||||
if len(bits) == 4:
|
if len(bits) == 4:
|
||||||
if bits[2] != 'as':
|
if bits[2] != "as":
|
||||||
raise template.TemplateSyntaxError(error_str % {'tagname': bits[0]})
|
raise template.TemplateSyntaxError(error_str % {"tagname": bits[0]})
|
||||||
if bits[2] == 'as':
|
if bits[2] == "as":
|
||||||
varname = bits[3].strip("'\"")
|
varname = bits[3].strip("'\"")
|
||||||
model = "categories.category"
|
model = "categories.category"
|
||||||
if len(bits) == 6:
|
if len(bits) == 6:
|
||||||
if bits[2] not in ('using', 'as') or bits[4] not in ('using', 'as'):
|
if bits[2] not in ("using", "as") or bits[4] not in ("using", "as"):
|
||||||
raise template.TemplateSyntaxError(error_str % {'tagname': bits[0]})
|
raise template.TemplateSyntaxError(error_str % {"tagname": bits[0]})
|
||||||
if bits[2] == 'as':
|
if bits[2] == "as":
|
||||||
varname = bits[3].strip("'\"")
|
varname = bits[3].strip("'\"")
|
||||||
model = bits[5].strip("'\"")
|
model = bits[5].strip("'\"")
|
||||||
if bits[2] == 'using':
|
if bits[2] == "using":
|
||||||
varname = bits[5].strip("'\"")
|
varname = bits[5].strip("'\"")
|
||||||
model = bits[3].strip("'\"")
|
model = bits[3].strip("'\"")
|
||||||
category = FilterExpression(bits[1], parser)
|
category = FilterExpression(bits[1], parser)
|
||||||
return CategoryDrillDownNode(category, varname, model)
|
return CategoryDrillDownNode(category, varname, model)
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('categories/breadcrumbs.html')
|
@register.inclusion_tag("categories/breadcrumbs.html")
|
||||||
def breadcrumbs(category_string, separator=' > ', using='categories.category'):
|
def breadcrumbs(category_string, separator=" > ", using="categories.category"):
|
||||||
"""
|
"""
|
||||||
{% breadcrumbs category 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)
|
cat = get_category(category_string, using)
|
||||||
|
|
||||||
return {'category': cat, 'separator': separator}
|
return {"category": cat, "separator": separator}
|
||||||
|
|
||||||
|
|
||||||
@register.inclusion_tag('categories/ul_tree.html')
|
@register.inclusion_tag("categories/ul_tree.html")
|
||||||
def display_drilldown_as_ul(category, using='categories.Category'):
|
def display_drilldown_as_ul(category, using="categories.Category"):
|
||||||
"""
|
"""
|
||||||
Render the category with ancestors and children using the
|
Render the category with ancestors and children using the
|
||||||
``categories/ul_tree.html`` template.
|
``categories/ul_tree.html`` template.
|
||||||
|
|
@ -185,13 +192,13 @@ def display_drilldown_as_ul(category, using='categories.Category'):
|
||||||
"""
|
"""
|
||||||
cat = get_category(category, using)
|
cat = get_category(category, using)
|
||||||
if cat is None:
|
if cat is None:
|
||||||
return {'category': cat, 'path': []}
|
return {"category": cat, "path": []}
|
||||||
else:
|
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')
|
@register.inclusion_tag("categories/ul_tree.html")
|
||||||
def display_path_as_ul(category, using='categories.Category'):
|
def display_path_as_ul(category, using="categories.Category"):
|
||||||
"""
|
"""
|
||||||
Render the category with ancestors, but no children using the
|
Render the category with ancestors, but no children using the
|
||||||
``categories/ul_tree.html`` template.
|
``categories/ul_tree.html`` template.
|
||||||
|
|
@ -219,7 +226,7 @@ def display_path_as_ul(category, using='categories.Category'):
|
||||||
else:
|
else:
|
||||||
cat = get_category(category)
|
cat = get_category(category)
|
||||||
|
|
||||||
return {'category': cat, 'path': cat.get_ancestors() or []}
|
return {"category": cat, "path": cat.get_ancestors() or []}
|
||||||
|
|
||||||
|
|
||||||
class TopLevelCategoriesNode(template.Node):
|
class TopLevelCategoriesNode(template.Node):
|
||||||
|
|
@ -229,8 +236,8 @@ class TopLevelCategoriesNode(template.Node):
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
model = get_cat_model(self.model)
|
model = get_cat_model(self.model)
|
||||||
context[self.varname] = model.objects.filter(parent=None).order_by('name')
|
context[self.varname] = model.objects.filter(parent=None).order_by("name")
|
||||||
return ''
|
return ""
|
||||||
|
|
||||||
|
|
||||||
@register.tag
|
@register.tag
|
||||||
|
|
@ -247,14 +254,14 @@ def get_top_level_categories(parser, token):
|
||||||
bits = token.split_contents()
|
bits = token.split_contents()
|
||||||
usage = 'Usage: {%% %s [using "app.Model"] as <variable> %%}' % bits[0]
|
usage = 'Usage: {%% %s [using "app.Model"] as <variable> %%}' % bits[0]
|
||||||
if len(bits) == 3:
|
if len(bits) == 3:
|
||||||
if bits[1] != 'as':
|
if bits[1] != "as":
|
||||||
raise template.TemplateSyntaxError(usage)
|
raise template.TemplateSyntaxError(usage)
|
||||||
varname = bits[2]
|
varname = bits[2]
|
||||||
model = "categories.category"
|
model = "categories.category"
|
||||||
elif len(bits) == 5:
|
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)
|
raise template.TemplateSyntaxError(usage)
|
||||||
if bits[1] == 'using':
|
if bits[1] == "using":
|
||||||
model = bits[2].strip("'\"")
|
model = bits[2].strip("'\"")
|
||||||
varname = bits[4].strip("'\"")
|
varname = bits[4].strip("'\"")
|
||||||
else:
|
else:
|
||||||
|
|
@ -264,22 +271,21 @@ def get_top_level_categories(parser, token):
|
||||||
return TopLevelCategoriesNode(varname, model)
|
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)
|
m = apps.get_model(app_label, model_name)
|
||||||
if not isinstance(category, CategoryBase):
|
if not isinstance(category, CategoryBase):
|
||||||
category = Category.objects.get(slug=str(category))
|
category = Category.objects.get(slug=str(category))
|
||||||
children = category.children.all()
|
children = category.children.all()
|
||||||
ids = []
|
ids = []
|
||||||
for cat in list(children) + [category]:
|
for cat in list(children) + [category]:
|
||||||
if hasattr(cat, '%s_set' % set_name):
|
if hasattr(cat, "%s_set" % set_name):
|
||||||
ids.extend([x.pk for x in getattr(cat, '%s_set' % set_name).all()[:num]])
|
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):
|
class LatestObjectsNode(Node):
|
||||||
def __init__(self, var_name, category, app_label, model_name, set_name,
|
def __init__(self, var_name, category, app_label, model_name, set_name, date_field="pub_date", num=15):
|
||||||
date_field='pub_date', num=15):
|
|
||||||
"""
|
"""
|
||||||
Get latest objects of app_label.model_name
|
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)
|
result = get_latest_objects_by_category(category, app_label, model_name, set_name, date_field, num)
|
||||||
context[self.var_name] = result
|
context[self.var_name] = result
|
||||||
|
|
||||||
return ''
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def do_get_latest_objects_by_category(parser, token):
|
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] %}"
|
proper_form = "{% get_latest_objects_by_category category app_name model_name set_name [date_field] [number] as [var_name] %}"
|
||||||
bits = token.split_contents()
|
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))
|
raise TemplateSyntaxError("%s tag shoud be in the form: %s" % (bits[0], proper_form))
|
||||||
if len(bits) < 7:
|
if len(bits) < 7:
|
||||||
raise TemplateSyntaxError("%s tag shoud be in the form: %s" % (bits[0], proper_form))
|
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)
|
model_name = FilterExpression(bits[3], parser)
|
||||||
set_name = FilterExpression(bits[4], parser)
|
set_name = FilterExpression(bits[4], parser)
|
||||||
var_name = bits[-1]
|
var_name = bits[-1]
|
||||||
if bits[5] != 'as':
|
if bits[5] != "as":
|
||||||
date_field = FilterExpression(bits[5], parser)
|
date_field = FilterExpression(bits[5], parser)
|
||||||
else:
|
else:
|
||||||
date_field = FilterExpression(None, parser)
|
date_field = FilterExpression(None, parser)
|
||||||
if bits[6] != 'as':
|
if bits[6] != "as":
|
||||||
num = FilterExpression(bits[6], parser)
|
num = FilterExpression(bits[6], parser)
|
||||||
else:
|
else:
|
||||||
num = FilterExpression(None, parser)
|
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
|
Converts a normal queryset from an MPTT model to include all the ancestors
|
||||||
so a filtered subset of items can be formatted correctly
|
so a filtered subset of items can be formatted correctly
|
||||||
"""
|
"""
|
||||||
from django.db.models.query import QuerySet
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
|
from django.db.models.query import QuerySet
|
||||||
|
|
||||||
if not isinstance(value, QuerySet):
|
if not isinstance(value, QuerySet):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
@ -365,9 +373,9 @@ def tree_queryset(value):
|
||||||
# this cuts down the number of queries considerably since all ancestors
|
# this cuts down the number of queries considerably since all ancestors
|
||||||
# will already be in include_pages when they are checked, thus not
|
# will already be in include_pages when they are checked, thus not
|
||||||
# trigger additional queries.
|
# 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:
|
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)
|
include_pages.update(ancestor_id_list)
|
||||||
|
|
||||||
if include_pages:
|
if include_pages:
|
||||||
|
|
@ -400,10 +408,10 @@ def recursetree(parser, token):
|
||||||
"""
|
"""
|
||||||
bits = token.contents.split()
|
bits = token.contents.split()
|
||||||
if len(bits) != 2:
|
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)
|
queryset_var = FilterExpression(bits[1], parser)
|
||||||
|
|
||||||
template_nodes = parser.parse(('endrecursetree',))
|
template_nodes = parser.parse(("endrecursetree",))
|
||||||
parser.delete_first_token()
|
parser.delete_first_token()
|
||||||
|
|
||||||
return RecurseTreeNode(template_nodes, queryset_var)
|
return RecurseTreeNode(template_nodes, queryset_var)
|
||||||
|
|
|
||||||
|
|
@ -1,68 +1,68 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.urls import reverse
|
|
||||||
from django.test import Client, TestCase
|
from django.test import Client, TestCase
|
||||||
|
from django.urls import reverse
|
||||||
from django.utils.encoding import smart_text
|
from django.utils.encoding import smart_text
|
||||||
|
|
||||||
from categories.models import Category
|
from categories.models import Category
|
||||||
|
|
||||||
|
|
||||||
class TestCategoryAdmin(TestCase):
|
class TestCategoryAdmin(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = Client()
|
self.client = Client()
|
||||||
|
|
||||||
def test_adding_parent_and_child(self):
|
def test_adding_parent_and_child(self):
|
||||||
User.objects.create_superuser('testuser', 'testuser@example.com', 'password')
|
User.objects.create_superuser("testuser", "testuser@example.com", "password")
|
||||||
self.client.login(username='testuser', password='password')
|
self.client.login(username="testuser", password="password")
|
||||||
url = reverse('admin:categories_category_add')
|
url = reverse("admin:categories_category_add")
|
||||||
data = {
|
data = {
|
||||||
'parent': '',
|
"parent": "",
|
||||||
'name': smart_text('Parent Catégory'),
|
"name": smart_text("Parent Catégory"),
|
||||||
'thumbnail': '',
|
"thumbnail": "",
|
||||||
'filename': '',
|
"filename": "",
|
||||||
'active': 'on',
|
"active": "on",
|
||||||
'alternate_title': '',
|
"alternate_title": "",
|
||||||
'alternate_url': '',
|
"alternate_url": "",
|
||||||
'description': '',
|
"description": "",
|
||||||
'meta_keywords': '',
|
"meta_keywords": "",
|
||||||
'meta_extra': '',
|
"meta_extra": "",
|
||||||
'order': 0,
|
"order": 0,
|
||||||
'slug': 'parent',
|
"slug": "parent",
|
||||||
'_save': '_save',
|
"_save": "_save",
|
||||||
'categoryrelation_set-TOTAL_FORMS': '0',
|
"categoryrelation_set-TOTAL_FORMS": "0",
|
||||||
'categoryrelation_set-INITIAL_FORMS': '0',
|
"categoryrelation_set-INITIAL_FORMS": "0",
|
||||||
'categoryrelation_set-MIN_NUM_FORMS': '1000',
|
"categoryrelation_set-MIN_NUM_FORMS": "1000",
|
||||||
'categoryrelation_set-MAX_NUM_FORMS': '1000',
|
"categoryrelation_set-MAX_NUM_FORMS": "1000",
|
||||||
}
|
}
|
||||||
resp = self.client.post(url, data=data)
|
resp = self.client.post(url, data=data)
|
||||||
self.assertEqual(resp.status_code, 302)
|
self.assertEqual(resp.status_code, 302)
|
||||||
self.assertEqual(1, Category.objects.count())
|
self.assertEqual(1, Category.objects.count())
|
||||||
|
|
||||||
# update parent
|
# update parent
|
||||||
data.update({'name': smart_text('Parent Catégory (Changed)')})
|
data.update({"name": smart_text("Parent Catégory (Changed)")})
|
||||||
resp = self.client.post(reverse('admin:categories_category_change', args=(1,)), data=data)
|
resp = self.client.post(reverse("admin:categories_category_change", args=(1,)), data=data)
|
||||||
self.assertEqual(resp.status_code, 302)
|
self.assertEqual(resp.status_code, 302)
|
||||||
self.assertEqual(1, Category.objects.count())
|
self.assertEqual(1, Category.objects.count())
|
||||||
|
|
||||||
# add a child
|
# add a child
|
||||||
data.update({
|
data.update(
|
||||||
'parent': '1',
|
{
|
||||||
'name': smart_text('Child Catégory'),
|
"parent": "1",
|
||||||
'slug': smart_text('child-category'),
|
"name": smart_text("Child Catégory"),
|
||||||
})
|
"slug": smart_text("child-category"),
|
||||||
|
}
|
||||||
|
)
|
||||||
resp = self.client.post(url, data=data)
|
resp = self.client.post(url, data=data)
|
||||||
self.assertEqual(resp.status_code, 302)
|
self.assertEqual(resp.status_code, 302)
|
||||||
self.assertEqual(2, Category.objects.count())
|
self.assertEqual(2, Category.objects.count())
|
||||||
|
|
||||||
# update child
|
# update child
|
||||||
data.update({'name': 'Child (Changed)'})
|
data.update({"name": "Child (Changed)"})
|
||||||
resp = self.client.post(reverse('admin:categories_category_change', args=(2,)), data=data)
|
resp = self.client.post(reverse("admin:categories_category_change", args=(2,)), data=data)
|
||||||
self.assertEqual(resp.status_code, 302)
|
self.assertEqual(resp.status_code, 302)
|
||||||
self.assertEqual(2, Category.objects.count())
|
self.assertEqual(2, Category.objects.count())
|
||||||
|
|
||||||
# test the admin list view
|
# test the admin list view
|
||||||
url = reverse('admin:categories_category_changelist')
|
url = reverse("admin:categories_category_changelist")
|
||||||
resp = self.client.get(url)
|
resp = self.client.get(url)
|
||||||
self.assertEqual(resp.status_code, 200)
|
self.assertEqual(resp.status_code, 200)
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,21 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.conf import settings
|
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.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):
|
class CategoryImportTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _import_file(self, filename):
|
def _import_file(self, filename):
|
||||||
root_cats = ['Category 1', 'Category 2', 'Category 3']
|
root_cats = ["Category 1", "Category 2", "Category 3"]
|
||||||
testfile = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'fixtures', filename))
|
testfile = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), "fixtures", filename))
|
||||||
cmd = Command()
|
cmd = Command()
|
||||||
cmd.handle(testfile)
|
cmd.handle(testfile)
|
||||||
roots = Category.tree.root_nodes()
|
roots = Category.tree.root_nodes()
|
||||||
|
|
@ -26,31 +27,31 @@ class CategoryImportTest(TestCase):
|
||||||
for item in roots:
|
for item in roots:
|
||||||
assert item.name in root_cats
|
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]
|
cat21 = cat2.children.all()[0]
|
||||||
self.assertEqual(cat21.name, 'Category 2-1')
|
self.assertEqual(cat21.name, "Category 2-1")
|
||||||
cat211 = cat21.children.all()[0]
|
cat211 = cat21.children.all()[0]
|
||||||
self.assertEqual(cat211.name, 'Category 2-1-1')
|
self.assertEqual(cat211.name, "Category 2-1-1")
|
||||||
|
|
||||||
def testImportSpaceDelimited(self):
|
def testImportSpaceDelimited(self):
|
||||||
Category.objects.all().delete()
|
Category.objects.all().delete()
|
||||||
self._import_file('test_category_spaces.txt')
|
self._import_file("test_category_spaces.txt")
|
||||||
|
|
||||||
items = Category.objects.all()
|
items = Category.objects.all()
|
||||||
|
|
||||||
self.assertEqual(items[0].name, 'Category 1')
|
self.assertEqual(items[0].name, "Category 1")
|
||||||
self.assertEqual(items[1].name, 'Category 1-1')
|
self.assertEqual(items[1].name, "Category 1-1")
|
||||||
self.assertEqual(items[2].name, 'Category 1-2')
|
self.assertEqual(items[2].name, "Category 1-2")
|
||||||
|
|
||||||
def testImportTabDelimited(self):
|
def testImportTabDelimited(self):
|
||||||
Category.objects.all().delete()
|
Category.objects.all().delete()
|
||||||
self._import_file('test_category_tabs.txt')
|
self._import_file("test_category_tabs.txt")
|
||||||
|
|
||||||
items = Category.objects.all()
|
items = Category.objects.all()
|
||||||
|
|
||||||
self.assertEqual(items[0].name, 'Category 1')
|
self.assertEqual(items[0].name, "Category 1")
|
||||||
self.assertEqual(items[1].name, 'Category 1-1')
|
self.assertEqual(items[1].name, "Category 1-1")
|
||||||
self.assertEqual(items[2].name, 'Category 1-2')
|
self.assertEqual(items[2].name, "Category 1-2")
|
||||||
|
|
||||||
def testMixingTabsSpaces(self):
|
def testMixingTabsSpaces(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
# test active returns only active items
|
# test active returns only active items
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from categories.models import Category
|
from categories.models import Category
|
||||||
|
|
||||||
|
|
||||||
class CategoryManagerTest(TestCase):
|
class CategoryManagerTest(TestCase):
|
||||||
fixtures = ['categories.json']
|
fixtures = ["categories.json"]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
pass
|
pass
|
||||||
|
|
@ -16,7 +17,7 @@ class CategoryManagerTest(TestCase):
|
||||||
all_count = Category.objects.all().count()
|
all_count = Category.objects.all().count()
|
||||||
self.assertEqual(Category.objects.active().count(), 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.active = False
|
||||||
cat1.save()
|
cat1.save()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
from django.core import management
|
from django.core import management
|
||||||
from django.core.management.base import CommandError
|
from django.core.management.base import CommandError
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
|
@ -6,7 +5,6 @@ from django.test import TestCase
|
||||||
|
|
||||||
|
|
||||||
class TestMgmtCommands(TestCase):
|
class TestMgmtCommands(TestCase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
connection.disable_constraint_checking()
|
connection.disable_constraint_checking()
|
||||||
|
|
@ -18,13 +16,13 @@ class TestMgmtCommands(TestCase):
|
||||||
connection.enable_constraint_checking()
|
connection.enable_constraint_checking()
|
||||||
|
|
||||||
def test_add_category_fields(self):
|
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):
|
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):
|
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):
|
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)
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,19 @@ import os
|
||||||
|
|
||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
from django.core.files.uploadedfile import UploadedFile
|
from django.core.files.uploadedfile import UploadedFile
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
from categories.models import Category
|
from categories.models import Category
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
|
|
||||||
class TestCategoryThumbnail(TestCase):
|
class TestCategoryThumbnail(TestCase):
|
||||||
|
|
||||||
def test_thumbnail(self):
|
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:
|
with open(os.path.join(os.path.dirname(__file__), file_name), "rb") as f:
|
||||||
test_image = UploadedFile(File(f), content_type='image/jpeg')
|
test_image = UploadedFile(File(f), content_type="image/jpeg")
|
||||||
category = Category.objects.create(name='Test Category', slug='test-category', thumbnail=test_image)
|
category = Category.objects.create(name="Test Category", slug="test-category", thumbnail=test_image)
|
||||||
self.assertEqual(category.pk, 1)
|
self.assertEqual(category.pk, 1)
|
||||||
self.assertEqual(category.thumbnail_width, 640)
|
self.assertEqual(category.thumbnail_width, 640)
|
||||||
self.assertEqual(category.thumbnail_height, 480)
|
self.assertEqual(category.thumbnail_height, 480)
|
||||||
|
|
|
||||||
|
|
@ -14,43 +14,39 @@ class CategoryRegistrationTest(TestCase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def test_foreignkey_string(self):
|
def test_foreignkey_string(self):
|
||||||
FK_REGISTRY = {
|
FK_REGISTRY = {"flatpages.flatpage": "category"}
|
||||||
'flatpages.flatpage': 'category'
|
|
||||||
}
|
|
||||||
_process_registry(FK_REGISTRY, registry.register_fk)
|
_process_registry(FK_REGISTRY, registry.register_fk)
|
||||||
from django.contrib.flatpages.models import FlatPage
|
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):
|
def test_foreignkey_dict(self):
|
||||||
FK_REGISTRY = {
|
FK_REGISTRY = {"flatpages.flatpage": {"name": "category"}}
|
||||||
'flatpages.flatpage': {'name': 'category'}
|
|
||||||
}
|
|
||||||
_process_registry(FK_REGISTRY, registry.register_fk)
|
_process_registry(FK_REGISTRY, registry.register_fk)
|
||||||
from django.contrib.flatpages.models import FlatPage
|
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):
|
def test_foreignkey_list(self):
|
||||||
FK_REGISTRY = {
|
FK_REGISTRY = {"flatpages.flatpage": ({"name": "category", "related_name": "cats"},)}
|
||||||
'flatpages.flatpage': (
|
|
||||||
{'name': 'category', 'related_name': 'cats'},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_process_registry(FK_REGISTRY, registry.register_fk)
|
_process_registry(FK_REGISTRY, registry.register_fk)
|
||||||
from django.contrib.flatpages.models import FlatPage
|
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:
|
if django.VERSION[1] >= 7:
|
||||||
|
|
||||||
def test_new_foreignkey_string(self):
|
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
|
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):
|
class Categorym2mTest(TestCase):
|
||||||
def test_m2m_string(self):
|
def test_m2m_string(self):
|
||||||
M2M_REGISTRY = {
|
M2M_REGISTRY = {"flatpages.flatpage": "categories"}
|
||||||
'flatpages.flatpage': 'categories'
|
|
||||||
}
|
|
||||||
_process_registry(M2M_REGISTRY, registry.register_m2m)
|
_process_registry(M2M_REGISTRY, registry.register_m2m)
|
||||||
from django.contrib.flatpages.models import FlatPage
|
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()])
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
from django.test import TestCase
|
|
||||||
from django import template
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
from categories.models import Category
|
from categories.models import Category
|
||||||
|
|
||||||
|
|
||||||
class CategoryTagsTest(TestCase):
|
class CategoryTagsTest(TestCase):
|
||||||
|
|
||||||
fixtures = ['musicgenres.json']
|
fixtures = ["musicgenres.json"]
|
||||||
|
|
||||||
def render_template(self, template_string, context={}):
|
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.
|
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):
|
def testBasicUsage(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -30,46 +33,50 @@ class CategoryTagsTest(TestCase):
|
||||||
# display_path_as_ul
|
# display_path_as_ul
|
||||||
rock_resp = '<ul><li><a href="/categories/">Top</a></li></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 = 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)
|
self.assertEqual(resp, rock_resp)
|
||||||
|
|
||||||
# display_drilldown_as_ul
|
# 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>'
|
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(
|
resp = self.render_template(
|
||||||
'{% load category_tags %}'
|
"{% load category_tags %}" '{% display_drilldown_as_ul "/World/Worldbeat" "categories.category" %}'
|
||||||
'{% display_drilldown_as_ul "/World/Worldbeat" "categories.category" %}')
|
)
|
||||||
resp = re.sub(r'\n$', "", resp)
|
resp = re.sub(r"\n$", "", resp)
|
||||||
self.assertEqual(resp, expected_resp)
|
self.assertEqual(resp, expected_resp)
|
||||||
|
|
||||||
# breadcrumbs
|
# breadcrumbs
|
||||||
expected_resp = '<a href="/categories/world/">World</a> > Worldbeat'
|
expected_resp = '<a href="/categories/world/">World</a> > Worldbeat'
|
||||||
resp = self.render_template(
|
resp = self.render_template(
|
||||||
'{% load category_tags %}'
|
"{% load category_tags %}" '{% breadcrumbs "/World/Worldbeat" " > " "categories.category" %}'
|
||||||
'{% breadcrumbs "/World/Worldbeat" " > " "categories.category" %}')
|
)
|
||||||
self.assertEqual(resp, expected_resp)
|
self.assertEqual(resp, expected_resp)
|
||||||
|
|
||||||
# get_top_level_categories
|
# 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(
|
resp = self.render_template(
|
||||||
'{% load category_tags %}'
|
"{% load category_tags %}"
|
||||||
'{% get_top_level_categories using "categories.category" as varname %}'
|
'{% 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)
|
self.assertEqual(resp, expected_resp)
|
||||||
|
|
||||||
# get_category_drilldown
|
# get_category_drilldown
|
||||||
expected_resp = "World|World > Worldbeat|"
|
expected_resp = "World|World > Worldbeat|"
|
||||||
resp = self.render_template(
|
resp = self.render_template(
|
||||||
'{% load category_tags %}'
|
"{% load category_tags %}"
|
||||||
'{% get_category_drilldown "/World" using "categories.category" as var %}'
|
'{% 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)
|
self.assertEqual(resp, expected_resp)
|
||||||
|
|
||||||
# recursetree
|
# 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>'
|
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"))}
|
ctxt = {"nodes": Category.objects.filter(name__in=("Worldbeat", "Urban Cowboy"))}
|
||||||
resp = self.render_template(
|
resp = self.render_template(
|
||||||
'{% load category_tags %}'
|
"{% load category_tags %}"
|
||||||
'<ul>{% recursetree nodes|tree_queryset %}<li>{{ node.name }}'
|
"<ul>{% recursetree nodes|tree_queryset %}<li>{{ node.name }}"
|
||||||
'{% if not node.is_leaf_node %}<ul>{{ children }}'
|
"{% if not node.is_leaf_node %}<ul>{{ children }}"
|
||||||
'</ul>{% endif %}</li>{% endrecursetree %}</ul>', ctxt)
|
"</ul>{% endif %}</li>{% endrecursetree %}</ul>",
|
||||||
|
ctxt,
|
||||||
|
)
|
||||||
self.assertEqual(resp, expected_resp)
|
self.assertEqual(resp, expected_resp)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
from django.http import Http404
|
|
||||||
from django.contrib.auth.models import AnonymousUser
|
from django.contrib.auth.models import AnonymousUser
|
||||||
from django.test import Client, TestCase, RequestFactory
|
from django.http import Http404
|
||||||
from categories.models import Category, CategoryRelation
|
from django.test import Client, RequestFactory, TestCase
|
||||||
|
|
||||||
from categories import views
|
from categories import views
|
||||||
|
from categories.models import Category, CategoryRelation
|
||||||
|
|
||||||
|
|
||||||
class MyCategoryRelationView(views.CategoryRelatedDetail):
|
class MyCategoryRelationView(views.CategoryRelatedDetail):
|
||||||
|
|
@ -10,16 +11,18 @@ class MyCategoryRelationView(views.CategoryRelatedDetail):
|
||||||
|
|
||||||
|
|
||||||
class TestCategoryViews(TestCase):
|
class TestCategoryViews(TestCase):
|
||||||
fixtures = ['musicgenres.json', ]
|
fixtures = [
|
||||||
|
"musicgenres.json",
|
||||||
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.client = Client()
|
self.client = Client()
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
def test_category_detail(self):
|
def test_category_detail(self):
|
||||||
cat0 = Category.objects.get(slug='country', level=0)
|
cat0 = Category.objects.get(slug="country", level=0)
|
||||||
cat1 = cat0.children.get(slug='country-pop')
|
cat1 = cat0.children.get(slug="country-pop")
|
||||||
cat2 = Category.objects.get(slug='urban-cowboy')
|
cat2 = Category.objects.get(slug="urban-cowboy")
|
||||||
url = cat0.get_absolute_url()
|
url = cat0.get_absolute_url()
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEquals(response.status_code, 200)
|
||||||
|
|
@ -33,51 +36,48 @@ class TestCategoryViews(TestCase):
|
||||||
self.assertEquals(response.status_code, 404)
|
self.assertEquals(response.status_code, 404)
|
||||||
|
|
||||||
def test_get_category_for_path(self):
|
def test_get_category_for_path(self):
|
||||||
cat0 = Category.objects.get(slug='country', level=0)
|
cat0 = Category.objects.get(slug="country", level=0)
|
||||||
cat1 = cat0.children.get(slug='country-pop')
|
cat1 = cat0.children.get(slug="country-pop")
|
||||||
cat2 = Category.objects.get(slug='urban-cowboy')
|
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)
|
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)
|
self.assertEquals(result, cat1)
|
||||||
result = views.get_category_for_path('/country/')
|
result = views.get_category_for_path("/country/")
|
||||||
self.assertEquals(result, cat0)
|
self.assertEquals(result, cat0)
|
||||||
|
|
||||||
def test_categorydetailview(self):
|
def test_categorydetailview(self):
|
||||||
request = self.factory.get('')
|
request = self.factory.get("")
|
||||||
request.user = AnonymousUser()
|
request.user = AnonymousUser()
|
||||||
self.assertRaises(AttributeError, views.CategoryDetailView.as_view(), request)
|
self.assertRaises(AttributeError, views.CategoryDetailView.as_view(), request)
|
||||||
|
|
||||||
request = self.factory.get('')
|
request = self.factory.get("")
|
||||||
request.user = AnonymousUser()
|
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)
|
self.assertEquals(response.status_code, 200)
|
||||||
|
|
||||||
request = self.factory.get('')
|
request = self.factory.get("")
|
||||||
request.user = AnonymousUser()
|
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):
|
def test_categoryrelateddetailview(self):
|
||||||
from simpletext.models import SimpleText
|
from simpletext.models import SimpleText
|
||||||
stext = SimpleText.objects.create(
|
|
||||||
name='Test',
|
stext = SimpleText.objects.create(name="Test", description="test description")
|
||||||
description='test description'
|
cat = Category.objects.get(slug="urban-cowboy")
|
||||||
)
|
cat_rel = CategoryRelation.objects.create(category=cat, content_object=stext) # NOQA
|
||||||
cat = Category.objects.get(slug='urban-cowboy')
|
request = self.factory.get("")
|
||||||
cat_rel = CategoryRelation.objects.create( # NOQA
|
|
||||||
category=cat,
|
|
||||||
content_object=stext
|
|
||||||
)
|
|
||||||
request = self.factory.get('')
|
|
||||||
request.user = AnonymousUser()
|
request.user = AnonymousUser()
|
||||||
self.assertRaises(AttributeError, MyCategoryRelationView.as_view(), request)
|
self.assertRaises(AttributeError, MyCategoryRelationView.as_view(), request)
|
||||||
|
|
||||||
request = self.factory.get('')
|
request = self.factory.get("")
|
||||||
request.user = AnonymousUser()
|
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)
|
self.assertEquals(response.status_code, 200)
|
||||||
|
|
||||||
request = self.factory.get('')
|
request = self.factory.get("")
|
||||||
request.user = AnonymousUser()
|
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/"
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,11 @@
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
from .models import Category
|
|
||||||
from . import views
|
from . import views
|
||||||
|
from .models import Category
|
||||||
|
|
||||||
|
categorytree_dict = {"queryset": Category.objects.filter(level=0)}
|
||||||
|
|
||||||
categorytree_dict = {
|
urlpatterns = (url(r"^$", ListView.as_view(**categorytree_dict), name="categories_tree_list"),)
|
||||||
'queryset': Category.objects.filter(level=0)
|
|
||||||
}
|
|
||||||
|
|
||||||
urlpatterns = (
|
urlpatterns += (url(r"^(?P<path>.+)/$", views.category_detail, name="categories_category"),)
|
||||||
url(
|
|
||||||
r'^$', ListView.as_view(**categorytree_dict), name='categories_tree_list'
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
urlpatterns += (
|
|
||||||
url(r'^(?P<path>.+)/$', views.category_detail, name='categories_category'),
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
from django.http import Http404, HttpResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.http import HttpResponse, Http404
|
|
||||||
from django.template.loader import select_template
|
from django.template.loader import select_template
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
|
|
@ -7,139 +7,126 @@ from django.views.generic import DetailView, ListView
|
||||||
from .models import Category
|
from .models import Category
|
||||||
|
|
||||||
|
|
||||||
def category_detail(request, path, template_name='categories/category_detail.html', extra_context={}):
|
def category_detail(request, path, template_name="categories/category_detail.html", extra_context={}):
|
||||||
path_items = path.strip('/').split('/')
|
path_items = path.strip("/").split("/")
|
||||||
if len(path_items) >= 2:
|
if len(path_items) >= 2:
|
||||||
category = get_object_or_404(
|
category = get_object_or_404(
|
||||||
Category,
|
Category, 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:
|
else:
|
||||||
category = get_object_or_404(
|
category = get_object_or_404(Category, slug__iexact=path_items[-1], level=len(path_items) - 1)
|
||||||
Category,
|
|
||||||
slug__iexact=path_items[-1],
|
|
||||||
level=len(path_items) - 1)
|
|
||||||
|
|
||||||
templates = []
|
templates = []
|
||||||
while path_items:
|
while path_items:
|
||||||
templates.append('categories/%s.html' % '_'.join(path_items))
|
templates.append("categories/%s.html" % "_".join(path_items))
|
||||||
path_items.pop()
|
path_items.pop()
|
||||||
templates.append(template_name)
|
templates.append(template_name)
|
||||||
|
|
||||||
context = {'category': category}
|
context = {"category": category}
|
||||||
if extra_context:
|
if extra_context:
|
||||||
context.update(extra_context)
|
context.update(extra_context)
|
||||||
return HttpResponse(select_template(templates).render(context))
|
return HttpResponse(select_template(templates).render(context))
|
||||||
|
|
||||||
|
|
||||||
def get_category_for_path(path, queryset=Category.objects.all()):
|
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:
|
if len(path_items) >= 2:
|
||||||
queryset = queryset.filter(
|
queryset = queryset.filter(
|
||||||
slug__iexact=path_items[-1],
|
slug__iexact=path_items[-1], level=len(path_items) - 1, parent__slug__iexact=path_items[-2]
|
||||||
level=len(path_items) - 1,
|
)
|
||||||
parent__slug__iexact=path_items[-2])
|
|
||||||
else:
|
else:
|
||||||
queryset = queryset.filter(
|
queryset = queryset.filter(slug__iexact=path_items[-1], level=len(path_items) - 1)
|
||||||
slug__iexact=path_items[-1],
|
|
||||||
level=len(path_items) - 1)
|
|
||||||
return queryset.get()
|
return queryset.get()
|
||||||
|
|
||||||
|
|
||||||
class CategoryDetailView(DetailView):
|
class CategoryDetailView(DetailView):
|
||||||
model = Category
|
model = Category
|
||||||
path_field = 'path'
|
path_field = "path"
|
||||||
|
|
||||||
def get_object(self, **kwargs):
|
def get_object(self, **kwargs):
|
||||||
if self.path_field not in self.kwargs:
|
if self.path_field not in self.kwargs:
|
||||||
raise AttributeError("Category detail view %s must be called with "
|
raise AttributeError(
|
||||||
"a %s." % (self.__class__.__name__, self.path_field))
|
"Category detail view %s must be called with " "a %s." % (self.__class__.__name__, self.path_field)
|
||||||
|
)
|
||||||
if self.queryset is None:
|
if self.queryset is None:
|
||||||
queryset = self.get_queryset()
|
queryset = self.get_queryset()
|
||||||
try:
|
try:
|
||||||
return get_category_for_path(self.kwargs[self.path_field], self.model.objects.all())
|
return get_category_for_path(self.kwargs[self.path_field], self.model.objects.all())
|
||||||
except Category.DoesNotExist:
|
except Category.DoesNotExist:
|
||||||
raise Http404(_("No %(verbose_name)s found matching the query") %
|
raise Http404(
|
||||||
{'verbose_name': queryset.model._meta.verbose_name})
|
_("No %(verbose_name)s found matching the query") % {"verbose_name": queryset.model._meta.verbose_name}
|
||||||
|
)
|
||||||
|
|
||||||
def get_template_names(self):
|
def get_template_names(self):
|
||||||
names = []
|
names = []
|
||||||
path_items = self.kwargs[self.path_field].strip('/').split('/')
|
path_items = self.kwargs[self.path_field].strip("/").split("/")
|
||||||
while path_items:
|
while path_items:
|
||||||
names.append('categories/%s.html' % '_'.join(path_items))
|
names.append("categories/%s.html" % "_".join(path_items))
|
||||||
path_items.pop()
|
path_items.pop()
|
||||||
names.extend(super(CategoryDetailView, self).get_template_names())
|
names.extend(super(CategoryDetailView, self).get_template_names())
|
||||||
return names
|
return names
|
||||||
|
|
||||||
|
|
||||||
class CategoryRelatedDetail(DetailView):
|
class CategoryRelatedDetail(DetailView):
|
||||||
path_field = 'category_path'
|
path_field = "category_path"
|
||||||
object_name_field = None
|
object_name_field = None
|
||||||
|
|
||||||
def get_object(self, **kwargs):
|
def get_object(self, **kwargs):
|
||||||
if self.path_field not in self.kwargs:
|
if self.path_field not in self.kwargs:
|
||||||
raise AttributeError("Category detail view %s must be called with "
|
raise AttributeError(
|
||||||
"a %s." % (self.__class__.__name__, self.path_field))
|
"Category detail view %s must be called with " "a %s." % (self.__class__.__name__, self.path_field)
|
||||||
|
)
|
||||||
queryset = super(CategoryRelatedDetail, self).get_queryset()
|
queryset = super(CategoryRelatedDetail, self).get_queryset()
|
||||||
try:
|
try:
|
||||||
category = get_category_for_path(self.kwargs[self.path_field])
|
category = get_category_for_path(self.kwargs[self.path_field])
|
||||||
except Category.DoesNotExist:
|
except Category.DoesNotExist:
|
||||||
raise Http404(_("No %(verbose_name)s found matching the query") %
|
raise Http404(
|
||||||
{'verbose_name': queryset.model._meta.verbose_name})
|
_("No %(verbose_name)s found matching the query") % {"verbose_name": queryset.model._meta.verbose_name}
|
||||||
|
)
|
||||||
return queryset.get(category=category)
|
return queryset.get(category=category)
|
||||||
|
|
||||||
def get_template_names(self):
|
def get_template_names(self):
|
||||||
names = []
|
names = []
|
||||||
opts = self.object._meta
|
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:
|
if self.object_name_field:
|
||||||
path_items.append(getattr(self.object, self.object_name_field))
|
path_items.append(getattr(self.object, self.object_name_field))
|
||||||
while path_items:
|
while path_items:
|
||||||
names.append('%s/category_%s_%s%s.html' % (
|
names.append(
|
||||||
opts.app_label,
|
"%s/category_%s_%s%s.html"
|
||||||
'_'.join(path_items),
|
% (opts.app_label, "_".join(path_items), opts.object_name.lower(), self.template_name_suffix)
|
||||||
opts.object_name.lower(),
|
|
||||||
self.template_name_suffix)
|
|
||||||
)
|
)
|
||||||
path_items.pop()
|
path_items.pop()
|
||||||
names.append('%s/category_%s%s.html' % (
|
names.append("%s/category_%s%s.html" % (opts.app_label, opts.object_name.lower(), self.template_name_suffix))
|
||||||
opts.app_label,
|
|
||||||
opts.object_name.lower(),
|
|
||||||
self.template_name_suffix)
|
|
||||||
)
|
|
||||||
names.extend(super(CategoryRelatedDetail, self).get_template_names())
|
names.extend(super(CategoryRelatedDetail, self).get_template_names())
|
||||||
return names
|
return names
|
||||||
|
|
||||||
|
|
||||||
class CategoryRelatedList(ListView):
|
class CategoryRelatedList(ListView):
|
||||||
path_field = 'category_path'
|
path_field = "category_path"
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
if self.path_field not in self.kwargs:
|
if self.path_field not in self.kwargs:
|
||||||
raise AttributeError("Category detail view %s must be called with "
|
raise AttributeError(
|
||||||
"a %s." % (self.__class__.__name__, self.path_field))
|
"Category detail view %s must be called with " "a %s." % (self.__class__.__name__, self.path_field)
|
||||||
|
)
|
||||||
queryset = super(CategoryRelatedList, self).get_queryset()
|
queryset = super(CategoryRelatedList, self).get_queryset()
|
||||||
category = get_category_for_path(self.kwargs[self.path_field])
|
category = get_category_for_path(self.kwargs[self.path_field])
|
||||||
return queryset.filter(category=category)
|
return queryset.filter(category=category)
|
||||||
|
|
||||||
def get_template_names(self):
|
def get_template_names(self):
|
||||||
names = []
|
names = []
|
||||||
if hasattr(self.object_list, 'model'):
|
if hasattr(self.object_list, "model"):
|
||||||
opts = self.object_list.model._meta
|
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:
|
while path_items:
|
||||||
names.append('%s/category_%s_%s%s.html' % (
|
names.append(
|
||||||
opts.app_label,
|
"%s/category_%s_%s%s.html"
|
||||||
'_'.join(path_items),
|
% (opts.app_label, "_".join(path_items), opts.object_name.lower(), self.template_name_suffix)
|
||||||
opts.object_name.lower(),
|
|
||||||
self.template_name_suffix)
|
|
||||||
)
|
)
|
||||||
path_items.pop()
|
path_items.pop()
|
||||||
names.append('%s/category_%s%s.html' % (
|
names.append(
|
||||||
opts.app_label,
|
"%s/category_%s%s.html" % (opts.app_label, opts.object_name.lower(), self.template_name_suffix)
|
||||||
opts.object_name.lower(),
|
|
||||||
self.template_name_suffix)
|
|
||||||
)
|
)
|
||||||
names.extend(super(CategoryRelatedList, self).get_template_names())
|
names.extend(super(CategoryRelatedList, self).get_template_names())
|
||||||
return names
|
return names
|
||||||
|
|
|
||||||
|
|
@ -770,4 +770,4 @@ display:inline;
|
||||||
}
|
}
|
||||||
li p {
|
li p {
|
||||||
margin-top:8px;
|
margin-top:8px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,4 +17,4 @@ var TOC = {
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
TOC.load();
|
TOC.load();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -20,4 +20,4 @@ Reconfiguring Fields
|
||||||
|
|
||||||
You can make changes to the field configurations as long as they do not change the underlying database structure. For example, adding a ``related_name`` (see :ref:`registering_a_m2one_relationship`\ ) because it only affects Django code. Changing the name of the field, however, is a different matter.
|
You can make changes to the field configurations as long as they do not change the underlying database structure. For example, adding a ``related_name`` (see :ref:`registering_a_m2one_relationship`\ ) because it only affects Django code. Changing the name of the field, however, is a different matter.
|
||||||
|
|
||||||
Django Categories provides a complementary management command to drop a field from the database (the field must still be in the configuration to do so): ``python manage.py drop_category_field app_name model_name field_name``
|
Django Categories provides a complementary management command to drop a field from the database (the field must still be in the configuration to do so): ``python manage.py drop_category_field app_name model_name field_name``
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,4 @@ class SimpleCategory(CategoryBase):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = 'simple categories'
|
verbose_name_plural = "simple categories"
|
||||||
|
|
|
||||||
|
|
@ -5,29 +5,25 @@ from categories.base import CategoryBase
|
||||||
class Category(CategoryBase):
|
class Category(CategoryBase):
|
||||||
thumbnail = models.FileField(
|
thumbnail = models.FileField(
|
||||||
upload_to=settings.THUMBNAIL_UPLOAD_PATH,
|
upload_to=settings.THUMBNAIL_UPLOAD_PATH,
|
||||||
null=True, blank=True,
|
null=True,
|
||||||
storage=settings.THUMBNAIL_STORAGE,)
|
blank=True,
|
||||||
|
storage=settings.THUMBNAIL_STORAGE,
|
||||||
|
)
|
||||||
thumbnail_width = models.IntegerField(blank=True, null=True)
|
thumbnail_width = models.IntegerField(blank=True, null=True)
|
||||||
thumbnail_height = models.IntegerField(blank=True, null=True)
|
thumbnail_height = models.IntegerField(blank=True, null=True)
|
||||||
order = models.IntegerField(default=0)
|
order = models.IntegerField(default=0)
|
||||||
alternate_title = models.CharField(
|
alternate_title = models.CharField(
|
||||||
blank=True,
|
blank=True, default="", max_length=100, help_text="An alternative title to use on pages with this category."
|
||||||
default="",
|
)
|
||||||
max_length=100,
|
|
||||||
help_text="An alternative title to use on pages with this category.")
|
|
||||||
alternate_url = models.CharField(
|
alternate_url = models.CharField(
|
||||||
blank=True,
|
blank=True,
|
||||||
max_length=200,
|
max_length=200,
|
||||||
help_text="An alternative URL to use instead of the one derived from "
|
help_text="An alternative URL to use instead of the one derived from " "the category hierarchy.",
|
||||||
"the category hierarchy.")
|
)
|
||||||
description = models.TextField(blank=True, null=True)
|
description = models.TextField(blank=True, null=True)
|
||||||
meta_keywords = models.CharField(
|
meta_keywords = models.CharField(
|
||||||
blank=True,
|
blank=True, default="", max_length=255, help_text="Comma-separated keywords for search engines."
|
||||||
default="",
|
)
|
||||||
max_length=255,
|
|
||||||
help_text="Comma-separated keywords for search engines.")
|
|
||||||
meta_extra = models.TextField(
|
meta_extra = models.TextField(
|
||||||
blank=True,
|
blank=True, default="", help_text="(Advanced) Any additional HTML to be placed verbatim " "in the <head>"
|
||||||
default="",
|
)
|
||||||
help_text="(Advanced) Any additional HTML to be placed verbatim "
|
|
||||||
"in the <head>")
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,9 @@ from categories.models import Category
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if self.thumbnail:
|
if self.thumbnail:
|
||||||
from django.core.files.images import get_image_dimensions
|
|
||||||
import django
|
import django
|
||||||
|
from django.core.files.images import get_image_dimensions
|
||||||
|
|
||||||
if django.VERSION[1] < 2:
|
if django.VERSION[1] < 2:
|
||||||
width, height = get_image_dimensions(self.thumbnail.file)
|
width, height = get_image_dimensions(self.thumbnail.file)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ from categories.base import CategoryBase
|
||||||
|
|
||||||
|
|
||||||
class Meta(CategoryBase.Meta):
|
class Meta(CategoryBase.Meta):
|
||||||
verbose_name_plural = 'categories'
|
verbose_name_plural = "categories"
|
||||||
|
|
||||||
|
|
||||||
class MPTTMeta:
|
class MPTTMeta:
|
||||||
order_insertion_by = ('order', 'name')
|
order_insertion_by = ("order", "name")
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ class CategoryAdminForm(CategoryBaseAdminForm):
|
||||||
model = Category
|
model = Category
|
||||||
|
|
||||||
def clean_alternate_title(self):
|
def clean_alternate_title(self):
|
||||||
if self.instance is None or not self.cleaned_data['alternate_title']:
|
if self.instance is None or not self.cleaned_data["alternate_title"]:
|
||||||
return self.cleaned_data['name']
|
return self.cleaned_data["name"]
|
||||||
else:
|
else:
|
||||||
return self.cleaned_data['alternate_title']
|
return self.cleaned_data["alternate_title"]
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,21 @@ from categories.base import CategoryBaseAdmin
|
||||||
|
|
||||||
class CategoryAdmin(CategoryBaseAdmin):
|
class CategoryAdmin(CategoryBaseAdmin):
|
||||||
form = CategoryAdminForm
|
form = CategoryAdminForm
|
||||||
list_display = ('name', 'alternate_title', 'active')
|
list_display = ("name", "alternate_title", "active")
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {
|
(None, {"fields": ("parent", "name", "thumbnail", "active")}),
|
||||||
'fields': ('parent', 'name', 'thumbnail', 'active')
|
(
|
||||||
}),
|
"Meta Data",
|
||||||
('Meta Data', {
|
{
|
||||||
'fields': ('alternate_title', 'alternate_url', 'description',
|
"fields": ("alternate_title", "alternate_url", "description", "meta_keywords", "meta_extra"),
|
||||||
'meta_keywords', 'meta_extra'),
|
"classes": ("collapse",),
|
||||||
'classes': ('collapse',),
|
},
|
||||||
}),
|
),
|
||||||
('Advanced', {
|
(
|
||||||
'fields': ('order', 'slug'),
|
"Advanced",
|
||||||
'classes': ('collapse',),
|
{
|
||||||
}),
|
"fields": ("order", "slug"),
|
||||||
|
"classes": ("collapse",),
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
#
|
||||||
# Django Categories documentation build configuration file, created by
|
# Django Categories documentation build configuration file, created by
|
||||||
# sphinx-quickstart on Tue Oct 6 07:53:33 2009.
|
# 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
|
# All configuration values have a default; values that are commented out
|
||||||
# serve to show the default.
|
# serve to show the default.
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# 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
|
# 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.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
sys.path.append(os.path.abspath('..'))
|
sys.path.append(os.path.abspath(".."))
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings'
|
os.environ["DJANGO_SETTINGS_MODULE"] = "example.settings"
|
||||||
|
|
||||||
import categories # noqa
|
import categories # noqa
|
||||||
|
|
||||||
|
|
@ -32,17 +31,17 @@ extensions = []
|
||||||
templates_path = ['_templates']
|
templates_path = ['_templates']
|
||||||
|
|
||||||
# The suffix of source filenames.
|
# The suffix of source filenames.
|
||||||
source_suffix = '.rst'
|
source_suffix = ".rst"
|
||||||
|
|
||||||
# The encoding of source files.
|
# The encoding of source files.
|
||||||
# ource_encoding = 'utf-8'
|
# ource_encoding = 'utf-8'
|
||||||
|
|
||||||
# The master toctree document.
|
# The master toctree document.
|
||||||
master_doc = 'index'
|
master_doc = "index"
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = 'Django Categories'
|
project = "Django Categories"
|
||||||
copyright = '2010-2012, Corey Oordt'
|
copyright = "2010-2012, Corey Oordt"
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |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
|
# List of directories, relative to source directory, that shouldn't be searched
|
||||||
# for source files.
|
# for source files.
|
||||||
exclude_trees = ['_build']
|
exclude_trees = ["_build"]
|
||||||
|
|
||||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
# efault_role = None
|
# efault_role = None
|
||||||
|
|
@ -85,7 +84,7 @@ exclude_trees = ['_build']
|
||||||
# how_authors = False
|
# how_authors = False
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# 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.
|
# A list of ignored prefixes for module index sorting.
|
||||||
# odindex_common_prefix = []
|
# odindex_common_prefix = []
|
||||||
|
|
@ -124,7 +123,7 @@ html_theme = 'default'
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# 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,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# 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,
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
# using the given strftime format.
|
# using the given strftime format.
|
||||||
|
|
@ -162,7 +161,7 @@ html_static_path = ['_static']
|
||||||
# tml_file_suffix = ''
|
# tml_file_suffix = ''
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = 'DjangoCategoriesdoc'
|
htmlhelp_basename = "DjangoCategoriesdoc"
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output --------------------------------------------------
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
|
@ -176,7 +175,7 @@ htmlhelp_basename = 'DjangoCategoriesdoc'
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
('index', 'DjangoCategories.tex', 'Django Categories Documentation', 'CoreyOordt', 'manual'),
|
("index", "DjangoCategories.tex", "Django Categories Documentation", "CoreyOordt", "manual"),
|
||||||
]
|
]
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top of
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
|
|
|
||||||
|
|
@ -41,4 +41,3 @@ Indices and tables
|
||||||
* :ref:`genindex`
|
* :ref:`genindex`
|
||||||
* :ref:`modindex`
|
* :ref:`modindex`
|
||||||
* :ref:`search`
|
* :ref:`search`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,4 +35,4 @@ drop_category_field
|
||||||
|
|
||||||
Drop the ``field_name`` field from the ``app_name_model_name`` table, if the field is currently registered in ``CATEGORIES_SETTINGS``\ .
|
Drop the ``field_name`` field from the ``app_name_model_name`` table, if the field is currently registered in ``CATEGORIES_SETTINGS``\ .
|
||||||
|
|
||||||
Requires Django South.
|
Requires Django South.
|
||||||
|
|
|
||||||
|
|
@ -128,4 +128,4 @@ ADMIN_FIELDSETS
|
||||||
|
|
||||||
**Default:** ``{}``
|
**Default:** ``{}``
|
||||||
|
|
||||||
**Description:** Allows for selective customization of the default behavior of adding the fields to the admin class. See :ref:`admin_settings` for more information.
|
**Description:** Allows for selective customization of the default behavior of adding the fields to the admin class. See :ref:`admin_settings` for more information.
|
||||||
|
|
|
||||||
|
|
@ -59,4 +59,3 @@ comma-separated list of feature names. The valid feature names are:
|
||||||
Books -> []
|
Books -> []
|
||||||
Sci-fi -> [u'Books']
|
Sci-fi -> [u'Books']
|
||||||
Dystopian Futures -> [u'Books', u'Sci-fi']
|
Dystopian Futures -> [u'Books', u'Sci-fi']
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,4 @@
|
||||||
<p><em>No entries for {{ category }}</em></p>
|
<p><em>No entries for {{ category }}</em></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -16,108 +16,105 @@ ADMINS = (
|
||||||
MANAGERS = ADMINS
|
MANAGERS = ADMINS
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
"default": {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
'NAME': 'dev.db',
|
"NAME": "dev.db",
|
||||||
'USER': '',
|
"USER": "",
|
||||||
'PASSWORD': '',
|
"PASSWORD": "",
|
||||||
'HOST': '',
|
"HOST": "",
|
||||||
'PORT': '',
|
"PORT": "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTALLED_APPS = (
|
INSTALLED_APPS = (
|
||||||
'django.contrib.admin',
|
"django.contrib.admin",
|
||||||
'django.contrib.auth',
|
"django.contrib.auth",
|
||||||
'django.contrib.contenttypes',
|
"django.contrib.contenttypes",
|
||||||
'django.contrib.sessions',
|
"django.contrib.sessions",
|
||||||
'django.contrib.sites',
|
"django.contrib.sites",
|
||||||
'django.contrib.messages',
|
"django.contrib.messages",
|
||||||
'django.contrib.staticfiles',
|
"django.contrib.staticfiles",
|
||||||
'django.contrib.flatpages',
|
"django.contrib.flatpages",
|
||||||
'categories',
|
"categories",
|
||||||
'categories.editor',
|
"categories.editor",
|
||||||
'mptt',
|
"mptt",
|
||||||
'simpletext',
|
"simpletext",
|
||||||
)
|
)
|
||||||
|
|
||||||
TIME_ZONE = 'America/Chicago'
|
TIME_ZONE = "America/Chicago"
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
LANGUAGE_CODE = "en-us"
|
||||||
|
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
|
|
||||||
USE_I18N = True
|
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_DIRS = ()
|
||||||
|
|
||||||
STATICFILES_FINDERS = (
|
STATICFILES_FINDERS = (
|
||||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
"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 = (
|
MIDDLEWARE = (
|
||||||
'django.middleware.security.SecurityMiddleware',
|
"django.middleware.security.SecurityMiddleware",
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
'django.middleware.common.CommonMiddleware',
|
"django.middleware.common.CommonMiddleware",
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
)
|
)
|
||||||
|
|
||||||
ROOT_URLCONF = 'urls'
|
ROOT_URLCONF = "urls"
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
'APP_DIRS': True,
|
"APP_DIRS": True,
|
||||||
'DIRS': [os.path.abspath(os.path.join(os.path.dirname(__file__), 'templates'))],
|
"DIRS": [os.path.abspath(os.path.join(os.path.dirname(__file__), "templates"))],
|
||||||
'OPTIONS': {
|
"OPTIONS": {
|
||||||
'debug': DEBUG,
|
"debug": DEBUG,
|
||||||
'context_processors': [
|
"context_processors": [
|
||||||
'django.contrib.auth.context_processors.auth',
|
"django.contrib.auth.context_processors.auth",
|
||||||
'django.template.context_processors.debug',
|
"django.template.context_processors.debug",
|
||||||
'django.template.context_processors.i18n',
|
"django.template.context_processors.i18n",
|
||||||
'django.template.context_processors.media',
|
"django.template.context_processors.media",
|
||||||
'django.template.context_processors.static',
|
"django.template.context_processors.static",
|
||||||
'django.template.context_processors.tz',
|
"django.template.context_processors.tz",
|
||||||
'django.contrib.messages.context_processors.messages',
|
"django.contrib.messages.context_processors.messages",
|
||||||
],
|
],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
CATEGORIES_SETTINGS = {
|
CATEGORIES_SETTINGS = {
|
||||||
'ALLOW_SLUG_CHANGE': True,
|
"ALLOW_SLUG_CHANGE": True,
|
||||||
'RELATION_MODELS': ['simpletext.simpletext', 'flatpages.flatpage'],
|
"RELATION_MODELS": ["simpletext.simpletext", "flatpages.flatpage"],
|
||||||
'FK_REGISTRY': {
|
"FK_REGISTRY": {
|
||||||
'flatpages.flatpage': (
|
"flatpages.flatpage": ("category", {"on_delete": models.CASCADE}),
|
||||||
'category',
|
"simpletext.simpletext": (
|
||||||
{'on_delete': models.CASCADE}
|
"primary_category",
|
||||||
),
|
{"name": "secondary_category", "related_name": "simpletext_sec_cat"},
|
||||||
'simpletext.simpletext': (
|
|
||||||
'primary_category',
|
|
||||||
{'name': 'secondary_category', 'related_name': 'simpletext_sec_cat'},
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
'M2M_REGISTRY': {
|
"M2M_REGISTRY": {
|
||||||
# 'simpletext.simpletext': {'name': 'categories', 'related_name': 'm2mcats'},
|
# 'simpletext.simpletext': {'name': 'categories', 'related_name': 'm2mcats'},
|
||||||
'flatpages.flatpage': (
|
"flatpages.flatpage": (
|
||||||
{'name': 'other_categories', 'related_name': 'other_cats'},
|
{"name": "other_categories", "related_name": "other_cats"},
|
||||||
{'name': 'more_categories', 'related_name': 'more_cats'},
|
{"name": "more_categories", "related_name": "more_cats"},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_RUNNER = 'django.test.runner.DiscoverRunner'
|
TEST_RUNNER = "django.test.runner.DiscoverRunner"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# Django settings for sample project.
|
# Django settings for sample project.
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import django
|
import django
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
@ -16,109 +17,106 @@ ADMINS = (
|
||||||
MANAGERS = ADMINS
|
MANAGERS = ADMINS
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
"default": {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
'NAME': 'dev.db',
|
"NAME": "dev.db",
|
||||||
'USER': '',
|
"USER": "",
|
||||||
'PASSWORD': '',
|
"PASSWORD": "",
|
||||||
'HOST': '',
|
"HOST": "",
|
||||||
'PORT': '',
|
"PORT": "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTALLED_APPS = (
|
INSTALLED_APPS = (
|
||||||
'django.contrib.admin',
|
"django.contrib.admin",
|
||||||
'django.contrib.auth',
|
"django.contrib.auth",
|
||||||
'django.contrib.contenttypes',
|
"django.contrib.contenttypes",
|
||||||
'django.contrib.sessions',
|
"django.contrib.sessions",
|
||||||
'django.contrib.sites',
|
"django.contrib.sites",
|
||||||
'django.contrib.messages',
|
"django.contrib.messages",
|
||||||
'django.contrib.staticfiles',
|
"django.contrib.staticfiles",
|
||||||
'django.contrib.flatpages',
|
"django.contrib.flatpages",
|
||||||
'categories',
|
"categories",
|
||||||
'categories.editor',
|
"categories.editor",
|
||||||
'mptt',
|
"mptt",
|
||||||
'simpletext',
|
"simpletext",
|
||||||
)
|
)
|
||||||
|
|
||||||
TIME_ZONE = 'America/Chicago'
|
TIME_ZONE = "America/Chicago"
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
LANGUAGE_CODE = "en-us"
|
||||||
|
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
|
|
||||||
USE_I18N = True
|
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_DIRS = ()
|
||||||
|
|
||||||
STATICFILES_FINDERS = (
|
STATICFILES_FINDERS = (
|
||||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
"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 = (
|
MIDDLEWARE = (
|
||||||
'django.middleware.security.SecurityMiddleware',
|
"django.middleware.security.SecurityMiddleware",
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
'django.middleware.common.CommonMiddleware',
|
"django.middleware.common.CommonMiddleware",
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
"django.middleware.csrf.CsrfViewMiddleware",
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
)
|
)
|
||||||
|
|
||||||
ROOT_URLCONF = 'urls'
|
ROOT_URLCONF = "urls"
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
'APP_DIRS': True,
|
"APP_DIRS": True,
|
||||||
'DIRS': [os.path.abspath(os.path.join(os.path.dirname(__file__), 'templates'))],
|
"DIRS": [os.path.abspath(os.path.join(os.path.dirname(__file__), "templates"))],
|
||||||
'OPTIONS': {
|
"OPTIONS": {
|
||||||
'debug': DEBUG,
|
"debug": DEBUG,
|
||||||
'context_processors': [
|
"context_processors": [
|
||||||
'django.contrib.auth.context_processors.auth',
|
"django.contrib.auth.context_processors.auth",
|
||||||
'django.template.context_processors.debug',
|
"django.template.context_processors.debug",
|
||||||
'django.template.context_processors.i18n',
|
"django.template.context_processors.i18n",
|
||||||
'django.template.context_processors.media',
|
"django.template.context_processors.media",
|
||||||
'django.template.context_processors.static',
|
"django.template.context_processors.static",
|
||||||
'django.template.context_processors.tz',
|
"django.template.context_processors.tz",
|
||||||
'django.contrib.messages.context_processors.messages',
|
"django.contrib.messages.context_processors.messages",
|
||||||
],
|
],
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
CATEGORIES_SETTINGS = {
|
CATEGORIES_SETTINGS = {
|
||||||
'ALLOW_SLUG_CHANGE': True,
|
"ALLOW_SLUG_CHANGE": True,
|
||||||
'RELATION_MODELS': ['simpletext.simpletext', 'flatpages.flatpage'],
|
"RELATION_MODELS": ["simpletext.simpletext", "flatpages.flatpage"],
|
||||||
'FK_REGISTRY': {
|
"FK_REGISTRY": {
|
||||||
'flatpages.flatpage': (
|
"flatpages.flatpage": ("category", {"on_delete": models.CASCADE}),
|
||||||
'category',
|
"simpletext.simpletext": (
|
||||||
{'on_delete': models.CASCADE}
|
"primary_category",
|
||||||
),
|
{"name": "secondary_category", "related_name": "simpletext_sec_cat"},
|
||||||
'simpletext.simpletext': (
|
|
||||||
'primary_category',
|
|
||||||
{'name': 'secondary_category', 'related_name': 'simpletext_sec_cat'},
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
'M2M_REGISTRY': {
|
"M2M_REGISTRY": {
|
||||||
'simpletext.simpletext': {'name': 'categories', 'related_name': 'm2mcats'},
|
"simpletext.simpletext": {"name": "categories", "related_name": "m2mcats"},
|
||||||
'flatpages.flatpage': (
|
"flatpages.flatpage": (
|
||||||
{'name': 'other_categories', 'related_name': 'other_cats'},
|
{"name": "other_categories", "related_name": "other_cats"},
|
||||||
{'name': 'more_categories', 'related_name': 'more_cats'},
|
{"name": "more_categories", "related_name": "more_cats"},
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if django.VERSION[1] > 5:
|
if django.VERSION[1] > 5:
|
||||||
TEST_RUNNER = 'django.test.runner.DiscoverRunner'
|
TEST_RUNNER = "django.test.runner.DiscoverRunner"
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,28 @@
|
||||||
from .models import SimpleText, SimpleCategory
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from categories.admin import CategoryBaseAdmin, CategoryBaseAdminForm
|
from categories.admin import CategoryBaseAdmin, CategoryBaseAdminForm
|
||||||
|
|
||||||
|
from .models import SimpleCategory, SimpleText
|
||||||
|
|
||||||
|
|
||||||
class SimpleTextAdmin(admin.ModelAdmin):
|
class SimpleTextAdmin(admin.ModelAdmin):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {
|
(
|
||||||
'fields': ('name', 'description', )
|
None,
|
||||||
}),
|
{
|
||||||
|
"fields": (
|
||||||
|
"name",
|
||||||
|
"description",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SimpleCategoryAdminForm(CategoryBaseAdminForm):
|
class SimpleCategoryAdminForm(CategoryBaseAdminForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SimpleCategory
|
model = SimpleCategory
|
||||||
fields = '__all__'
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
class SimpleCategoryAdmin(CategoryBaseAdmin):
|
class SimpleCategoryAdmin(CategoryBaseAdmin):
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.9.13 on 2017-10-12 20:27
|
# Generated by Django 1.9.13 on 2017-10-12 20:27
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import mptt.fields
|
import mptt.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
@ -15,35 +14,45 @@ class Migration(migrations.Migration):
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='SimpleCategory',
|
name="SimpleCategory",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||||
('name', models.CharField(max_length=100, verbose_name='name')),
|
("name", models.CharField(max_length=100, verbose_name="name")),
|
||||||
('slug', models.SlugField(verbose_name='slug')),
|
("slug", models.SlugField(verbose_name="slug")),
|
||||||
('active', models.BooleanField(default=True, verbose_name='active')),
|
("active", models.BooleanField(default=True, verbose_name="active")),
|
||||||
('lft', models.PositiveIntegerField(db_index=True, editable=False)),
|
("lft", models.PositiveIntegerField(db_index=True, editable=False)),
|
||||||
('rght', models.PositiveIntegerField(db_index=True, editable=False)),
|
("rght", models.PositiveIntegerField(db_index=True, editable=False)),
|
||||||
('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
|
("tree_id", models.PositiveIntegerField(db_index=True, editable=False)),
|
||||||
('level', 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')),
|
(
|
||||||
|
"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={
|
options={
|
||||||
'verbose_name_plural': 'simple categories',
|
"verbose_name_plural": "simple categories",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='SimpleText',
|
name="SimpleText",
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||||
('name', models.CharField(max_length=255)),
|
("name", models.CharField(max_length=255)),
|
||||||
('description', models.TextField(blank=True)),
|
("description", models.TextField(blank=True)),
|
||||||
('created', models.DateTimeField(auto_now_add=True)),
|
("created", models.DateTimeField(auto_now_add=True)),
|
||||||
('updated', models.DateTimeField(auto_now=True)),
|
("updated", models.DateTimeField(auto_now=True)),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'ordering': ('-created',),
|
"ordering": ("-created",),
|
||||||
'get_latest_by': 'updated',
|
"get_latest_by": "updated",
|
||||||
'verbose_name_plural': 'Simple Text',
|
"verbose_name_plural": "Simple Text",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,46 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.10.5 on 2017-12-04 07:21
|
# Generated by Django 1.10.5 on 2017-12-04 07:21
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import django.db.models.manager
|
import django.db.models.manager
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('categories', '0002_auto_20170217_1111'),
|
("categories", "0002_auto_20170217_1111"),
|
||||||
('simpletext', '0001_initial'),
|
("simpletext", "0001_initial"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterModelManagers(
|
migrations.AlterModelManagers(
|
||||||
name='simplecategory',
|
name="simplecategory",
|
||||||
managers=[
|
managers=[
|
||||||
('tree', django.db.models.manager.Manager()),
|
("tree", django.db.models.manager.Manager()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='simpletext',
|
model_name="simpletext",
|
||||||
name='categories',
|
name="categories",
|
||||||
field=models.ManyToManyField(blank=True, related_name='m2mcats', to='categories.Category'),
|
field=models.ManyToManyField(blank=True, related_name="m2mcats", to="categories.Category"),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='simpletext',
|
model_name="simpletext",
|
||||||
name='primary_category',
|
name="primary_category",
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='categories.Category'),
|
field=models.ForeignKey(
|
||||||
|
blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="categories.Category"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='simpletext',
|
model_name="simpletext",
|
||||||
name='secondary_category',
|
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'),
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="simpletext_sec_cat",
|
||||||
|
to="categories.Category",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -6,23 +6,23 @@ from django.db import migrations, models
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('simpletext', '0002_auto_20171204_0721'),
|
("simpletext", "0002_auto_20171204_0721"),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='simplecategory',
|
model_name="simplecategory",
|
||||||
name='level',
|
name="level",
|
||||||
field=models.PositiveIntegerField(editable=False),
|
field=models.PositiveIntegerField(editable=False),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='simplecategory',
|
model_name="simplecategory",
|
||||||
name='lft',
|
name="lft",
|
||||||
field=models.PositiveIntegerField(editable=False),
|
field=models.PositiveIntegerField(editable=False),
|
||||||
),
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='simplecategory',
|
model_name="simplecategory",
|
||||||
name='rght',
|
name="rght",
|
||||||
field=models.PositiveIntegerField(editable=False),
|
field=models.PositiveIntegerField(editable=False),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@ class SimpleText(models.Model):
|
||||||
updated = models.DateTimeField(auto_now=True)
|
updated = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = 'Simple Text'
|
verbose_name_plural = "Simple Text"
|
||||||
ordering = ('-created',)
|
ordering = ("-created",)
|
||||||
get_latest_by = 'updated'
|
get_latest_by = "updated"
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
@ -24,16 +24,19 @@ class SimpleText(models.Model):
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
try:
|
try:
|
||||||
from django.db.models import permalink
|
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:
|
except ImportError:
|
||||||
from django.urls import reverse
|
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):
|
class SimpleCategory(CategoryBase):
|
||||||
"""A Test of catgorizing"""
|
"""A Test of catgorizing"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = 'simple categories'
|
verbose_name_plural = "simple categories"
|
||||||
|
|
||||||
|
|
||||||
# mport categories
|
# mport categories
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,11 @@ class SimpleTest(TestCase):
|
||||||
self.assertEqual(1 + 1, 2)
|
self.assertEqual(1 + 1, 2)
|
||||||
|
|
||||||
|
|
||||||
__test__ = {"doctest": """
|
__test__ = {
|
||||||
|
"doctest": """
|
||||||
Another way to test that 1 + 1 is equal to 2.
|
Another way to test that 1 + 1 is equal to 2.
|
||||||
|
|
||||||
>>> 1 + 1 == 2
|
>>> 1 + 1 == 2
|
||||||
True
|
True
|
||||||
"""}
|
"""
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,4 +62,4 @@
|
||||||
.treeTable .ui-draggable-dragging {
|
.treeTable .ui-draggable-dragging {
|
||||||
color: #000;
|
color: #000;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -455,4 +455,4 @@
|
||||||
function parentOf(node) {
|
function parentOf(node) {
|
||||||
return $(node).parentOf();
|
return $(node).parentOf();
|
||||||
}
|
}
|
||||||
})(django.jQuery);
|
})(django.jQuery);
|
||||||
|
|
|
||||||
|
|
@ -17,4 +17,4 @@ function showGenericRelatedObjectLookupPopup(triggeringLink, ctArray) {
|
||||||
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
|
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
|
||||||
win.focus();
|
win.focus();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,23 +12,16 @@ ROOT_PATH = os.path.dirname(os.path.dirname(__file__))
|
||||||
urlpatterns = (
|
urlpatterns = (
|
||||||
# Example:
|
# Example:
|
||||||
# (r'^sample/', include('sample.foo.urls')),
|
# (r'^sample/', include('sample.foo.urls')),
|
||||||
|
|
||||||
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
|
# Uncomment the admin/doc line below and add 'django.contrib.admindocs'
|
||||||
# to INSTALLED_APPS to enable admin documentation:
|
# to INSTALLED_APPS to enable admin documentation:
|
||||||
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||||
|
|
||||||
# Uncomment the next line to enable the admin:
|
# Uncomment the next line to enable the admin:
|
||||||
url(r'^admin/', admin.site.urls),
|
url(r"^admin/", admin.site.urls),
|
||||||
url(r'^categories/', include('categories.urls')),
|
url(r"^categories/", include("categories.urls")),
|
||||||
# r'^cats/', include('categories.urls')),
|
# r'^cats/', include('categories.urls')),
|
||||||
|
url(r"^static/categories/(?P<path>.*)$", serve, {"document_root": ROOT_PATH + "/categories/media/categories/"}),
|
||||||
url(r'^static/categories/(?P<path>.*)$', serve,
|
|
||||||
{'document_root': ROOT_PATH + '/categories/media/categories/'}),
|
|
||||||
|
|
||||||
# (r'^static/editor/(?P<path>.*)$', 'django.views.static.serve',
|
# (r'^static/editor/(?P<path>.*)$', 'django.views.static.serve',
|
||||||
# {'document_root': ROOT_PATH + '/editor/media/editor/',
|
# {'document_root': ROOT_PATH + '/editor/media/editor/',
|
||||||
# 'show_indexes':True}),
|
# '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')}),
|
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue