From 74a6cadf8dfa002959c9e93469d39e3ec3b68f4c Mon Sep 17 00:00:00 2001 From: Brandon Taylor Date: Mon, 24 Aug 2015 12:55:56 -0400 Subject: [PATCH] Added legacy model definition back for seamless backwards compatibility. Updated readme. Added sample legacy model implementation. --- README.md | 42 +++++++++++--------- adminsortable/__init__.py | 2 +- adminsortable/models.py | 6 ++- sample_project/app/admin.py | 3 +- sample_project/app/models.py | 11 +++++ sample_project/database/test_project.sqlite | Bin 112640 -> 81920 bytes 6 files changed, 43 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 8ddbbaa..82df3d2 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://travis-ci.org/iambrandontaylor/django-admin-sortable.svg?branch=master)](https://travis-ci.org/iambrandontaylor/django-admin-sortable) -Current version: 1.8.5 +Current version: 2.0.0 This project makes it easy to add drag-and-drop ordering to any model in Django admin. Inlines for a sortable model may also be made sortable, @@ -92,7 +92,11 @@ Sample Model: def __unicode__(self): return self.title +#### Important Note About Ordering... +If you do not set the `Meta` `ordering` option to the same value as `order_field_name`, your objects will not be sorted/ordered correctly. + +#### Common Use Case A common use case is to have child objects that are sortable relative to a parent. If your parent object is also sortable, here's how you would set up your models and admin options: # models.py @@ -175,6 +179,24 @@ Sometimes you might have a parent model that is not sortable, but has child mode The `NonSortableParentAdmin` class is necessary to wire up the additional URL patterns and JavaScript that Django Admin Sortable needs to make your models sortable. The child model does not have to be an inline model, it can be wired directly to Django admin and the objects will be grouped by the non-sortable foreign key when sorting. +### Backwards Compatibility +If you previously used Django Admin Sortable, **DON'T PANIC** - everything will still work exactly as before ***without any changes to your code***. Going forward, it is recommended that you use the new `SortableMixin` on your models, as pre-2.0 compatibility might not be a permanent thing. + +Please note however that the `Sortable` class still contains the hard-coded `order` field, and meta inheritance requirements: + + # legacy model definition + + from adminsortable.models import Sortable + + class Project(Sortable): + class Meta(Sortable.Meta): + pass + title = models.CharField(max_length=50) + + def __unicode__(self): + return self.title + + #### Model Instance Methods Each instance of a sortable model has two convenience methods to get the next or previous instance: @@ -203,22 +225,6 @@ You may also pass in additional ORM "extra_filters" as a dictionary, should you your_instance.get_next(extra_filters={'title__icontains': 'blue'}) -### Backwards Compatibility -If you previously used Django Admin Sortable, don't worry. Your models only require a very small change to work. - -Previously, the `Sortable` class defined a `PositiveIntegerField` called `order` and the `ordering` Meta option. The `Sortable` class is still available to import, so you don't have to change your model definitions, but now that `Sortable` is a mixin, you'll need to add this column and Meta option to your model: - - class YourModel(Sortable): - . . . - - order = models.PositiveIntegerField(default=0, editable=False, db_index=True) - - class Meta: - ordering = ['order'] - -because your previous model defined this field, it's already in your database, so no migrations are necessary. The `order_field_name` also defaults to 'order', so if you defined an `order` field, you don't have to specify `order_field_name`. - - ### Adding Sorting to an existing model #### Django 1.6.x or below @@ -456,7 +462,7 @@ ordering on top of that just seemed a little much in my opinion. django-admin-sortable is currently used in production. -### What's new in 1.8.5? +### What's new in 2.0.0? - Sortable class has become SortableMixin which is now unobtrusive, allowing you to create sortable abstract classes. diff --git a/adminsortable/__init__.py b/adminsortable/__init__.py index 643e54c..c78eaf5 100644 --- a/adminsortable/__init__.py +++ b/adminsortable/__init__.py @@ -1,4 +1,4 @@ -VERSION = (1, 8, 5) # following PEP 386 +VERSION = (2, 0, 0) # following PEP 386 DEV_N = None diff --git a/adminsortable/models.py b/adminsortable/models.py index d3cdb67..0caec5d 100644 --- a/adminsortable/models.py +++ b/adminsortable/models.py @@ -124,8 +124,12 @@ class SortableMixin(models.Model): extra_filters, filter_on_sortable_fk) -# for easier legacy support of existing implementations +# for legacy support of existing implementations class Sortable(SortableMixin): class Meta: abstract = True + ordering = ['order'] + + order = models.PositiveIntegerField(default=0, editable=False, + db_index=True) diff --git a/sample_project/app/admin.py b/sample_project/app/admin.py index f575195..8193e27 100644 --- a/sample_project/app/admin.py +++ b/sample_project/app/admin.py @@ -7,7 +7,7 @@ from adminsortable.utils import get_is_sortable from app.models import (Category, Widget, Project, Credit, Note, GenericNote, Component, Person, NonSortableCategory, SortableCategoryWidget, SortableNonInlineCategory, NonSortableCredit, NonSortableNote, - CustomWidget, CustomWidgetComponent) + CustomWidget, CustomWidgetComponent, BackwardCompatibleWidget) admin.site.register(Category, SortableAdmin) @@ -105,3 +105,4 @@ class CustomWidgetAdmin(SortableAdmin): admin.site.register(SortableNonInlineCategory, SortableAdmin) admin.site.register(CustomWidget, CustomWidgetAdmin) +admin.site.register(BackwardCompatibleWidget, SortableAdmin) diff --git a/sample_project/app/models.py b/sample_project/app/models.py index 4a472f0..50272c2 100644 --- a/sample_project/app/models.py +++ b/sample_project/app/models.py @@ -240,3 +240,14 @@ class CustomWidgetComponent(SortableMixin, SimpleModel): def __str__(self): return self.title + + +@python_2_unicode_compatible +class BackwardCompatibleWidget(Sortable, SimpleModel): + + class Meta(Sortable.Meta): + verbose_name = 'Backward Compatible Widget' + verbose_name_plural = 'Backward Compatible Widgets' + + def __str__(self): + return self.title diff --git a/sample_project/database/test_project.sqlite b/sample_project/database/test_project.sqlite index 7e4b0e7a7ee2c6f732b0b758cc2413ab725ea67b..83d7d34cb51f5c2d3c3ea76f3d5ecb0c6218a763 100644 GIT binary patch delta 6835 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 TW|O8FiKgy&!ZZoDP{{s&fbFJ1 literal 112640 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