From b14d747f82510f4f44cbcc22a6e3dbd7838734b4 Mon Sep 17 00:00:00 2001 From: Brandon Taylor Date: Sun, 23 Aug 2015 22:23:45 -0400 Subject: [PATCH] Work in progress on refactoring the Sortable class into a non-obtrusive mixin. --- sample_project/app/models.py | 83 ++++++++++++-------- sample_project/app/tests.py | 19 +++-- sample_project/database/test_project.sqlite | Bin 100352 -> 105472 bytes 3 files changed, 63 insertions(+), 39 deletions(-) diff --git a/sample_project/app/models.py b/sample_project/app/models.py index bf8b563..c4c44dd 100644 --- a/sample_project/app/models.py +++ b/sample_project/app/models.py @@ -4,7 +4,7 @@ from django.db import models from django.utils.encoding import python_2_unicode_compatible from adminsortable.fields import SortableForeignKey -from adminsortable.models import Sortable +from adminsortable.models import Sortable, SortableMixin @python_2_unicode_compatible @@ -19,59 +19,65 @@ class SimpleModel(models.Model): # A model that is sortable -class Category(SimpleModel, Sortable): - class Meta(Sortable.Meta): - """ - Classes that inherit from Sortable must define an inner - Meta class that inherits from Sortable.Meta or ordering - won't work as expected - """ +class Category(SimpleModel, SortableMixin): + class Meta: verbose_name_plural = 'Categories' + ordering = ['order'] + + order = models.PositiveIntegerField(default=0, editable=False) # A model with an override of its queryset for admin @python_2_unicode_compatible -class Widget(SimpleModel, Sortable): - class Meta(Sortable.Meta): - pass +class Widget(SimpleModel, SortableMixin): + class Meta: + ordering = ['order'] def __str__(self): return self.title + order = models.PositiveIntegerField(default=0, editable=False) + # A model that is sortable relative to a foreign key that is also sortable # uses SortableForeignKey field. Works with versions 1.3+ -class Project(SimpleModel, Sortable): - class Meta(Sortable.Meta): - pass +class Project(SimpleModel, SortableMixin): + class Meta: + ordering = ['order'] category = SortableForeignKey(Category) description = models.TextField() + order = models.PositiveIntegerField(default=0, editable=False) + # Registered as a tabular inline on `Project` @python_2_unicode_compatible -class Credit(Sortable): - class Meta(Sortable.Meta): - pass +class Credit(SortableMixin): + class Meta: + ordering = ['order'] project = models.ForeignKey(Project) first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) + order = models.PositiveIntegerField(default=0, editable=False) + def __str__(self): return '{0} {1}'.format(self.first_name, self.last_name) # Registered as a stacked inline on `Project` @python_2_unicode_compatible -class Note(Sortable): - class Meta(Sortable.Meta): - pass +class Note(SortableMixin): + class Meta: + ordering = ['order'] project = models.ForeignKey(Project) text = models.CharField(max_length=100) + order = models.PositiveIntegerField(default=0, editable=False) + def __str__(self): return self.text @@ -99,15 +105,17 @@ class NonSortableNote(models.Model): # A generic bound model @python_2_unicode_compatible -class GenericNote(SimpleModel, Sortable): +class GenericNote(SimpleModel, SortableMixin): content_type = models.ForeignKey(ContentType, verbose_name=u"Content type", related_name="generic_notes") object_id = models.PositiveIntegerField(u"Content id") content_object = generic.GenericForeignKey(ct_field='content_type', fk_field='object_id') - class Meta(Sortable.Meta): - pass + order = models.PositiveIntegerField(default=0, editable=False) + + class Meta: + ordering = ['order'] def __str__(self): return u'{0}: {1}'.format(self.title, self.content_object) @@ -115,25 +123,30 @@ class GenericNote(SimpleModel, Sortable): # An model registered as an inline that has a custom queryset @python_2_unicode_compatible -class Component(SimpleModel, Sortable): - class Meta(Sortable.Meta): - pass +class Component(SimpleModel, SortableMixin): + class Meta: + ordering = ['order'] widget = SortableForeignKey(Widget) + order = models.PositiveIntegerField(default=0, editable=False) + def __str__(self): return self.title @python_2_unicode_compatible -class Person(Sortable): - class Meta(Sortable.Meta): +class Person(SortableMixin): + class Meta: + ordering = ['order'] verbose_name_plural = 'People' first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) is_board_member = models.BooleanField('Board Member', default=False) + order = models.PositiveIntegerField(default=0, editable=False) + # Sorting Filters allow you to set up sub-sets of objects that need # to have independent sorting. They are listed in order, from left # to right in the sorting change list. You can use any standard @@ -158,26 +171,32 @@ class NonSortableCategory(SimpleModel): @python_2_unicode_compatible -class SortableCategoryWidget(SimpleModel, Sortable): - class Meta(Sortable.Meta): +class SortableCategoryWidget(SimpleModel, SortableMixin): + class Meta: + ordering = ['order'] verbose_name = 'Sortable Category Widget' verbose_name_plural = 'Sortable Category Widgets' non_sortable_category = SortableForeignKey(NonSortableCategory) + order = models.PositiveIntegerField(default=0, editable=False) + def __str__(self): return self.title @python_2_unicode_compatible -class SortableNonInlineCategory(SimpleModel, Sortable): +class SortableNonInlineCategory(SimpleModel, SortableMixin): """Example of a model that is sortable, but has a SortableForeignKey that is *not* sortable, and is also not defined as an inline of the SortableForeignKey field.""" non_sortable_category = SortableForeignKey(NonSortableCategory) - class Meta(Sortable.Meta): + order = models.PositiveIntegerField(default=0, editable=False) + + class Meta: + ordering = ['order'] verbose_name = 'Sortable Non-Inline Category' verbose_name_plural = 'Sortable Non-Inline Categories' diff --git a/sample_project/app/tests.py b/sample_project/app/tests.py index 69cd7ac..9d4501a 100644 --- a/sample_project/app/tests.py +++ b/sample_project/app/tests.py @@ -10,14 +10,19 @@ from django.db import models from django.test import TestCase from django.test.client import Client -from adminsortable.models import Sortable +from adminsortable.models import SortableMixin from adminsortable.utils import get_is_sortable from app.models import Category, Person, Project -class TestSortableModel(Sortable): +class TestSortableModel(SortableMixin): title = models.CharField(max_length=100) + order = models.PositiveIntegerField(default=0, editable=False) + + class Meta: + ordering = ['order'] + def __unicode__(self): return self.title @@ -110,7 +115,7 @@ class SortableTestCase(TestCase): self.assertEqual(response.status_code, httplib.OK, 'Admin sort request failed.') - #assert adminsortable change list templates are used + # assert adminsortable change list templates are used template_names = [t.name for t in response.templates] self.assertTrue('adminsortable/change_list.html' in template_names, 'adminsortable/change_list.html was not rendered') @@ -121,7 +126,7 @@ class SortableTestCase(TestCase): self.assertTrue(logged_in, 'User is not logged in') category1, category2, category3 = self.make_test_categories() - #make a normal POST + # make a normal POST response = self.client.post(self.get_sorting_url(), data=self.get_category_indexes(category1, category2, category3)) content = json.loads(response.content.decode(encoding='UTF-8'), @@ -134,10 +139,10 @@ class SortableTestCase(TestCase): password=self.user_raw_password) self.assertTrue(logged_in, 'User is not logged in') - #make categories + # make categories category1, category2, category3 = self.make_test_categories() - #make an Ajax POST + # make an Ajax POST response = self.client.post(self.get_sorting_url(), data=self.get_category_indexes(category3, category2, category1), HTTP_X_REQUESTED_WITH='XMLHttpRequest') @@ -146,7 +151,7 @@ class SortableTestCase(TestCase): self.assertTrue(content.get('objects_sorted'), 'Objects should have been sorted.') - #assert order is correct + # assert order is correct categories = Category.objects.all() cat1 = categories[0] cat2 = categories[1] diff --git a/sample_project/database/test_project.sqlite b/sample_project/database/test_project.sqlite index 77631b38996f50b70917394c883b53128ddb8816..af8ac5391519e0fd13c41aefc98c0da956caa8c9 100644 GIT binary patch delta 3853 zcma);3s98T7017O&hD}w$O5uxC@T00;^OkXAB!)Ziij^zq?sm#fPkVjh^VcwCWF>- z(nR_>Wir#IHa0U&Gnt6nxK7)dj!t7IaXPjdQ^rvfQZteNmfXgf~u(+rX4wJ zDyiQR@f?*3Pve(3grDP0{0Dx7mv9izMbf;-5gPQ!U}gRUzXv~Jy5_IzsN&C4-~{|0 zqHq-6fM3C3co|-TgYY8khdr28+N373M)MOouGUfD{k`$yekK`JCK{EZ_GO>HqL4;tKof+(?LTJ!dWF zI?lD6HJsI)YdBYPuHszDxq`Ebb2(=v=Q7R;&ZV47ILkT9IOmu0w-U}`&LYmmoQpUY zaxP%>&gU%Tbl!M!a^%GR65?~CBj9WJ9{vHZqeH9aS)Al|y95$&*EDVI%qgkwY-(=r z*qNh8UVHH_5_AqbD+xsY<)ry1xbdn$(|esJ_+B*CB7+ESw{!jV>G&BAMbai*LFd++ z?gT&=5)mU`z8T6QZpS!X=jf-?omWn8PWYG>{tf(q7d%U=+c|e;QnJg||l9Nx;4U|Dq%kmmp^H&vGK( zp`NZL;D2b`SLr`}sHZ>T92^on%ny69o)H-V1dO%OR%$7VM{l~kiGqs;^eKC&@vqT2 zekZl>koDtKuZLIMZ94jk)Sg-NZwPrT9ghee>=N*KJdGEz z3*W+@QhyB?_PabZ{wxvJv^O+$bjG;VM?8XZkf+SxMELsgSAy-?ZI(a~B{ zzq75q18>nd{cie0R;Jfg+)=-!v3*PIrxalFbT3xdx3%r8-qw_cv>_u?#H!X!O|`9U zTidtL(9&owS%~r1u_<&aCJcJ$4mF6E@g9D`=K3TC@P@!A3iw2Uho#HqW)mgyAwEbV zM(@ug$I@qdYg-$en>usKAcYFaR3gS3N+AV#Lqh+cNp$jAG<7|B2m4kf^Tn3zJnMCxohagS7Q38Z;>lWU}@7;q-- zQ>+mCf7VQHKG`+Zd3)Cs=kTssQ0`{b|Wmt;GsAX&nv!U^G^uz^bXD}QaQ$}=AN2$CoRHs*m`_ zz>2XVF+-9O)HGS8DTyaAP&Jm=VEZuLBrP?pjKsZstU!#AYS3LTmd#)^Y(yi}`LnmjTy7+5-19CW|6 zf(q3t=5Rvtmc-+DFzNC7ljV?Z26b7bfyCmNTRv9IRH_6O$&l2z6!Xd+@*5u?-M!_Y zL8owd4%O0_G&i1#@W|mPOCePc8q_ZhSgv7L!uV+}G%P3@-$Jt`UG*u$1`4u$z=f3pp2b&j2UgNMMInX*bul-Y zqk~oW5y6Cbi3Ov&{Al(U4DY7bX(f8CGm#>q6AHVSd|29!wSW1*OY=G zO3DL}2FMmV>h%E=g%=7jKt~t?4=6$9=YfbI3&l#c#?TTeK`@0FyfaHd=i$sffA^g8 zedlgGs&Cw(?#9!spd4O)y1+wTPT1&4}HRZEmc9k7xyHx5jGbmzmdWt-9 zpGl=MWzu+=FsWEBbky!elt*RKYtmGCr&C!kc2bG7J5|ufj=HXs!txF06jQ<+Y#S~6 zI;dC_;YE@k@A*`npuCZyr{(38 zx@q`oP@YMt)K8Qq-*-eTMKiiL3V|5!H$9uySh;&wbE|(#nEWxmpDB3m;udb;TQk4C z#@gP#oc4qT$?MBWnBB{At#7v0l24$Ep$ivr!Vb)&%@_j(?KB79|9w_h$1sSSd(77e z)*UmLH%efDsR{$C#(hOW8r;Xsf&C?lw;`qX!Mt@q^8f;5aO=T=nnG2#>fD=V<++f# z`&^UfHh#w-eo?VU;dG^$Kb{|xt!WyFbm2&0-8-L^7s3xr$4;}E`3NgL4S zC&8$g*EOzK8E*|oqJG`c^%1?wuQ|FH(JPS+!HnV&y+Vzc^Pe&O!U@NSdxoDox;CPh z`&6uswC*B1fPR5AdQ~T<-1fypjk~SuZtLzCs+@zT3xSdbq5G!r`he36r_qByRrWrZ zNB<7Y3zEhtV({8z(|gJ0cyntw8sx4CPfXtg;^ku3# zy@0!=cb^hp5#@*6DYg-|Ddw)>dmO|rm9`pfCCc;o%ORanXsp3!CmR+wHMXwG)s5!e3k)o5 zd^cX#m{^l+&f(6LA+K0mpGd67;Xs4`we_v>mC2U%;aVQ_x+d+h zX*O5AO@54O<^XxPjwbO4mu+=aYLriO+IiXF^0Spx6v(VuFLU@*toGXenREt|@jwCK z&SyAM^Ey)ZIMlj($K97^KU}I@