使用 Django 項目中的 ORM 編寫偽造測試數據腳本

使用 Django 項目中的 ORM 編寫偽造測試數據腳本

作者:HelloGitHub-追夢人物

為了防止博客首頁展示的文章過多以及提升加載速度,可以對文章列表進行分頁展示。不過這需要比較多的文章才能達到分頁效果,但本地開發時一般都只有幾篇測試文章,如果一篇篇手工添加將會非常麻煩。

解決方案是我們可以寫一個腳本,自動生成任意數量的測試數據。腳本寫好後,只需運行腳本就可以往數據庫填充大量測試數據。腳本就是一段普通的 Python 代碼,非常簡單,但是通過這個腳本你將學會如何在 django 外使用 ORM,而不僅僅在 django 應用的內部模塊使用。

腳本目錄結構

一般習慣於將項目有關的腳本統一放在項目根目錄的>

依據慣例,我們博客項目中腳本的目錄結構如下:

HelloDjango-blog-tutorial\\
blog\\
blogproject\\
...
/> __init__.py
fake.py
md.sample

其中 fake.py 是生成測試數據的腳本,md.sample 是一個純文本文件,內容是用於測試 Markdown 的文本。

使用 Faker 快速生成測試數據

博客文章包含豐富的內容元素,例如標題、正文、分類、標籤。如果手工輸入這些相關元素的文本會非常耗時,我們將藉助一個 Python 的第三方庫 Faker[3] 來快速生成這些測試用的文本內容。Faker 意為造假工廠,顧名即可思義。

首先安裝 Faker:

$ pipenv install Faker

Faker 通過不同的 Provider 來提供各種不同類型的假數據,我們將在下面的腳本中講解它的部分用法,完整的用法可以參考其官方文檔[4]

批量生成測試數據

現在我們來編寫一段 Python 腳本用於自動生成博客測試數據。思路非常簡單,博客內容包括作者、分類、標籤、文章等元素,只需依次生成這些元素的內容即可。當然為了使腳本能夠正常運行,很多細節需要注意,我們會對需要注意的地方進行詳細講解。

先來看腳本 fake.py 開頭的內容:

import os
import pathlib
import random
import sys
from datetime import timedelta
import django
import faker
from django.utils import timezone
# 將項目根目錄添加到 Python 的模塊搜索路徑中
back = os.path.dirname
BASE_DIR = back(back(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)

這一段很簡單,只是導入一些會用到的模塊,然後通過腳本所在文件找到項目根目錄,將根目錄添加到 Python 的模塊搜索路徑中,這樣在運行腳本時 Python 才能夠找到相應的模塊並執行。

接下來是腳本的邏輯,先看第一段:

if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "blogproject.settings.local")
django.setup()
from blog.models import Category, Post, Tag

from comments.models import Comment
from django.contrib.auth.models import User

這是整個腳本最為重要的部分。首先設置 DJANGO_SETTINGS_MODULE 環境變量,這將指定 django 啟動時使用的配置文件,然後運行 django.setup() 啟動 django。這是關鍵步驟,只有在 django 啟動後,我們才能使用 django 的 ORM 系統。django 啟動後,就可以導入各個模型,以便創建數據。

接下來的邏輯就很簡單了,不斷生成所需的測試數據即可,我們來一段一段地看:

 print('clean database')
Post.objects.all().delete()
Category.objects.all().delete()
Tag.objects.all().delete()
Comment.objects.all().delete()
User.objects.all().delete()

這一段腳本用於清除舊數據,因此每次運行腳本,都會清除原有數據,然後重新生成。

 print('create a blog user')
user = User.objects.create_superuser('admin', '[email protected]', 'admin')
category_list = ['Python學習筆記', '開源項目', '工具資源', '程序員生活感悟', 'test category']
tag_list = ['django', 'Python', 'Pipenv', 'Docker', 'Nginx', 'Elasticsearch', 'Gunicorn', 'Supervisor', 'test tag']
a_year_ago = timezone.now() - timedelta(days=365)
print('create categories and tags')
for cate in category_list:
Category.objects.create(name=cate)
for tag in tag_list:
Tag.objects.create(name=tag)
print('create a markdown sample post')
Post.objects.create(
title='Markdown 與代碼高亮測試',

body=pathlib.Path(BASE_DIR).joinpath('scripts', 'md.sample').read_text(encoding='utf-8'),
category=Category.objects.create(name='Markdown測試'),
author=user,
)

