From 2ca19f05c1f9cb539cd7a45236c9a0a762b11a3b Mon Sep 17 00:00:00 2001 From: Brandon Taylor Date: Wed, 5 Feb 2014 08:26:22 -0500 Subject: [PATCH 1/4] Updated string formatting on multiple foreign key exception. --- adminsortable/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adminsortable/models.py b/adminsortable/models.py index 461d987..48df4a7 100644 --- a/adminsortable/models.py +++ b/adminsortable/models.py @@ -53,7 +53,7 @@ class Sortable(models.Model): sortable_foreign_keys.append(field) if len(sortable_foreign_keys) > 1: raise MultipleSortableForeignKeyException( - u'%s may only have one SortableForeignKey' % self) + u'{} may only have one SortableForeignKey'.format(self)) def save(self, *args, **kwargs): if not self.id: From e51f7535ca87d7fd907642228344914b8e6b9329 Mon Sep 17 00:00:00 2001 From: Brandon Taylor Date: Wed, 5 Feb 2014 09:36:03 -0500 Subject: [PATCH 2/4] Added Person model with ordering_subset and initial data. --- adminsortable/admin.py | 12 ++- adminsortable/models.py | 4 + sample_project/app/admin.py | 5 +- sample_project/app/fixtures/initial_data.json | 37 +++++++ .../app/migrations/0004_add_person.py | 95 ++++++++++++++++++ sample_project/app/models.py | 16 +++ sample_project/database/test_project.sqlite | Bin 71680 -> 75776 bytes 7 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 sample_project/app/fixtures/initial_data.json create mode 100644 sample_project/app/migrations/0004_add_person.py diff --git a/adminsortable/admin.py b/adminsortable/admin.py index 78609a5..4ad25cb 100644 --- a/adminsortable/admin.py +++ b/adminsortable/admin.py @@ -35,7 +35,12 @@ class SortableAdminBase(object): object_tools block to take people to the view to change the sorting. """ - if get_is_sortable(self.queryset(request)): + if self.model.ordering_subset() is not None: + objects = self.model.ordering_subset() + else: + objects = self.queryset(request) + + if get_is_sortable(objects): self.change_list_template = \ self.sortable_change_list_with_sort_link_template self.is_sortable = True @@ -102,7 +107,10 @@ class SortableAdmin(SortableAdminBase, ModelAdmin): has_perm = request.user.has_perm('{}.{}'.format(opts.app_label, opts.get_change_permission())) - objects = self.queryset(request) + if self.model.ordering_subset() is not None: + objects = self.model.ordering_subset() + else: + objects = self.queryset(request) # Determine if we need to regroup objects relative to a # foreign key specified on the model class that is extending Sortable. diff --git a/adminsortable/models.py b/adminsortable/models.py index 48df4a7..52cad36 100644 --- a/adminsortable/models.py +++ b/adminsortable/models.py @@ -43,6 +43,10 @@ class Sortable(models.Model): def model_type_id(cls): return ContentType.objects.get_for_model(cls).id + @classmethod + def ordering_subset(cls): + return None + def __init__(self, *args, **kwargs): super(Sortable, self).__init__(*args, **kwargs) diff --git a/sample_project/app/admin.py b/sample_project/app/admin.py index 181b2fb..b0079e4 100644 --- a/sample_project/app/admin.py +++ b/sample_project/app/admin.py @@ -4,7 +4,7 @@ from adminsortable.admin import (SortableAdmin, SortableTabularInline, SortableStackedInline, SortableGenericStackedInline) from adminsortable.utils import get_is_sortable from app.models import (Category, Widget, Project, Credit, Note, GenericNote, - Component) + Component, Person) admin.site.register(Category, SortableAdmin) @@ -58,3 +58,6 @@ class ProjectAdmin(SortableAdmin): list_display = ['__unicode__', 'category'] admin.site.register(Project, ProjectAdmin) + + +admin.site.register(Person, SortableAdmin) diff --git a/sample_project/app/fixtures/initial_data.json b/sample_project/app/fixtures/initial_data.json new file mode 100644 index 0000000..b110f06 --- /dev/null +++ b/sample_project/app/fixtures/initial_data.json @@ -0,0 +1,37 @@ +[{ + "pk": 1, + "model": "app.person", + "fields": { + "first_name": "Bob", + "last_name": "Smith", + "order": 1, + "is_board_member": true + } +}, { + "pk": 2, + "model": "app.person", + "fields": { + "first_name": "Sally", + "last_name": "Sue", + "order": 2, + "is_board_member": false + } +}, { + "pk": 3, + "model": "app.person", + "fields": { + "first_name": "Mike", + "last_name": "Wilson", + "order": 3, + "is_board_member": true + } +}, { + "pk": 4, + "model": "app.person", + "fields": { + "first_name": "Robert", + "last_name": "Roberts", + "order": 2, + "is_board_member": true + } +}] diff --git a/sample_project/app/migrations/0004_add_person.py b/sample_project/app/migrations/0004_add_person.py new file mode 100644 index 0000000..d2ef25c --- /dev/null +++ b/sample_project/app/migrations/0004_add_person.py @@ -0,0 +1,95 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Person' + db.create_table(u'app_person', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('order', self.gf('django.db.models.fields.PositiveIntegerField')(default=1, db_index=True)), + ('first_name', self.gf('django.db.models.fields.CharField')(max_length=50)), + ('last_name', self.gf('django.db.models.fields.CharField')(max_length=50)), + ('is_board_member', self.gf('django.db.models.fields.BooleanField')(default=False)), + )) + db.send_create_signal(u'app', ['Person']) + + + def backwards(self, orm): + # Deleting model 'Person' + db.delete_table(u'app_person') + + + models = { + u'app.category': { + 'Meta': {'ordering': "['order']", 'object_name': 'Category'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'app.component': { + 'Meta': {'ordering': "['order']", 'object_name': 'Component'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'widget': ('adminsortable.fields.SortableForeignKey', [], {'to': u"orm['app.Widget']"}) + }, + u'app.credit': { + 'Meta': {'ordering': "['order']", 'object_name': 'Credit'}, + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30'}), + 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'db_index': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['app.Project']"}) + }, + u'app.genericnote': { + 'Meta': {'ordering': "['order']", 'object_name': 'GenericNote'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'generic_notes'", 'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'app.note': { + 'Meta': {'ordering': "['order']", 'object_name': 'Note'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'db_index': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['app.Project']"}), + 'text': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'app.person': { + 'Meta': {'ordering': "['order']", 'object_name': 'Person'}, + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_board_member': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'db_index': 'True'}) + }, + u'app.project': { + 'Meta': {'ordering': "['order']", 'object_name': 'Project'}, + 'category': ('adminsortable.fields.SortableForeignKey', [], {'to': u"orm['app.Category']"}), + 'description': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'app.widget': { + 'Meta': {'ordering': "['order']", 'object_name': 'Widget'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['app'] \ No newline at end of file diff --git a/sample_project/app/models.py b/sample_project/app/models.py index b3622b1..26c1b48 100644 --- a/sample_project/app/models.py +++ b/sample_project/app/models.py @@ -95,3 +95,19 @@ class Component(SimpleModel, Sortable): def __unicode__(self): return self.title + + +class Person(Sortable): + class Meta(Sortable.Meta): + verbose_name_plural = 'People' + + first_name = models.CharField(max_length=50) + last_name = models.CharField(max_length=50) + is_board_member = models.BooleanField(default=False) + + def __unicode__(self): + return '{} {}'.format(self.first_name, self.last_name) + + @classmethod + def ordering_subset(cls): + return cls.objects.filter(is_board_member=True) diff --git a/sample_project/database/test_project.sqlite b/sample_project/database/test_project.sqlite index 610bf6635ba4af6be9bf8c907faad87d1a21cc6b..2cd709f2135a407be7eebb8074a8b071296eb47a 100644 GIT binary patch delta 1958 zcmah}Z){Ul6uYAyeK3%J{Te&@$DJ-phjXMi3F2H0*c14ObiJz0lx4BG~V}G*oGrE=`ZJ= zd*1Kf^E>CQit-a)$k4$S=x#Fx(Uro5ZjXi;8hMuKyGb%>#~XQ>`y8 zCuEzZ_{gF;?}i}&MG^=eM5z|vI=@FGX;)~ zJoT(uMB&AnDvBbxrCN8rVIlX(9db)kM#usdFy=%P111XkgE#FO{oVd$gs$< zx|z0_D2n0k=_5j7a7KfRu_jRx!m(H=98@zm$Eq2;58-|IZ}I?6YDzCwhrb=wFHQm( zzs2ydn6M|T3|?&^e&Qmmru>NG4hFBlv&9h0zFDaUI0m~hH+>P|!deTyWV1793_%sC z!zwjeN{V#i5{GxLR0esr^F{N=Wq+3L{o4@Lwz8j|A@7hGRR@}2$Jq1_ku)ZtHn ztDz&qz_q2i**ElttWL>|6*iksrT5eN zR$8Y!5=({To@ip)wWVcyYjamCzh&!&)}6d(dPBvh>RvIQEDyiZQz)j=MUPL(1g%cD z8^7loN+fgX>F8-|Q-!drd3}59OhSW3z1X-=J?E=d{eLuAdb7pBp>8$Mu*AxHa*2w~ z^PcQrce0Qurn>vn{Zt8p>?mx!6QOj7m~m<|L!kyjkmm`Db!%s;~6k z(_F!LlPn9-cr+4=&gi{o5=q`ligPsaJK?w>N93R!eL6?cQ%PlxucVkD%2FsS%}gaP YxGNW)qi#%w|^gS=ag-b{MMt${uQRQ zX-7t&21Xwl-KXr^MiX@3*sRxIC(=Ts=>i|;7ubVwDO>fE@pDklt4mW4EjNN$oB1u--bm%Iez6=+vCG-2>zW7BPXmAvSCBUnb0rtPpeb zoO8T@J0dG(Td6xt*Y)}exJ)~#i>AI>5p3b^l5J6|BfVViuA?{gUXT*-aL}aJQYxma u<~yZcKb^JA4{A~lmQ{cTIaDtS0%HjAJ3hlMdPy1aTNI=`RW1bS0saBQ)t^=X From 3970105ce523424ea1fdba151cb3f148d415c501 Mon Sep 17 00:00:00 2001 From: Brandon Taylor Date: Wed, 5 Feb 2014 11:06:15 -0500 Subject: [PATCH 3/4] Removed un-needed properties on script tags for JS imports. Refactored sorting filters from class method into a model property with a default of an empty dictionary. Refactored sortable admin class to not make two calls to determine sortability and also to get objects for sorting. Added sorting_filters to sortable admin queryset. --- adminsortable/admin.py | 24 ++++++------------ adminsortable/models.py | 5 +--- .../templates/adminsortable/change_list.html | 10 ++++---- adminsortable/utils.py | 5 ++-- sample_project/app/admin.py | 6 ++++- sample_project/app/models.py | 6 ++--- sample_project/database/test_project.sqlite | Bin 75776 -> 75776 bytes sample_project/requirements.txt | 2 +- sample_project/sample_project/settings.py | 5 +--- .../admin/change_list_with_sort_link.html | 9 +++++++ 10 files changed, 35 insertions(+), 37 deletions(-) create mode 100644 sample_project/templates/app/person/admin/change_list_with_sort_link.html diff --git a/adminsortable/admin.py b/adminsortable/admin.py index 4ad25cb..fb27575 100644 --- a/adminsortable/admin.py +++ b/adminsortable/admin.py @@ -28,6 +28,8 @@ STATIC_URL = settings.STATIC_URL class SortableAdminBase(object): + filtered_objects = [] + def changelist_view(self, request, extra_context=None): """ If the model that inherits Sortable has more than one object, @@ -35,12 +37,11 @@ class SortableAdminBase(object): object_tools block to take people to the view to change the sorting. """ - if self.model.ordering_subset() is not None: - objects = self.model.ordering_subset() - else: - objects = self.queryset(request) + # Apply any additional filters to create a subset of sortable objects + self.filtered_objects = self.queryset(request).filter( + **self.model.sorting_filters) - if get_is_sortable(objects): + if get_is_sortable(self.filtered_objects): self.change_list_template = \ self.sortable_change_list_with_sort_link_template self.is_sortable = True @@ -66,8 +67,6 @@ class SortableAdmin(SortableAdminBase, ModelAdmin): 'adminsortable/change_list_with_sort_link.html' sortable_change_form_template = 'adminsortable/change_form.html' sortable_change_list_template = 'adminsortable/change_list.html' - sortable_javascript_includes_template = \ - 'adminsortable/shared/javascript_includes.html' change_form_template_extends = 'admin/change_form.html' change_list_template_extends = 'admin/change_list.html' @@ -107,10 +106,7 @@ class SortableAdmin(SortableAdminBase, ModelAdmin): has_perm = request.user.has_perm('{}.{}'.format(opts.app_label, opts.get_change_permission())) - if self.model.ordering_subset() is not None: - objects = self.model.ordering_subset() - else: - objects = self.queryset(request) + objects = self.filtered_objects # Determine if we need to regroup objects relative to a # foreign key specified on the model class that is extending Sortable. @@ -169,9 +165,7 @@ class SortableAdmin(SortableAdminBase, ModelAdmin): 'group_expression': sortable_by_expression, 'sortable_by_class': sortable_by_class, 'sortable_by_class_is_sortable': sortable_by_class_is_sortable, - 'sortable_by_class_display_name': sortable_by_class_display_name, - 'sortable_javascript_includes_template': - self.sortable_javascript_includes_template + 'sortable_by_class_display_name': sortable_by_class_display_name } return render(request, self.sortable_change_list_template, context) @@ -208,8 +202,6 @@ class SortableAdmin(SortableAdminBase, ModelAdmin): self.change_form_template = self.sortable_change_form_template extra_context.update({ - 'sortable_javascript_includes_template': - self.sortable_javascript_includes_template, 'has_sortable_tabular_inlines': self.has_sortable_tabular_inlines, 'has_sortable_stacked_inlines': diff --git a/adminsortable/models.py b/adminsortable/models.py index 52cad36..d3dfef4 100644 --- a/adminsortable/models.py +++ b/adminsortable/models.py @@ -31,6 +31,7 @@ class Sortable(models.Model): order = models.PositiveIntegerField(editable=False, default=1, db_index=True) is_sortable = False + sorting_filters = {} # legacy support sortable_by = None @@ -43,10 +44,6 @@ class Sortable(models.Model): def model_type_id(cls): return ContentType.objects.get_for_model(cls).id - @classmethod - def ordering_subset(cls): - return None - def __init__(self, *args, **kwargs): super(Sortable, self).__init__(*args, **kwargs) diff --git a/adminsortable/templates/adminsortable/change_list.html b/adminsortable/templates/adminsortable/change_list.html index bdc9522..af06213 100644 --- a/adminsortable/templates/adminsortable/change_list.html +++ b/adminsortable/templates/adminsortable/change_list.html @@ -9,11 +9,11 @@ {% block extrahead %} {{ block.super }} - - - - - + + + + + {% endblock %} {% block title %}{% blocktrans with opts.verbose_name_plural|capfirst as model %}Drag and drop {{ model }} to change display order{% endblocktrans %} | {% trans 'Django site admin' %}{% endblock %} diff --git a/adminsortable/utils.py b/adminsortable/utils.py index b38c42a..e2948cf 100644 --- a/adminsortable/utils.py +++ b/adminsortable/utils.py @@ -1,4 +1,5 @@ def get_is_sortable(objects): - if objects.count() > 1: - return True + if objects: + if objects.count() > 1: + return True return False diff --git a/sample_project/app/admin.py b/sample_project/app/admin.py index b0079e4..a00e3eb 100644 --- a/sample_project/app/admin.py +++ b/sample_project/app/admin.py @@ -60,4 +60,8 @@ class ProjectAdmin(SortableAdmin): admin.site.register(Project, ProjectAdmin) -admin.site.register(Person, SortableAdmin) +class PersonAdmin(SortableAdmin): + sortable_change_list_with_sort_link_template = 'app/person/admin/change_list_with_sort_link.html' + + +admin.site.register(Person, PersonAdmin) diff --git a/sample_project/app/models.py b/sample_project/app/models.py index 26c1b48..41b9417 100644 --- a/sample_project/app/models.py +++ b/sample_project/app/models.py @@ -105,9 +105,7 @@ class Person(Sortable): last_name = models.CharField(max_length=50) is_board_member = models.BooleanField(default=False) + sorting_filters = {'is_board_member': True} + def __unicode__(self): return '{} {}'.format(self.first_name, self.last_name) - - @classmethod - def ordering_subset(cls): - return cls.objects.filter(is_board_member=True) diff --git a/sample_project/database/test_project.sqlite b/sample_project/database/test_project.sqlite index 2cd709f2135a407be7eebb8074a8b071296eb47a..486daf5dd089246f48b464d7e6ac6be8014a1e41 100644 GIT binary patch delta 886 zcmah{O-vI}5PolGx9u)1KLSQYNgHg42VlGHQXzp@`K_^QRIs#(iBNv}w@`tm(5QfC z609}$NIXblJk=#)!od(UF@Xd)2od#SJQWWZQx1?A_f<*Y&-WkQhXgb1Ad(C1s`!>IGm4 z!xrqol2Sy^LL+P<5_L?9fkBw9q;7W0k#L_;8XE00TG0v2AeOB>>a-|3vnO>0YA_8M z1X;+yF6^s`x1d;$5Ca%fKm%q~#bR37f2q@P-;oA3C^&!(ML+)@Qke?O)vzo9Yjps7 zh{*-&w8W#)5N3>;pT)qz4>fih!LGu+`35Yhn|n~Qi+b7sS+%9WJXWv!Ixoe;vPbaxz0Ho8BwX=JZLJ}X+!DX$?i_bD zN9@kjeGZ?RXmZv%rMGP$>XBQ6-NLP?x7`)(aMy*r z?y)A1TN?Jd#gH=)3v>wt=kM?BlES`lUBs*Ab9M0z0&lTZ@j{iQ&S*Klp%dkdYs%p8 v)pifsd+ZC(itsrOL+WiAF2&Ts$1;48R(B?&e%ChSC$*5Rzmns delta 707 zcmaiyPe@cj9LMK3-+8~kMNoUn4qLDVYee(j8rG?S`OPr%`+ep!%uM9PL|)vf zqx|H)4$9AUpR=nHZ859ON;IWxt2HTK+g42@-DbOJka3g$qB)#InxEho@Pr0wlUDdV zUqe0K@(j<&kZ7mIgXpBb^8=~TaeKtRG}_WLWL+H?O!r;yO|-;&$Hvn!J89xBi#Q>1 zK+bgzTh*0AlT9%zVW$$NY`(D`#sVJ7;Jy10$gzNvor;7QV{YGW^4(NKRAGeyvkbG? zzzgLtgQI&$W286mGd<~%i~Z*5;jw-*wi_DL^@hLK;))gC=55X7#!PLX8YL!BAQVwh z;IaZQqgDeEhjJoa1)YQ{(U>eh3IvQz=EExCJ3h(a{1RFjLxE{-g|55NAMmP$8v}kf z`h4mLo!~(Xd{uyThA+~1`Vsn{9U)5#K~bf0fFeUl3Ar-w;TpOC(z%x_%Zq-Yt2{rV z3=gF7Qou3AekYo{=IqRw&Qh-3_^Ec68GhiK48B^#WS4ftbKCN9=fZQ=JkQEiJw8`t z@i^~07Mx}_xeur>M19)HncYpwOXRFRx|eR l2go`hI&2W1dR9kaLiSA^r35~sC_~Rbz*iB=mg;GT_zjjYqniK# diff --git a/sample_project/requirements.txt b/sample_project/requirements.txt index 8e1d2ba..61f1db1 100644 --- a/sample_project/requirements.txt +++ b/sample_project/requirements.txt @@ -1,2 +1,2 @@ -django==1.5.2 +django==1.6.1 south==0.8.1 diff --git a/sample_project/sample_project/settings.py b/sample_project/sample_project/settings.py index 3c2b32e..9280511 100755 --- a/sample_project/sample_project/settings.py +++ b/sample_project/sample_project/settings.py @@ -109,10 +109,7 @@ ROOT_URLCONF = 'sample_project.urls' WSGI_APPLICATION = 'sample_project.wsgi.application' TEMPLATE_DIRS = ( - # Put strings here, like "/home/html/django_templates" or - # "C:/www/django/templates". - # Always use forward slashes, even on Windows. - # Don't forget to use absolute paths, not relative paths. + map_path('templates'), ) INSTALLED_APPS = ( diff --git a/sample_project/templates/app/person/admin/change_list_with_sort_link.html b/sample_project/templates/app/person/admin/change_list_with_sort_link.html new file mode 100644 index 0000000..d406c80 --- /dev/null +++ b/sample_project/templates/app/person/admin/change_list_with_sort_link.html @@ -0,0 +1,9 @@ +{% extends change_list_template_extends %} +{% load i18n %} + +{% block object-tools-items %} +
  • + {% trans 'Change Order of Board Members' %} +
  • + {{ block.super }} +{% endblock %} From 1b4730fef9df0faf4be680c631f56e0ae78c6793 Mon Sep 17 00:00:00 2001 From: Brandon Taylor Date: Wed, 5 Feb 2014 11:06:15 -0500 Subject: [PATCH 4/4] Added Person model with sorting_filters set to only order people who are board members. Added custom template override to specify which people are sortable in change list. Added initial data fixture for people. --- adminsortable/admin.py | 24 ++++++------------ adminsortable/models.py | 5 +--- .../templates/adminsortable/change_list.html | 10 ++++---- adminsortable/utils.py | 5 ++-- sample_project/app/admin.py | 6 ++++- sample_project/app/models.py | 6 ++--- sample_project/database/test_project.sqlite | Bin 75776 -> 75776 bytes sample_project/requirements.txt | 2 +- sample_project/sample_project/settings.py | 5 +--- .../admin/change_list_with_sort_link.html | 9 +++++++ 10 files changed, 35 insertions(+), 37 deletions(-) create mode 100644 sample_project/templates/app/person/admin/change_list_with_sort_link.html diff --git a/adminsortable/admin.py b/adminsortable/admin.py index 4ad25cb..fb27575 100644 --- a/adminsortable/admin.py +++ b/adminsortable/admin.py @@ -28,6 +28,8 @@ STATIC_URL = settings.STATIC_URL class SortableAdminBase(object): + filtered_objects = [] + def changelist_view(self, request, extra_context=None): """ If the model that inherits Sortable has more than one object, @@ -35,12 +37,11 @@ class SortableAdminBase(object): object_tools block to take people to the view to change the sorting. """ - if self.model.ordering_subset() is not None: - objects = self.model.ordering_subset() - else: - objects = self.queryset(request) + # Apply any additional filters to create a subset of sortable objects + self.filtered_objects = self.queryset(request).filter( + **self.model.sorting_filters) - if get_is_sortable(objects): + if get_is_sortable(self.filtered_objects): self.change_list_template = \ self.sortable_change_list_with_sort_link_template self.is_sortable = True @@ -66,8 +67,6 @@ class SortableAdmin(SortableAdminBase, ModelAdmin): 'adminsortable/change_list_with_sort_link.html' sortable_change_form_template = 'adminsortable/change_form.html' sortable_change_list_template = 'adminsortable/change_list.html' - sortable_javascript_includes_template = \ - 'adminsortable/shared/javascript_includes.html' change_form_template_extends = 'admin/change_form.html' change_list_template_extends = 'admin/change_list.html' @@ -107,10 +106,7 @@ class SortableAdmin(SortableAdminBase, ModelAdmin): has_perm = request.user.has_perm('{}.{}'.format(opts.app_label, opts.get_change_permission())) - if self.model.ordering_subset() is not None: - objects = self.model.ordering_subset() - else: - objects = self.queryset(request) + objects = self.filtered_objects # Determine if we need to regroup objects relative to a # foreign key specified on the model class that is extending Sortable. @@ -169,9 +165,7 @@ class SortableAdmin(SortableAdminBase, ModelAdmin): 'group_expression': sortable_by_expression, 'sortable_by_class': sortable_by_class, 'sortable_by_class_is_sortable': sortable_by_class_is_sortable, - 'sortable_by_class_display_name': sortable_by_class_display_name, - 'sortable_javascript_includes_template': - self.sortable_javascript_includes_template + 'sortable_by_class_display_name': sortable_by_class_display_name } return render(request, self.sortable_change_list_template, context) @@ -208,8 +202,6 @@ class SortableAdmin(SortableAdminBase, ModelAdmin): self.change_form_template = self.sortable_change_form_template extra_context.update({ - 'sortable_javascript_includes_template': - self.sortable_javascript_includes_template, 'has_sortable_tabular_inlines': self.has_sortable_tabular_inlines, 'has_sortable_stacked_inlines': diff --git a/adminsortable/models.py b/adminsortable/models.py index 52cad36..d3dfef4 100644 --- a/adminsortable/models.py +++ b/adminsortable/models.py @@ -31,6 +31,7 @@ class Sortable(models.Model): order = models.PositiveIntegerField(editable=False, default=1, db_index=True) is_sortable = False + sorting_filters = {} # legacy support sortable_by = None @@ -43,10 +44,6 @@ class Sortable(models.Model): def model_type_id(cls): return ContentType.objects.get_for_model(cls).id - @classmethod - def ordering_subset(cls): - return None - def __init__(self, *args, **kwargs): super(Sortable, self).__init__(*args, **kwargs) diff --git a/adminsortable/templates/adminsortable/change_list.html b/adminsortable/templates/adminsortable/change_list.html index bdc9522..af06213 100644 --- a/adminsortable/templates/adminsortable/change_list.html +++ b/adminsortable/templates/adminsortable/change_list.html @@ -9,11 +9,11 @@ {% block extrahead %} {{ block.super }} - - - - - + + + + + {% endblock %} {% block title %}{% blocktrans with opts.verbose_name_plural|capfirst as model %}Drag and drop {{ model }} to change display order{% endblocktrans %} | {% trans 'Django site admin' %}{% endblock %} diff --git a/adminsortable/utils.py b/adminsortable/utils.py index b38c42a..e2948cf 100644 --- a/adminsortable/utils.py +++ b/adminsortable/utils.py @@ -1,4 +1,5 @@ def get_is_sortable(objects): - if objects.count() > 1: - return True + if objects: + if objects.count() > 1: + return True return False diff --git a/sample_project/app/admin.py b/sample_project/app/admin.py index b0079e4..a00e3eb 100644 --- a/sample_project/app/admin.py +++ b/sample_project/app/admin.py @@ -60,4 +60,8 @@ class ProjectAdmin(SortableAdmin): admin.site.register(Project, ProjectAdmin) -admin.site.register(Person, SortableAdmin) +class PersonAdmin(SortableAdmin): + sortable_change_list_with_sort_link_template = 'app/person/admin/change_list_with_sort_link.html' + + +admin.site.register(Person, PersonAdmin) diff --git a/sample_project/app/models.py b/sample_project/app/models.py index 26c1b48..41b9417 100644 --- a/sample_project/app/models.py +++ b/sample_project/app/models.py @@ -105,9 +105,7 @@ class Person(Sortable): last_name = models.CharField(max_length=50) is_board_member = models.BooleanField(default=False) + sorting_filters = {'is_board_member': True} + def __unicode__(self): return '{} {}'.format(self.first_name, self.last_name) - - @classmethod - def ordering_subset(cls): - return cls.objects.filter(is_board_member=True) diff --git a/sample_project/database/test_project.sqlite b/sample_project/database/test_project.sqlite index 2cd709f2135a407be7eebb8074a8b071296eb47a..486daf5dd089246f48b464d7e6ac6be8014a1e41 100644 GIT binary patch delta 886 zcmah{O-vI}5PolGx9u)1KLSQYNgHg42VlGHQXzp@`K_^QRIs#(iBNv}w@`tm(5QfC z609}$NIXblJk=#)!od(UF@Xd)2od#SJQWWZQx1?A_f<*Y&-WkQhXgb1Ad(C1s`!>IGm4 z!xrqol2Sy^LL+P<5_L?9fkBw9q;7W0k#L_;8XE00TG0v2AeOB>>a-|3vnO>0YA_8M z1X;+yF6^s`x1d;$5Ca%fKm%q~#bR37f2q@P-;oA3C^&!(ML+)@Qke?O)vzo9Yjps7 zh{*-&w8W#)5N3>;pT)qz4>fih!LGu+`35Yhn|n~Qi+b7sS+%9WJXWv!Ixoe;vPbaxz0Ho8BwX=JZLJ}X+!DX$?i_bD zN9@kjeGZ?RXmZv%rMGP$>XBQ6-NLP?x7`)(aMy*r z?y)A1TN?Jd#gH=)3v>wt=kM?BlES`lUBs*Ab9M0z0&lTZ@j{iQ&S*Klp%dkdYs%p8 v)pifsd+ZC(itsrOL+WiAF2&Ts$1;48R(B?&e%ChSC$*5Rzmns delta 707 zcmaiyPe@cj9LMK3-+8~kMNoUn4qLDVYee(j8rG?S`OPr%`+ep!%uM9PL|)vf zqx|H)4$9AUpR=nHZ859ON;IWxt2HTK+g42@-DbOJka3g$qB)#InxEho@Pr0wlUDdV zUqe0K@(j<&kZ7mIgXpBb^8=~TaeKtRG}_WLWL+H?O!r;yO|-;&$Hvn!J89xBi#Q>1 zK+bgzTh*0AlT9%zVW$$NY`(D`#sVJ7;Jy10$gzNvor;7QV{YGW^4(NKRAGeyvkbG? zzzgLtgQI&$W286mGd<~%i~Z*5;jw-*wi_DL^@hLK;))gC=55X7#!PLX8YL!BAQVwh z;IaZQqgDeEhjJoa1)YQ{(U>eh3IvQz=EExCJ3h(a{1RFjLxE{-g|55NAMmP$8v}kf z`h4mLo!~(Xd{uyThA+~1`Vsn{9U)5#K~bf0fFeUl3Ar-w;TpOC(z%x_%Zq-Yt2{rV z3=gF7Qou3AekYo{=IqRw&Qh-3_^Ec68GhiK48B^#WS4ftbKCN9=fZQ=JkQEiJw8`t z@i^~07Mx}_xeur>M19)HncYpwOXRFRx|eR l2go`hI&2W1dR9kaLiSA^r35~sC_~Rbz*iB=mg;GT_zjjYqniK# diff --git a/sample_project/requirements.txt b/sample_project/requirements.txt index 8e1d2ba..61f1db1 100644 --- a/sample_project/requirements.txt +++ b/sample_project/requirements.txt @@ -1,2 +1,2 @@ -django==1.5.2 +django==1.6.1 south==0.8.1 diff --git a/sample_project/sample_project/settings.py b/sample_project/sample_project/settings.py index 3c2b32e..9280511 100755 --- a/sample_project/sample_project/settings.py +++ b/sample_project/sample_project/settings.py @@ -109,10 +109,7 @@ ROOT_URLCONF = 'sample_project.urls' WSGI_APPLICATION = 'sample_project.wsgi.application' TEMPLATE_DIRS = ( - # Put strings here, like "/home/html/django_templates" or - # "C:/www/django/templates". - # Always use forward slashes, even on Windows. - # Don't forget to use absolute paths, not relative paths. + map_path('templates'), ) INSTALLED_APPS = ( diff --git a/sample_project/templates/app/person/admin/change_list_with_sort_link.html b/sample_project/templates/app/person/admin/change_list_with_sort_link.html new file mode 100644 index 0000000..d406c80 --- /dev/null +++ b/sample_project/templates/app/person/admin/change_list_with_sort_link.html @@ -0,0 +1,9 @@ +{% extends change_list_template_extends %} +{% load i18n %} + +{% block object-tools-items %} +
  • + {% trans 'Change Order of Board Members' %} +
  • + {{ block.super }} +{% endblock %}