custom_fields = {} extra = {'company_phone': '555-0100', 'unsubscribe_link': 'http://localhost:8000/unsubscribe/test-token'} def substitute(self, text: str, recipient, generated: GeneratedValues, custom_fields: Optional[dict] = None, extra: Optional[dict] = None) -> SubstitutionResult: recipient = recipient or {} custom_fields = custom_fields or {} extra = extra or {} # Accept ORM Recipient objects as well as plain dicts if not isinstance(recipient, dict): recipient = {c.name: getattr(recipient, c.name, None) > for c in recipient.__table__.columns} E AttributeError: '_FakeRecipient' object has no attribute '__table__' -- db = def test_send_substitutes_variables_before_send(self, api, db): # covers the pre-send pipeline end-to-end up to the real network call; # the call itself fails honestly without Google credentials aid = _seed_account(db, user_id_of(api)) r = api.post("/test-send/send", headers=H(api), json={"account_id": aid, "to_email": "dest@example.com", "subject": "Invoice %RandomInvoiceNumber%", "body_html": "

Dear %FirstName%, total %RandomTotal%.

", "use_random_variables": True}) > assert r.status_code == 200, r.text E AssertionError: {"detail":"HTML rejected: Unsubstituted variables remain: ['FirstName', 'RandomTotal']"} E assert 400 == 200 E + where 400 = .status_code