在django-admin中过滤外键时添加额外限制
在根据单位获取成员时,我只想获取那些现在真的在这个单位的成员。
我的模型大概是这样的:
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)
如你所见,成员可能在单位中有一个“假”的会员身份,这只是历史记录,不应该在管理员的搜索和列表中考虑。不过,他们应该在单个对象的变更页面中显示出来。
管理员界面是这样的:
class MembershipInline(admin.TabularInline):
model = Membership
extra = 1
class MemberAdmin(admin.ModelAdmin):
list_filter = ('unit',)
inlines = [MembershipInline,]
那么我该如何做呢?如果可以的话,在过滤单位时只获取那些membership__stop__isnull=True
的单位?
我尝试过使用管理器,我可以让它们在管理员界面上对模型起作用,但在过滤/搜索时却不行。还有一个def queryset(self)
的方法可以重写,但我搞不清楚该如何用它来解决我的问题。
编辑,这是如何使用的:一个成员在一个单位中只有一个会员身份,但他们可能之前是成员,但已经结束(有停止)。所以我只想过滤(并在列表视图中显示)那些有开放会员身份的成员(也就是说,他们现在是这个单位的成员)。
有什么想法吗?
3 个回答
一种确保实现这个功能的方法是为 has_open_ended_membership
添加一个非规范化字段。
要做到这一点,只需在会员模型中添加一个布尔字段(BooleanField),并确保它的一致性。
根据Django的文档,这似乎是唯一一种不需要在ModelAdmin对象中编写特殊代码的方法:
设置 list_filter 可以在管理界面的更改列表页面右侧激活过滤器。这应该是一个字段名称的列表,每个指定的字段应该是布尔字段、字符字段、日期字段、日期时间字段、整数字段或外键。
我对其他方法很感兴趣——list_filter 确实有些局限。
所以你是想获取某个特定单位的成员,对吧?
unit = Unit.objects.select_related().get(id=some_id)
这个代码会从数据库中提取出这个单位的信息,还有属于它的会员和用户。你可以通过以下方式来访问和筛选这些用户:
for member in unit.membership__set.filter(stop__isnull=True):
print member.name
希望这对你有帮助?我可能有错,我没有测试过这个。
我通过在成员中添加一个去规范化的字段来解决这个问题,这个字段有一个指向活动单位的外键。然后,为了让它在管理后台正常工作并自动更新,我为会员功能写了一个专门的保存函数。
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)
在管理后台使用list_filter = ('unit_denorm',)
后,它就能完全按照我想要的方式工作了。
太好了!当然,应该只有一个字段满足stop__isnull=True
这个条件。我还没弄明白怎么去限制这一点,不过使用这个系统的人都知道不应该这样做。