views:

556

answers:

3

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.

A: 

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

Daniel Roseman
I have an instance of Foo ({{foo|pprint}} works, I double-checked that), and all simple attributes (like {{foo.id}}) work. There is a relation and there are related Bar objects, which I test by running print foo.bar_set.all() from view before rendering the template.
Maciej Pasternacki
Hmm, odd. Can you post your view code?
Daniel Roseman
I found this, this was interaction with django-fts (I know I didn't mention this in first version of question text, I didn't suspect that to be the cause). See my answer for extended explanation.
Maciej Pasternacki
A: 

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.

AlbertoPL
Methods with no parameters are called by template language (which works with other methods as well, and if problem would be with a method call, {{foo.bar_set|pprint}} would render *something*). This is documented at http://docs.djangoproject.com/en/1.0/topics/templates/#variables and described in first question related to this one at http://stackoverflow.com/questions/1014591/traversing-foreign-key-related-tables-in-django-templates
Maciej Pasternacki
You're right, but have you tried my suggestion yet?
AlbertoPL
Yes, I already used that as a workaround until I can find a solution to the problem (that works as the ready result set is just another template argument). I downvoted your answer because it was a misinformation (about not being able to call methods), but I see you edited it, so I take the downvote back.
Maciej Pasternacki
thank you for that, I'm trying to figure out why that doesn't work for you, myself. Truth is I've never gotten .object_set.all to work in the template. Same problem.
AlbertoPL
Are you sure that foo is not being passed from bar. For example, are you sure that foo is coming from: foo = Foo.objects.get() vs the foo that is in your bar model? That could cause the problem.
AlbertoPL
No, that is not the cause. I found the cause, after I work out the code, I will post the full answer, as it is not obvious (it comes from django-fts actually, which was main reason to switch development to Postgres).
Maciej Pasternacki
+1  A: 

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.

Maciej Pasternacki