首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用码头容器时Python FastApi模型验证失败

使用码头容器时Python FastApi模型验证失败
EN

Stack Overflow用户
提问于 2022-10-07 18:26:52
回答 1查看 69关注 0票数 0

在使用Redis的python FastAPI应用程序时,我遇到了一个非常奇怪的问题。最后,我试图找出场景2(见下文)失败的原因。

下面我描述了这些场景和一些一般的想法。

下面是一些场景

设想方案1(工作)

  • Redis-db使用localhost:6379在docker容器上运行。
  • Fastapi在没有容器的机器上本地运行。
  • 创建引用BaseModel/EmbeddedJsonModel的简单JSONModel
  • 在运行app时,我可以从日志中看到快速api连接到redis。
  • 如果我发布到快速app应用程序,redis db将被填充,而不存在任何问题。

方案2(不工作)

  • Redis-db使用localhost:6379在docker容器上运行。
  • Fastapi应用程序运行在与redis-db相同的容器netowkr上的docker容器中。
  • 创建引用BaseModel/EmbeddedJsonModel的简单JSONModel
  • 当我运行docker-组合up...both容器时,我看到快速api应用程序连接到redis db。
  • 如果我发布到exception应用程序,我会得到异常提示:如果我将我的简单模型转换为HashModel并删除对BaseModel/EmbeddedJsonModel的所有引用,就没有问题了。

FastApi代码

代码语言:javascript
运行
复制
import logging
from typing import Optional, Any

from fastapi import FastAPI, HTTPException, Response, status
from redis_om import Migrator, NotFoundError, get_redis_connection, HashModel, EmbeddedJsonModel, Field, JsonModel
from pydantic import BaseModel, Extra, ValidationError, validator

app = FastAPI(title="FastAPI_Test_App")
log = logging.getLogger(__name__)
log.warning("CONNECTING TO REDIS_HOST: redis-db")
redis_con = get_redis_connection(host="redis-db", decode_responses=True)

class CustomerBase(EmbeddedJsonModel, BaseModel):
    first_name: str
    last_name: str
    email: str
    age: int

#CustomerBase.Meta.database = redis_con
log.warning(f"CustomerBase DB connection data: {CustomerBase.Meta.database}") # default database uses localhost
class Customer(JsonModel):
    base: CustomerBase

Customer.Meta.database = redis_con  # type: ignore
log.warning(f"Customer DB connection data: {Customer.Meta.database}") # this shows redis-db which is CORRECT
@app.get('/customers')
async def all():
    customer_pks = Customer.all_pks()
    return [format(pk) for pk in customer_pks]

def format(pk: str):
    c = Customer.get(pk)
    return {
        'id': c.pk,
        'name': f"{c.first_name} {c.last_name}",
        'age': c.age
    }
@app.post('/customers')
async def create(customer_base: CustomerBase):
    c = Customer(base=customer_base)
    #c.Meta.database = redis_con
    return c.save()
    #return customer.save() # THIS WORKS ALONE...NO OTHER LINES NEED IN THIS FUNCTION

if __name__ == "__main__":  # pragma: no cover
    log.warning("Running customer app")

    # Create a RediSearch index (required for all processes that operate with Tasks)
    Migrator().run()

    # TODO Move uvicorn to extras_require (if not used in prod)?
    import uvicorn

    # TODO Add cache in "on event startup"
    uvicorn.run("main_REPRODUCE_FAILURE:app", reload=True, host="0.0.0.0", port=5000, log_level="debug")

Dockerfile

代码语言:javascript
运行
复制
FROM python:3.9
RUN apt-get update && apt-get -y install iputils-ping
RUN groupadd -g 1000 appuser && \
    useradd -rm -s /bin/bash -d /home/appuser -u 1000 -g 1000 appuser


WORKDIR /home/appuser
RUN python -m pip install --upgrade pip

COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt

COPY . .
RUN pip install -vvv -e .


EXPOSE 5000
ENTRYPOINT [ "/bin/bash", "-l", "-c" ]
CMD ["python main_REPRODUCE_FAILURE.py"]

码头工

代码语言:javascript
运行
复制
version: '3.8'

services:
  redis-db:
    image: redislabs/rejson:latest
    restart: always
    command: redis-server --loglevel debug
    volumes:
      - fastapi-redis-test:/data
    ports:
      - "6379:6379"
    networks:
      - fastapi-redis-test
    environment:
      fastapi-redis-test_ENV: development
  fastapi-redis-test:
    build:
      context: .
      dockerfile: Dockerfile
    #command: python main.py
    image: fastapi-redis-test
    depends_on:
      - redis-db
    ports:
      - "5000:5000"
    networks:
      - fastapi-redis-test
    environment:
      REDIS_HOST: redis-db
    volumes:
      - .:/home/appuser
