Django ORM按字段值过滤相关对象?

时间:2017-02-13 21:51:44

标签: django django-rest-framework django-orm

我正在开发一个涉及频道/邮政机制的项目。我目前正在实施"删除"通过设置名为"删除"的字段来发布帖子到它被删除的时间戳。它需要保持历史意义(最后的活动)并跟踪煽动的人(服务是整个项目的特权部分)。

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" object是null的时间戳。然后,如何在"频道"中查询时,如何避免返回所有具有时间戳的帖子?级别,通过他们的关系和单独的序列化程序加载帖子?基本上,我需要能够查询"频道"并获得我所参与的频道列表,然后通过他们与ManyToMany的关系" Post"加载所有相关帖子,不包括那些在"删除"领域。如果没有为每个频道调用ORM,然后过滤掉已删除的帖子,这是否可行?要清楚,"频道"如果有删除的帖子,只有" Post"本身应该被隐藏。

如果我没有提供足够的信息,请告诉我您需要查看的内容。

如果答案是在没有重复的ORM呼叫的情况下这是不可能的,建议其他选项将被赞赏,包括权衡选项" archive"将帖子发送到重复的模型,在这些情况下不要调用钩子并删除原始模型。

编辑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_postsself.channel_users都会返回一个空列表。该属性被称为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

2 个答案:

答案 0 :(得分:1)

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())

答案 1 :(得分:0)

我猜这一刻,你正在做类似的事情:

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

并且您想在每次通话时摆脱过滤器?

这是一种痛苦,我同意,我一直试图做一些类似于存档的'我的webapp的一些模型中的变量。

我不相信可以解决这个问题。您可以像这样创建一个自定义管理器:

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上指定这个新的管理器,并且它最终会变得冗长(如果有的话)更多DRY):

channels_and_posts = []
for channel in user.channel_membership:
    posts = channel.channel_posts.all(manager='undeleted').all()
    channels_and_posts.append(channel, posts)

这也是一个相关的帖子:How to use custom manager with related objects?

我觉得它很复杂,因为每个人都希望在不同情况下略有不同的行为。例如我想要归档的' Events'仍然能够运行报告,但不会显示给最终用户选择,所以我在面向消费者的地方使用自定义管理器。

如果你想要做的只是稍后计算报告,可能选择添加channel_deleted_posts ManyToManyField并将帖子(不删除它)从channel_posts移到channel_deleted_posts 。必要的钩子,我很害怕。

我真的希望有更好的答案,你想要的是微不足道的。我很想被证明是错的! :)