Question

I have a pair of Django models with a foreign key, one of which is searchable with django-fts, say:

class Foo(models.Model):
    ...
class Bar(fts.SearchableModel):
    foo = models.ForeignKey(Foo)

When I have an instance of Foo, in view I can insert print foo.bar_set.all() and I see an array of results. However if I try to use it in a view, in any of following ways:

{{foo.bar_set|pprint}}
{{foo.bar_set.all|ppring}}
{{foo.bar_set.count}}
{{foo.bar_set.all|length}}
{% for bar in foo.bar_set.all %} {{bar}} {% endfor %}

and literally any construct that I can think of behaves as if foo instance had no bar_set attribute.

Edit: I am sure that I have a Foo instance in the template, I tested following to work as expected:

{{foo|pprint}}
{{foo.id}} (and any other simple attributes of Foo)

I am sure there are related Bar objects, as I check that from view (print foo.bar_set.all()). And if the QuerySet was empty, {{foo.bar_set.all|pprint}} would yield [], not '' (which it does on {{foo.bar_set.all|pprint}}, {{foo.bar_set|pprint}}, and any {{foo.nonexistent_attribute|pprint}}).

This behaviour started when I moved development from SQLite database to PostgreSQL, with psycopg2 driver, to use django-fts full text search.

I could not find any other answer, because googling this is very hard: "reverse relationship" or "reverse foreign key" is all littered with unrelated django.core.urlresolvers.reverse references, and I have no idea how to name "*_set" thing to Google. A hint on how to google this one would be helpful as well.

Was it helpful?

Solution

Thanks to Marcin Kaszyński's help off the SO I managed to trace the bug down to django-fts BaseManager. Django-fts is a full-text search engine, that adds .search(query) method to manager of searchable classes, co I can write Foo.objects.search("bar"). For convenience, it adds __call__ method to the manager, which is inherited by RelatedManager (instance of which is the bar_set).

The template code, in django.templates.Variable._resolve_lookup method, sees that bar_set is callable (line 717), tries to call it with no arguments (line 722), and treats the variable as empty because arguments were required (lines 724-726).

Removing the __call__ method from django-fts' BaseManager helps.

OTHER TIPS

{{ foo.bar_set.all }} should definitely work. Are you sure you actually have an instance of Foo?

I suggest attempting to pass foo.bar_set.all as a dictionary element in the view function before rendering the page to see if you get the same result.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top