本练习重点要掌握的地方
1. 根据项目描述,定义数据模型,数据模型的设计有原则,要符合数据库的三范式,要尽可能的解耦。
2. 熟悉表的外键关联
3. ORM框架
4. 根据业务需求,在Django框架中添加索引,以及唯一约束。
5. 借助Django框架编写单元测试,调试定义的模型。
LintCode 2427 实现推文模型
根据描述定义字段,只有三个字段,其中user字段是外键,使用Django框架定义好的,content是text,创建时间是日期类型,使用自动添加模版。
from django.db import models
from django.contrib.auth.models import User
class Tweet(models.Model):
# write your code here
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
content = models.CharField(max_length=140)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.created_at} {self.user}: {self.content}'
让我们来过一遍一下单元测试:
from django.contrib.auth.models import User
from django.test import TestCase
from tweets.models import Tweet
from datetime import date
import re
class TweetTests(TestCase):
def test_tweet_model_attributes(self):
self.assertEqual(hasattr(Tweet, 'user'), True)
self.assertEqual(hasattr(Tweet, 'content'), True)
self.assertEqual(hasattr(Tweet, 'created_at'), True)
def test_tweet_model(self):
user = User.objects.create(username='linghu', password='god')
tweet = Tweet.objects.create(content='jiuzhangdafahao')
self.assertEqual(tweet.user, None)
self.assertEqual(tweet.created_at.day, date.today().day)
tweet.delete()
Tweet.objects.create(user=user, content='jiuzhangdafahao')
user.delete()
self.assertEqual(Tweet.objects.all().count(), 1)
self.assertEqual(Tweet.objects.first().user, None)
self.assertEqual(Tweet.content.field.max_length, 140)
def test_auto_now_add(self):
tweet: Tweet = Tweet.objects.create(content='jiuzhangdafahao')
old_created_time = tweet.created_at
tweet.content = 'updated tweet'
tweet.save()
self.assertEqual(old_created_time, tweet.created_at)
def test_str(self):
user = User.objects.create(username='linghu', password='god')
tweet: Tweet = Tweet.objects.create(
user=user, content='jiuzhangdafahao')
self.assertEqual(str(tweet.created_at) in str(tweet), True)
self.assertEqual(tweet.user.username in str(tweet), True)
self.assertEqual(tweet.content in str(tweet), True)
查看tweet发了多久
from django.db import models
from django.contrib.auth.models import User
from utils.time_helpers import utc_now
from datetime import datetime
class Tweet(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
content = models.CharField(max_length=140)
created_at = models.DateTimeField(auto_now_add=True)
@property
def hours_to_now(self):
return (utc_now() - self.created_at).seconds // 3600
2434 · 定义联合索引和默认的排序规则
为tweet添加索引,先按用户id排序,然后再按创建时间降序排序
from django.db import models
from django.contrib.auth.models import User
class Tweet(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
content = models.CharField(max_length=140)
created_at = models.DateTimeField(auto_now_add=True)
# write your code here
class Meta:
index_together = ('user', 'created_at')
ordering = ('user', '-created_at')
同样看一下如何测试排序
from django.test import TestCase
from tweets.models import Tweet
from datetime import datetime, timezone
import random
class TweetTests(TestCase):
def test_tweet_meta(self):
self.assertEqual(len(Tweet._meta.index_together), 1)
self.assertEqual(Tweet._meta.index_together[0], ('user', 'created_at'))
self.assertEqual(Tweet._meta.ordering, ('user', '-created_at'))
def test_tweet_ordering(self):
for i in range(10):
Tweet.objects.create(content='default tweet content')
tweets = Tweet.objects.all()
for index in range(len(tweets)-1):
self.assertEqual(
tweets[index].created_at > tweets[index+1].created_at, True)
random_tweet: Tweet = tweets[random.randint(
0, len(tweets)-1)]
random_tweet.created_at = datetime(
random.randint(2000, 2020), 6, 1, 0, 0, 0).replace(tzinfo=timezone.utc)
random_tweet.save()
tweets = Tweet.objects.all()
for index in range(len(tweets)-1):
self.assertEqual(
tweets[index].created_at > tweets[index+1].created_at, True)
定义评论模型
评论user和tweet作为外键,索引先按tweet排序,再按create_at排序
from django.db import models
from django.contrib.auth.models import User
from tweets.models import Tweet
class Comment(models.Model):
# write your code here
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
tweet = models.ForeignKey(Tweet, on_delete=models.SET_NULL, null=True)
content = models.CharField(max_length=140)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# write your code here
class Meta:
index_together = ('tweet', 'created_at')
def __str__(self):
return f"{self.created_at} - {self.user} says {self.content} at tweet {self.tweet_id}"
2430 · 实现好友关系模型
这个除了索引以外,还需要有一个唯一约束,即不能关注同一个人两次。
from django.db import models
from django.contrib.auth.models import User
class Friendship(models.Model):
# write your code here
from_user = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
related_name='following_friendship_set',
)
to_user = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
related_name='follower_friendship_set',
)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
index_together = (
('from_user_id', 'created_at'),
('to_user_id', 'created_at'),
)
unique_together = (('from_user_id', 'to_user_id'),)
def __str__(self):
return f'{self.from_user_id} followed {self.to_user_id}'
定义新鲜事模型
from django.db import models
from django.contrib.auth.models import User
from tweets.models import Tweet
class NewsFeed(models.Model):
# write your code here
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
tweet = models.ForeignKey(Tweet, on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
index_together = ('user','created_at')
unique_together = ('user', 'tweet')
ordering = ('user', '-created_at')
def __str__(self):
return f"{self.created_at} inbox of {self.user}: {self.tweet}"
定义点赞模型
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
class Like(models.Model):
# write your code here
content_type = models.ForeignKey(
ContentType,
on_delete=models.SET_NULL,
null=True,
)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)
# write your code here
class Meta:
index_together = ('content_type', 'object_id', 'created_at')
unique_together = ('content_type', 'object_id', 'user')
def __str__(self):
return f"{self.created_at} - {self.user} liked {self.content_type} {self.object_id}"