首页 > 编程问答 >tortoise.exceptions.OperationalError:运行测试时关系不存在

tortoise.exceptions.OperationalError:运行测试时关系不存在

时间:2024-07-27 16:31:59浏览次数:14  
标签:python fastapi tortoise-orm

我正在学习 TDD fastapi、docker 和 pytest 课程的第一部分。我遇到了一个奇怪的问题,需要您的帮助。

当我创建第一个使用 torotoise 的测试时,它工作正常,将记录添加到数据库,并从 fastapi 获取它,没有任何问题。

分钟我添加另一个测试(添加的测试,然后读取记录),我收到此错误:

tortoise.exceptions.OperationalError: relation "textsummery" does not exist

(请忽略“摘要”一词中的拼写错误,它一开始是一个错误,但我用它作为强迫自己的一种方式不要盲目地复制粘贴所有内容)

如果我删除第一个测试,这个错误现在就消失了,并且测试顺利进行

为什么它不只使用已经创建的表?

代码:

# C:\src\tdd-fastapi\project\test\conftest.py
@pytest.fixture(scope="module")
def test_app_with_db():
    # set up
    app = create_applications()
    app.dependency_overrides[get_settings] = get_settings_override

    register_tortoise(
        app,
        db_url=os.environ.get("DATABASE_TEST_URL"),
        modules={"models": ["app.models.tortoise"]},
        generate_schemas=True,
        add_exception_handlers=True,
    )

# C:\src\tdd-fastapi\project\test\test_summeries.py
def test_create_summery(test_app_with_db):
    response = test_app_with_db.post("/summeries/", data=json.dumps({"url": "https://foo.bar"}))

    assert response.status_code == 201
    assert response.json()["url"] == "https://foo.bar"
    assert response.json()["id"] != 0

# ...

def test_read_summery(test_app_with_db):
    response = test_app_with_db.post("/summeries/", data=json.dumps({"url": "https://foo.bar"}))

    assert response.status_code == 201
    assert response.json()["url"] == "https://foo.bar"
    assert response.json()["id"] != 0

    summery_id = response.json()["id"]

    response = test_app_with_db.get(f"/summeries/{summery_id}/")
    assert response.status_code == 200

    response_dict = response.json()
    assert response_dict["id"] == summery_id
    assert response_dict["url"] == "https://foo.bar"
    assert response_dict["summery"]
    assert response_dict["created_at"]

根据评论建议,我添加了 torotoise 和 testconf

from tortoise import fields, models
from tortoise.contrib.pydantic import pydantic_model_creator
# C:\src\tdd-fastapi\project\app\models\tortoise.py
class TextSummery(models.Model):
    url = fields.TextField()
    summery = fields.TextField()
    created_at = fields.DatetimeField(auto_now_add=True)
    updated_at = fields.DatetimeField(auto_now=True)

    def __str__(self):
        return self.url

SummerySchema = pydantic_model_creator(TextSummery)

# C:\src\tdd-fastapi\project\test\conftest.py
import os

import pytest
from starlette.testclient import TestClient

from app.main import create_applications
from app.config import get_settings, Settings
from tortoise.contrib.fastapi import register_tortoise


def get_settings_override() -> Settings:
    return Settings(testing=1, database_url=os.environ.get('DATABASE_TEST_URL'))

@pytest.fixture(scope='module')
def test_app():
    # setup
    app = create_applications()
    app.dependency_overrides[get_settings] = get_settings_override

    with TestClient(app) as test_client:

        yield test_client

@pytest.fixture(scope="module")
def test_app_with_db():
    # set up
    app = create_applications()
    app.dependency_overrides[get_settings] = get_settings_override

    register_tortoise(
        app,
        db_url=os.environ.get("DATABASE_TEST_URL"),
        modules={"models": ["app.models.tortoise"]},
        generate_schemas=True,
        add_exception_handlers=True,
    )

    with TestClient(app) as test_client:

        # testing
        yield test_client

    # tear down

这个错误“tortoise.exceptions.OperationalError: relation "textsummery" does not exist”通常在你试图使用 Tortoise ORM 与你的数据库交互但数据库中还没有创建该表时出现。这在你运行测试时很常见,尤其是当你没有在测试之间正确设置和拆除你的数据库时。

以下是你遇到的问题的分步说明以及如何解决:

问题:

  1. 测试隔离: 你的测试应该彼此隔离,这意味着一个测试不应依赖于另一个测试的结果或状态。在你这个例子中,看起来你的第二个测试“test_read_summery”依赖于第一个测试“test_create_summery”创建的“textsummery”表。
  2. 数据库设置和拆除: 为了确保测试隔离,你需要为每个测试设置一个干净的数据库。这意味着你需要在每个测试之前创建“textsummery”表,并在测试之后将其删除。

解决方案:

你可以使用 Tortoise ORM 的测试实用程序来简化此过程。以下是如何修改你的 conftest.py 文件来为每个测试设置和拆除你的数据库:

import os
import pytest
from tortoise.contrib.fastapi import register_tortoise
from tortoise.contrib.test import finalizer, initializer
from starlette.testclient import TestClient

from app.main import create_applications  # 替换成你实际的应用导入
from app.config import get_settings, Settings
from app.models.tortoise import TextSummery  # 替换成你实际的模型导入


def get_settings_override() -> Settings:
    return Settings(testing=1, database_url=os.environ.get('DATABASE_TEST_URL'))

