ManyToManyField and Reverse Relations:

From this step, we gonna handle “Following”, it requires many to many relationships.

pick up tweets of a user and tweets liked by that user:

  • add a related_name for user in /tweets/models.py ```python class Tweet(models.Model):

    Maps to SQL data

    id = models.AutoField(primary_key=True)

    parent = models.ForeignKey(“self”, null=True, on_delete=models.SET_NULL)

    user = models.ForeignKey(User, on_delete=models.CASCADE) # many users can many tweets

    user = models.ForeignKey(
    1. User, on_delete=models.CASCADE, related_name="tweets") # new
    likes = models.ManyToManyField(
    1. User, related_name='tweet_user', blank=True, through=TweetLike)
    content = models.TextField(blank=True, null=True) image = models.FileField(upload_to=’images/‘, blank=True, null=True) timestamp = models.DateTimeField(auto_now_add=True)

  1. - cause we changed model, makemigrations and migrate
  2. ```bash
  3. [root@localhost Twittme]# python3 ./manage.py makemigrations
  4. Migrations for 'tweets':
  5. tweets/migrations/0002_auto_20200728_0239.py
  6. - Alter field user on tweet
  7. [root@localhost Twittme]# python3 ./manage.py migrate
  8. Operations to perform:
  9. Apply all migrations: admin, auth, contenttypes, profiles, sessions, tweets
  10. Running migrations:
  11. Applying tweets.0002_auto_20200728_0239... OK
  • use python3 shell to test something

    1. [root@localhost Twittme]# python3 ./manage.py shell
    2. Python 3.7.3 (default, Jun 17 2020, 18:21:41)
    3. [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
    4. Type "help", "copyright", "credits" or "license" for more information.
    5. (InteractiveConsole)
    6. >>> from django.contrib.auth import get_user_model
    7. >>> User = get_user_model()
    8. >>> f = User.objects.first()
    9. >>> f
    10. <User: root>
    11. >>> f.tweets.all()
    12. <QuerySet [<Tweet: Tweet object (131)>, <Tweet: Tweet object (130)>, <Tweet: Tweet object (129)>, <Tweet: Tweet object (128)>, <Tweet: Tweet object (127)>, <Tweet: Tweet object (126)>, <Tweet: Tweet object (125)>, <Tweet: Tweet object (124)>, <Tweet: Tweet object (123)>, <Tweet: Tweet object (122)>, <Tweet: Tweet object (121)>, <Tweet: Tweet object (120)>, <Tweet: Tweet object (119)>, <Tweet: Tweet object (118)>, <Tweet: Tweet object (117)>, <Tweet: Tweet object (116)>, <Tweet: Tweet object (115)>, <Tweet: Tweet object (114)>, <Tweet: Tweet object (113)>, <Tweet: Tweet object (112)>, '...(remaining elements truncated)...']>
    13. >>> f.tweetlike_set.all()
    14. <QuerySet [<TweetLike: TweetLike object (8)>, <TweetLike: TweetLike object (9)>, <TweetLike: TweetLike object (10)>, <TweetLike: TweetLike object (11)>, <TweetLike: TweetLike object (44)>, <TweetLike: TweetLike object (46)>, <TweetLike: TweetLike object (53)>]>
    15. >>> my_likes = [x.tweet for x in f.tweetlike_set.all()]
    16. >>> my_likes
    17. [<Tweet: Tweet object (51)>, <Tweet: Tweet object (49)>, <Tweet: Tweet object (50)>, <Tweet: Tweet object (48)>, <Tweet: Tweet object (118)>, <Tweet: Tweet object (120)>, <Tweet: Tweet object (126)>]
    18. >>> len(my_likes)
    19. 7
    20. >>> f.tweets.count()
    21. 127
    22. >>> f.tweet_user.all()
    23. <QuerySet [<Tweet: Tweet object (126)>, <Tweet: Tweet object (120)>, <Tweet: Tweet object (118)>, <Tweet: Tweet object (51)>, <Tweet: Tweet object (50)>, <Tweet: Tweet object (49)>, <Tweet: Tweet object (48)>]>
    24. >>> len(f.tweet_user.all())
    25. 7
    26. >>> f.tweet_user.count()
    27. 7

    we got our use “root” as “f” here, and at this point:

    • root has sent 127 tweets
      • f.tweets.all()
        • , , , , , , , , , , , , , , , , , , , , ‘…(remaining elements truncated)…’]>
        • OR

f.tweet_set.all()
before we change in related_name of “user” /tweets/models.py

  • root liked 7 tweets
    • like objects are
      • f.tweetlike_set.all()
      • , , , , , , ]>
      • Tweetlike objects will be deleted if we hit unlike, so it don’t mena that root liked tweets with id 8, 9, 10, 11…etc.
    • actually liked tweets are
      • f.tweet_user.all()
      • related_name of likes in /tweets/models.py is “tweet_user”
      • , , , , , , ]>
      • so root actually liked tweets with id 48, 49, 50, 51, 118, 120, 126.

test many to many relationship:

  • apply methods above, add and change test cases in /tweets/tests.py ```python

    new

    def test_tweets_related_name(self): # is the number of tweets related to a user correct

    1. user = self.user
    2. self.assertEqual(user.tweets.count(), 2) # we have created 2 tweets for user before

    def test_action_like(self): # is the like action done successfully

    1. client = self.get_client()
    2. response = client.post("/api/tweets/action/",
    3. {"id": 1, "action": "like"})
    4. self.assertEqual(response.status_code, 200)
    5. like_count = response.json().get("likes")
    6. self.assertEqual(like_count, 1)
    7. # new
    8. user = self.user
    9. my_like_instances_count = user.tweetlike_set.count()
    10. self.assertEqual(my_like_instances_count, 1) # we have created 1 like object for user

  1. use terminal command to test:
  2. ```bash
  3. [root@localhost Twittme]# python3 ./manage.py test
  4. Creating test database for alias 'default'...
  5. System check identified no issues (0 silenced).
  6. ...cfe
  7. ......
  8. ----------------------------------------------------------------------
  9. Ran 9 tests in 10.459s
  10. OK
  11. Destroying test database for alias 'default'...

all tests passed.

  • test is that count of related_likes equals count of like instances in /tweets/tests.py

    1. # ...
    2. def test_action_like(self): # is the like action done successfully
    3. client = self.get_client()
    4. response = client.post("/api/tweets/action/",
    5. {"id": 1, "action": "like"})
    6. self.assertEqual(response.status_code, 200)
    7. like_count = response.json().get("likes")
    8. self.assertEqual(like_count, 1)
    9. user = self.user
    10. my_like_instances_count = user.tweetlike_set.count()
    11. self.assertEqual(my_like_instances_count, 1) # we have created 1 like object for user
    12. # new
    13. my_related_likes = user.tweet_user.count()
    14. self.assertEqual(my_like_instances_count, my_related_likes) # is the number of related liked tweets equals count of like instances
    15. # ...

    use terminal command to test: ```bash [root@localhost Twittme]# python3 ./manage.py test Creating test database for alias ‘default’… System check identified no issues (0 silenced). …cfe ……


Ran 9 tests in 7.546s

OK Destroying test database for alias ‘default’… ```