UMR
UMR

Reputation: 361

Prevent an entry in model from adding itself in many2many relationship

I want to prevent a user from following themselves in Django. The User model is defined like bellow:

class User(AbstractUser):
    followers = models.ManyToManyField(‘self’, related_name=“following”, symmetrical= False)

Changing save() method in the User model doesn’t help since add() method doesn’t call save(). The seems to be a solution using m2m_changed or UniqueConstraint but I don’t know how to implement them. How can I solve this problem?

Upvotes: 1

Views: 313

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477160

A UniqueConstraint will not help since this will only enforce that you do not follow the same user twice.

What you can do is construct a through=… model [Django-doc], and enforce that the follower and the followee are not the same:

from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import F, Q

class User(AbstractUser):
    # …
    followers = models.ManyToManyField(
        'self',
        related_name='following',
        symmetrical=False,
        through='Follow',
        through_fields=('followee', 'follower'),
    )

In the Follow model we then enforce that follower and followee can not be the same:

class Follow(models.Model):
    followee = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='followee_set'
    )
    follower = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='following_set'
    )

    def clean(self, *args, **kwargs):
        if self.follower_id == self.followee_id:
            raise ValidationError('Can not follow self.')
        return super().clean(*args, **kwargs)
    
    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['follower', 'followee'], name='follow_once'),
            models.CheckConstraint(check=~Q(follower=F('followee')), name='not_follow_self')
        ]

Not all databases however enforce the CheckConstraint. Therefore it might be better to implement a clean method as well.

Upvotes: 1

Related Questions