Opened 17 years ago

Closed 17 years ago

Last modified 13 years ago

#7107 closed (fixed)

ManyToMany self-referential models *need* a related_name

Reported by: tsouanas Owned by: Malcolm Tredinnick
Component: Uncategorized Version: dev
Severity: Keywords: qsrf-cleanup manytomany, infinite loop
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Without a related_name, self-referential ManyToMany models seem cause an infinite loop when trying to insert new objects.

Change History (9)

comment:1 by Malcolm Tredinnick, 17 years ago

I cannot repeat this problem. Can you please provide a short example model and instructions for how to trigger the problem?

I've been using this model for testing and I can insert, query and remove things from the m2m field without any problems.

class M2MSelfRef(models.Model):
    name = models.CharField(max_length=10)
    similar = models.ManyToManyField('self')

    def __unicode__(self):
        return '%s - %s' % (self.name, ', '.join(self.similar.values_list('name', flat=True)))

comment:2 by anonymous, 17 years ago

Using this model, when I try to edit it from the admin interface (newforms-admin) without the related_name attribute, I get a

Infinite loop caused by ordering.

error.

(I also used to have an "order_with_respect_to = 'parents'" inside the Meta class but I removed it.)

class Topic(models.Model):
    topic = models.CharField(max_length=128)
    parents = models.ManyToManyField('self', related_name='topic_parents', null=True, blank=True)

    class Meta:
        ordering = ('topic',)

    def ancestors(self):
        if not self.parents:
            return [self]
        else:
            return sum([parent.ancestors() for parent in self.parents], [self])

    def subtopic_of(self, topic):
        return topic in self.ancestors()

    def related_with(self, topic):
        return self.subtopic_of(topic) or topic.subtopic_of(self)

comment:3 by George Vilches, 17 years ago

Keywords: qsrf-cleanup added

comment:4 by Jacob, 17 years ago

milestone: 1.0

comment:5 by Malcolm Tredinnick, 17 years ago

Component: DocumentationUncategorized
Version: SVNnewforms-admin

I'm going to reassign this to the newforms-admin branch, since it doesn't seem to be core-related. The normal core code will not be using parent anywhere in the ordering querying, since it's just not specified. If newforms-admin is somehow introducing an implicit ordering on that many-to-many relation, it needs to work out how it's going to avoid the infinite loop problem. I haven't looked at the newforms-admin code to see what's going on here, but, as indicated in the first comment, I can't repeat it with normal code on trunk.

So Brian or Karen ... any big ideas what's going on here?

comment:6 by Karen Tracey <kmtracey@…>, 17 years ago

Version: newforms-adminSVN

I don't think the problem is in the admin. I can recreate the problem by just doing a Topic.objects.all() in a python shell with the given model. The problem seems to relate to having specified an ordering on a field that is identical except for case to the name of the Model. If I change the model name so that is different than the field name then the problem goes away. That is, given these models:

class Topic(models.Model):
    topic = models.CharField(max_length=128)
    parents = models.ManyToManyField('self', null=True, blank=True)

    class Meta:
        ordering = ('topic',)

class TopicModel(models.Model):
    topic = models.CharField(max_length=128)
    parents = models.ManyToManyField('self', null=True, blank=True)

    class Meta:
        ordering = ('topic',)

this is what I see:

Python 2.5.1 (r251:54863, Mar  7 2008, 04:10:12)
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> import django
>>> django.get_version()
u'0.97-pre-SVN-7739'
>>> from m2m.models import Topic,TopicModel
>>> Topic.objects.all()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/homedir/django/trunk/django/db/models/query.py", line 129, in __repr__
    return repr(list(self))
  File "/homedir/django/trunk/django/db/models/query.py", line 141, in __len__
    self._result_cache.extend(list(self._iter))
  File "/homedir/django/trunk/django/db/models/query.py", line 248, in iterator
    for row in self.query.results_iter():
  File "/homedir/django/trunk/django/db/models/sql/query.py", line 200, in results_iter
    for rows in self.execute_sql(MULTI):
  File "/homedir/django/trunk/django/db/models/sql/query.py", line 1464, in execute_sql
    sql, params = self.as_sql()
  File "/homedir/django/trunk/django/db/models/sql/query.py", line 247, in as_sql
    ordering = self.get_ordering()
  File "/homedir/django/trunk/django/db/models/sql/query.py", line 583, in get_ordering
    self.model._meta, default_order=asc):
  File "/homedir/django/trunk/django/db/models/sql/query.py", line 627, in find_ordering_name
    order, already_seen))
  File "/homedir/django/trunk/django/db/models/sql/query.py", line 621, in find_ordering_name
    raise FieldError('Infinite loop caused by ordering.')
FieldError: Infinite loop caused by ordering.
>>> TopicModel.objects.all()
[]
>>>

comment:7 by Malcolm Tredinnick, 17 years ago

Owner: changed from nobody to Malcolm Tredinnick
Triage Stage: UnreviewedAccepted

Right, my bad then. I'll fix it.

comment:8 by Malcolm Tredinnick, 17 years ago

Resolution: fixed
Status: newclosed

(In [7764]) Fixed the way symmetrical many-to-many relations are recorded in the Options class.

These types of relations don't have reverse accessor names, so that name can be
used by a normal field on the model. Fixed #7107.

comment:9 by Jacob, 13 years ago

milestone: 1.0

Milestone 1.0 deleted

Note: See TracTickets for help on using tickets.
Back to Top