Added support for queryset() overrides on admin classes and inline admin classes.
Updated version to 1.4.5. Updated README with explanation of requirements for overriding queryset() on inline models. Added extra models to sample project to demonstrate sortable models with custom querysets. Improved JavaScript of sortables to be more efficient with better comparison checking. Fixed highlighting of stacked inlines on sort finish.
This commit is contained in:
@@ -2,12 +2,42 @@ from django.contrib import admin
|
||||
|
||||
from adminsortable.admin import (SortableAdmin, SortableTabularInline,
|
||||
SortableStackedInline, SortableGenericStackedInline)
|
||||
from app.models import Category, Project, Credit, Note, GenericNote
|
||||
from adminsortable.utils import get_is_sortable
|
||||
from app.models import (Category, Widget, Project, Credit, Note, GenericNote,
|
||||
Component)
|
||||
|
||||
|
||||
admin.site.register(Category, SortableAdmin)
|
||||
|
||||
|
||||
class ComponentInline(SortableStackedInline):
|
||||
model = Component
|
||||
|
||||
def queryset(self, request):
|
||||
qs = super(ComponentInline, self).queryset(request).exclude(title__icontains='2')
|
||||
if get_is_sortable(qs):
|
||||
self.model.is_sortable = True
|
||||
else:
|
||||
self.model.is_sortable = False
|
||||
return qs
|
||||
|
||||
|
||||
class WidgetAdmin(SortableAdmin):
|
||||
def queryset(self, request):
|
||||
"""
|
||||
A simple example demonstrating that adminsortable works even in
|
||||
situations where you need to filter the queryset in admin. Here,
|
||||
we are just filtering out `widget` instances with an pk higher
|
||||
than 3
|
||||
"""
|
||||
qs = super(WidgetAdmin, self).queryset(request)
|
||||
return qs.filter(id__lte=3)
|
||||
|
||||
inlines = [ComponentInline]
|
||||
|
||||
admin.site.register(Widget, WidgetAdmin)
|
||||
|
||||
|
||||
class CreditInline(SortableTabularInline):
|
||||
model = Credit
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
# -*- 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 'Category'
|
||||
db.create_table(u'app_category', (
|
||||
(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)),
|
||||
('title', self.gf('django.db.models.fields.CharField')(max_length=50)),
|
||||
))
|
||||
db.send_create_signal(u'app', ['Category'])
|
||||
|
||||
# Adding model 'Project'
|
||||
db.create_table(u'app_project', (
|
||||
(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)),
|
||||
('title', self.gf('django.db.models.fields.CharField')(max_length=50)),
|
||||
('category', self.gf('adminsortable.fields.SortableForeignKey')(to=orm['app.Category'])),
|
||||
('description', self.gf('django.db.models.fields.TextField')()),
|
||||
))
|
||||
db.send_create_signal(u'app', ['Project'])
|
||||
|
||||
# Adding model 'Credit'
|
||||
db.create_table(u'app_credit', (
|
||||
(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)),
|
||||
('project', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['app.Project'])),
|
||||
('first_name', self.gf('django.db.models.fields.CharField')(max_length=30)),
|
||||
('last_name', self.gf('django.db.models.fields.CharField')(max_length=30)),
|
||||
))
|
||||
db.send_create_signal(u'app', ['Credit'])
|
||||
|
||||
# Adding model 'Note'
|
||||
db.create_table(u'app_note', (
|
||||
(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)),
|
||||
('project', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['app.Project'])),
|
||||
('text', self.gf('django.db.models.fields.CharField')(max_length=100)),
|
||||
))
|
||||
db.send_create_signal(u'app', ['Note'])
|
||||
|
||||
# Adding model 'GenericNote'
|
||||
db.create_table(u'app_genericnote', (
|
||||
(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)),
|
||||
('title', self.gf('django.db.models.fields.CharField')(max_length=50)),
|
||||
('content_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='generic_notes', to=orm['contenttypes.ContentType'])),
|
||||
('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
|
||||
))
|
||||
db.send_create_signal(u'app', ['GenericNote'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'Category'
|
||||
db.delete_table(u'app_category')
|
||||
|
||||
# Deleting model 'Project'
|
||||
db.delete_table(u'app_project')
|
||||
|
||||
# Deleting model 'Credit'
|
||||
db.delete_table(u'app_credit')
|
||||
|
||||
# Deleting model 'Note'
|
||||
db.delete_table(u'app_note')
|
||||
|
||||
# Deleting model 'GenericNote'
|
||||
db.delete_table(u'app_genericnote')
|
||||
|
||||
|
||||
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.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.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'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']
|
||||
@@ -0,0 +1,78 @@
|
||||
# -*- 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 'Widget'
|
||||
db.create_table(u'app_widget', (
|
||||
(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)),
|
||||
('title', self.gf('django.db.models.fields.CharField')(max_length=50)),
|
||||
))
|
||||
db.send_create_signal(u'app', ['Widget'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'Widget'
|
||||
db.delete_table(u'app_widget')
|
||||
|
||||
|
||||
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.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.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']
|
||||
@@ -0,0 +1,86 @@
|
||||
# -*- 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 'Component'
|
||||
db.create_table(u'app_component', (
|
||||
(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)),
|
||||
('title', self.gf('django.db.models.fields.CharField')(max_length=50)),
|
||||
('widget', self.gf('adminsortable.fields.SortableForeignKey')(to=orm['app.Widget'])),
|
||||
))
|
||||
db.send_create_signal(u'app', ['Component'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'Component'
|
||||
db.delete_table(u'app_component')
|
||||
|
||||
|
||||
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.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']
|
||||
@@ -16,7 +16,7 @@ class SimpleModel(models.Model):
|
||||
return self.title
|
||||
|
||||
|
||||
#a model that is sortable
|
||||
# A model that is sortable
|
||||
class Category(SimpleModel, Sortable):
|
||||
class Meta(Sortable.Meta):
|
||||
"""
|
||||
@@ -27,8 +27,17 @@ class Category(SimpleModel, Sortable):
|
||||
verbose_name_plural = 'Categories'
|
||||
|
||||
|
||||
#a model that is sortable relative to a foreign key that is also sortable
|
||||
#uses SortableForeignKey field. Works with versions 1.3+
|
||||
# A model with an override of its queryset for admin
|
||||
class Widget(SimpleModel, Sortable):
|
||||
class Meta(Sortable.Meta):
|
||||
pass
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
# 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
|
||||
@@ -37,7 +46,7 @@ class Project(SimpleModel, Sortable):
|
||||
description = models.TextField()
|
||||
|
||||
|
||||
#registered as a tabular inline on `Project`
|
||||
# Registered as a tabular inline on `Project`
|
||||
class Credit(Sortable):
|
||||
class Meta(Sortable.Meta):
|
||||
pass
|
||||
@@ -50,7 +59,7 @@ class Credit(Sortable):
|
||||
return '{0} {1}'.format(self.first_name, self.last_name)
|
||||
|
||||
|
||||
#registered as a stacked inline on `Project`
|
||||
# Registered as a stacked inline on `Project`
|
||||
class Note(Sortable):
|
||||
class Meta(Sortable.Meta):
|
||||
pass
|
||||
@@ -62,7 +71,7 @@ class Note(Sortable):
|
||||
return self.text
|
||||
|
||||
|
||||
#a generic bound model
|
||||
# A generic bound model
|
||||
class GenericNote(SimpleModel, Sortable):
|
||||
content_type = models.ForeignKey(ContentType,
|
||||
verbose_name=u"Content type", related_name="generic_notes")
|
||||
@@ -75,3 +84,14 @@ class GenericNote(SimpleModel, Sortable):
|
||||
|
||||
def __unicode__(self):
|
||||
return u'{0} : {1}'.format(self.title, self.content_object)
|
||||
|
||||
|
||||
# An model registered as an inline that has a custom queryset
|
||||
class Component(SimpleModel, Sortable):
|
||||
class Meta(Sortable.Meta):
|
||||
pass
|
||||
|
||||
widget = SortableForeignKey(Widget)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.title
|
||||
|
||||
Binary file not shown.
@@ -122,13 +122,12 @@ INSTALLED_APPS = (
|
||||
'django.contrib.sites',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
# Uncomment the next line to enable the admin:
|
||||
'django.contrib.admin',
|
||||
# Uncomment the next line to enable admin documentation:
|
||||
'django.contrib.admindocs',
|
||||
|
||||
'adminsortable',
|
||||
'app',
|
||||
'south',
|
||||
)
|
||||
|
||||
# A sample logging configuration. The only tangible logging
|
||||
|
||||
Reference in New Issue
Block a user