I've got roughly models like these:
class Person(models.Model):
...
name = models.CharField(max_length=40)
eyes = models.CharField(max_length=12)
...
relationships = models.ManyToManyField('self', through='Relationship',
symmetrical=False, related_name='related_to')
class Relationship(models.Model):
from_person = models.ForeignKey(Person, related_name='from_people', ...)
to_person = models.ForeignKey(Person, related_name='to_people', ...)
status = models.CharField(max_length=20)
I've got reasonable database entries, and can see them in the Python shell:
>>> p1 = Person.objects.get(...)
>>> r = Relationship.objects.filter(from_person=p1)
<QuerySet [<Relationship: p1 p2 status>], [<Relationship: p1 p99 status>]>
>>> r[0].status
Friend
>>> r[0].from_person.name
p1
>>> r[0].from_person.eyes
Brown # p1's eye color
>>> r[0].to_person.name
p2
>>> r[0].to_person.eyes
Blue # p2's eye color !!!
Now I want to access this same information from my templates, but I don't see what I expect:
<p>name={{ person.name }} ({{ person.eyes }})</p>
<p>len={{ person.relationships.all|length }}</p>
{% for rel in person.relationships.all %}
<p> rel[{{ forloop.counter }}]={{ rel.status }}
{{ rel.from_person.name }} ({{ rel.from_person.eyes }})
{{ rel.to_person.name }} ({{ rel.to_person.eyes }})</p>
{% endfor %}
This displays:
name=p1 (Brown)
len=2
rel[0]=
rel[1]=
I expect:
name=p1 (Brown)
len=2
rel[0]=Friend p1 (Brown) p2 (Blue)
rel[1]=Enemy p1 (Brown) p99 (Red!)
I added accessor methods to the 'Relationship' model, but it didn't make a difference. I messed with using the template "slice" filter. I'm missing something basic...
Thanks for your help!
edit: This template code does what I want for the example above. Thanks to the two commenters below:
{% for rel in person.from_people.all %}
<p> rel[{{ forloop.counter }}]={{ rel.status }}
{{ rel.from_person.name }} ({{ rel.from_person.eyes }})
{{ rel.to_person.name }} ({{ rel.to_person.eyes }})</p>
{% endfor %}
Look at this line of code in your Person
model:
relationships = models.ManyToManyField('self', through='Relationship', ...)
If you address the attribute relationships
of a Person
instance, it returns a Manager for the target model (which is also Person
).
So p1.relationships.all()
returns all persons that p1
is related to, not the Relationship instances.
If you want to access the intermediate data of your through
model, you have to directly query it:
Relationship.objects.filter(from_person=p1)
See also the examples in the Docs (Django 2.0): Extra fields on many-to-many relationships
Edit : Dan's comment might show the correct way to access the Relationship data given a Person from the template. I think the template language should be able to handle backward relations. Can you try this and tell if it works?
<p>name={{ person.name }} ({{ person.eyes }})</p>
<p>len={{ person.relationships.all|length }}</p>
{% for rel in person.from_people.all %}
{# rel is now a Relationship where 'from_person' is 'person' #}
<p> rel[{{ forloop.counter }}]={{ rel.status }}
{{ rel.from_person.name }} ({{ rel.from_person.eyes }})
{{ rel.to_person.name }} ({{ rel.to_person.eyes }})</p>
{% endfor %}
This template code does what I want for the example above. Thanks to the two commenters:
{% for rel in person.from_people.all %}
<p> rel[{{ forloop.counter }}]={{ rel.status }}
{{ rel.from_person.name }} ({{ rel.from_person.eyes }})
{{ rel.to_person.name }} ({{ rel.to_person.eyes }})</p>
{% endfor %}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.