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

做者:HelloGitHub-追夢人物python

文中所涉及的示例代碼,已同步更新到 HelloGitHub-Team 倉庫git

爲了防止博客首頁展現的文章過多以及提高加載速度,能夠對文章列表進行分頁展現。不過這須要比較多的文章才能達到分頁效果,但本地開發時通常都只有幾篇測試文章,若是一篇篇手工添加將會很是麻煩。程序員

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

腳本目錄結構

通常習慣於將項目有關的腳本統一放在項目根目錄的 scripts 包中,固然這只是一個慣例,你也能夠採用本身以爲合理的目錄結構,只要保證這個包所在目錄可以被 Python 找到。數據庫

依據慣例,咱們博客項目中腳本的目錄結構以下:django

HelloDjango-blog-tutorial\
	blog\
	blogproject\
	...
	scripts\
		__init__.py
		fake.py
		md.sample
複製代碼

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

使用 Faker 快速生成測試數據

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

首先安裝 Faker:服務器

$ pipenv install Faker
複製代碼

Faker 經過不一樣的 Provider 來提供各類不一樣類型的假數據,咱們將在下面的腳本中講解它的部分用法,完整的用法能夠參考其官方文檔markdown

批量生成測試數據

如今咱們來編寫一段 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', 'admin@hellogithub.com', '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 庫來自動生成文本內容。腳本邏輯很清晰,只對其中涉及的幾個知識點進行講解:

  • fake = faker.Faker(),要使用 Faker 自動生成數據,首先實例化一個 Faker 對象,而後咱們能夠在腳本中使用這個實例的一些方法生成須要的數據。Faker 默認生成英文數據,但也支持國際化。至於如何生成中文數據在下一段腳本中會看到。

  • order_by('?') 將返回隨機排序的結果,腳本中這塊代碼的做用是達到隨機選擇標籤(Tag) 和分類(Category) 的效果。

  • 而後就是 2 個 Faker 的 API 了:

    • fake.date_time_between

      這個方法將返回 2 個指定日期間的隨機日期。三個參數分別是起始日期,終止日期和時區。咱們在這裏設置起始日期爲 1 年前(-1y),終止日期爲當下(now),時區爲 get_current_timezone 返回的時區,這個函數是 django.utils.timezone 模塊的輔助函數,它會根據 django 設置文件中 TIME_ZONE 的值返回對應的時區對象。

    • '\n\n'.join(fake.paragraphs(10))

      fake.paragraphs(10) 用於生成 10 個段落文本,以列表形式返回,列表的每一個元素即爲一個段落。要注意使用 2 個換行符連起來是爲了符合 Markdown 語法,Markdown 中只有 2 個換行符分隔的文本纔會被解析爲段落。

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 scripts.fake
複製代碼

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

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!
複製代碼

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

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


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

相關文章
相關標籤/搜索