Django-mptt using get_root() on TreeQuerySet element inside for loop and output stored in list does not persist outside of for loop
I am trying to use get_root()
on individual TreeQuerySet elements in a for loop. This seems to work in the Django Python shell as shown below. Upon pressing [Enter]
twice to indicate the end of the for loop, the result of applying the get_root()
to each element is printed automatically in the shell. The result is correct in that the output does indeed list the top level root of each TreeQuerySet
element.
However outside of the loop, the list in which these should be stored in is not updated. Instead this list contains the original TreeQuerySet
elements.
In Django Python shell:
>>> topics_w_arttype_articles = topics.Topic.objects.filter(to_ArtTypeArticle_topic_from_Topic__title__isnull=False).distinct()
>>> topics_w_arttype_articles
<TreeQuerySet [<Topic: Level1Topic1>, <Topic: Level1Topic2>, <Topic: Level1Topic3>, <Topic: Level1Topic4>, <Topic: Level2Topic1>, <Topic: Level2Topic2>, <Topic: Level2Topic3>, <Topic: Level2Topic4>]>
>>> arttype_topic_roots_list = []
>>> for item in topics_w_arttype_articles.iterator():
... item.get_root()
... arttype_topic_roots_list.append(item)
...
<Topic: Level0Topic1>
<Topic: Level0Topic2>
<Topic: Level0Topic2>
<Topic: Level0Topic3>
<Topic: Level0Topic4>
<Topic: Level0Topic5>
<Topic: Level0Topic5>
<Topic: Level0Topic5>
>>> arttype_topic_roots_list
[<Topic: Level1Topic1>, <Topic: Level1Topic2>, <Topic: Level1Topic3>, <Topic: Level1Topic4>, <Topic: Level2Topic1>, <Topic: Level2Topic2>, <Topic: Level2Topic3>, <Topic: Level2Topic4>]
>>>
I placed the logic of the shell interaction in a custom template tag, shown below (with additional code that should remove duplicates from the list, if it had persistedd outside of the loop) to provide the list to templates. When used in the template the elements are the original TreeQuerySet
items and not the root nodes produced in the for loop. What is the problem?
@register.simple_tag
def get_topic_roots_warticles():
topics_w_arttype_articles = topics.Topic.objects.filter(to_ArtTypeArticle_topic_from_Topic__title__isnull=False).distinct()
#topics_w_arttype_articles_list = list(topics_w_arttype_articles)
arttype_topic_roots_list = []
for item in topics_w_arttype_articles:
item.get_root()
arttype_topic_roots_list.append(item)
arttype_topic_roots_list_unique = []
for val in arttype_topic_roots_list:
if val not in arttype_topic_roots_list_unique:
arttype_topic_roots_list_unique.append(val)
return arttype_topic_roots_list_unique
In case it matters, the relevant article model is:
class ArtTypeArticle(articles.models.Article):
topic = TreeManyToManyField(
Topic,
related_name = 'to_ArtTypeArticle_topic_from_Topic',
blank=True,
)
def __str__(self):
"""
String for representing the Model object.
"""
return self.title # defined in articles.models.Article
And the Topic MPTT model is:
class Topic(MPTTModel):
name = models.CharField(max_length=100)
parent = TreeForeignKey(
'self',
related_name='return_to_Topic_parent_from_self',
on_delete=models.SET_NULL,
null=True,
blank=True)
description = models.TextField(
max_length=1000,
default='',
null=True,
blank=True)
class MPTTMeta:
order_insertion_by = ['name']
class Meta:
verbose_name = 'Topic'
verbose_name_plural = 'Topics'
def __str__(self):
return self.name
def get_first_ancestor(self):
if (self.level >= 1):
topic_object_first_ancestor = self.get_ancestors(ascending=True, include_self=False)[0]
return topic_object_first_ancestor
def get_second_ancestor(self):
if (self.level >=2):
topic_object_second_ancestor = self.get_ancestors(ascending=True, include_self=False)[1]
return topic_object_second_ancestor
def get_all_descendants(self):
topic_object_all_descendants = self.get_descendants(include_self=False)
return topic_object_all_descendants
I've tried replacing the TreeQuerySet
into a list with list()
and using this list as the items in the for loop. This makes no difference.
I've also tried replacing item.get_root()
with item.first().get_root
, but this results in an error similar to "first is not an attribute of object".
What I expected was, in the shell, >>> arttype_topic_roots_list
to produce a list of Level0 categories as inside the for loop, i.e.,
<Topic: Level0Topic1>
<Topic: Level0Topic2>
<Topic: Level0Topic2>
<Topic: Level0Topic3>
<Topic: Level0Topic4>
<Topic: Level0Topic5>
<Topic: Level0Topic5>
<Topic: Level0Topic5>
and not:
>>> arttype_topic_roots_list
[<Topic: Level1Topic1>, <Topic: Level1Topic2>, <Topic: Level1Topic3>, <Topic: Level1Topic4>, <Topic: Level2Topic1>, <Topic: Level2Topic2>, <Topic: Level2Topic3>, <Topic: Level2Topic4>]
In the template I expected the top level roots to be available and not the original TreeQuerySet
items.