Code Monkey home page Code Monkey logo

django-ordered-model's Introduction

django-ordered-model

Build Status PyPI version Code style: black

django-ordered-model allows models to be ordered and provides a simple admin interface for reordering them.

Based on https://djangosnippets.org/snippets/998/ and https://djangosnippets.org/snippets/259/

See our compatability notes for the appropriate version to use with older Django and Python releases.

Installation

Please install using Pip:

$ pip install django-ordered-model

Or if you have checked out the repository:

$ python setup.py install

Or to use the latest development code from our master branch:

$ pip uninstall django-ordered-model
$ pip install git+git://github.com/django-ordered-model/django-ordered-model.git

Usage

Add ordered_model to your SETTINGS.INSTALLED_APPS.

Inherit your model from OrderedModel to make it ordered:

from django.db import models
from ordered_model.models import OrderedModel


class Item(OrderedModel):
    name = models.CharField(max_length=100)

Then run the usual $ ./manage.py makemigrations and $ ./manage.py migrate to update your database schema.

Model instances now have a set of methods to move them relative to each other. To demonstrate those methods we create two instances of Item:

foo = Item.objects.create(name="Foo")
bar = Item.objects.create(name="Bar")

Swap positions

foo.swap(bar)

This swaps the position of two objects.

Move position up on position

foo.up()
foo.down()

Moving an object up or down just makes it swap its position with the neighbouring object directly above of below depending on the direction.

Move to arbitrary position

foo.to(12)
bar.to(13)

Move the object to an arbitrary position in the stack. This essentially sets the order value to the specified integer. Objects between the original and the new position get their order value increased or decreased according to the direction of the move.

Move object above or below reference

foo.above(bar)
foo.below(bar)

Move the object directly above or below the reference object, increasing or decreasing the order value for all objects between the two, depending on the direction of the move.

Move to top of stack

foo.top()

This sets the order value to the lowest value found in the stack and increases the order value of all objects that were above the moved object by one.

Move to bottom of stack

foo.bottom()

This sets the order value to the highest value found in the stack and decreases the order value of all objects that were below the moved object by one.

Updating fields that would be updated during save()

For performance reasons, the delete(), to(), below(), above(), top(), and bottom() methods use Django's update() method to change the order of other objects that are shifted as a result of one of these calls. If the model has fields that are typically updated in a customized save() method, or through other app level functionality such as DateTimeField(auto_now=True), you can add additional fields to be passed through to update(). This will only impact objects where their order is being shifted as a result of an operation on the target object, not the target object itself.

foo.to(12, extra_update={'modified': now()})

Get the previous or next objects

foo.previous()
foo.next()

The previous() and next() methods return the neighbouring objects directly above or below within the ordered stack.

Subset Ordering

In some cases, ordering objects is required only on a subset of objects. For example, an application that manages contact lists for users, in a many-to-one/many relationship, would like to allow each user to order their contacts regardless of how other users choose their order. This option is supported via the order_with_respect_to parameter.

A simple example might look like so:

class Contact(OrderedModel):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    phone = models.CharField()
    order_with_respect_to = 'user'

If objects are ordered with respect to more than one field, order_with_respect_to supports tuples to define multiple fields:

class Model(OrderedModel)
    # ...
    order_with_respect_to = ('field_a', 'field_b')

In a many-to-many relationship you need to use a separate through model which is derived from the OrderedModel. For example, an application which manages pizzas with toppings.

A simple example might look like so:

class Topping(models.Model):
    name = models.CharField(max_length=100)


class Pizza(models.Model):
    name = models.CharField(max_length=100)
    toppings = models.ManyToManyField(Topping, through='PizzaToppingsThroughModel')


class PizzaToppingsThroughModel(OrderedModel):
    pizza = models.ForeignKey(Pizza, on_delete=models.CASCADE)
    topping = models.ForeignKey(Topping, on_delete=models.CASCADE)
    order_with_respect_to = 'pizza'

    class Meta:
        ordering = ('pizza', 'order')

You can also specify order_with_respect_to to a field on a related model. An example use-case can be made with the following models:

