保存自定义用户模型时 Django ManyToMany 覆盖

Posted

技术标签:

【中文标题】保存自定义用户模型时 Django ManyToMany 覆盖【英文标题】:Django ManyToMany overwritten when saving CustomUser model 【发布时间】:2020-08-07 17:07:01 【问题描述】:

概述

我正在创建一个友谊模型来跟踪相互关注的用户。我创建了一个扩展 AbstractUser 的 CustomUser 模型和一个用于创建/跟踪交互的单独的 UserRelationship 模型。我还有一个 signal.py 文件,用于处理两个模型(CustomUser 和 UserRelationship)之间的实际保存。

问题

创建友谊时(PK=1 的用户关注 PK=2 的用户),PK=1 的用户的 Follow ManyToMany 字段会正确更新,但是如果 PK=1 的用户更新他们的个人资料,所有 ManyToMany 字段都是擦。

自定义用户模型

class CustomUser(AbstractUser):
    # Model manager
    objects = CustomUserManager()
    # Local
    email = models.CharField(max_length=80, blank=True)
    biography = models.CharField(max_length=100, blank=True)
    photo = models.ImageField(
        upload_to="users/%Y/%m/%d/",
        blank=True,
        default="static/images/default_profile_pic.png",
    )
    hidden = models.BooleanField(default=False)
    # ForeignKey
    following = models.ManyToManyField(
        "CustomUser", related_name="followings", blank=True
    )
    follower = models.ManyToManyField(
        "CustomUser", related_name="followers", blank=True
    )
    blocked = models.ManyToManyField("CustomUser", related_name="blocks", blank=True)
    blocked_user_visible = models.ManyToManyField(
        "CustomUser", related_name="block_user_visible", blank=True
    )
    hidden_request = models.ManyToManyField(
        "CustomUser", related_name="hidden_requests", blank=True
    )

    def get_relationships(self):
        return self.relationship.all()

    def get_following(self):
        return self.followings.all()

    def get_relationships_number(self):
        return self.relationship.all().count()

    def __str__(self):
        return self.username

    def get_absolute_url(self):
        return f"/users/self.username"

用户关系模型

class UserRelationship(models.Model):
    STATUS_CHOICES = (
        ("sent", "sent"),
        ("accepted", "accepted"),
        ("deleted", "deleted"),
        ("blocked", "blocked"),
    )
    # Local
    status = models.CharField(max_length=8, choices=STATUS_CHOICES, blank=True)
    updated = models.DateTimeField(auto_now=True)
    date_added = models.DateTimeField(auto_now_add=True)
    # ForeignKey
    # You have received a request from
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="from_user"
    )
    # You have sent a request to
    following = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        null=True,
        related_name="to_user",
    )

    def __str__(self):
        return f"self.user sent a friend request to self.following, status == self.status"

示例:创建 UserRelationship 后的 JSON


    "url": "http://localhost:8000/api/v1/users/1/",
    "pk": 1,
    "username": "test",
    "first_name": "removed for***",
    "last_name": "removed for***",
    "email": "removed for***",
    "hidden": false,
    "following": [
        44
    ],
    "follower": [],
    "blocked": [],
    "hidden_request": [],
    "photo": "https://azure.blob.core.windows.net/media/users/2020/06/28/test-36b9cdd1da48477dbb738b2f63ffca5f.jpg",
    "biography": "Friday Aug 7"

示例:更新用户传记字段后的 JSON 供用户测试(以下不再显示用户的 pk "44")


    "url": "http://localhost:8000/api/v1/users/1/",
    "pk": 1,
     "username": "test",
    "first_name": "removed for***",
    "last_name": "removed for***",
    "email": "removed for***",
    "hidden": false,
    "following": [],
    "follower": [],
    "blocked": [],
    "hidden_request": [],
    "photo": "https://azure.blob.core.windows.net/media/users/2020/06/28/test-36b9cdd1da48477dbb738b2f63ffca5f.jpg",
    "biography": "This was updated"

Signals.py

@receiver(post_save, sender=UserRelationship)
def add_friend(sender, instance, created, **kwargs):
    """Signal function that responds to status of either
    accepted, sent, blocked"""
    sender_ = instance.user
    receiver_ = instance.following

    if instance.status == "accepted":
        # If receiver's pk is already in sender's following M2M
        if sender_.following.filter(pk=receiver_.pk).exists():
            # Print (turn to HttpResponse)
            print(f"You're already friends with receiver_.username")
        # If sender's status is hidden
        if receiver_.hidden:
            instance.status = "sent"
            # Add sender to receiver's hidden_request M2M (for notification & approvals)
            receiver_.hidden_request.add(sender_.pk)
            receiver_.save()
        else:
            # Else sender_ now follows the receiver
            sender_.following.add(receiver_.pk)
            # Receiver_ also now follows the sender
            # Optional (Facebook style friendship, remove for twitter style friendship)
            # receiver_.follower.add(sender_.pk)
            # Save sender_
            sender_.save()
            # Save receiver_
            receiver_.save()

编辑 我忘了提一下,我正在使用管理门户创建 UserRelationship,并且我还使用 Django-Rest-Framework 来更新 PK=1 的用户的传记字段。我还尝试在signals.py中删除add_friend函数的else块中的保存部分,只留下添加部分,结果是一样的。

【问题讨论】:

【参考方案1】:

我通过将 PUT 更改为 PATCH 解决了这个问题

【讨论】:

以上是关于保存自定义用户模型时 Django ManyToMany 覆盖的主要内容,如果未能解决你的问题,请参考以下文章

如何在用户导航期间在 Django 中保存自定义用户模型

使用注册表单添加到自定义用户字段(django)

django-rest-auth 自定义注册无法保存额外字段

如何在 Django 会话模型中设置自定义字段?

Django - 自定义模型保存方法不显示属性值

django模型中的自定义保存方法