jpwatts / django-positions

A Django field for custom model ordering.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

PositionField on inherited class?

pcompassion opened this issue · comments

I added positionField to one of the subclasses (multi-table inheritance)

I was unable to create a subclass instance because of the following lines (fields.py: 65)

    if not add and self.collection is not None:
        previous_instance = type(model_instance)._default_manager.get(pk=model_instance.pk)

It trys to get the instance which was not yet saved.

Thanks for submitting the issue. I'm surprised to see you getting to this point in the code when adding a new instance. That should only happen if the value Django passes in for add when it calls the field's pre_save method is False, which it shouldn't be here.

Could you please write up a failing test so I can reproduce the problem?

@jpwatts - I came across this same issue myself. I placed an example project with a test that fails here. It should work out of the box as it uses sqllite. Here is a summary:

Models:

class AbstractModel(models.Model):

    created_at = DateTimeField(default=timezone.now(), db_index=True)


class Person(AbstractModel):

    name = CharField(max_length=50)
    fave_cars = ManyToManyField('Car', through='FaveCar')


class Car(AbstractModel):

    model_name = CharField(max_length=50)


class FaveCar(AbstractModel):

    car = ForeignKey('Car')
    person = ForeignKey('Person')

    position = PositionField(collection='person')

    class Meta(object):
        unique_together = ('car', 'person')

Test:

class Test(TestCase):

    def setUp(self):
        super(Test, self).setUp()
        self.person = models.Person.objects.create(name='Bob')
        self.bmw = models.Car.objects.create(model_name='bmw')
        self.ford = models.Car.objects.create(model_name='ford')

    def test_can_create_fave_cars(self):
        models.FaveCar(car=self.bmw, person=self.person).save()
        models.FaveCar(car=self.ford, person=self.person).save()

Error:

Creating test database for alias 'default'...
E
======================================================================
ERROR: test_can_create_fave_cars (dpb.tests.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/mtford/Playground/django_positions_bug/dpb/tests.py", line 16, in test_can_create_fave_cars
    models.FaveCar(car=self.bmw, person=self.person).save()
  File "/Library/Python/2.7/site-packages/django/db/models/base.py", line 545, in save
    force_update=force_update, update_fields=update_fields)
  File "/Library/Python/2.7/site-packages/django/db/models/base.py", line 573, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "/Library/Python/2.7/site-packages/django/db/models/base.py", line 632, in _save_table
    for f in non_pks]
  File "/Library/Python/2.7/site-packages/positions/fields.py", line 72, in pre_save
    previous_instance = type(model_instance)._default_manager.get(pk=model_instance.pk)
  File "/Library/Python/2.7/site-packages/django/db/models/manager.py", line 151, in get
    return self.get_queryset().get(*args, **kwargs)
  File "/Library/Python/2.7/site-packages/django/db/models/query.py", line 307, in get
    self.model._meta.object_name)
DoesNotExist: FaveCar matching query does not exist.

----------------------------------------------------------------------
Ran 1 test in 0.003s

FAILED (errors=1)
Destroying test database for alias 'default'...

And here is my proposed fix for this: https://gist.github.com/mtford90/9854075. Let me know what you think.

I added a try/except around the get query:

try:
    previous_instance = type(model_instance)._default_manager.get(pk=model_instance.pk)
    for field_name in self.collection:
        field = model_instance._meta.get_field(field_name)
        current_field_value = getattr(model_instance, field.attname)
        previous_field_value = getattr(previous_instance, field.attname)
        if previous_field_value != current_field_value:
            collection_changed = True
            break
except model_instance.DoesNotExist:
    pass

I'm sorry this has just been sitting here for so long; I haven't had any time lately to spend on this project. I'm not sure when that's going to change, but thank you for digging into the problem and giving me something to work from.