volumes:
  fastapi-redis-test:
    driver: local

networks:
  fastapi-redis-test:

使用localhost运行post时出现异常:5000/docs

代码语言:javascript
运行
复制
fastapi_redis-fastapi-redis-test-1  | CONNECT TO REDIS_HOST: redis-db
fastapi_redis-fastapi-redis-test-1  | CustomerBase DB connection data: Redis<ConnectionPool<Connection<host=localhost,port=6379,db=0>>>
fastapi_redis-fastapi-redis-test-1  | Customer DB connection data: Redis<ConnectionPool<Connection<host=redis-db,port=6379,db=0>>>
fastapi_redis-fastapi-redis-test-1  | CONNECT TO REDIS_HOST: redis-db
fastapi_redis-fastapi-redis-test-1  | CustomerBase DB connection data: Redis<ConnectionPool<Connection<host=localhost,port=6379,db=0>>>
fastapi_redis-fastapi-redis-test-1  | Customer DB connection data: Redis<ConnectionPool<Connection<host=redis-db,port=6379,db=0>>>
fastapi_redis-fastapi-redis-test-1  | INFO:     Started server process [14]
fastapi_redis-fastapi-redis-test-1  | INFO:     Waiting for application startup.
fastapi_redis-fastapi-redis-test-1  | INFO:     Application startup complete.
fastapi_redis-redis-db-1            | 1:M 07 Oct 2022 18:08:40.685 - DB 0: 27 keys (0 volatile) in 32 slots HT.
fastapi_redis-redis-db-1            | 1:M 07 Oct 2022 18:08:40.685 . 1 clients connected (0 replicas), 838736 bytes in use
fastapi_redis-fastapi-redis-test-1  | INFO:     172.21.0.1:58606 - "POST /customers HTTP/1.1" 500 Internal Server Error
fastapi_redis-fastapi-redis-test-1  | ERROR:    Exception in ASGI application
fastapi_redis-fastapi-redis-test-1  | Traceback (most recent call last):
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/redis/connection.py", line 611, in connect
fastapi_redis-fastapi-redis-test-1  |     sock = self.retry.call_with_retry(
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/redis/retry.py", line 46, in call_with_retry
fastapi_redis-fastapi-redis-test-1  |     return do()
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/redis/connection.py", line 612, in <lambda>
fastapi_redis-fastapi-redis-test-1  |     lambda: self._connect(), lambda error: self.disconnect(error)
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/redis/connection.py", line 677, in _connect
fastapi_redis-fastapi-redis-test-1  |     raise err
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/redis/connection.py", line 665, in _connect
fastapi_redis-fastapi-redis-test-1  |     sock.connect(socket_address)
fastapi_redis-fastapi-redis-test-1  | OSError: [Errno 99] Cannot assign requested address
fastapi_redis-fastapi-redis-test-1  | 
fastapi_redis-fastapi-redis-test-1  | During handling of the above exception, another exception occurred:
fastapi_redis-fastapi-redis-test-1  | 
fastapi_redis-fastapi-redis-test-1  | Traceback (most recent call last):
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py", line 404, in run_asgi
fastapi_redis-fastapi-redis-test-1  |     result = await app(  # type: ignore[func-returns-value]
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
fastapi_redis-fastapi-redis-test-1  |     return await self.app(scope, receive, send)
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/fastapi/applications.py", line 270, in __call__
fastapi_redis-fastapi-redis-test-1  |     await super().__call__(scope, receive, send)
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/starlette/applications.py", line 124, in __call__
fastapi_redis-fastapi-redis-test-1  |     await self.middleware_stack(scope, receive, send)
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 184, in __call__
fastapi_redis-fastapi-redis-test-1  |     raise exc
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 162, in __call__
fastapi_redis-fastapi-redis-test-1  |     await self.app(scope, receive, _send)
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/starlette/middleware/exceptions.py", line 75, in __call__
fastapi_redis-fastapi-redis-test-1  |     raise exc
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/starlette/middleware/exceptions.py", line 64, in __call__
fastapi_redis-fastapi-redis-test-1  |     await self.app(scope, receive, sender)
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__
fastapi_redis-fastapi-redis-test-1  |     raise e
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
fastapi_redis-fastapi-redis-test-1  |     await self.app(scope, receive, send)
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 680, in __call__
fastapi_redis-fastapi-redis-test-1  |     await route.handle(scope, receive, send)
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 275, in handle
fastapi_redis-fastapi-redis-test-1  |     await self.app(scope, receive, send)
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 65, in app
fastapi_redis-fastapi-redis-test-1  |     response = await func(request)
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/fastapi/routing.py", line 221, in app
fastapi_redis-fastapi-redis-test-1  |     solved_result = await solve_dependencies(
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 561, in solve_dependencies
fastapi_redis-fastapi-redis-test-1  |     ) = await request_body_to_args(  # body_params checked above
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 696, in request_body_to_args
fastapi_redis-fastapi-redis-test-1  |     v_, errors_ = field.validate(value, values, loc=loc)
fastapi_redis-fastapi-redis-test-1  |   File "pydantic/fields.py", line 884, in pydantic.fields.ModelField.validate
fastapi_redis-fastapi-redis-test-1  |   File "pydantic/fields.py", line 1101, in pydantic.fields.ModelField._validate_singleton
fastapi_redis-fastapi-redis-test-1  |   File "pydantic/fields.py", line 1148, in pydantic.fields.ModelField._apply_validators
fastapi_redis-fastapi-redis-test-1  |   File "pydantic/class_validators.py", line 318, in pydantic.class_validators._generic_validator_basic.lambda13
fastapi_redis-fastapi-redis-test-1  |   File "pydantic/main.py", line 711, in pydantic.main.BaseModel.validate
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/redis_om/model/model.py", line 1469, in __init__
fastapi_redis-fastapi-redis-test-1  |     if not has_redis_json(self.db()):
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/redis_om/checks.py", line 17, in has_redis_json
fastapi_redis-fastapi-redis-test-1  |     command_exists = check_for_command(conn, "json.set")
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/redis_om/checks.py", line 9, in check_for_command
fastapi_redis-fastapi-redis-test-1  |     cmd_info = conn.execute_command("command", "info", cmd)
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/redis/client.py", line 1235, in execute_command
fastapi_redis-fastapi-redis-test-1  |     conn = self.connection or pool.get_connection(command_name, **options)
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/redis/connection.py", line 1387, in get_connection
fastapi_redis-fastapi-redis-test-1  |     connection.connect()
fastapi_redis-fastapi-redis-test-1  |   File "/usr/local/lib/python3.9/site-packages/redis/connection.py", line 617, in connect
fastapi_redis-fastapi-redis-test-1  |     raise ConnectionError(self._error_message(e))
fastapi_redis-fastapi-redis-test-1  | redis.exceptions.ConnectionError: Error 99 connecting to localhost:6379. Cannot assign requested address.

一些疑难解答的想法:

在redis连接期间,我可以看到stacktrace正确地使用了容器名: redis-db,我发现有趣的是,堆栈跟踪表明,在发布期间,它尝试连接到localhost,而我知道它是坏的b/c,这意味着app试图连接到我的本地机器,而不是redis容器。

看到这个错误后,我试着在CustomerBase和Customer类的Meta类中添加redis-db连接。这样做,我不再有例外。相反,我得到了一个HTTP 422不可处理的实体故障。我想这仅仅是b/c,因为做了一些我打破了正常模型的事情。所以这就让我来看看wonder...why,是使用本地主机而不是redis-db的应用程序吗?OR...is还有其他一些奇怪的问题。我真的在想,正如前面提到的那样,b/c上还有其他的东西,当应用程序不在容器上,而redis在容器上时,earlier...this 失败的安装工作得很好。

我不确定这是否是基于staack跟踪的实际problem...but,这就是我所相信的。我愿意接受所有建议:)

任何帮助都会被指定!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-10-07 19:09:18

这里的问题似乎与redis_om库有关。查看跟踪中的最后一行,我们看到了redis.exceptions.ConnectionError: Error 99 connecting to localhost:6379. Cannot assign requested address.,也就是说,它仍然试图连接到localhost上的redis,尽管您似乎已经将它配置为连接到redis-db

查看文献资料,有两种方法可以配置库,要么通过设置REDIS_OM_URL环境变量(通过在docker-compose.yml文件中设置REDIS_OM_URL=redis://redis-db:6379来实现),要么通过向模型中添加内部类:

代码语言:javascript
运行
复制
class Customer(JsonModel):
    base: CustomerBase

    class Meta:
        database = redis_con

我想,尝试在类声明之后配置连接,就像您在使用Customer.Meta.database = redis_con时所做的那样,是行不通的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73991306

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档