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.master
parent
b6e68fa367
commit
014f6d1660
48
README.md
48
README.md
|
|
@ -137,6 +137,47 @@ There are also generic equivalents that you can inherit from:
|
||||||
"""Your generic inline options go here"""
|
"""Your generic inline options go here"""
|
||||||
|
|
||||||
|
|
||||||
|
### Overriding `queryset()`
|
||||||
|
django-admin-sortable now supports custom queryset overrides on admin models
|
||||||
|
and inline models in Django admin!
|
||||||
|
|
||||||
|
If you're providing an override of a SortableAdmin or Sortable inline model,
|
||||||
|
you don't need to do anything extra. django-admin-sortable will automatically
|
||||||
|
honor your queryset.
|
||||||
|
|
||||||
|
Have a look at the WidgetAdmin class in the sample project for an example of
|
||||||
|
an admin class with a custom `queryset()` override.
|
||||||
|
|
||||||
|
#### Overriding `queryset()` for an inline model
|
||||||
|
This is a special case, which requires a few lines of extra code to properly
|
||||||
|
determine the sortability of your model. Example:
|
||||||
|
|
||||||
|
# add this import to your admin.py
|
||||||
|
from adminsortable.utils import get_is_sortable
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentInline(SortableStackedInline):
|
||||||
|
model = Component
|
||||||
|
|
||||||
|
def queryset(self, request):
|
||||||
|
qs = super(ComponentInline, self).queryset(request).filter(
|
||||||
|
title__icontains='foo')
|
||||||
|
|
||||||
|
# You'll need to add these lines to determine if your model
|
||||||
|
# is sortable once we hit the change_form() for the parent model.
|
||||||
|
|
||||||
|
if get_is_sortable(qs):
|
||||||
|
self.model.is_sortable = True
|
||||||
|
else:
|
||||||
|
self.model.is_sortable = False
|
||||||
|
return qs
|
||||||
|
|
||||||
|
If you override the queryset of an inline, the number of objects present
|
||||||
|
may change, and adminsortable won't be able to automatically determine
|
||||||
|
if the inline model is sortable from here, which is why we have to set the
|
||||||
|
`is_sortable` property of the model in this method.
|
||||||
|
|
||||||
|
|
||||||
*** IMPORTANT ***
|
*** IMPORTANT ***
|
||||||
With stacked inline models, their height can dynamically increase,
|
With stacked inline models, their height can dynamically increase,
|
||||||
which can cause sortable stacked inlines to not behave as expected.
|
which can cause sortable stacked inlines to not behave as expected.
|
||||||
|
|
@ -162,9 +203,10 @@ ordering on top of that just seemed a little much in my opinion.
|
||||||
django-admin-sortable is currently used in production.
|
django-admin-sortable is currently used in production.
|
||||||
|
|
||||||
|
|
||||||
### What's new in 1.4.4?
|
### What's new in 1.4.5?
|
||||||
- Decided to go with the simplest approach to add the sorting urls to inlines
|
- Support for queryset overrides!
|
||||||
for Django <= 1.4 and Django 1.5.x support
|
- More efficient JavaScript in sortables
|
||||||
|
- Fixed highlight effect for stacked inlines on sort finish
|
||||||
|
|
||||||
|
|
||||||
### Future
|
### Future
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
VERSION = (1, 4, 4) # following PEP 386
|
VERSION = (1, 4, 5) # following PEP 386
|
||||||
DEV_N = None
|
DEV_N = None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,30 @@ from django.shortcuts import render
|
||||||
from django.template.defaultfilters import capfirst
|
from django.template.defaultfilters import capfirst
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
|
from adminsortable.utils import get_is_sortable
|
||||||
from adminsortable.fields import SortableForeignKey
|
from adminsortable.fields import SortableForeignKey
|
||||||
from adminsortable.models import Sortable
|
from adminsortable.models import Sortable
|
||||||
|
|
||||||
STATIC_URL = settings.STATIC_URL
|
STATIC_URL = settings.STATIC_URL
|
||||||
|
|
||||||
|
|
||||||
class SortableAdmin(ModelAdmin):
|
class SortableAdminBase(object):
|
||||||
|
def changelist_view(self, request, extra_context=None):
|
||||||
|
"""
|
||||||
|
If the model that inherits Sortable has more than one object,
|
||||||
|
its sort order can be changed. This view adds a link to the
|
||||||
|
object_tools block to take people to the view to change the sorting.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if get_is_sortable(self.queryset(request)):
|
||||||
|
self.change_list_template = \
|
||||||
|
self.sortable_change_list_with_sort_link_template
|
||||||
|
self.is_sortable = True
|
||||||
|
return super(SortableAdminBase, self).changelist_view(request,
|
||||||
|
extra_context=extra_context)
|
||||||
|
|
||||||
|
|
||||||
|
class SortableAdmin(SortableAdminBase, ModelAdmin):
|
||||||
"""
|
"""
|
||||||
Admin class to add template overrides and context objects to enable
|
Admin class to add template overrides and context objects to enable
|
||||||
drag-and-drop ordering.
|
drag-and-drop ordering.
|
||||||
|
|
@ -52,18 +69,20 @@ class SortableAdmin(ModelAdmin):
|
||||||
break
|
break
|
||||||
return sortable_foreign_key
|
return sortable_foreign_key
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
# def __init__(self, *args, **kwargs):
|
||||||
super(SortableAdmin, self).__init__(*args, **kwargs)
|
# super(SortableAdmin, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.has_sortable_tabular_inlines = False
|
# self.has_sortable_tabular_inlines = False
|
||||||
self.has_sortable_stacked_inlines = False
|
# self.has_sortable_stacked_inlines = False
|
||||||
for klass in self.inlines:
|
# for klass in self.inlines:
|
||||||
if issubclass(klass, SortableTabularInline):
|
# print type(klass)
|
||||||
if klass.model.is_sortable():
|
# is_sortable = get_is_sortable(
|
||||||
self.has_sortable_tabular_inlines = True
|
# klass.model._default_manager.get_query_set())
|
||||||
if issubclass(klass, SortableStackedInline):
|
# print is_sortable
|
||||||
if klass.model.is_sortable():
|
# if issubclass(klass, SortableTabularInline) and is_sortable:
|
||||||
self.has_sortable_stacked_inlines = True
|
# self.has_sortable_tabular_inlines = True
|
||||||
|
# if issubclass(klass, SortableStackedInline) and is_sortable:
|
||||||
|
# self.has_sortable_stacked_inlines = True
|
||||||
|
|
||||||
def get_urls(self):
|
def get_urls(self):
|
||||||
urls = super(SortableAdmin, self).get_urls()
|
urls = super(SortableAdmin, self).get_urls()
|
||||||
|
|
@ -88,7 +107,8 @@ class SortableAdmin(ModelAdmin):
|
||||||
opts = self.model._meta
|
opts = self.model._meta
|
||||||
has_perm = request.user.has_perm('{0}.{1}'.format(opts.app_label,
|
has_perm = request.user.has_perm('{0}.{1}'.format(opts.app_label,
|
||||||
opts.get_change_permission()))
|
opts.get_change_permission()))
|
||||||
objects = self.model.objects.all()
|
|
||||||
|
objects = self.queryset(request)
|
||||||
|
|
||||||
# Determine if we need to regroup objects relative to a
|
# Determine if we need to regroup objects relative to a
|
||||||
# foreign key specified on the model class that is extending Sortable.
|
# foreign key specified on the model class that is extending Sortable.
|
||||||
|
|
@ -97,6 +117,7 @@ class SortableAdmin(ModelAdmin):
|
||||||
|
|
||||||
# `sortable_by` defined as a SortableForeignKey
|
# `sortable_by` defined as a SortableForeignKey
|
||||||
sortable_by_fk = self._get_sortable_foreign_key()
|
sortable_by_fk = self._get_sortable_foreign_key()
|
||||||
|
sortable_by_class_is_sortable = get_is_sortable(objects)
|
||||||
|
|
||||||
if sortable_by_property:
|
if sortable_by_property:
|
||||||
# backwards compatibility for < 1.1.1, where sortable_by was a
|
# backwards compatibility for < 1.1.1, where sortable_by was a
|
||||||
|
|
@ -110,7 +131,6 @@ class SortableAdmin(ModelAdmin):
|
||||||
|
|
||||||
sortable_by_class_display_name = sortable_by_class._meta \
|
sortable_by_class_display_name = sortable_by_class._meta \
|
||||||
.verbose_name_plural
|
.verbose_name_plural
|
||||||
sortable_by_class_is_sortable = sortable_by_class.is_sortable()
|
|
||||||
|
|
||||||
elif sortable_by_fk:
|
elif sortable_by_fk:
|
||||||
# get sortable by properties from the SortableForeignKey
|
# get sortable by properties from the SortableForeignKey
|
||||||
|
|
@ -119,10 +139,6 @@ class SortableAdmin(ModelAdmin):
|
||||||
._meta.verbose_name_plural
|
._meta.verbose_name_plural
|
||||||
sortable_by_class = sortable_by_fk.rel.to
|
sortable_by_class = sortable_by_fk.rel.to
|
||||||
sortable_by_expression = sortable_by_fk.name.lower()
|
sortable_by_expression = sortable_by_fk.name.lower()
|
||||||
try:
|
|
||||||
sortable_by_class_is_sortable = sortable_by_class.is_sortable()
|
|
||||||
except AttributeError:
|
|
||||||
sortable_by_class_is_sortable = False
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# model is not sortable by another model
|
# model is not sortable by another model
|
||||||
|
|
@ -132,7 +148,7 @@ class SortableAdmin(ModelAdmin):
|
||||||
|
|
||||||
if sortable_by_property or sortable_by_fk:
|
if sortable_by_property or sortable_by_fk:
|
||||||
# Order the objects by the property they are sortable by,
|
# Order the objects by the property they are sortable by,
|
||||||
#then by the order, otherwise the regroup
|
# then by the order, otherwise the regroup
|
||||||
# template tag will not show the objects correctly
|
# template tag will not show the objects correctly
|
||||||
objects = objects.order_by(sortable_by_expression, 'order')
|
objects = objects.order_by(sortable_by_expression, 'order')
|
||||||
|
|
||||||
|
|
@ -157,19 +173,17 @@ class SortableAdmin(ModelAdmin):
|
||||||
}
|
}
|
||||||
return render(request, self.sortable_change_list_template, context)
|
return render(request, self.sortable_change_list_template, context)
|
||||||
|
|
||||||
def changelist_view(self, request, extra_context=None):
|
|
||||||
"""
|
|
||||||
If the model that inherits Sortable has more than one object,
|
|
||||||
its sort order can be changed. This view adds a link to the
|
|
||||||
object_tools block to take people to the view to change the sorting.
|
|
||||||
"""
|
|
||||||
if self.model.is_sortable():
|
|
||||||
self.change_list_template = \
|
|
||||||
self.sortable_change_list_with_sort_link_template
|
|
||||||
return super(SortableAdmin, self).changelist_view(request,
|
|
||||||
extra_context=extra_context)
|
|
||||||
|
|
||||||
def change_view(self, request, object_id, extra_context=None):
|
def change_view(self, request, object_id, extra_context=None):
|
||||||
|
self.has_sortable_tabular_inlines = False
|
||||||
|
self.has_sortable_stacked_inlines = False
|
||||||
|
|
||||||
|
for klass in self.inlines:
|
||||||
|
is_sortable = klass.model.is_sortable
|
||||||
|
if issubclass(klass, SortableTabularInline) and is_sortable:
|
||||||
|
self.has_sortable_tabular_inlines = True
|
||||||
|
if issubclass(klass, SortableStackedInline) and is_sortable:
|
||||||
|
self.has_sortable_stacked_inlines = True
|
||||||
|
|
||||||
if self.has_sortable_tabular_inlines or \
|
if self.has_sortable_tabular_inlines or \
|
||||||
self.has_sortable_stacked_inlines:
|
self.has_sortable_stacked_inlines:
|
||||||
self.change_form_template = self.sortable_change_form_template
|
self.change_form_template = self.sortable_change_form_template
|
||||||
|
|
@ -222,7 +236,7 @@ class SortableAdmin(ModelAdmin):
|
||||||
mimetype='application/json')
|
mimetype='application/json')
|
||||||
|
|
||||||
|
|
||||||
class SortableInlineBase(InlineModelAdmin):
|
class SortableInlineBase(SortableAdminBase, InlineModelAdmin):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(SortableInlineBase, self).__init__(*args, **kwargs)
|
super(SortableInlineBase, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
@ -230,7 +244,13 @@ class SortableInlineBase(InlineModelAdmin):
|
||||||
raise Warning(u'Models that are specified in SortableTabluarInline'
|
raise Warning(u'Models that are specified in SortableTabluarInline'
|
||||||
' and SortableStackedInline must inherit from Sortable')
|
' and SortableStackedInline must inherit from Sortable')
|
||||||
|
|
||||||
self.is_sortable = self.model.is_sortable()
|
def queryset(self, request):
|
||||||
|
qs = super(SortableInlineBase, self).queryset(request)
|
||||||
|
if get_is_sortable(qs):
|
||||||
|
self.model.is_sortable = True
|
||||||
|
else:
|
||||||
|
self.model.is_sortable = False
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
class SortableTabularInline(SortableInlineBase, TabularInline):
|
class SortableTabularInline(SortableInlineBase, TabularInline):
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,11 @@ class Sortable(models.Model):
|
||||||
|
|
||||||
`save` the override of save increments the last/highest value of
|
`save` the override of save increments the last/highest value of
|
||||||
order by 1
|
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,
|
order = models.PositiveIntegerField(editable=False, default=1,
|
||||||
db_index=True)
|
db_index=True)
|
||||||
|
is_sortable = False
|
||||||
|
|
||||||
# legacy support
|
# legacy support
|
||||||
sortable_by = None
|
sortable_by = None
|
||||||
|
|
@ -42,14 +39,18 @@ class Sortable(models.Model):
|
||||||
abstract = True
|
abstract = True
|
||||||
ordering = ['order']
|
ordering = ['order']
|
||||||
|
|
||||||
@classmethod
|
# @classmethod
|
||||||
def is_sortable(cls):
|
# def determine_if_sortable(cls):
|
||||||
try:
|
# try:
|
||||||
max_order = cls.objects.aggregate(
|
# max_order = cls.objects.aggregate(
|
||||||
models.Max('order'))['order__max']
|
# models.Max('order'))['order__max']
|
||||||
except (TypeError, IndexError):
|
# except (TypeError, IndexError):
|
||||||
max_order = 0
|
# max_order = 0
|
||||||
return True if max_order > 1 else False
|
|
||||||
|
# if max_order > 1:
|
||||||
|
# cls.is_sortable = True
|
||||||
|
# else:
|
||||||
|
# cls.is_sortable = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def model_type_id(cls):
|
def model_type_id(cls):
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ jQuery(function($){
|
||||||
items : 'li',
|
items : 'li',
|
||||||
stop : function(event, ui)
|
stop : function(event, ui)
|
||||||
{
|
{
|
||||||
var indexes = Array();
|
var indexes = [];
|
||||||
ui.item.parent().children('li').each(function(i)
|
ui.item.parent().children('li').each(function(i)
|
||||||
{
|
{
|
||||||
indexes.push($(this).find(':hidden[name="pk"]').val());
|
indexes.push($(this).find(':hidden[name="pk"]').val());
|
||||||
|
|
@ -17,5 +17,7 @@ jQuery(function($){
|
||||||
data: { indexes: indexes.join(',') }
|
data: { indexes: indexes.join(',') }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}).click(function(e){
|
||||||
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,14 @@ jQuery(function($){
|
||||||
items : '.inline-related',
|
items : '.inline-related',
|
||||||
stop : function(event, ui)
|
stop : function(event, ui)
|
||||||
{
|
{
|
||||||
var indexes = Array();
|
var indexes = [];
|
||||||
ui.item.parent().children('.inline-related').each(function(i)
|
ui.item.parent().children('.inline-related').each(function(i)
|
||||||
{
|
{
|
||||||
index_value = $(this).find(':hidden[name$="-id"]').val();
|
var index_value = $(this).find(':hidden[name$="-id"]').val();
|
||||||
if (index_value != "" && index_value != undefined)
|
if (index_value !== "" && index_value !== undefined)
|
||||||
|
{
|
||||||
indexes.push(index_value);
|
indexes.push(index_value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
|
@ -24,7 +26,7 @@ jQuery(function($){
|
||||||
data: { indexes : indexes.join(',') },
|
data: { indexes : indexes.join(',') },
|
||||||
success: function()
|
success: function()
|
||||||
{
|
{
|
||||||
ui.item.effect('highlight', {}, 1000);
|
ui.item.find('.form-row').effect('highlight', {}, 1000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,14 @@ jQuery(function($){
|
||||||
items : 'tr:not(.add-row)',
|
items : 'tr:not(.add-row)',
|
||||||
stop : function(event, ui)
|
stop : function(event, ui)
|
||||||
{
|
{
|
||||||
var indexes = Array();
|
var indexes = [];
|
||||||
ui.item.parent().children('tr').each(function(i)
|
ui.item.parent().children('tr').each(function(i)
|
||||||
{
|
{
|
||||||
index_value = $(this).find('.original :hidden:first').val();
|
var index_value = $(this).find('.original :hidden:first').val();
|
||||||
if (index_value != "" && index_value != undefined)
|
if (index_value !== '' && index_value !== undefined)
|
||||||
|
{
|
||||||
indexes.push(index_value);
|
indexes.push(index_value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
def get_is_sortable(objects):
|
||||||
|
if len(objects) > 1:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
@ -2,12 +2,42 @@ from django.contrib import admin
|
||||||
|
|
||||||
from adminsortable.admin import (SortableAdmin, SortableTabularInline,
|
from adminsortable.admin import (SortableAdmin, SortableTabularInline,
|
||||||
SortableStackedInline, SortableGenericStackedInline)
|
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)
|
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):
|
class CreditInline(SortableTabularInline):
|
||||||
model = Credit
|
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
|
return self.title
|
||||||
|
|
||||||
|
|
||||||
#a model that is sortable
|
# A model that is sortable
|
||||||
class Category(SimpleModel, Sortable):
|
class Category(SimpleModel, Sortable):
|
||||||
class Meta(Sortable.Meta):
|
class Meta(Sortable.Meta):
|
||||||
"""
|
"""
|
||||||
|
|
@ -27,8 +27,17 @@ class Category(SimpleModel, Sortable):
|
||||||
verbose_name_plural = 'Categories'
|
verbose_name_plural = 'Categories'
|
||||||
|
|
||||||
|
|
||||||
#a model that is sortable relative to a foreign key that is also sortable
|
# A model with an override of its queryset for admin
|
||||||
#uses SortableForeignKey field. Works with versions 1.3+
|
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 Project(SimpleModel, Sortable):
|
||||||
class Meta(Sortable.Meta):
|
class Meta(Sortable.Meta):
|
||||||
pass
|
pass
|
||||||
|
|
@ -37,7 +46,7 @@ class Project(SimpleModel, Sortable):
|
||||||
description = models.TextField()
|
description = models.TextField()
|
||||||
|
|
||||||
|
|
||||||
#registered as a tabular inline on `Project`
|
# Registered as a tabular inline on `Project`
|
||||||
class Credit(Sortable):
|
class Credit(Sortable):
|
||||||
class Meta(Sortable.Meta):
|
class Meta(Sortable.Meta):
|
||||||
pass
|
pass
|
||||||
|
|
@ -50,7 +59,7 @@ class Credit(Sortable):
|
||||||
return '{0} {1}'.format(self.first_name, self.last_name)
|
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 Note(Sortable):
|
||||||
class Meta(Sortable.Meta):
|
class Meta(Sortable.Meta):
|
||||||
pass
|
pass
|
||||||
|
|
@ -62,7 +71,7 @@ class Note(Sortable):
|
||||||
return self.text
|
return self.text
|
||||||
|
|
||||||
|
|
||||||
#a generic bound model
|
# A generic bound model
|
||||||
class GenericNote(SimpleModel, Sortable):
|
class GenericNote(SimpleModel, Sortable):
|
||||||
content_type = models.ForeignKey(ContentType,
|
content_type = models.ForeignKey(ContentType,
|
||||||
verbose_name=u"Content type", related_name="generic_notes")
|
verbose_name=u"Content type", related_name="generic_notes")
|
||||||
|
|
@ -75,3 +84,14 @@ class GenericNote(SimpleModel, Sortable):
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return u'{0} : {1}'.format(self.title, self.content_object)
|
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.sites',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
# Uncomment the next line to enable the admin:
|
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
# Uncomment the next line to enable admin documentation:
|
|
||||||
'django.contrib.admindocs',
|
'django.contrib.admindocs',
|
||||||
|
|
||||||
'adminsortable',
|
'adminsortable',
|
||||||
'app',
|
'app',
|
||||||
|
'south',
|
||||||
)
|
)
|
||||||
|
|
||||||
# A sample logging configuration. The only tangible logging
|
# A sample logging configuration. The only tangible logging
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue