From 1b9c5c0a641e3b8f7055157792af0fb262a4a433 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 22 Nov 2011 20:42:36 -0600 Subject: [PATCH] Refactored sortable_by into a property instead of a classmethod. Added backwards compatibility for pre 1.1.1 versions that still have sortable_by defined as a classmethod. Need to address dynamic regroup template tag to see why categories are not being grouped properly in sample app. --- adminsortable/admin.py | 15 ++-- adminsortable/models.py | 4 ++ .../templates/adminsortable/change_list.html | 2 +- .../templatetags/django_template_additions.py | 2 +- sample_project/adminsortable.sqlite | Bin 56320 -> 62464 bytes sample_project/app/admin.py | 3 +- .../app/migrations/0003_add_sample.py | 68 ++++++++++++++++++ sample_project/app/models.py | 21 +++++- sample_project/app/tests.py | 6 +- sample_project/settings.py | 1 - 10 files changed, 107 insertions(+), 15 deletions(-) create mode 100644 sample_project/app/migrations/0003_add_sample.py diff --git a/adminsortable/admin.py b/adminsortable/admin.py index f80064a..b389546 100755 --- a/adminsortable/admin.py +++ b/adminsortable/admin.py @@ -53,13 +53,18 @@ class SortableAdmin(ModelAdmin): has_perm = request.user.has_perm(opts.app_label + '.' + opts.get_change_permission()) objects = self.model.objects.all() - """ - Determine if we need to regroup objects relative to a foreign key specified on the - model class that is extending Sortable. - """ + #Determine if we need to regroup objects relative to a foreign key specified on the + # model class that is extending Sortable. sortable_by = getattr(self.model, 'sortable_by', None) if sortable_by: - sortable_by_class, sortable_by_expression = sortable_by() + #backwards compatibility for < 1.1.1, where sortable_by was a classmethod instead of a property + try: + sortable_by_class, sortable_by_expression = sortable_by() + except ValueError: + sortable_by_class = self.model.sortable_by + sortable_by_expression = sortable_by_class.__name__.lower() + + print sortable_by_expression sortable_by_class_display_name = sortable_by_class._meta.verbose_name_plural sortable_by_class_is_sortable = sortable_by_class.is_sortable() else: diff --git a/adminsortable/models.py b/adminsortable/models.py index 0d74262..af299de 100755 --- a/adminsortable/models.py +++ b/adminsortable/models.py @@ -14,8 +14,12 @@ class Sortable(models.Model): `model_type_id` returns the ContentType.id for the Model that inherits Sortable `save` the override of save increments the last/highest value of order by 1 + + Override `sortable_by` method to make your model be sortable by a foreign key field. + Set `sortable_by` to the class specified in the foreign key relationship. """ order = models.PositiveIntegerField(editable=False, default=1, db_index=True) + sortable_by = None class Meta: abstract = True diff --git a/adminsortable/templates/adminsortable/change_list.html b/adminsortable/templates/adminsortable/change_list.html index f5e7562..9d85464 100755 --- a/adminsortable/templates/adminsortable/change_list.html +++ b/adminsortable/templates/adminsortable/change_list.html @@ -55,7 +55,7 @@ {% if objects %}
{% if group_expression %} - {% render_nested_sortable_objects objects group_expression %} + {% render_nested_sortable_objects objects group_expression %} {% else %} {% render_sortable_objects objects %} {% endif %} diff --git a/adminsortable/templatetags/django_template_additions.py b/adminsortable/templatetags/django_template_additions.py index 252b2e8..4de7630 100755 --- a/adminsortable/templatetags/django_template_additions.py +++ b/adminsortable/templatetags/django_template_additions.py @@ -69,7 +69,7 @@ def dynamic_regroup(parser, token): """ expression = lastbits_reversed[2][::-1] var_name = lastbits_reversed[0][::-1] - + print expression """ We also need to hand the parser to the node in order to convert the value for `expression` to a FilterExpression. diff --git a/sample_project/adminsortable.sqlite b/sample_project/adminsortable.sqlite index 5f71f74fd2089379a0c0d5353eee0de128b0ba1b..df957570192a8276eb1960cebd565c98cfd72807 100755 GIT binary patch delta 1905 zcmb_dU2GIp6u$SK{i$u4ZMRK-P|9>IZFiyD`RV@53YZmIMeUE$1V~7i+1=T;xZT}s zw?*PZTk)-lptoq^AA*5k2oEww;i0jBKA4bD->}F7^#LDHVjq04A@T0aZr2(JFHSNy zckcPlx#ynmJ9nlp!}M=(;_2!=2%#P9Ek2#^ZIfJh59(NMva+=@xRtj&2dnTG{0aBr z9xTHhxb@Zaekf-K(ZdE$i@ppl3wY3%)Lu4V{Rz#pnOfGX)L3vE=HgmX%W08ubu5$A zJmy+#L`@AB*W6VW+>ALj9xs5}_2se^uR{`fNe+pDkT2l%2L=7tKyxL}ky!+H;RalR z&tVb{KsU6LRdSd7NOW?V%<9#Z!=$d3v!ceDMsrU!lQAdy>7E#_;s6jFhMlk(oZtn} zoi9Bg4$e~3h}V#u?tEpXtlUN(B6tzLArJM+!3V_M0=?KTCj5ymEf5zH0Yy}mgsAwt zB*CvFl&)AniC5%7bmTTqd-nBjQB_e(B%<-4FY41221>+KpBVK=eLi0>;aAHIv|}%? zFYRl!u&fcnu*ycX16FnJ;O|h$=Gz_lYJ0#!?T7kpPzB{+VFx`==;C)4j`LUnW6o`i z?Rs78j~1s5dh%tu24BECpg|9`F?=mNzf%%Ku|t$P1YfDRa=g;NYKk|t$eVIlugn(r8p>2>5w%XMP3>0X#?ARyEU6--l%Ia; zQ_aSDXAYq`+(RU2fjM|L&u8hOnX_3vbM^$GFJJrR()mfigdVxz!=PWhFjPh>ZfwvS zzFq>la7XrHz+wApnGoG=IrsCS0tX_o{ zipWt_2x>94w505My*oVE&F|^k-Myc8l?)VLuW^_qaq;c0bT+PK=?B*jJKktBgFDRN zwDtNDy>oSpHeCIIURj9H!3FKo_mrn^U7nEfPKIy|lQ(sQ$K5uefm!p23iBAVQ2b!p0d$7?Ch|;C*%m znQWg)Y_-=nH{)aVMu-xPrCG4R^kDdfSG(6St)9F1ze#3l+$iFjWL)lKhpCt9|BA6o zipRQ?XsncC=y76<6tOP8zwh6<=l6-JIc+$dJsKH}yWD3ETDUcmZ^CCqk~^JGp)kgd zO?boFZDIULWFaI5z5XtCSqPYu3oOaFv-&X(k`j^xuP-1P4pPBE!ktx*cMxg=UQv+@ K53%4O;(q|7JO-!$ delta 625 zcmZvZUr3Wt7{<>z&(`;))3q_IOy>HU8lmI+=KeSd!HX;kOGvQ1sB~^Yl#BwUs}^V(P_b5$(dZ&igm#n7x(K@PD##k-yeLAbya>vh&Wf;$Ue3Asz0Y~x=bV%Ng!BwD z+gt(0SXk}9)q1rxXt1xWt=wQ&Ohc*Cv9N~k_=XjHL>VP*^P}_;(1#{_MYGG0Wf(TJ zurg_SAGsY9vaGKLoL1NuZ0~H3bcUhk)c>9X(q$T;LuR}NF-v8grZi5IimZ~%gK4zCl?2rZFg2|T9wM*f;!Sp98#m_ z1T8$X$@=(b|0X*Um7GP~#bqSWquOEkx`L)4Wwqo!$Y^rD+n?_xb&nz#rj z?kUqobt;XDDLuF{_y=bGg*2!OWJXNi9|H%C7s6b~hlOK0RnNF&!{axg)#a_Sdgg~# zr?Y-0_KT-z#&ureTiC1ujOyp2P|kE?K0&+ZkJEVZB~=yAkbmmE8nZb4!sgKhA7$bs cA5{T|xg34_lT-76j}G?jpu)Ru&;MTM7gsy9ng9R* diff --git a/sample_project/app/admin.py b/sample_project/app/admin.py index a1eb445..f365aab 100755 --- a/sample_project/app/admin.py +++ b/sample_project/app/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin from adminsortable.admin import SortableAdmin, SortableTabularInline, SortableStackedInline -from app.models import Category, Project, Credit, Note +from app.models import Category, Project, Credit, Note, Sample admin.site.register(Category, SortableAdmin) @@ -20,3 +20,4 @@ class ProjectAdmin(SortableAdmin): list_display = ['__unicode__', 'category'] admin.site.register(Project, ProjectAdmin) +admin.site.register(Sample, SortableAdmin) diff --git a/sample_project/app/migrations/0003_add_sample.py b/sample_project/app/migrations/0003_add_sample.py new file mode 100644 index 0000000..f683706 --- /dev/null +++ b/sample_project/app/migrations/0003_add_sample.py @@ -0,0 +1,68 @@ +# encoding: 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 'Sample' + db.create_table('app_sample', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('order', self.gf('django.db.models.fields.PositiveIntegerField')(default=1, db_index=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=50)), + ('category', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['app.Category'])), + ('description', self.gf('django.db.models.fields.TextField')()), + )) + db.send_create_signal('app', ['Sample']) + + + def backwards(self, orm): + + # Deleting model 'Sample' + db.delete_table('app_sample') + + + models = { + 'app.category': { + 'Meta': {'ordering': "['order']", 'object_name': 'Category'}, + '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'}) + }, + 'app.credit': { + 'Meta': {'ordering': "['order']", 'object_name': 'Credit'}, + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30'}), + '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': "orm['app.Project']"}) + }, + 'app.note': { + 'Meta': {'ordering': "['order']", 'object_name': 'Note'}, + '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': "orm['app.Project']"}), + 'text': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'app.project': { + 'Meta': {'ordering': "['order']", 'object_name': 'Project'}, + 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['app.Category']"}), + 'description': ('django.db.models.fields.TextField', [], {}), + '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'}) + }, + 'app.sample': { + 'Meta': {'ordering': "['order']", 'object_name': 'Sample'}, + 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['app.Category']"}), + 'description': ('django.db.models.fields.TextField', [], {}), + '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'}) + } + } + + complete_apps = ['app'] diff --git a/sample_project/app/models.py b/sample_project/app/models.py index caba787..2e8aab0 100755 --- a/sample_project/app/models.py +++ b/sample_project/app/models.py @@ -29,13 +29,28 @@ class Project(SimpleModel, Sortable): class Meta(Sortable.Meta): pass - @classmethod - def sortable_by(cls): - return Category, 'category' +# @classmethod +# def sortable_by(cls): +# return Category, 'category' category = models.ForeignKey(Category) description = models.TextField() + sortable_by = Category + + +#a model that is sortable relative to a foreign key that is also sortable +class Sample(SimpleModel, Sortable): + class Meta(Sortable.Meta): + pass + + category = models.ForeignKey(Category) + description = models.TextField() + + #field to define which foreign key the model is sortable by. + #works with versions > 1.1.1 + sortable_by = Category + #registered as a tabular inline on project class Credit(Sortable): diff --git a/sample_project/app/tests.py b/sample_project/app/tests.py index 3eb1677..13a59ad 100755 --- a/sample_project/app/tests.py +++ b/sample_project/app/tests.py @@ -1,12 +1,9 @@ import httplib import json -from django.contrib.auth.forms import authenticate, UserCreationForm from django.contrib.auth.models import User from django.core.urlresolvers import reverse -from django.middleware import csrf from django.db import models -from django.template.defaultfilters import urlencode from django.test import TestCase from django.test.client import Client, RequestFactory @@ -83,6 +80,9 @@ class SortableTestCase(TestCase): def get_category_indexes(self, *categories): return {'indexes' : ','.join([str(c.id) for c in categories])} + def test_sortable_by_backwards_compatibility(self): + pass + def test_adminsortable_changelist_templates(self): logged_in = self.client.login(username=self.user.username, password=self.user_raw_password) self.assertTrue(logged_in, 'User is not logged in') diff --git a/sample_project/settings.py b/sample_project/settings.py index c16655e..0309bf8 100755 --- a/sample_project/settings.py +++ b/sample_project/settings.py @@ -102,7 +102,6 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', - 'debug_toolbar.middleware.DebugToolbarMiddleware', ) TEMPLATE_CONTEXT_PROCESSORS = (