#๐Ÿ”’ Exclude FastApi DI Attribute from Excpected Request Body

39 messages ยท Page 1 of 1 (latest)

radiant vapor
#
@router.post("/mails", tags=["mails"])
async def send_mail(
    payload: PostMailsRequest,
    rabbitmq: Annotated[RabbitMQ, Depends()],
) -> Response:
    ...

rabbitmq is injected like this: app.dependency_overrides[RabbitMQ] = lambda: rabbitmq

Now my request body is:

{
  "payload": {
    ...
  },
  "config": {
    ...
  }
}

What I actually want is just (which happens when I remove rabbitmq: Annotated[RabbitMQ, Depends()],:

{
 # payload data
}
grave foxBOT
#

@radiant vapor

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

frank blaze
#

Can you share your request model? Is RabbitMQ your own class?

radiant vapor
#

Yes, RabbitMQ is my own class

class PostMailsRequest(BaseModel):
    """Model for sending email requests."""

    subject: str
    body: str
    recipient_email: EmailStr
frank blaze
#

And how does RabbitMQ look?

radiant vapor
frank blaze
#

I'm thinking fastapi doesn't look at override annotations for validation

radiant vapor
#

but rabbitmq should not included at all in the request body, it should just be provided y dependency injection

frank blaze
#

So you should use a getter function

#

FastAPI is ignoring the overrides for validation

#

It looks at the RabbitMQ constructor and seeing it needs config

radiant vapor
#

like this?

def get_rabbitmq() -> RabbitMQ:
    ..

@router.post("/mails", tags=["mails"])
async def send_mail(
    payload: PostMailsRequest,
    rabbitmq: RabbitMQ = Depends(get_rabbitmq),
frank blaze
#

Yes

dapper tapir
radiant vapor
#

well that works for excluding it from the request but the my config should be provided on startup

rabbitmq = RabbitMQ(config)

app = FastAPI()
app.dependency_overrides[RabbitMQ] = lambda: rabbitmq
frank blaze
#

If you init RabbitMQ during startup, you can yield a dict in the lifespan function for request.state

dapper tapir
radiant vapor
#

so that would be smth like this?

# startup
app.state.rabbitmq = RabbitMQ(config)

# router
async def get_rabbitmq(request: Request) -> RabbitMQ:
    return request.state.rabbitmq
#

I think that's a solution but a bit unfortunate that I can't do it with the override and as attr rabbitmq: Annotated[RabbitMQ, Depends()], would have been cleaner

frank blaze
#

Make a wrapper class for it

dapper tapir
frank blaze
#
class Rabbit:
  def __init__(self, request: Request):
    self.rabbit = request.state.rabbitmq
radiant vapor
#

Gimme a couple of minutes I will try that

frank blaze
#

Call it RabbitService and include functions that wrap RabbitMQ

radiant vapor
#

hmmm, this gives me an AttributeError: 'State' object has no attribute 'rabbitmq'

app = FastAPI()
app.state.rabbitmq = rabbitmq

app.include_router(users.router)
app.include_router(mails.router)

app.middleware("http")(logging_mw)
class RabbitService:
    def __init__(self, request: Request) -> None:
        self.rabbit = request.state.rabbitmq
@router.post("/mails", tags=["mails"])
async def send_mail(
    payload: PostMailsRequest,
    rabbit_service: Annotated[RabbitService, Depends(RabbitService)],
) -> Response:
    """Send a mail."""
    triggered_event = MailTriggeredEvent(
        subject=payload.subject,
        body=payload.body,
        recipient_email=payload.recipient_email,
    )
    rabbit_service.rabbit.publish(triggered_event)
    return Response(status_code=status.HTTP_200_OK)
radiant vapor
frank blaze
#

You need to use app lifespan

radiant vapor
#
@asynccontextmanager
async def lifespan(app: FastAPI):
    app.state.rabbitmq = rabbitmq
    yield

app = FastAPI(lifespan=lifespan)

doesn't do the trick

frank blaze
#
import contextlib
from fastapi import FastAPI

config = Config()
rabbitmq = RabbitMQ(config)

@contextlib.asynccontextmanager
async def lifespan(app: FastAPI):
  yield {
    "config": config,
    "rabbitmq": rabbitmq,
  }

app = FastAPI(lifespan=lifespan)
radiant vapor
#

okay that works, interesting how they implemented that.

radiant vapor
frank blaze
#

This is unrelated to di

#

This is initializing state during lifespan

radiant vapor
#

Yeah your right I understand that, but essentially what I wanted was "DI" for a service that depended on a config. I was more wondering if this is the common way to do this?

#

I haven't spent much time with python / fastapi yet so I'm wondering if what I'm doing maybe is not the common way how things are done in python

radiant vapor
#

!close

grave foxBOT
#
Python help channel closed with !close

This help channel has been closed. Feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.