@pytest.fixture(scope='module')
async def test_app_with_db():
    """使用数据库的测试应用程序的固定装置。"""
    app = create_applications()
    app.dependency_overrides[get_settings] = get_settings_override

    # 从环境变量中获取测试数据库 URL
    db_url = os.environ.get("DATABASE_TEST_URL")

    # 使用测试数据库 URL 初始化 Tortoise ORM
    initializer(["app.models.tortoise"], db_url=db_url)

    # 为测试应用程序注册 Tortoise ORM
    register_tortoise(
        app,
        db_url=db_url,
        modules={"models": ["app.models.tortoise"]},
        generate_schemas=True,
        add_exception_handlers=True,
    )

    # 生成数据库架构
    await Tortoise.generate_schemas()

    with TestClient(app) as test_client:
        # 在这里屈服测试客户端以运行测试
        yield test_client

    # 在每个测试之后清理数据库
    await Tortoise._drop_databases()
    finalizer()

解释:

  • @pytest.fixture(scope="module") : 这确保了数据库设置和拆除在每个测试模块(文件)中只执行一次,从而使你的测试运行更快。
  • initializer(["app.models.tortoise"], db_url=db_url) : 这将使用你的测试数据库 URL 初始化 Tortoise ORM 并加载你的模型。确保将 "app.models.tortoise" 替换成你的模型模块的实际路径。
  • await Tortoise.generate_schemas() : 这会在你的测试数据库中创建表。
  • yield test_client : 这将测试客户端传递给你的测试函数。
  • await Tortoise._drop_databases() : 这会在每个测试模块之后删除数据库,确保一个干净的状态。
  • finalizer() : 这会清理 Tortoise ORM。

如何使用它:

只需将 test_app_with_db 固定装置传递给你的测试函数,就像这样:

def test_create_summery(test_app_with_db):
   # ...你的测试代码...

def test_read_summery(test_app_with_db):
   # ...你的测试代码...

这将确保在每个测试之前创建数据库并在之后将其删除,从而防止出现“关系不存在”错误并使你的测试更加可靠。

请记住将占位符值(如数据库 URL 和模型路径)替换成你的实际配置。

标签:python,fastapi,tortoise-orm
From: 70138399

相关文章

  • Python 与 Visual Studio Professional 2022(64 位)- 预览版本 5.0 交互窗口挂起
    我正在MicrosoftVisualStudioProfessional2022(64位)-预览版17.11.0预览版5.0上运行Python开发工作负载。我正在关注VisualStudio中的Python教程https://learn.microsoft.com/en-us/visualstudio/python/tutorial-working-with-python-in-visual-studio-st......
  • Python面试宝典第19题:最小路径和
    题目        给定一个包含非负整数的mxn网格grid,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。说明:每次只能向下或者向右移动一步。        示例1:输入:grid=[[1,3,1],[1,5,1],[4,2,1]]输出:7解释:因为路径1→3→1→1→1的总......
  • 基于python的出租车管理网站的设计与实现【源码+文档+PPT】
    ......
  • 如何在Linux上的python中以后台模式打开程序?
    我需要在Linux上以后台模式使用python打开另一个程序。我尝试过subprocess.call("yourcommand")但它不是后台模式。并且os.startfile("file")在Linux上不起作用。请帮助我。可以使用Python的subprocess模块在Linux上以后台模......
  • 【学习笔记】Matlab和python双语言的学习(TOPSIS法)
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言一、TOPSIS法1.模型原理2.基本步骤(1)原始矩阵正向化(2)正向矩阵标准化(3)计算得分并归一化二、代码实现----Matlab1.主程序2.正向化处理函数3.极小型正向化函数4.中间型正向化函数5.区间型正向化......
  • 基于Python flask 的豆瓣电影top250数据评分可视化
    跟着CSDN上学习一下爬虫和简单的可视化分析,最后完成了一个简单的小项目。1.项目简介        基于Pythonflask的豆瓣电影评分可视化。通过采用Python编程语言,使用flask框架搭建影视系统,并使用相关技术实现对豆瓣网站的爬取、数据存储和可视化分析。2、成果展示:......
  • 获取 Python Decimal 的精确十进制字符串表示形式?
    如果我有一个PythonDecimal,我怎样才能可靠地获得数字的精确十进制字符串(即不是科学记数法)表示而不带尾随零?例如,如果我有:>>>d=Decimal('1e-14')我会像:>>>get_decimal_string(d)'0.00000000000001'但是:Decimal类没有任何to_......
  • python datetime timedelta 对于没有小数部分的时间返回 0.0
    我正在使用datetime.timedelta来获取python中进程的持续时间。defget_time_difference(start_time,end_time):time_in_seconds=(end_time-start_time)returnstr(datetime.timedelta(seconds=time_in_seconds))[:-3]文档指出“所有参数都是可选的......
  • 如何运行从我正在编写的另一个 Python 脚本获取命令行参数的 Python 脚本?
    我有一个python3脚本,如下所示:...defmain():parser=argparse.ArgumentParser(description='Performnormalisationchecksonpass2files')parser.add_argument('-p','--parser',action='store',help='parse......
  • Python 抓取 urllib2 HTTP 错误
    我正在尝试抓取一个网站,但我的代码仅在我打开该网站然后刷新它时才有效。我尝试了多种方法,但不断出现以下两个错误:第一个:ValueError:“HTTPError:HTTP错误416:请求的范围无法满足”urlslist=open("list_urls.txt").read()urlslist=urlslist.split("\n")forurlslistinurl......