Which database design is better for django follow/unfollow?

Viewed 90

I've been looking for a good database design for a twitter like social network site in my django project and I found two possibilities: This one down here

class Following(models.Model):
    follower = models.ForeignKey(User, on_delete=models.CASCADE,
                                 related_name='following')
    following = models.ForeignKey(User, on_delete=models.CASCADE,
                                  related_name='followers')

And this other one

class User(AbstractUser):
    follows = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='followed_by')
    pass

Are these the same? Is there any difference here? Which one should I choose? I'm kind of new to this so I can`t figure out which one is the best option. I find the first one easier to understand.

2 Answers

I would suggest both models code will work fine.

If you want to create custom user model with new fields then use below code format.

  • AbstractUser: Use existing fields in the user model
  • AbstractBaseUser:In case want to create your own user model from scratch

class User(AbstractUser):
    follows = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='followed_by')
    pass

You want to segregate your app related changes then use below models code.

class Following(models.Model):
    follower = models.ForeignKey(User, on_delete=models.CASCADE,
                                 related_name='following')
    following = models.ForeignKey(User, on_delete=models.CASCADE,
                                  related_name='followers')

If I add this to my user model

following = models.ManyToManyField('self', related_name="followers")

and run (assuming auth is the app where your user model is, and replacing 000X by the number of the generated migration)

python manage.py makemigrations auth
python manage.py sqlmigrate auth 000X

this is what I get:

CREATE TABLE `auth_user_following` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, 
    `from_user_id` integer NOT NULL, `to_user_id` integer NOT NULL);
ALTER TABLE `auth_user_following` ADD CONSTRAINT `auth_user__from_user_id_b9318b74_fk_auth_` 
    FOREIGN KEY (`from_user_id`) REFERENCES `auth_user` (`id`);
ALTER TABLE `auth_user_following` ADD CONSTRAINT `auth_user__to_user_id_b51bc961_fk_auth_` 
    FOREIGN KEY (`to_user_id`) REFERENCES `auth_user` (`id`);
ALTER TABLE `auth_user_following` ADD CONSTRAINT `auth_user_foll_from_user_id_to_au_88cd5a29_uniq` 
    UNIQUE (`from_user_id`, `to_user_id`);

So it creates a table with an auto-generated id and two foreign key columns, just as it would do with the explicit relation-only model, i.e. on the database side, there is no structural difference.

For code readability, I would much prefer to keep the relation in the model and not define it in a different class. However, if you want to add additional data to the relation (e.g. date_started_following), you will need an explicit relation model. Then, you might still want to mention this many-to-many-relation in your user model and point to the explicit relation using the through argument:

However, sometimes you may need to associate data with the relationship between two models. [...] Django allows you to specify the model that will be used to govern the many-to-many relationship. You can then put extra fields on the intermediate model. The intermediate model is associated with the ManyToManyField using the through argument to point to the model that will act as an intermediary.

One other reason for the first approach or an explicit through model is that it might facilitate some queries about the relationship, e.g. "find users who follow each other".

Related