Django ORM 按字段值过滤相关对象?
Django ORM Filtering Related Objects By Field Value?
我正在从事一个涉及 channel/post 机制的项目。我目前正在通过将名为 "deleted" 的字段设置为删除时间的时间戳来实现 "delete" posts 的功能。它需要保留历史意义(最后 activity)并跟踪煽动性的人(该服务是整个项目的基于特权的部分)。
models.py
class Post(models.Model):
deleted = models.DateTimeField(null=True, blank=True)
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now_add=True)
post_text = models.CharField(max_length=1000)
post_to = models.ForeignKey('Channel', related_name='post_to_channel')
post_from = models.ForeignKey(User, related_name='user_from_post')
class Channel(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
channel_title = models.CharField(max_length=50, unique=True)
channel_description = models.CharField(max_length=100)
channel_creator = models.ForeignKey(User)
channel_private = models.BooleanField(default=False)
channel_users = models.ManyToManyField(User, through='Membership', related_name='channels', blank=True)
channel_posts = models.ManyToManyField(Post, related_name='channel_post', blank=True)
initial_invites = models.ForeignKey(User, null=True, blank=True, related_name='initial_invites')
objects = models.Manager()
class Membership(models.Model):
channel = models.ForeignKey(Channel)
channel_user = models.ForeignKey(User, related_name='channel_membership')
join_date = models.DateTimeField(auto_now_add=True)
我有一个端点可以将 "Post" 对象的 "deleted" 字段更新为空的时间戳。然后,如何避免在 "Channel" 级别查询时返回所有具有时间戳的帖子,并通过它们的关系和单独的序列化程序加载帖子?基本上,我需要能够查询 "Channel" 并获取我所属的频道列表,然后通过它们与 "Post" 的 ManyToMany 关系加载所有相关帖子,但不包括那些在"deleted" 字段。这是否可能无需为每个通道调用 ORM,随后过滤掉已删除的 posts?需要明确的是,如果删除了 post,"Channel" 仍应通过,只有 "Post" 本身应该被隐藏。
如果我没有提供足够的信息,请告诉我您需要看什么。
如果答案是如果没有重复的 ORM 调用这是不可能的,建议其他选项将不胜感激,包括权衡 "archive" post 到重复模型的选项,在这些情况下没有调用钩子并删除原始钩子。
编辑 1:
按照所选答案中的说明添加 属性,但它看不到 self.channel_posts
。
models.py
class Channel(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
channel_title = models.CharField(max_length=50, unique=True)
channel_description = models.CharField(max_length=100)
channel_creator = models.ForeignKey(User)
channel_private = models.BooleanField(default=False)
channel_users = models.ManyToManyField(User, through='Membership', related_name='channels', blank=True)
channel_posts = models.ManyToManyField(Post, related_name='channel_post', blank=True)
initial_invites = models.ForeignKey(User, null=True, blank=True, related_name='initial_invites')
objects = models.Manager()
@property
def get_viewable_posts(self):
print (self.channel_users) # prints []
print (self.channel_posts) # prints []
return CourtyardPost.objects.filter(deleted__isnull=True, courtyard_post_to=self.pk) #works, but adds IO strain
目前,如上所示,它是每个通道的 ORM 调用,但在所有情况下都打印 self.channel_posts
或 self.channel_users
returns 一个空列表。 属性 作为 Django Rest Framework 的序列化程序的一部分被调用,如下所示:
class CourtyardChannelSerializer(serializers.ModelSerializer):
current_user = serializers.SerializerMethodField('_user')
post_to_channel = CourtyardPostSerializer(many=True, source='get_viewable_posts', read_only=True)
most_recent = CourtyardPostSerializer(many=True, source='latest_post', read_only=True)
channel_users = CourtyardUserSerializer(many=True, read_only=True)
)
invite_list = serializers.ReadOnlyField(source='get_invitation_set', required=False)
def _user(self, obj):
user = self.context['request'].user.username
return user
我猜你现在正在做类似的事情:
def channels_and_posts_for_user(user):
for channel in user.channel_membership:
posts = channel.channel_posts.all().filter(deleted__isnull=True)
channels_and_posts.append(channel, posts)
return channels_and_posts
您想在每次调用时都去掉该过滤器吗?
这很痛苦,我同意,我一直在尝试对我的网络应用程序的某些模型中的 'archived' 变量做一些类似的事情。
我认为没有解决办法。您可以像这样创建自定义管理器:
class ChannelManagerWithUndeleted(models.Manager):
def get_queryset(self):
return super(ChannelManagerWithUndeleted, self).get_queryset().filter(deleted__isnull=True)
class Channel(models.Model):
#...
objects = models.Manager() # Default Manager
undeleted = EntryManager() # Custom Manager
然后通过 Channel.undeleted.all()
而不是 Channel.objects.all
直接访问对象,但是你仍然需要在 related calls anyway 上指定这个新的管理器,它最终几乎是冗长的(如果再干一点):
channels_and_posts = []
for channel in user.channel_membership:
posts = channel.channel_posts.all(manager='undeleted').all()
channels_and_posts.append(channel, posts)
这也是一个相关的post:How to use custom manager with related objects?。
我认为这很复杂,因为每个人都希望在不同情况下表现出略微不同的行为。例如我希望我的存档 'Events' 仍然能够 运行 报告,但不会向最终用户显示 select,所以我到处都使用自定义管理器 consumer-facing .
如果您只想稍后计算它们以进行报告,也许一个选择是添加一个 channel_deleted_posts
ManyToManyField 并从 channel_posts
移动 Post(不删除它)至 channel_deleted_posts
。恐怕需要挂钩。
我真的希望有一个更好的答案并且你想要的是微不足道的。我很想被证明是错误的! :)
class Channel(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
channel_title = models.CharField(max_length=50, unique=True)
channel_description = models.CharField(max_length=100)
channel_creator = models.ForeignKey(User)
channel_private = models.BooleanField(default=False)
channel_users = models.ManyToManyField(User, through='Membership', related_name='channels', blank=True)
channel_posts = models.ManyToManyField(Post, related_name='channel_post', blank=True)
initial_invites = models.ForeignKey(User, null=True, blank=True, related_name='initial_invites')
objects = models.Manager()
@property
def active_posts(self):
return self.channel_posts.filter(deleted=None)
很简单,就是多加了一个属性,现在就可以这样用了
channel = Channel.objects.first()
print(channel.active_posts.count())
我正在从事一个涉及 channel/post 机制的项目。我目前正在通过将名为 "deleted" 的字段设置为删除时间的时间戳来实现 "delete" posts 的功能。它需要保留历史意义(最后 activity)并跟踪煽动性的人(该服务是整个项目的基于特权的部分)。
models.py
class Post(models.Model):
deleted = models.DateTimeField(null=True, blank=True)
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now_add=True)
post_text = models.CharField(max_length=1000)
post_to = models.ForeignKey('Channel', related_name='post_to_channel')
post_from = models.ForeignKey(User, related_name='user_from_post')
class Channel(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
channel_title = models.CharField(max_length=50, unique=True)
channel_description = models.CharField(max_length=100)
channel_creator = models.ForeignKey(User)
channel_private = models.BooleanField(default=False)
channel_users = models.ManyToManyField(User, through='Membership', related_name='channels', blank=True)
channel_posts = models.ManyToManyField(Post, related_name='channel_post', blank=True)
initial_invites = models.ForeignKey(User, null=True, blank=True, related_name='initial_invites')
objects = models.Manager()
class Membership(models.Model):
channel = models.ForeignKey(Channel)
channel_user = models.ForeignKey(User, related_name='channel_membership')
join_date = models.DateTimeField(auto_now_add=True)
我有一个端点可以将 "Post" 对象的 "deleted" 字段更新为空的时间戳。然后,如何避免在 "Channel" 级别查询时返回所有具有时间戳的帖子,并通过它们的关系和单独的序列化程序加载帖子?基本上,我需要能够查询 "Channel" 并获取我所属的频道列表,然后通过它们与 "Post" 的 ManyToMany 关系加载所有相关帖子,但不包括那些在"deleted" 字段。这是否可能无需为每个通道调用 ORM,随后过滤掉已删除的 posts?需要明确的是,如果删除了 post,"Channel" 仍应通过,只有 "Post" 本身应该被隐藏。
如果我没有提供足够的信息,请告诉我您需要看什么。
如果答案是如果没有重复的 ORM 调用这是不可能的,建议其他选项将不胜感激,包括权衡 "archive" post 到重复模型的选项,在这些情况下没有调用钩子并删除原始钩子。
编辑 1:
按照所选答案中的说明添加 属性,但它看不到 self.channel_posts
。
models.py
class Channel(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
channel_title = models.CharField(max_length=50, unique=True)
channel_description = models.CharField(max_length=100)
channel_creator = models.ForeignKey(User)
channel_private = models.BooleanField(default=False)
channel_users = models.ManyToManyField(User, through='Membership', related_name='channels', blank=True)
channel_posts = models.ManyToManyField(Post, related_name='channel_post', blank=True)
initial_invites = models.ForeignKey(User, null=True, blank=True, related_name='initial_invites')
objects = models.Manager()
@property
def get_viewable_posts(self):
print (self.channel_users) # prints []
print (self.channel_posts) # prints []
return CourtyardPost.objects.filter(deleted__isnull=True, courtyard_post_to=self.pk) #works, but adds IO strain
目前,如上所示,它是每个通道的 ORM 调用,但在所有情况下都打印 self.channel_posts
或 self.channel_users
returns 一个空列表。 属性 作为 Django Rest Framework 的序列化程序的一部分被调用,如下所示:
class CourtyardChannelSerializer(serializers.ModelSerializer):
current_user = serializers.SerializerMethodField('_user')
post_to_channel = CourtyardPostSerializer(many=True, source='get_viewable_posts', read_only=True)
most_recent = CourtyardPostSerializer(many=True, source='latest_post', read_only=True)
channel_users = CourtyardUserSerializer(many=True, read_only=True)
)
invite_list = serializers.ReadOnlyField(source='get_invitation_set', required=False)
def _user(self, obj):
user = self.context['request'].user.username
return user
我猜你现在正在做类似的事情:
def channels_and_posts_for_user(user):
for channel in user.channel_membership:
posts = channel.channel_posts.all().filter(deleted__isnull=True)
channels_and_posts.append(channel, posts)
return channels_and_posts
您想在每次调用时都去掉该过滤器吗?
这很痛苦,我同意,我一直在尝试对我的网络应用程序的某些模型中的 'archived' 变量做一些类似的事情。
我认为没有解决办法。您可以像这样创建自定义管理器:
class ChannelManagerWithUndeleted(models.Manager):
def get_queryset(self):
return super(ChannelManagerWithUndeleted, self).get_queryset().filter(deleted__isnull=True)
class Channel(models.Model):
#...
objects = models.Manager() # Default Manager
undeleted = EntryManager() # Custom Manager
然后通过 Channel.undeleted.all()
而不是 Channel.objects.all
直接访问对象,但是你仍然需要在 related calls anyway 上指定这个新的管理器,它最终几乎是冗长的(如果再干一点):
channels_and_posts = []
for channel in user.channel_membership:
posts = channel.channel_posts.all(manager='undeleted').all()
channels_and_posts.append(channel, posts)
这也是一个相关的post:How to use custom manager with related objects?。
我认为这很复杂,因为每个人都希望在不同情况下表现出略微不同的行为。例如我希望我的存档 'Events' 仍然能够 运行 报告,但不会向最终用户显示 select,所以我到处都使用自定义管理器 consumer-facing .
如果您只想稍后计算它们以进行报告,也许一个选择是添加一个 channel_deleted_posts
ManyToManyField 并从 channel_posts
移动 Post(不删除它)至 channel_deleted_posts
。恐怕需要挂钩。
我真的希望有一个更好的答案并且你想要的是微不足道的。我很想被证明是错误的! :)
class Channel(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
channel_title = models.CharField(max_length=50, unique=True)
channel_description = models.CharField(max_length=100)
channel_creator = models.ForeignKey(User)
channel_private = models.BooleanField(default=False)
channel_users = models.ManyToManyField(User, through='Membership', related_name='channels', blank=True)
channel_posts = models.ManyToManyField(Post, related_name='channel_post', blank=True)
initial_invites = models.ForeignKey(User, null=True, blank=True, related_name='initial_invites')
objects = models.Manager()
@property
def active_posts(self):
return self.channel_posts.filter(deleted=None)
很简单,就是多加了一个属性,现在就可以这样用了
channel = Channel.objects.first()
print(channel.active_posts.count())