這個腳本沒什麼說的,簡單地使用 django 的 ORM API 生成博客用戶、分類、標籤以及一篇 Markdown 測試文章。

 print('create some faked posts published within the past year')
fake = faker.Faker() # English
for _ in range(100):
tags = Tag.objects.order_by('?')
tag1 = tags.first()
tag2 = tags.last()
cate = Category.objects.order_by('?').first()
created_time = fake.date_time_between(start_date='-1y', end_date="now",
tzinfo=timezone.get_current_timezone())
post = Post.objects.create(
title=fake.sentence().rstrip('.'),
body='\\n\\n'.join(fake.paragraphs(10)),
created_time=created_time,
category=cate,
author=user,
)
post.tags.add(tag1, tag2)
post.save()

這段腳本用於生成 100 篇英文博客文章。博客文章通常內容比較長,因此我們使用了之前提及的 Faker 庫來自動生成文本內容。腳本邏輯很清晰,只對其中涉及的幾個知識點進行講解:

使用 Django 項目中的 ORM 編寫偽造測試數據腳本

 fake = faker.Faker('zh_CN')
for _ in range(100): # Chinese
tags = Tag.objects.order_by('?')
tag1 = tags.first()
tag2 = tags.last()
cate = Category.objects.order_by('?').first()
created_time = fake.date_time_between(start_date='-1y', end_date="now",
tzinfo=timezone.get_current_timezone())
post = Post.objects.create(
title=fake.sentence().rstrip('.'),
body='\\n\\n'.join(fake.paragraphs(10)),
created_time=created_time,
category=cate,
author=user,
)
post.tags.add(tag1, tag2)
post.save()

這一段腳本和上一段幾乎完全一樣,唯一不同的是構造 Faker 實例時,傳入了一個語言代碼 zh_CN,這將生成中文的虛擬數據,而不是默認的英文。

 print('create some comments')
for post in Post.objects.all()[:20]:
post_created_time = post.created_time
delta_in_days = '-' + str((timezone.now() - post_created_time).days) + 'd'
for _ in range(random.randrange(3, 15)):
Comment.objects.create(
name=fake.name(),
email=fake.email(),
url=fake.uri(),
text=fake.paragraph(),
created_time=fake.date_time_between(
start_date=delta_in_days,
end_date="now",
tzinfo=timezone.get_current_timezone()),
post=post,
)
print('done!')

最後依葫蘆畫瓢,給前 20 篇文章(Post) 生成評論數據。要注意的是評論的發佈時間必須位於被評論文章的發佈時間和當前時間之間,這就是 delta_in_days = '-' + str((timezone.now() - post_created_time).days) + 'd' 這句代碼的作用。

執行腳本

腳本寫好了,在項目根目錄執行下面的命令運行整個腳本:

$ pipenv run python -m/>

看到如下的輸出說明腳本執行成功了。

clean database
create a blog user
create categories and tags
create a markdown sample post
create some faked posts published within the past year
create some comments
done!

運行開發服務器,訪問博客首頁可以看到生成的測試數據,是不是有點以假亂真的感覺?


使用 Django 項目中的 ORM 編寫偽造測試數據腳本


現在,我們有了 200 多篇測試文章,用來測試分頁效果就十分簡單了,接下來讓我們來實現功能完整的分頁效果。

[1]HelloGitHub-追夢人物: https://www.zmrenwu.com

[2]HelloGitHub-Team 倉庫: https://github.com/HelloGitHub-Team/HelloDjango-blog-tutorial

[3]Faker: https://github.com/joke2k/faker

[4]官方文檔: http://faker.rtfd.org/


『講解開源項目系列』——讓對開源項目感興趣的人不再畏懼、讓開源項目的發起者不再孤單。跟著我們的文章,你會發現編程的樂趣、使用和發現參與開源項目如此簡單。歡迎留言聯繫我們、加入我們,讓更多人愛上開源、貢獻開源~


分享到:


相關文章: