views:

434

answers:

3

When getting members based on Unit, I only want to get the ones who are actually in that unit as of now.

I've got a model looking like this:

class Member(models.Model):
    name = models.CharField(max_length=256)
    unit = models.ManyToManyField(Unit, through='Membership')

class Membership(models.Model):
    member = models.ForeignKey(Member)
    unit = models.ForeignKey(Unit)
    start = models.DateField(default=date.today)
    stop = models.DateField(blank=True, null=True)

class Unit(models.Model):
    name = models.CharField(max_length=256)

As you can see, members can have a "fake" membership in unit, that is only history and should not be considered in the searches and listings of the admin. They should be shown in the change-page for a single object though.

The admin looks like this:

class MembershipInline(admin.TabularInline):
    model = Membership
    extra = 1

class MemberAdmin(admin.ModelAdmin):
    list_filter = ('unit',)
    inlines = [MembershipInline,]

So how can I (if at all possible this way), when filtering on unit only get those units whose membership__stop__isnull=True?

I tried Managers, I can make them work on the model in the admin itself, but not on the filtering/searches. There is also a def queryset(self) method that is overrideable, but I can't wrap my head around how to use it to fix my problem.

Edit, how this is used: A member has only one membership in a unit, however, they could be members from before, but they are ended (with stop). So I only want to filter (and show, in the list view) those members who have an open-ended membership (like, that they are members of that unit now).

Any ideas?

A: 

So you're trying to get the members of a specific Unit, right?

unit = Unit.objects.select_related().get(id=some_id)

This will pull the unit out of the database for you, along with the Memberships and Users that belong to it. You can access and filter the users by:

for member in unit.membership__set.filter(stop__isnull=True):
    print member.name

I hope this helps? I may be wrong, I haven't tested this.

Fragsworth
Yes, Django is great because this is so easy. But I want this in the admin, is there a way I can put that code into a list_filter? Like: def unit_without_old(self, unit_id): unit = member.unit.filter(unit=unit_id).filter(membership__stop__isnull=True) list_filter = ('unit_without_old',)
Velmont
A: 

One way to certainly achieve this is by adding a denormalized field for has_open_ended_membership.

To do this just add a BooleaneField like that to the Member and make sure it's consistent.

From the django documentation this seems to be the only way without writing specialized code in the ModelAdmin object:

Set list_filter to activate filters in the right sidebar of the change list page of the admin. This should be a list of field names, and each specified field should be either a BooleanField, CharField, DateField, DateTimeField, IntegerField or ForeignKey.

I'm curious about other approaches - list_filter certainly is limited.

phoku
Ah, I think you misunderstand what the membership is. Is for providing historic information. So everyone ALWAYS has at least one open ended membership. But I only want to filter on the open ended membership, and not on the closed ones (because they're not members there any more).Sorry, I should've explained more in the text.I've also looked at denormalized fields, I think it might be the solution. To have the newest unit as a normal foreignkey and keeping it consistent.
Velmont
Ok, let me clarify. You basically want to only ever see the Member#s with `membership__stop__isnull=True` - then you need a manager and override get_query_set. Did you try that?
phoku
Yes, I've written a manager, but I could've done it in a wrong way. Anyway, I *want* to see them when I edit the page, but not when I filter. I guess that may be a bit too much to ask of django-admin. I'm thinking about going the easy way and making a denormalized field that's always a ForeignKey to the active unit.
Velmont
A: 

I fixed it with putting in a denormalized field in member, with a foreign-key to the active unit. Then, to make it work and be automatically updated in the admin, I made the specialized save-function for Membership.

class Member(models.Model):
  name = models.CharField(max_length=256)
  unit = models.ManyToManyField(Unit, through='Membership')
  unit_denorm = models.ForeignKey(Unit)

class Membership(models.Model):
  member = models.ForeignKey(Member)
  unit = models.ForeignKey(Unit)
  start = models.DateField(default=date.today)
  stop = models.DateField(blank=True, null=True)

  def save(self, *args, **kwargs):
    if not self.stop:
      self.member.unit_denorm = self.unit
      self.member.save()
    super(Membership, self).save(*args, **kwargs)

class Unit(models.Model):
  name = models.CharField(max_length=256)

And with list_filter = ('unit_denorm',) in the admin, it does exactly what I want.

Great! Of course, there should only be one field with stop__isnull=True. I haven't figured out how to make that restriction. but people using the system know they shouldn't do that anyway.

Velmont