【问题标题】:Access model instance inside model field访问模型字段内的模型实例
【发布时间】:2021-09-16 11:35:19
【问题描述】:

我有一个模型(事件),它有一个外键到用户模型(事件的所有者)。 该用户可以使用以下 ManyToManyField 邀请其他用户:

    invites = models.ManyToManyField(
                  User, related_name="invited_users", 
                  verbose_name=_("Invited Users"), blank=True
              )

这个邀请字段生成一个简单的表,包含 ID、event_id 和 user_id。

如果活动所有者删除了他的个人资料,我不希望删除活动,而是将所有权传递给第一个被邀请的用户。

所以我想出了这个功能:

    def get_new_owner():
        try:
            invited_users = Event.objects.get(id=id).invites.order_by("-id").filter(is_active=True)
            if invited_users.exists():
                return invited_users.first()
            else:
                Event.objects.get(id=id).delete()
        except ObjectDoesNotExist:
            pass

这会找到 Event 实例,并返回按 Invite 表 ID 排序的活动受邀用户,因此我可以获取此查询集的第一项,它对应于第一个受邀用户。

为了在用户被删除时运行该函数,我使用了 on_delete=models.SET:

    owner = models.ForeignKey(User, related_name='evemt_owner', verbose_name=_("Owner"), on_delete=models.SET(get_new_owner()))

然后我遇到了一些问题:

  1. 它无法访问我正在传递的字段的 ID
  2. 我找不到将它用作类方法之类的方法,所以我不得不将函数放在模型之上。显然这意味着它无法再访问它下面的类,所以我尝试将 Event 模型作为函数的参数传递,但无法使其工作。

有什么想法吗?

【问题讨论】:

    标签: python django class django-models instance


    【解决方案1】:

    首先,我们可以为Owner 字段定义一个策略,该策略将使用已更新的对象调用函数。我们可以定义这样的删除,例如在<i.app_name/deletion.py文件中:

    # app_name/deletion.py
    
    def SET_WITH(value):
        if callable(value):
            def set_with_delete(collector, field, sub_objs, using):
                for obj in sub_objs:
                    collector.add_field_update(field, value(obj), [obj])
        else:
            def set_with_delete(collector, field, sub_objs, using):
                collector.add_field_update(field, value, sub_objs)
        set_with_delete.deconstruct = lambda: ('app_name.SET_WITH', (value,), {})
        return set_with_delete

    您应该将 callable 传递给SET,而不是调用该函数,因此您将其实现为:

    from django.conf import settings
    from django.db.models import Q
    from app_name.deletion import SET_WITH
    
    def get_new_owner(event):
        invited_users = event.invites.order_by(
            'eventinvites__id'
        ).filter(~Q(pk=event.owner_id), is_active=True).first()
        if invited_users is not None:
            return invited_users
        else:
            event.delete()
    
    
    class Event(models.Model):
        # …
        owner = models.ForeignKey(
            settings.AUTH_USER_MODEL,
            related_name='owned_events',
            verbose_name=_('Owner'),
            on_delete=models.SET_WITH(get_new_owner)
        )

    因此,我们将在此查看邀请以找到将对象转移到的用户。也许您需要从.inivites 的集合中排除get_new_owner 中事件的当前.owner

    作为@AbdulAzizBarkat says,我们可以更好地使用 CASCADE,而不是显式删除 Event 对象,因为这将避免无限递归,其中 User 删除触发 Event 删除可能会触发 User删除:目前这是不可能的,但稍后如果实施额外的逻辑,可能会在这种情况下结束。在这种情况下,我们可以使用:

    from django.db.models import CASCADE
    
    def SET_WITH(value):
        if callable(value):
            def set_with_delete(collector, field, sub_objs, using):
                for obj in sub_objs:
                    val = value(obj)
                    if val is None:
                        CASCADE(collector, field, [obj], using)
                    else:
                        collector.add_field_update(field, val, [obj])
        else:
            def set_with_delete(collector, field, sub_objs, using):
                collector.add_field_update(field, value, sub_objs)
        set_with_delete.deconstruct = lambda: ('app_name.SET_WITH', (value,), {})
        return set_with_delete

    并将get_new_owner 重写为:

    def get_new_owner(event):
        invited_users = event.invites.order_by(
            'eventinvites__id'
        ).filter(~Q(pk=event.owner_id), is_active=True).first()
        if invited_users is not None:
            return invited_users
        else:  # strictly speaking not necessary, but explicit over implicit
            return None

    【讨论】:

    • 也许删除不应该由get_new_owner直接完成,而应该调用CASCADE,这样就不会发生奇怪的循环或类似情况?
    • @AbdulAzizBarkat:是的,确实如此。在这种情况下,如果get_new_owner 返回None,则SET_WITH 应该这样做,因为我们需要访问收集器等,而使用SET_WITH,我们没有这样的收集器。跨度>
    猜你喜欢
    • 2014-07-19
    • 1970-01-01
    • 2021-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-16
    相关资源
    最近更新 更多