Getting the last member of a group on an intermediary M2M - django

If we look at the existing docs, what is the best way to get the last member added? This is similar to this but what I want to do is to be able to do this.
group = Group.objects.get(id=1)
group.get_last_member_added() #This is by ('-date_added')
<Person: FOO>
I think the best way is through a manager but how do you do this on an intermediary model?
class Person(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __unicode__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)

No need for a manager! Unless you'd like to add some kind of try/except in case there is no latest member.
group = Group.objects.get(id=1)
latest_person = group.members.latest('membership__date_joined')

Related

Django filter queryset based on many to many extra fields model

Here is a models example.
class Person(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
I want to get all Person objects, and for each one adding a column 'is_member' that values True if the person is member at least of one group.
I try with annotate and count, but I'm a bit confused... Thank you.
Try this :
Person.objects.annotate(
is_member=Case(
When(membership=True, then=True), default=False, output_field=BooleanField()
)
).distinct('id')
if you want learn more about this, conditional-expressions can help u.
The following should work for you:
persons = []
for p in Person.objects.all():
if p.membership_set.all():
p.is_member = True
else:
p.is_member = False
persons.append(p)

Django filter on multiple many-to-many rows

I'm pretty new to Django and I have a problem on filtering on many-to-many objects.
I have a many-to-many relationship
class Person(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __unicode__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
I can filter on Persons that are related to One single group
persons=Person.objects.filter(Q(group__name='Group1'))
But what I want to achieve is to filter on Persons that are exactly in two groups.
Example:
Person1 is in Group1
Person2 is in Group1 and Group2.
The filter should only return Person2.
Any tips how to create a filter for this?
I think this should do it ...
groups = ["Group1","Group2"]
persons = Person.objects.annotate(count=Count('name')).filter(count__gte=len(groups)).filter(reduce(operator.or_, (Q(group__name=x) for x in groups)))

Order a ManyToMany relationship, Django

With these models:
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
order_index = models.IntegerField(unique=True)
class Meta:
ordering = ['order_index']
I've been trying to get the following code to work:
group = Group.objects.get(id=1)
group.members.all() # I want the resulting membership objects to ordered by order_index as default.
Is this possible? The answers to similar questions require you to write group.members.all().order_by(...). I am trying to have it so the results are ordered by order_index by default.
This is how you get the user, and then the members associated with that user ordering them by date_joined. Not sure how you would do it automatically because the ordering is usually by the pk value which I'm assuming would correspond with the date joined because the higher the pk the later joined.
user = Person.objects.get(pk=request.user.id)
get_members = user.group.members.all().order_by('date_joined')

Filtering Many-to-Many relationship by Relationship field in Django

I'm trying to filter many-to-many relationship by some through Class field.
Quoting the Django documentation, i will explain my goal
class Person(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __unicode__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
In this example my goal sould be filter many to many relationship and obtain only the Person who has joined some Group starting from certain date (date_joined field).
Is it possible?
You can query across relationships with the django ORM (or in this case the reverse relationship):
person = Person.objects.filter(
membership__group=example_group,
membership__date_joined__gte=example_date
)
You can also do this:
person = example_group.members.filter(
membership__date_joined__gte=example_date
)

Default ordering for m2m items by intermediate model field in Django

I have an unusual problem. Let's consider such models (taken from django docs):
class Person(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __unicode__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
Now, let's say we've got 2 Beatles members in out Beatles band (following the example in django docs for intermediate models):
>>> beatles.members.all()
[<Person: Ringo Starr>, <Person: Paul McCartney>]
The above code will return members sorted by default ordering for Person model. If I specify:
>>> beatles.members.all().order_by('membership__date_joined')
the members, are sorted via the date joined. Can I somehow set this as default behavior for this ManyToMany field? That is to set default order of related items by field in the intermediate model? The ManyRelatedManager seems to have an init argument core_filters, but I have no vague idea how to access it withous subclassing the whole m2m field in django. Any creative ideas? :)
Thanks in advance :)
I've opened a ticket in django trac.
Here is a dirty-hack method to achieve this (look at Group model):
class Person(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
_members = models.ManyToManyField(Person, through='Membership')
#property
def members(self):
return self._members.order_by('membership__date_joined')
def __unicode__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
Didn't bother to create a set property decorator, but it should be quite easy to mimic the setting of original field. Ugly workaround, but seems to do the trick.
I think this should work:
class Membership(models.Model):
...
class Meta:
ordering = ['date_joined']