【问题标题】:How to make unique_together both ways?如何使 unique_together 双向?
【发布时间】:2020-03-15 09:38:02
【问题描述】:

我正在创建一个朋友模型来保存用户和他的朋友。我正在使用以下模型:

class Friend(models.Model):
    user = models.ForeignKey(UserProfile, related_name="user", on_delete=models.CASCADE)
    friends = models.ForeignKey(UserProfile, related_name="friends", on_delete=models.CASCADE)
    accepted = models.NullBooleanField(default=None)

    class Meta:
        unique_together = ('user', 'friends')

在这个模型中,我可以保存 ('User1', 'User2') 和 ('User2, 'User1'),这不是我想要的。那么,如何使这种关系独一无二?

【问题讨论】:

标签: django django-models


【解决方案1】:

不幸的是,你不能。

我在类似情况下得到了以下解决方案:


class Friendship(BaseModel):
    sender = models.ForeignKey(to='users.User',
                               on_delete=models.PROTECT,
                               related_name='sent_friendships',
                               verbose_name=_('Sender'))
    receiver = models.ForeignKey(to='users.User',
                                 on_delete=models.PROTECT,
                                 related_name='received_friendships',
                                 verbose_name=_('Receiver'))
    accepted = models.NullBooleanField(_('Accepted'), default=None)
    sender_noticed_result = models.BooleanField(_('Sender noticed result'), default=False,
                                                help_text=_('Shows that sender seen that receiver '
                                                            'accepted/declined the friendship'))

    def __str__(self):
        return f'{self.sender} to {self.receiver}'

    class Meta:
        ordering = ['id']

    def clean(self):
        if (self.__class__.objects
                .filter(Q(sender_id=self.receiver_id, receiver_id=self.sender_id)
                        | Q(sender_id=self.sender_id, receiver_id=self.receiver_id))
                .exclude(id=self.id).exists()):
            raise ValidationError(_(f"Friendship already declared between {self.sender} and {self.receiver}"))
        if self.sender_id == self.receiver_id:
            raise ValidationError(_("Can't declare friendship to yourself"))
        if self.sender_noticed_result and self.accepted is None:
            raise ValidationError(_("Can't notice not yet accepted/declined friendship"))

    def save(self, **kwargs):
        self.full_clean()
        super().save(**kwargs)

【讨论】:

  • 嗨,你能解释一下 def clean() 的第一个 if 条件吗?
  • 如果好友关系不是当前好友,其中 2 个用户中的一个向其他用户发送好友请求,我们会提出验证错误,说明其中一个用户已经提议另一个用户成为好友。我们只想要 2 个用户之间的一个友谊关系,不管是谁声明的。所以它在应用层几乎都有unique_together
  • 如果你仔细看,它的意思是(Q(user1=user1, user2=user2) or Q(user1=user2, user2=user1))
【解决方案2】:

@serializers.py

class FriendshipCreateSerializer(serializers.ModelSerializer):
class Meta:
    model = Friendship
    fields = [ 'id', 'sender', 'receiver']
    # you can use Serializer default validator for dulplicate user invites
    validators = [
        UniqueTogetherValidator(
            queryset=Friendship.objects.all(),
            fields=('sender', 'receiver'),
            message=_("You already sent an invite to this user")
        )
    ]

# and validate for invitee(already invited) to not be able to send a request
def validate(self, data):
    has_received_an_invite = Friendship.objects.all().filter(
        sender=data['receiver'], 
        receiver=data['sender']
    ).exists()
    if has_received_an_invite :
        raise serializers.ValidationError("User already sent you an invite.")
    return data

-- 两者都可以在 validate 方法中完成

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-09
    • 2021-08-31
    • 2017-04-01
    • 1970-01-01
    • 2019-09-10
    • 1970-01-01
    • 2010-12-08
    相关资源
    最近更新 更多