mirror of
https://github.com/jazzband/django-categories.git
synced 2026-05-02 20:54:45 +00:00
Merge branch '0.4'
This commit is contained in:
commit
cc25aabe3d
16 changed files with 199 additions and 7 deletions
|
|
@ -1,11 +1,14 @@
|
|||
from models import Category
|
||||
from django.contrib import admin
|
||||
from django import forms
|
||||
from django.template.defaultfilters import slugify
|
||||
|
||||
from mptt.forms import TreeNodeChoiceField
|
||||
from editor.tree_editor import TreeEditor
|
||||
from settings import ALLOW_SLUG_CHANGE
|
||||
from genericcollection import GenericCollectionTabularInline
|
||||
|
||||
from settings import ALLOW_SLUG_CHANGE, RELATION_MODELS
|
||||
from categories import registry
|
||||
from models import Category
|
||||
|
||||
class NullTreeNodeChoiceField(forms.ModelChoiceField):
|
||||
"""A ModelChoiceField for tree nodes."""
|
||||
|
|
@ -21,6 +24,11 @@ class NullTreeNodeChoiceField(forms.ModelChoiceField):
|
|||
"""
|
||||
return u'%s %s' % (self.level_indicator * getattr(obj, obj._meta.level_attr),
|
||||
obj)
|
||||
if RELATION_MODELS:
|
||||
from models import CategoryRelation
|
||||
|
||||
class InlineCategoryRelation(GenericCollectionTabularInline):
|
||||
model = CategoryRelation
|
||||
|
||||
|
||||
class CategoryAdminForm(forms.ModelForm):
|
||||
|
|
@ -84,7 +92,12 @@ class CategoryAdmin(TreeEditor, admin.ModelAdmin):
|
|||
'classes': ('collapse',),
|
||||
}),
|
||||
)
|
||||
|
||||
if RELATION_MODELS:
|
||||
inlines = [InlineCategoryRelation,]
|
||||
|
||||
class Media:
|
||||
js = ('js/genericcollections.js',)
|
||||
|
||||
|
||||
admin.site.register(Category, CategoryAdmin)
|
||||
|
||||
|
|
|
|||
25
categories/genericcollection.py
Normal file
25
categories/genericcollection.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
from django.contrib import admin
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
class GenericCollectionInlineModelAdmin(admin.options.InlineModelAdmin):
|
||||
ct_field = "content_type"
|
||||
ct_fk_field = "object_id"
|
||||
|
||||
def __init__(self, parent_model, admin_site):
|
||||
super(GenericCollectionInlineModelAdmin, self).__init__(parent_model, admin_site)
|
||||
ctypes = ContentType.objects.all().order_by('id').values_list('id', 'app_label', 'model')
|
||||
elements = ["%s: '%s/%s'" % (x, y, z) for x, y, z in ctypes]
|
||||
self.content_types = "{%s}" % ",".join(elements)
|
||||
|
||||
def get_formset(self, request, obj=None):
|
||||
result = super(GenericCollectionInlineModelAdmin, self).get_formset(request, obj)
|
||||
result.content_types = self.content_types
|
||||
result.ct_fk_field = self.ct_fk_field
|
||||
return result
|
||||
|
||||
class GenericCollectionTabularInline(GenericCollectionInlineModelAdmin):
|
||||
template = 'admin/edit_inline/gen_coll_tabular.html'
|
||||
|
||||
|
||||
class GenericCollectionStackedInline(GenericCollectionInlineModelAdmin):
|
||||
template = 'admin/edit_inline/gen_coll_stacked.html'
|
||||
20
categories/media/js/genericcollections.js
Normal file
20
categories/media/js/genericcollections.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
function showGenericRelatedObjectLookupPopup(triggeringLink, ctArray) {
|
||||
var realName = triggeringLink.id.replace(/^lookup_/, '');
|
||||
var name = id_to_windowname(realName);
|
||||
realName = realName.replace(/object_id/, 'content_type');
|
||||
var select = document.getElementById(realName);
|
||||
if (select.selectedIndex === 0) {
|
||||
alert("Select a content type first.");
|
||||
return false;
|
||||
}
|
||||
var selectedItem = select.item(select.selectedIndex).value;
|
||||
var href = triggeringLink.href.replace(/#/,'../../../'+ctArray[selectedItem]+"/?t=id");
|
||||
if (href.search(/\?/) >= 0) {
|
||||
href = href + '&pop=1';
|
||||
} else {
|
||||
href = href + '?pop=1';
|
||||
}
|
||||
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
|
||||
win.focus();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3,8 +3,14 @@ from django.core.urlresolvers import reverse
|
|||
from django.db.models import permalink
|
||||
from django.db import models
|
||||
from django.utils.encoding import force_unicode
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.contenttypes import generic
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
import mptt
|
||||
|
||||
from settings import RELATION_MODELS, RELATIONS
|
||||
|
||||
class Category(models.Model):
|
||||
parent = models.ForeignKey('self',
|
||||
blank=True,
|
||||
|
|
@ -49,3 +55,32 @@ class Category(models.Model):
|
|||
return ' > '.join([force_unicode(i.name) for i in ancestors]+[self.name,])
|
||||
|
||||
mptt.register(Category, order_insertion_by=['name'])
|
||||
|
||||
if RELATION_MODELS:
|
||||
category_relation_limits = reduce(lambda x,y: x|y, RELATIONS)
|
||||
class CategoryRelationManager(models.Manager):
|
||||
def get_content_type(self, content_type):
|
||||
qs = self.get_query_set()
|
||||
return qs.filter(content_type__name=content_type)
|
||||
|
||||
def get_relation_type(self, relation_type):
|
||||
qs = self.get_query_set()
|
||||
return qs.filter(relation_type=relation_type)
|
||||
|
||||
|
||||
class CategoryRelation(models.Model):
|
||||
"""Related story item"""
|
||||
story = models.ForeignKey(Category)
|
||||
content_type = models.ForeignKey(ContentType, limit_choices_to=category_relation_limits)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = generic.GenericForeignKey('content_type', 'object_id')
|
||||
relation_type = models.CharField(_("Relation Type"),
|
||||
max_length="200",
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_("A generic text field to tag a relation, like 'leadphoto'."))
|
||||
|
||||
objects = CategoryRelationManager()
|
||||
|
||||
def __unicode__(self):
|
||||
return u"CategoryRelation"
|
||||
|
|
|
|||
|
|
@ -2,4 +2,9 @@ from django.conf import settings
|
|||
|
||||
ALLOW_SLUG_CHANGE = getattr(settings, 'CATEGORIES_ALLOW_SLUG_CHANGE', False)
|
||||
|
||||
CACHE_VIEW_LENGTH = getattr(settings, 'CATEGORIES_CACHE_VIEW_LENGTH', 3600)
|
||||
CACHE_VIEW_LENGTH = getattr(settings, 'CATEGORIES_CACHE_VIEW_LENGTH', 3600)
|
||||
|
||||
from django.db.models import Q
|
||||
DEFAULT_RELATION_MODELS = []
|
||||
RELATION_MODELS = getattr(settings, 'CATEGORIES_RELATION_MODELS', DEFAULT_RELATION_MODELS) or []
|
||||
RELATIONS = [Q(app_label=al, model=m) for al, m in [x.split('.') for x in RELATION_MODELS]]
|
||||
|
|
|
|||
68
categories/templates/admin/edit_inline/gen_coll_tabular.html
Normal file
68
categories/templates/admin/edit_inline/gen_coll_tabular.html
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
{% load i18n adminmedia %}
|
||||
<div class="inline-group">
|
||||
<div class="tabular inline-related {% if forloop.last %}last-related{% endif %}">
|
||||
{{ inline_admin_formset.formset.management_form }}
|
||||
<fieldset class="module">
|
||||
<h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2>
|
||||
{{ inline_admin_formset.formset.non_form_errors }}
|
||||
<table>
|
||||
<thead><tr>
|
||||
{% for field in inline_admin_formset.fields %}
|
||||
<th {% if forloop.first %}colspan="2"{% endif %}>{{ field.label|capfirst }}</th>
|
||||
{% endfor %}
|
||||
{% if inline_admin_formset.formset.can_delete %}<th>{% trans "Delete?" %}</th>{% endif %}
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
{% for inline_admin_form in inline_admin_formset %}
|
||||
{% if inline_admin_form.form.non_field_errors %}
|
||||
<tr><td colspan="{{ inline_admin_form.field_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
|
||||
{% endif %}
|
||||
<tr class="{% cycle row1,row2 %} {% if inline_admin_form.original or inline_admin_form.show_url %}has_original{% endif %}">
|
||||
<td class="original">
|
||||
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
|
||||
{% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %}
|
||||
{% if inline_admin_form.show_url %}<a href="../../../r/{{ inline_admin_form.original.content_type_id }}/{{ inline_admin_form.original.id }}/">{% trans "View on site" %}</a>{% endif %}
|
||||
</p>{% endif %}
|
||||
{% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
|
||||
{{ inline_admin_form.fk_field.field }}
|
||||
{% spaceless %}
|
||||
{% for fieldset in inline_admin_form %}
|
||||
{% for line in fieldset %}
|
||||
{% for field in line %}
|
||||
{% if field.is_hidden %} {{ field.field }} {% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endspaceless %}
|
||||
</td>
|
||||
{% for fieldset in inline_admin_form %}
|
||||
{% for line in fieldset %}
|
||||
{% for field in line %}
|
||||
<td class="{{ field.field.name }}">
|
||||
{% if field.is_readonly %}
|
||||
<p>{{ field.contents }}</p>
|
||||
{% else %}
|
||||
{{ field.field.errors.as_ul }}
|
||||
{% ifequal field.field.name inline_admin_formset.formset.ct_fk_field %}
|
||||
{{ field.field }}
|
||||
<a id="lookup_id_{{field.field.html_name}}" class="related-lookup" onclick="return showGenericRelatedObjectLookupPopup(this, {{ inline_admin_formset.formset.content_types }});" href="#">
|
||||
<img width="16" height="16" alt="Lookup" src="{% admin_media_prefix %}img/admin/selector-search.gif"/>
|
||||
</a>
|
||||
{% else %}{{ field.field }} {% endifequal %}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% if inline_admin_formset.formset.can_delete %}
|
||||
<td class="delete">
|
||||
{% if inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }}{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -38,7 +38,7 @@ USE_I18N = True
|
|||
|
||||
# Absolute path to the directory that holds media.
|
||||
# Example: "/home/media/media.lawrence.com/"
|
||||
MEDIA_ROOT = APP + '/static/'
|
||||
MEDIA_ROOT = APP + '/media/'
|
||||
|
||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||
# trailing slash if there is a path component (optional in other cases).
|
||||
|
|
@ -66,7 +66,7 @@ MIDDLEWARE_CLASSES = (
|
|||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'sample.urls'
|
||||
ROOT_URLCONF = 'example.urls'
|
||||
|
||||
TEMPLATE_DIRS = (
|
||||
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
||||
|
|
@ -80,10 +80,12 @@ INSTALLED_APPS = (
|
|||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.flatpages',
|
||||
'categories',
|
||||
'editor',
|
||||
'mptt',
|
||||
'simpletext',
|
||||
)
|
||||
EDITOR_MEDIA_PATH = '/static/editor/'
|
||||
CATEGORIES_ALLOW_SLUG_CHANGE = True
|
||||
CATEGORIES_ALLOW_SLUG_CHANGE = True
|
||||
CATEGORIES_RELATION_MODELS = ['simpletext.simpletext','flatpages.flatpage']
|
||||
20
example/static/js/genericcollections.js
Normal file
20
example/static/js/genericcollections.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
function showGenericRelatedObjectLookupPopup(triggeringLink, ctArray) {
|
||||
var realName = triggeringLink.id.replace(/^lookup_/, '');
|
||||
var name = id_to_windowname(realName);
|
||||
realName = realName.replace(/object_id/, 'content_type');
|
||||
var select = document.getElementById(realName);
|
||||
if (select.selectedIndex === 0) {
|
||||
alert("Select a content type first.");
|
||||
return false;
|
||||
}
|
||||
var selectedItem = select.item(select.selectedIndex).value;
|
||||
var href = triggeringLink.href.replace(/#/,'../../../'+ctArray[selectedItem]+"/?t=id");
|
||||
if (href.search(/\?/) >= 0) {
|
||||
href = href + '&pop=1';
|
||||
} else {
|
||||
href = href + '?pop=1';
|
||||
}
|
||||
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
|
||||
win.focus();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -28,4 +28,8 @@ urlpatterns = patterns('',
|
|||
(r'^static/editor/(?P<path>.*)$', 'django.views.static.serve',
|
||||
{'document_root': ROOT_PATH + '/editor/media/editor/',
|
||||
'show_indexes':True}),
|
||||
|
||||
(r'^static/(?P<path>.*)$', 'django.views.static.serve',
|
||||
{'document_root': ROOT_PATH + '/example/static/'}),
|
||||
|
||||
)
|
||||
Loading…
Reference in a new issue