class ItemGroup(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    general_info = models.CharField(max_length=100)

class GroupedItem(OrderedModel):
    group = models.ForeignKey(ItemGroup, on_delete=models.CASCADE)
    specific_info = models.CharField(max_length=100)
    order_with_respect_to = 'group__user'

Here items are put into groups that have some general information used by its items, but the ordering of the items is independent of the group the item is in.

In all cases order_with_respect_to must specify a ForeignKey field on the model, or a Django Check E002, E005 or E006 error will be raised with further help.

When you want ordering on the baseclass instead of subclasses in an ordered list of objects of various classes, specify the full module path of the base class:

class BaseQuestion(OrderedModel):
    order_class_path = __module__ + '.BaseQuestion'
    question = models.TextField(max_length=100)

    class Meta:
        ordering = ('order',)

class MultipleChoiceQuestion(BaseQuestion):
    good_answer = models.TextField(max_length=100)
    wrong_answer1 = models.TextField(max_length=100)
    wrong_answer2 = models.TextField(max_length=100)
    wrong_answer3 = models.TextField(max_length=100)

class OpenQuestion(BaseQuestion):
    answer = models.TextField(max_length=100)

Ordering of ManyToMany Relationship query results

Django ManyToMany relationships created by ManyToManyField do not respect Meta.ordering on the intermediate model in results fetched from the 'members' queryset. For example with our usual Pizza example, getting the Toppings for a hawaiian_pizza instance using PizzaToppingsThroughModel.objects.filter(pizza=hawaiian_pizza).all() is correctly ordered (by the ThroughModel Meta.ordering). However hawaiian_pizza.toppings.all() is not, and returns the objects following the 'to' model ordering.

To work around this, explicitly add an ordering clause, e.g. with hawaiian_pizza.toppings.all().order_by('pizzatoppingsthroughmodel__order') or use our OrderedManyToManyField which does this by default:

from ordered_model.fields import OrderedManyToManyField

class Pizza(models.Model):
    name = models.CharField(max_length=100)
    toppings = OrderedManyToManyField(Topping, through="PizzaToppingsThroughModel")


class PizzaToppingsThroughModel(OrderedModel):
    pizza = models.ForeignKey(Pizza, on_delete=models.CASCADE)
    topping = models.ForeignKey(Topping, on_delete=models.CASCADE)
    order_with_respect_to = "pizza"

    class Meta:
        ordering = ("pizza", "order")

With this definition hawaiian_pizza.toppings.all() returns toppings in order.

Custom Manager and QuerySet

When your model extends OrderedModel, it inherits a custom ModelManager instance which in turn provides additional operations on the resulting QuerySet. For example if Item is an OrderedModel subclass, the queryset Item.objects.all() has functions:

  • above_instance(object),
  • below_instance(object),
  • get_min_order(),
  • get_max_order(),
  • above(index),
  • below(index)

If your Model uses a custom ModelManager (such as ItemManager below) please have it extend OrderedModelManager, or else Django Check E003 will be raised.

If your ModelManager returns a custom QuerySet (such as ItemQuerySet below) please have it extend OrderedModelQuerySet, or Django Check E004 will be raised.

from ordered_model.models import OrderedModel, OrderedModelManager, OrderedModelQuerySet

class ItemQuerySet(OrderedModelQuerySet):
    pass

class ItemManager(OrderedModelManager):
    def get_queryset(self):
        return ItemQuerySet(self.model, using=self._db)

class Item(OrderedModel):
    objects = ItemManager()

If another Django plugin requires you to use specific Model, QuerySet or ModelManager classes, you might need to construct intermediate classes using multiple inheritance, see an example in issue 270.

Custom ordering field

Extending OrderedModel creates a models.PositiveIntegerField field called order and the appropriate migrations. It customises the default class Meta to then order returned querysets by this field. If you wish to use an existing model field to store the ordering, subclass OrderedModelBase instead and set the attribute order_field_name to match your field name and the ordering attribute on Meta:

class MyModel(OrderedModelBase):
    ...
    sort_order = models.PositiveIntegerField(editable=False, db_index=True)
    order_field_name = "sort_order"

    class Meta:
        ordering = ("sort_order",)

Setting order_field_name is specific for this library to know which field to change when ordering actions are taken. The Meta ordering line is existing Django functionality to use a field for sorting. See tests/models.py object CustomOrderFieldModel for an example.

Admin integration

To add arrows in the admin change list page to do reordering, you can use the OrderedModelAdmin and the move_up_down_links field:

from django.contrib import admin
from ordered_model.admin import OrderedModelAdmin
from models import Item


class ItemAdmin(OrderedModelAdmin):
    list_display = ('name', 'move_up_down_links')

admin.site.register(Item, ItemAdmin)

ItemAdmin screenshot

For a many-to-many relationship you need one of the following inlines.

OrderedTabularInline or OrderedStackedInline just like the django admin.

For the OrderedTabularInline it will look like this:

from django.contrib import admin
from ordered_model.admin import OrderedTabularInline, OrderedInlineModelAdminMixin
from models import Pizza, PizzaToppingsThroughModel


class PizzaToppingsTabularInline(OrderedTabularInline):
    model = PizzaToppingsThroughModel
    fields = ('topping', 'order', 'move_up_down_links',)
    readonly_fields = ('order', 'move_up_down_links',)
    ordering = ('order',)
    extra = 1


class PizzaAdmin(OrderedInlineModelAdminMixin, admin.ModelAdmin):
    model = Pizza
    list_display = ('name', )
    inlines = (PizzaToppingsTabularInline, )


admin.site.register(Pizza, PizzaAdmin)

PizzaAdmin screenshot

For the OrderedStackedInline it will look like this:

from django.contrib import admin
from ordered_model.admin import OrderedStackedInline, OrderedInlineModelAdminMixin
from models import Pizza, PizzaToppingsThroughModel


class PizzaToppingsStackedInline(OrderedStackedInline):
    model = PizzaToppingsThroughModel
    fields = ('topping', 'move_up_down_links',)
    readonly_fields = ('move_up_down_links',)
    ordering = ('order',)
    extra = 1


class PizzaAdmin(OrderedInlineModelAdminMixin, admin.ModelAdmin):
    list_display = ('name', )
    inlines = (PizzaToppingsStackedInline, )


admin.site.register(Pizza, PizzaAdmin)

PizzaAdmin screenshot

Note: OrderedModelAdmin requires the inline subclasses of OrderedTabularInline and OrderedStackedInline to be listed on inlines so that we register appropriate URL routes. If you are using Django 3.0 feature get_inlines() or get_inline_instances() to return the list of inlines dynamically, consider it a filter and still add them to inlines or you might encounter a “No Reverse Match” error when accessing model change view.

Re-ordering models

In certain cases the models will end up in a not properly ordered state. This can be caused by bypassing the 'delete' / 'save' methods, or when a user changes a foreign key of a object which is part of the 'order_with_respect_to' fields. You can use the following command to re-order one or more models.

$ ./manage.py reorder_model <app_name>.<model_name> \
        [<app_name>.<model_name> ... ]

The arguments are as follows:
- `<app_name>`: Name of the application for the model.
- `<model_name>`: Name of the model that's an OrderedModel.

Django Rest Framework

To support updating ordering fields by Django Rest Framework, we include a serializer OrderedModelSerializer that intercepts writes to the ordering field, and calls OrderedModel.to() method to effect a re-ordering:

from rest_framework import routers, serializers, viewsets
from ordered_model.serializers import OrderedModelSerializer
from tests.models import CustomItem

class ItemSerializer(serializers.HyperlinkedModelSerializer, OrderedModelSerializer):
    class Meta:
        model = CustomItem
        fields = ['pkid', 'name', 'modified', 'order']

class ItemViewSet(viewsets.ModelViewSet):
    queryset = CustomItem.objects.all()
    serializer_class = ItemSerializer

router = routers.DefaultRouter()
router.register(r'items', ItemViewSet)

Note that you need to include the 'order' field (or your custom field name) in the Serializer's fields list, either explicitly or using __all__. See ordered_model/serializers.py for the implementation.

Test suite

To run the tests against your current environment, use:

$ pip install djangorestframework
$ django-admin test --pythonpath=. --settings=tests.settings

Otherwise please install tox and run the tests for a specific environment with -e or all environments:

$ tox -e py36-django30
$ tox

Compatibility with Django and Python

django-ordered-model version Django version Python version DRF (optional)
3.6.x 3.x, 4.x 3.5 and above 3.12 and above
3.5.x 3.x, 4.x 3.5 and above -
3.4.x 2.x, 3.x 3.5 and above -
3.3.x 2.x 3.4 and above -
3.2.x 2.x 3.4 and above -
3.1.x 2.x 3.4 and above -
3.0.x 2.x 3.4 and above -
2.1.x 1.x 2.7 to 3.6 -
2.0.x 1.x 2.7 to 3.6 -

Maintainers

django-ordered-model's People

Contributors

acurcie-st avatar airtonix avatar alexey-sveshnikov avatar bfirsh avatar charettes avatar cypreess avatar darbula avatar expert-m avatar fladi avatar flimm avatar gableroux avatar hdknr avatar ibaguio avatar imomaliev avatar jmiller1 avatar joke2k avatar jonahgeorge avatar michael-k avatar mk-ship-it avatar nikolas avatar rubengrill avatar shuckc avatar smcoll avatar soby avatar solomonhawk avatar szechyjs avatar timgraham avatar yuekui avatar yuvadm avatar zachcheung avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-ordered-model's Issues

suggestion: move tests out of app

I should probably make this issue ticket when I'm not overtired, but here goes:

With the django packages I create, I normally have the tests package along side the app package.
The main reason for this is :

  • project integrators don't run "out of project" tests
  • app developers will have the tests since they "git clone"
  • there exists a simple runtests.py which bootstraps django.conf.settings for the tests.

resources:

gah this is me being overtired: I can't find it yet, but I did read a convincing article that discouraged putting the tests for a pluggable django app within the app.

0.3.0 is broken / github fixed / why not release new version?

This small change here fixes a horrible bug which makes 0.3.0 almost useless (since all items get order 0, and because of _move implementation you can't reorder).

I lost +45min on this issue and would love to spare the agony to others... so... why don't you release a new version to pypi?

Options.module_name has been deprecated in favor of model_name

(Discovered this issue while trying to switch to mysql-connector from SQLite3.)

Apparently, _meta.module_name is going away, to be replaced by _meta.model_name. There are a couple of instances of _meta.module_name in the ordered-model code which should be replaced with model_name ... HOWEVER, that may break backward compatibility with older (1.5?) django.

django bug referring to deprecation:
https://code.djangoproject.com/ticket/19689

(mysql-connector has a bug where all warnings are considered errors if DEBUG = True, so it turned up this warning as an error
http://bugs.mysql.com/bug.php?id=71806)

save fails if model's primary key is not auto-assigned and called 'id'

If you have a model which has a manually-assigned primary key, it will not be 'None' going into save. Also, if that key isn't called 'id', the OrderedModel.Save() method will fail.

It also fails setting order when there are not records in the db already, because:

self.order = c and c + 1 or 0

fails if c is 0 (like it will be with an empty table)

Here's a fix (ordered_model\models.py):

    def save(self, *args, **kwargs):
        # print "save: %r %r" % (self.__class__, self.pk)
        # used to be 'if not self.key', but that fails when primary key 
        # isn't called 'id'.  so tried 'if not self.pk', but that fails 
        # when pk isn't autoassigned.
        if self.order is None:
            c = self.__class__.objects.all().aggregate(Max('order')).get('order__max')
            # this doesn't catch c == None.
            # self.order = c and c + 1 or 0
            self.order = c is not None and c + 1 or 0
            # print "c = %r" % (c,)
        # print "order=%r" % ( self.order, )
        super(OrderedModel, self).save(*args, **kwargs)

Installation via pip

When I searched about it on pip I have found it and the version 0.3 I installed it and when I checked it it seems that it is missing all function in model except move_up and move_down functions. So I suggest to enable it in pip with the full version to be easy to install and deploy.

thank you

six is used but not in requirements.txt

The Python package six is not declared in requirements.txt, but it is used by the library.

This wouldn't be an issue for most users as virtually all Python users have six installed already, but if a user didn't have six installed, they would run into an ImportError.

(I ran into this issue when installing django-ordered-model in a fresh Virtualenv).

Unicode string literal prefix breaks Python 3.2

It appears that the Unicode string literal prefixes break Python 3.2 (where support for the u'' prefix was removed). For Python >3.2, PEP 414 makes the Unicode string literal available again.

Django 1.8 officially supports Python >= 3.2, so support for this version of Python would be advisable.

Virtualenv Freeze List:
Django==1.8.6
daemonize==2.4.1
django-ordered-model==0.4.2
djangorestframework==3.3.1
wsgiref==0.1.2

Python 3.2.3
OS: Raspbian (Raspberry Pi Linux rpi 4.1.7+ #817 PREEMPT Sat Sep 19 15:25:36 BST 2015 armv6l GNU/Linux)

Works fine under Python 2.7 on the RPi and Ubuntu 14.04LTS (Python 3.4.3). I believe Debian Jessie ships with 3.2 as it's default implementation for Python 3.X.

I'm compiling 3.4.3 manually on the RPi for my own use with this library, but I suspect that won't be an issue.

Given that there is an outstanding issue for adding tests into Travis CI (#27), I would expect at that point for this issue to be illuminated.

If support for Python 3.2 is not desired, feel free to close the issue. I'm working around it by upgrading the Python version I'm using manually.

$ python manage.py makemigrations
Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/var/lib/storage/virtualenv/wrccdc-temp-py3/lib/python3.2/site-packages/django/core/management/__init__.py", line 354, in execute_from_command_line
    utility.execute()
  File "/var/lib/storage/virtualenv/wrccdc-temp-py3/lib/python3.2/site-packages/django/core/management/__init__.py", line 328, in execute
    django.setup()
  File "/var/lib/storage/virtualenv/wrccdc-temp-py3/lib/python3.2/site-packages/django/__init__.py", line 18, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "/var/lib/storage/virtualenv/wrccdc-temp-py3/lib/python3.2/site-packages/django/apps/registry.py", line 115, in populate
    app_config.ready()
  File "/var/lib/storage/virtualenv/wrccdc-temp-py3/build/django/django/contrib/admin/apps.py", line 22, in ready
  File "/var/lib/storage/virtualenv/wrccdc-temp-py3/build/django/django/contrib/admin/__init__.py", line 24, in autodiscover
  File "/var/lib/storage/virtualenv/wrccdc-temp-py3/lib/python3.2/site-packages/django/utils/module_loading.py", line 74, in autodiscover_modules
    import_module('%s.%s' % (app_config.name, module_to_search))
  File "/usr/lib/python3.2/importlib/__init__.py", line 124, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "/usr/lib/python3.2/importlib/_bootstrap.py", line 821, in _gcd_import
    loader.load_module(name)
  File "/usr/lib/python3.2/importlib/_bootstrap.py", line 436, in load_module
    return self._load_module(fullname)
  File "/usr/lib/python3.2/importlib/_bootstrap.py", line 141, in decorated
    return fxn(self, module, *args, **kwargs)
  File "/usr/lib/python3.2/importlib/_bootstrap.py", line 330, in _load_module
    code_object = self.get_code(name)
  File "/usr/lib/python3.2/importlib/_bootstrap.py", line 413, in get_code
    dont_inherit=True)
  File "/var/lib/storage/virtualenv/wrccdc-temp-py3/lib/python3.2/site-packages/ordered_model/admin.py", line 78
    move_up_down_links.short_description = _(u'Move')
                                                   ^
SyntaxError: invalid syntax

down() method.

Hey, i've noticed that in OrderedModelBase.down() method, ordering_queryset is missing order_by.

swap method seems to take an model instance but use a queryset

Hi,

The docstring of the swap method speaks of object but the method definition get qs (queryset??):

def swap(self, qs):
    """
    Swap the positions of this object with a reference object.
    """

The documentation (in the README) says

foo = Item.objects.create(name="Foo")
bar = Item.objects.create(name="Bar")
### Swap positions
foo.swap(bar)

BUT, in the code, the swap method try to get the first element of the qs (queryset??) argument.
A solution could be to use a isinstance(qs, QuerySet) check to handle both of the case.
The other is to rewrite the docstring and the documentation to reflect the actual code.

Thanks in advance,
Regards,

Pip install git+ don't pull templates

When execute pip install git+...., templates will not pulled and when look on sources in github, he show that "this path skips through empty directories". Maybe i do something wrong? But django doesn't see this templates too and in package this templates not exists too.

Django 1.10 RemovedInDjango20Warning: Importing from django.core.urlresolvers is deprecated in favor of django.urls.

In some locations, reverse is imported from django.core.urlresolvers which in Django 1.10 has been deprecated in favour of django.urls:
https://github.com/bfirsh/django-ordered-model/blob/0617d88984cbcc43fb69ba3cd5bfabdcb8444b20/ordered_model/models.py#L3

This causes a warning in Django 1.10 and will cease to work in Django 2.0.

Ref: https://docs.djangoproject.com/en/1.10/ref/urlresolvers/#module-django.urls

Admin integration: moving fails if the object has a `move` field

The title is self-explanatory. The OrderedModelAdmin.move_view() method tries to call the OrderedModel.move() method, which fails if the model has a field named move. The same might happen for up, down or any other fields that could clash with OrderedModel's methods.

django 1.7 support?

Hi,

Does this app fully support Django 1.7?
I am trying django-ordered-model==0.4.0 and there is a Deprecated warning:

/venv/local/lib/python2.7/site-packages/ordered_model/admin.py:19: RemovedInDjango18Warning: Options.module_name has been deprecated in favor of model_name
  model=self.model._meta.module_name)

It's not a big issue since it's just a warning but I am wondering whether there is something else I should be aware of when using it with django 1.7?

Thanks

Start at one?

How would I start the ordering at one?

Really not sure if i should even do this, but I am using this field for a multiplier used in score calculation. Would it be best for me to just use MyModel.order +1 in the calculation, and display whenever this is used?

Fix urlpatterns warning

lib/python3.5/site-packages/ordered_model/admin.py:32: RemovedInDjango110Warning: django.conf.urls.patterns() is deprecated and will be removed in Django 1.10. Update your urlpatterns to be a list of django.conf.urls.url() instances instead.
  name='{app}_{model}_order_down'.format(**self._get_model_info())),

Admin Integration many-to-many relationship issue

hey, i just try to run a project with the examples copied from documentation and it raises this error:

AttributeError at /admin/ord/pizza/add/ 'Manager' object has no attribute 'get_query_set'

my models.py and admin.py are both exactly copied from docs.

Bumping version

It seems that some great work was done lately, could you consider bumping version && uploading to pypi? Maybe after successful merging with #4 and #7 ?

Django 1.9 RemovedInDjango20Warning: Deprecated allow_tags attribute used

In Django 1.9, the attribute allow_tags was deprecated. However, it is currently used on the field move_up_down_links in OrderedModelAdmin and OrderedTabularInline. It is now preferred to use format_html(), format_html_join(), or mark_safe() when constructing the method's return value instead.

This causes a warning in Django 1.9 and will cease to work in Django 2.0.

Ref: https://docs.djangoproject.com/en/1.10/releases/1.9/#id1

Switching "with_respect_to" model breaks ordering

So with an initial ordering everything is great:

image

But if I change parents of a category, like moving one category inside another (almost like DELETING that category) I get something like this, which is borked:

image
(note missing #3)

What's the proper way to solve this? I could just basically replicate delete?

qs = self.get_ordering_queryset()
qs.filter(**{self.order_field_name + '__gt': getattr(self, self.order_field_name)})\
    .update(**{self.order_field_name: F(self.order_field_name) - 1})

Above fixes the problem, wondering if maybe OrderedTable could detect this on save or something?? Or maybe a switch_parents kind of method?

Side problem: When changing parents and then calling bottom() I seem to be getting weird order values as well.. still digging!

EDIT:
Setting order = None before calling bottom() seems to to have fixed some problems.

Deleting an object does not change subsequent orders

Given two objects with order (that have order 0 and 1, respectively), if the first object is deleted, the second object will remain with order 1. (This behavior obviously happens with larger lists of objects as well.)

This breaks the implicit promise (that might be incorrect) that order values are always in the range [0,n) for any n objects.

Is delete() the only place that this promise breaks, or are there other requirements for satisfying this invariant?

`to()` not updating all indices request.POST

I'm using the to() method and it's not updating all the object indices as expected. I have a number of images that are limited in order scope using order_with_respect_to " ("album",). When I use the to() method to move an object to a new location, not all objects are updated properly. Subsequently I have multiple objects with the same order index.

Specifically I'm using a jquery.ajax POST request to change the order of some images in a gallery. I send image_id and new_order via POST to a view that finds the image image=Image.objects.get(id=image_id) and then calls image.to(new_order). Only the image in question is updated.

In the django shell calling the same commands works as expected.

>>> from gallery.models import Image
>>> Image.objects.all()
[
    <Image: Gallery Image: 0-change-01>,
    <Image: Gallery Image: 1-change-02>,
    <Image: Gallery Image: 2-change-03>,
    <Image: Gallery Image: 3-change-04>
]
>>> Image.objects.all()[1].to(0)
>>> Image.objects.all()
[
    <Image: Gallery Image: 0-change-02>, 
    <Image: Gallery Image: 1-change-01>, 
    <Image: Gallery Image: 2-change-03>, 
    <Image: Gallery Image: 3-change-04>
]

I've tidied up the list display above so it's more readable on github

Here's my view code... pretty basic stuff:

def image_order(request):
    if request.method == 'POST':
        response = {}
        image_id=request.POST.get('imageid')
        new_order=request.POST.get('neworder')
        try:
            image = Image.objects.get(id=image_id)
        except Image.DoesNotExist:
            response = {'message': 'Image not found, yo.'}
            return HttpResponse( json.dumps(response), content_type="application/json" ) #404
        image.to(new_order)
        response = {
            'message': 'Image #{id} moved to  {neworder} successfully!'.format(id=image_id, neworder=new_order),
        }
        return HttpResponse( json.dumps(response), content_type="application/json" ) #200
    else:
        return HttpResponse( json.dumps({"Err": "that didn't work"}), content_type="application/json") #400


Configurable field name

Would be very useful to be able to configure the name of the field used by the package to order a model.
Googling around I saw that is not possible to add an attribute to Meta class of models.

what do you think about?

Order nulled on creation

Hi,

I realized that the order field doesn't increment on when you create new instances inside an empty table using Django admin.

I haven't fully debuged it yet, the code seems correct on first glimpse. If I increment a value manually and then add some more instances everything works just fine.

Anyone else having that problem on other DBMs maybe? I'm using sqlite3.

Cheers,
Joe

MPTT with OrderedModel

When using django-mptt with django-ordered-model. I have some problems.
First:
This problem happens when saving an instance
Manager isn't accessible via [Model] instances*
After checking the problem I have found that is line is causing the problem
in model > get_ordering_queryset function

qs = qs or self._default_manager.all()

I have seen MPTT code they change the default manager to a special manager called TreeManager.

If I change the code to this

qs = qs or self._tree_manager.all()

or this one

qs = qs or [Model]._tree_manager.all()

it will work. I am sorry if I could not help more than that.

Beside this is my order of inheritance in the model
class Name(MPTTModel, OrderedModel)

admin broken for custom pks

First off: Thanks for a nice reusable app.

I have a model w/ a custom pk and the admin is broken because it relies on 'id'. However the fix is trivial:

--- admin.py.org    2015-02-20 20:04:34.074953287 +0100
+++ admin.py    2015-02-20 19:52:52.182713848 +0100
@@ -65,10 +65,10 @@
         return render_to_string("ordered_model/admin/order_controls.html", {
             'app_label': self.model._meta.app_label,
             'module_name': self.model._meta.module_name,
-            'object_id': obj.id,
+            'object_id': obj.pk,
             'urls': {
-                'up': reverse("admin:{app}_{model}_order_up".format(**self.get_model_info()), args=[obj.id, 'up']),
-                'down': reverse("admin:{app}_{model}_order_down".format(**self.get_model_info()), args=[obj.id, 'down']),
+                'up': reverse("admin:{app}_{model}_order_up".format(**self.get_model_info()), args=[obj.pk, 'up']),
+                'down': reverse("admin:{app}_{model}_order_down".format(**self.get_model_info()), args=[obj.pk, 'down']),
             },
             'query_string': self.request_query_string
         })

New release

Since Django 1.8 has been released, it would be nice to have a PyPI release of django-ordered-model that is compatible with Django 1.8.

Subset Ordering with many-to-many relationship

When using Subset Ordering with many-to-many relationship This error appear:

ValueError: "<Item: Item object>" needs to have a value for field "item" before this many-to-many relationship can be used.

it1 = Item.objects.create(name="Foo")

Traceback (most recent call last):
File "", line 1, in
File "/home/yaser/.virtualenvs/aleqt/local/lib/python2.7/site-packages/django/db/models/manager.py", line 157, in create
return self.get_queryset().create(**kwargs)
File "/home/yaser/.virtualenvs/aleqt/local/lib/python2.7/site-packages/django/db/models/query.py", line 319, in create
obj.save(force_insert=True, using=self.db)
File "/home/yaser/.virtualenvs/aleqt/local/lib/python2.7/site-packages/ordered_model/models.py", line 40, in save
c = self.get_ordering_queryset().aggregate(Max('order')).get('order__max')
File "/home/yaser/.virtualenvs/aleqt/local/lib/python2.7/site-packages/ordered_model/models.py", line 34, in get_ordering_queryset
value = self._get_order_with_respect_to()
File "/home/yaser/.virtualenvs/aleqt/local/lib/python2.7/site-packages/ordered_model/models.py", line 23, in _get_order_with_respect_to
return getattr(self, self.order_with_respect_to)
File "/home/yaser/.virtualenvs/aleqt/local/lib/python2.7/site-packages/django/db/models/fields/related.py", line 815, in get
through=self.field.rel.through,
File "/home/yaser/.virtualenvs/aleqt/local/lib/python2.7/site-packages/django/db/models/fields/related.py", line 512, in init
(instance, source_field_name))
ValueError: "<Item: Item object>" needs to have a value for field "item" before this many-to-many relationship can be used.

class Issue(models.Model):
    number = models.IntegerField()
    pub_time = models.DateTimeField()

    class Meta:
        ordering = ["-number"]

class Item(OrderedModel):
    name = models.CharField(max_length=100)
    issue = models.ManyToManyField(Issue)
    order_with_respect_to = 'issue'

    class Meta(OrderedModel.Meta):
        pass

new release

Hi!

First of all, thanks for this awesome package! I was thinking about implementing something like this and when I looked to see if someone else implemented this, I found your project and it's exactly what I needed.

There is a small thing that bugs me and that's already fixed in #73, but it has not been released on PyPi yet.

Could you release a new version so I don't have this warning anymore?

Thanks in advance!

Add Python 3 to CI builds and testing

Whether intentionally or unintentionally, this lightweight library works out of the box with Python 3 (haven't tested the admin, just the core ordering). However, to feel comfortable using this project going forward, I have to know that Python 3 will have formal support. If there is interest in this I will take the time to make a pull request to add formal Python 3 support (whether that's changing the Travis config or fixing possible admin issues in Python 3 or whatever it may take). Please let me know what you think.

Order not recalculated when an order_with_respect_to field is modified

Hello everyone;

First, and before asking my question, congratulations on the work done.

I'm using the app to sort a set of polymorphic models.

The parent model uses order_with_respect_to (Subset Ordering) with two values and order_class_path.

Everything works great except editing an object, either in the admin or through an api.

When I edit one of the order_with_respect_to fields the order is not recalculated.

Any ideas? Thanks in advance.

inline OrederedModelAdmin

Hello

First, thanks for the great model.

The fact that we can do SubsetOrdering is very useful, but it becomes a problem to administrate, because there is no an INLINE OrderedModelAdmin.

I am not very strong in django internals, so I prefer not to offer my help, it will be more a problem than anything else.

Thanks again
Federico

use templates for buttons

I feel that it would be better for project integrators if the string interpolation were instead implemented as a django template.

incoming pull request...

Logic error in `below()` etc. after updating `order_with_respect_to` fields

I was just testing the below() method and I believe I've stumbled upon a logic error.

https://github.com/bfirsh/django-ordered-model/blob/master/ordered_model/models.py#L189

        if getattr(self, self.order_field_name) == getattr(ref, self.order_field_name):
            return

If the value of the order field on the object to be moved is equal to the value for the object you are trying to move below, then it should be decremented just like in the > case. Keeping them both at the same value breaks sorting.

That == equality check should be switched to a less than < check. ie "If I'm already below the other object, then there is no work to do, quit".

Admin URL config deprecation

Hi, thanks for a really great module.
Just thought I should report (since it doesn't seem to have been reported yet), I'm getting the following deprecation warning for this module.

[...]/site-packages/ordered_model/admin.py:32:
RemovedInDjango110Warning: django.conf.urls.patterns() is deprecated and will be removed in Django 1.10.
Update your urlpatterns to be a list of django.conf.urls.url() instances instead.
  name='{app}_{model}_order_down'.format(**self._get_model_info())),

No Reverse Match with OrderedTabularInline

With Django 1.11.2 i have an error like this:

Django Version: 1.11.2
Python Version: 2.7
Exception Type: NoReverseMatch
Exception Value:
Reverse for '{app_label}_{model_name}_order_up_inline' not found.

p.s. Obviously app_label and model_name are substituted from real attrs

If i use django_extensions show_urls command i cannot see them in project urls, but i can see the ones of the OrderedModelAdmin i used in the whole admin site

Ordering fails when using polymorphic collection of objects

When an ordered list contains various different models, all subclassed from a base model (which is a subclass of an OrderedModel) the order is managed per subclass, not for the complete list (base model class)
Reason is the self.class reference in the method get_ordering_queryset (line 34 in models.py)

Would be great if you could set a classname to use for the ordering somehow. In the example below one would set that classname to 'Question'.

Simplified example: an ordered list of various different kind of questions


class Question(OrderedModel):
    question = models.TextField()
    class Meta:
        ordering = ('order',)

class MultipleChoiceQuestion(Question):
    good_answer = models.TextField()
    wrong_answer1 = models.TextField()

class OpenQuestion(Question):
    answer = models.TextField()

Problem: Create two questions, one of each kind. They both obtain order=0.

Cannot save instances when the model inherits both from OrderedModel and PolymorphicModel

Hi,

I am currently playing with django-ordered-model and also django-polymorphic. I have created a, rather simple, model:

class CourseItem(PolymorphicModel, OrderedModel, VisibleModel):
    name = models.CharField(max_length=100, verbose_name=_('item name'))

    item_group = models.ForeignKey(CourseItemGroup, related_name='items', verbose_name=_('item group'))

    order_with_respect_to = 'item_group'

    def __str__(self):
        return self.name

    class Meta(OrderedModel.Meta):
        verbose_name = _('course item')
        verbose_name_plural = _('course items')

The problem is, I cannot save instances of this model: I get following exception:

Traceback (most recent call last):
  File "[...]\tests\tests\test_courses\test_course_item.py", line 8, in test___str__
    item = CourseItemFactory(name='foo')
  File "[...]\env\lib\site-packages\factory\base.py", line 82, in __call__
    return cls.create(**kwargs)
  File "[...]\env\lib\site-packages\factory\base.py", line 585, in create
    return cls._generate(True, attrs)
  File "[...]\env\lib\site-packages\factory\base.py", line 510, in _generate
    obj = cls._prepare(create, **attrs)
  File "[...]\env\lib\site-packages\factory\base.py", line 485, in _prepare
    return cls._create(model_class, *args, **kwargs)
  File "[...]\env\lib\site-packages\factory\django.py", line 153, in _create
    return manager.create(*args, **kwargs)
  File "[...]\env\lib\site-packages\django\db\models\manager.py", line 92, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "[...]\env\lib\site-packages\django\db\models\query.py", line 372, in create
    obj.save(force_insert=True, using=self.db)
  File "[...]\env\lib\site-packages\polymorphic\polymorphic_model.py", line 90, in save
    return super(PolymorphicModel, self).save(*args, **kwargs)
  File "[...]\env\lib\site-packages\ordered_model\models.py", line 40, in save
    c = self.get_ordering_queryset().aggregate(Max('order')).get('order__max')
  File "[...]\env\lib\site-packages\ordered_model\models.py", line 31, in get_ordering_queryset
    qs = qs or self._default_manager.all()
  File "[...]\env\lib\site-packages\django\db\models\manager.py", line 206, in __get__
    raise AttributeError("Manager isn't accessible via %s instances" % type.__name__)
AttributeError: Manager isn't accessible via CourseItem instances

I have figured out, that the problem sits in OrderedModel.get_ordering_queryset in this line:

qs = qs or self._default_manager.all()

After some googling I have found this StackOverflow answer; the 2nd comment proposes to use __class__.objects instead of _default_manager and after the change everything automagically started to work. I am not a Django expert, so I cannot say why this works, but it works :)

I will fill a pull request in a moment with this patch soon.

Couldn't install django-ordered-model

What I did:
python3 -m pip install django-ordered-model (python3 -m pip is the equivalent to pip3 on my computer)

The traceback I got:
`Collecting django-ordered-model
Downloading django-ordered-model-1.4.0.tar.gz
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "", line 1, in
File "/tmp/pip-build-d1idycat/django-ordered-model/setup.py", line 5, in
with open('requirements.txt') as f:
FileNotFoundError: [Errno 2] No such file or directory: 'requirements.txt'

----------------------------------------

Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-d1idycat/django-ordered-model/`

I got the same error when I downloaded the code here and built it myself.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.