Here's excerpts from (something analogous to) my models:
class Person(models.Model):
name = models.CharField(max_length=20)
relationships = models.ManyToManyField('self',
through='Relationship',
symmetrical=False,
related_name='related_to',
)
def __str__(self):
return self.name
class Relationship(models.Model):
from_person = models.ForeignKey(Person,
related_name='from_people',
on_delete=models.CASCADE,
)
to_person = models.ForeignKey(Person,
related_name='to_people',
on_delete=models.CASCADE,
)
status = models.CharField(max_length=20)
def __str__(self):
return "{} is {} {}".format(
self.from_person.name, self.status, self.to_person.name)
Here's the contents of my database:
>>> Person.objects.all()
<QuerySet [<Person: A>, <Person: B>, <Person: C>]>
>>> Relationship.objects.all()
<QuerySet [<Relationship: B is Following C>]>
If I want to see who a given person is following, I can build a new method into the Person class:
def get_following(self):
return self.relationships.filter(
to_people__status='Following',
to_people__from_person=self)
This works:
>>> p2.get_following()
<QuerySet [<Person: C>]>
I want to do the REVERSE of this. Instead of asking "Who does this person follow?", I want to ask "Who follows this person?". I can do that like this (although it returns Relationship objects, not Person objects):
>>> Relationship.objects.filter(to_person=p3, status='Following')
<QuerySet [<Relationship: B is Following to C>]>
My attempt is this (which returns an empty QuerySet):
def get_following(self):
return self.relationships.filter(
from_people__status='Following',
from_people__to_person=self)
Your help is appreciated!
EDIT: Here's the ANSWER I chose:
def get_followers(self):
return self.related_to.filter(from_people__status='Following')
You got a QuerySet like this:
<QuerySet [<Relationship: B is Following C>]>
. Think that one day (I guess that it is the propose of that) 'person' has a lot of followers and it probably might return so many followers, like this:<QuerySet [<Relationship: B is Following C>, <Relationship: A is Following C>]>
. So, I would use values_list() [1]:Returns:
<QuerySet [A, B, ...]>
If you only pass in a single field, you can also pass in the flat parameter. If True, this will mean the returned results are single values, rather than one-tuples.
or create a method:
Returns:
[A, B, ...]
values_list still is better, because you are working directly on the database.
[1] https://docs.djangoproject.com/en/2.0/ref/models/querysets/#values-list (Here have a good example of ManyToMany).
Take a look at this document. Otherwise here are some other methods...
self.relationships.from_people.objects.all()
would return all objects with of with the related namefrom_people
.I would, however, slightly change some code such that the use would be
self.relationships.from_people.objects.filter(status='Following')
Another way to do it (though not the most efficient) is to pass in the person model, and pass in the relationships model using the person.pk as a filter.
Just in case somebody else needs another way of implementing "Followers" exactly like you described, but with a different modelling scheme:
If you then start the server and go to localhost:8000/admin/ and navigate to a user's detail-page, you should see something like this on your screen:
I did not add counters so you are able to see the amount of followers at once in the list_view.
Note that the second FormField with the followers is read-only in the admin panel. A user cannot choose other users to follow him.
Here's the ANSWER I chose: