FastAPI AttributeError: 'job_board' object has no attribute 'query' - python

I'm trying to build a simple job board using Python, FastAPI and Async sqlalchemy by following the official FastAPI documentation.Problem occurring when I try to retrieve the job by the ID from the database, It's keep giving me this error AttributeError: 'job_board' object has no attribute 'query' when I hit the "/get/{id}"endpoint.
The Following is hopefully a minimum reproducible code segment:
schemas/jobs.py
from typing import Optional
from pydantic import BaseModel
from datetime import date, datetime
class JobBase(BaseModel):
title: Optional[str] = None
company_name: Optional[str] = None
company_url: Optional[str] = None
location: Optional[str] = "remote"
description: Optional[str] = None
date_posted: Optional[date] = datetime.now().date()
class JobCreate(JobBase):
title: str
company_name: str
location: str
description: str
class ShowJob(JobBase):
title: str
company_name: str
company_url: Optional[str]
location: str
date_posted: date
description: str
class Config():
orm_mode = True
routes/route_jobs.py
from fastapi import APIRouter, HTTPException, status
from fastapi import Depends
from sqlalchemy.orm.session import Session
from db.repository.job_board_dal import job_board
from db.models.jobs import Job as model_job
from schemas.jobs import JobCreate, ShowJob
from db.repository.job_board_dal import Job
from depends import get_db
router = APIRouter()
#router.post("/create-job",response_model=ShowJob)
async def create_user(Job: JobCreate, jobs: Job = Depends(get_db)):
owner_id = 1
return await jobs.create_new_job(Job, owner_id)
#router.get("/get/{id}")
def retreive_job_by_id(id:int, session: Session = Depends(get_db)):
#print(type(session))
job_id = job_board.retrieve_job(session, id=id)
if not job_id:
HTTPException(status_code=status.HTTP_404_NOT_FOUND,
detail=f"Job with id {id} does not exist")
return job_id
db/repository/job_board_dal.py
from sqlalchemy.orm import Session
from schemas.users import UserCreate
from schemas.jobs import JobCreate
from db.models.users import User
from db.models.jobs import Job
from core.hashing import Hasher
class job_board():
def __init__(self, db_session: Session):
self.db_session = db_session
async def register_user(self, user: UserCreate):
new_user = User(username=user.username,
email=user.email,
hashed_password=Hasher.get_password_hash(user.password),
is_active = False,
is_superuser=False
)
self.db_session.add(new_user)
await self.db_session.flush()
return new_user
async def create_new_job(self, job: JobCreate, owner_id: int):
new_job = Job(**job.dict(), owner_id = owner_id)
self.db_session.add(new_job)
await self.db_session.flush()
return new_job
def retrieve_job(db: Session, id:int):
item = db.query(Job).filter(Job.id == id).first()
return item
depends.py
from db.session import async_session
from db.repository.job_board_dal import job_board
async def get_db():
async with async_session() as session:
async with session.begin():
yield job_board(session)
I did try a lot of things I even try to retrieve with a separate async session which also gives me the AttributeError: 'AsyncSession' object has no attribute 'query'.
Any help would be much appreciated.

get_db doesnt return a session, it returns an instance of job_board. So when you do retreive_job_by_id(id:int, session: Session = Depends(get_db)) your are setting session to be a class of job_board. So when you call job_id = job_board.retrieve_job(session, id=id) you are passing session which is an instance of job_board. So when you do item = db.query(Job) db here is an instance of Job_board and job_board doesnt have a method called query. Instead you probably just want to update retrieve job to utilise the job_board class instance to access its session.
def retrieve_job(self, id:int):
item = self.db_session.query(Job).filter(Job.id == id).first()
return item
in your router you can make it more readable as
#router.get("/get/{id}")
def retreive_job_by_id(id:int, job_board = Depends(get_db)):
#print(type(session))
job_id = job_board.retrieve_job(job_board, id=id)
if not job_id:
HTTPException(status_code=status.HTTP_404_NOT_FOUND,
detail=f"Job with id {id} does not exist")
return job_id

Related

how to make the sqlalchemy async session(Async Session) generator as class-base?

I have the fast API application and run schedule task in a background thread as a startup event in fast API.
so when I use the SQlAlchemy async session in route scope like:
session: AsyncSession=Depends(instance_manger.db_instance.get_db_session) it's ok and runs as correct , but when it's run in the background thread I have the below error.
I use python module => SQLAlchemy[asyncio] asyncmy pymysql fastapi
database.py
class DBManager:
def __init__(self):
self.SQLALCHEMY_DATABASE_URL = None
self.config_reader_instance = None
self.engine = None
self._session_factory = None
self.logger_handler_instance = None
self.db = None
def initialize(self, config_reader_instance, logger_handler_instance):
self.logger_handler_instance = logger_handler_instance
self.config_reader_instance = config_reader_instance
self.SQLALCHEMY_DATABASE_URL = "mysql+asyncmy://{0}:{1}#{2}:{3}/{4}".format(
self.config_reader_instance.DB_INFO['db_username'], self.config_reader_instance.DB_INFO['db_password'],
self.config_reader_instance.DB_INFO['db_hostname'], self.config_reader_instance.DB_INFO['db_port'],
self.config_reader_instance.DB_INFO['db_name'])
self.engine = create_async_engine(self.SQLALCHEMY_DATABASE_URL, pool_pre_ping=True)
# self.engine.begi/n()
self._session_factory = async_scoped_session(sessionmaker(
self.engine, class_=AsyncSession, expire_on_commit=False), scopefunc=current_task)
# self._session_factory = orm.scoped_session(
# orm.sessionmaker(
# class_=AsyncSession,
# autoflush=False,
# bind=self.engine,
# ),
# )
async def get_db_session(self) -> AsyncSession:
async with self._session_factory() as session:
try:
yield session
except Exception as e:
self.logger_handler_instance.write_log(__name__, logging.FATAL,
'Session rollback because of exception')
self.logger_handler_instance.write_log(__name__, logging.FATAL, e)
await session.rollback()
raise
finally:
await session.close()
background_thread.py
class BackgroundRunnable:
def __init__(self):
self.instance_manger = None
self.core_process_instance = None
self.conf_reader_instance = None
self.process_id = None
self.process_name = "BTC"
def initialize(self, instance_manager: InstanceManager):
self.instance_manger = instance_manager
return self
def set_process_info(self, process_name):
self.process_id = os.getpid()
self.process_name = process_name
async def run_main(self):
self.instance_manger.logger_handler_instance.write_log(__name__, logging.INFO,
"Background Thread is start")
results = await CryptoCoinService(
CryptoCoinRepository(AsyncSession(self.instance_manger.db_instance.engine))).get_coin()
print(results)
crypto_coin_repository.py
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import class_mapper
from db.models.models import CryptoCoinModel
class CryptoCoinRepository:
def __init__(self, session: AsyncSession) -> None:
self.session = session
async def get_all(self) -> bool:
results = await self.session.execute(
select(CryptoCoinModel._id).where(CryptoCoinModel._symbol == 'BTC'))
results_ = results.fetchone()
if results_.__len__() == 0:
return False
else:
return True
main.py
from fastapi import APIRouter, Depends, Request, Response, FastAPI, status
from fastapi.responses import JSONResponse
from sqlalchemy.ext.asyncio import AsyncSession
from coin_server.background_thread import BackgroundRunnable
from coin_server.core_process import CoreProcess
from core.instance_manager import InstanceManager
from db.database import DBManager
from db.repository.crypto_coin_repository import CryptoCoinRepository
from db.services.crypto_coin_service import CryptoCoinService
deposit_Router = APIRouter()
instance_manager = InstanceManager()
instance_manager.initialize()
db_instance = DBManager()
db_instance.initialize(instance_manager.config_reader_instance, instance_manager.logger_handler_instance)
#deposit_Router.post('/')
async def index(request: Request, session: AsyncSession = Depends(db_instance.get_db_session)):
results = await CryptoCoinService(CryptoCoinRepository(session)).get_coin()
print(results)
deposit_app = FastAPI()
#deposit_app.on_event('startup')
async def app_startup():
background_runnable = BackgroundRunnable()
background_runnable.initialize(instance_manager)
asyncio.create_task(background_runnable.run_main())
# asyncio.create_task(BackgroundRunnable().initialize(instance_manager).run_main())
deposit_app.include_router(deposit_Router)
when I run fast API app error like belong output.
INFO: Uvicorn running on http://0.0.0.0:5000 (Press CTRL+C to quit)
INFO: Started reloader process [176] using watchgod
INFO: Started server process [179]
INFO: Waiting for application startup.
Task exception was never retrieved
future: <Task finished name='Task-3' coro=<BackgroundRunnable.run_main() done, defined at /mnt/c/Users/dr_r00t3r/Desktop/main/coin_server/background_thread.py:48> exception=At
tributeError("'async_generator' object has no attribute 'execute'")>
Traceback (most recent call last):
File "/mnt/c/Users/dr_r00t3r/Desktop/main/coin_server/background_thread.py", line 51, in run_main
results = await CryptoCoinService(
File "/mnt/c/Users/dr_r00t3r/Desktop/main/db/repository/crypto_coin_repository.py", line 17, in get_all
results = await self.session.execute(
AttributeError: 'async_generator' object has no attribute 'execute'
INFO: Application startup complete.
It's all note: when you use function get_db_session in database.py like a generator, close function of session doesn't work as auto, so you should close them like manually.
database.py
import logging
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, scoped_session
Base = declarative_base()
class DBManager:
def __init__(self):
self.SQLALCHEMY_DATABASE_URL = None
self.config_reader_instance = None
self.engine = None
self.session_factory = None
self.Base = declarative_base()
self.logger_handler_instance = None
def initialize(self, config_reader_instance, logger_handler_instance):
self.logger_handler_instance = logger_handler_instance
self.config_reader_instance = config_reader_instance
self.SQLALCHEMY_DATABASE_URL = "mysql+asyncmy://{0}:{1}#{2}:{3}/{4}".format(
self.config_reader_instance.DB_INFO['db_username'], self.config_reader_instance.DB_INFO['db_password'],
self.config_reader_instance.DB_INFO['db_hostname'], self.config_reader_instance.DB_INFO['db_port'],
self.config_reader_instance.DB_INFO['db_name'])
self.engine = create_async_engine(self.SQLALCHEMY_DATABASE_URL, pool_pre_ping=True, pool_size=30,
max_overflow=30, echo_pool=True, echo=False,
pool_recycle=3600) # recycle every hour
DBManager.Base = declarative_base()
self.session_factory = scoped_session(sessionmaker(
self.engine, class_=AsyncSession, expire_on_commit=False
))
def get_db_session(self):
session = self.session_factory()
try:
yield session
except Exception as e:
self.logger_handler_instance.log(__name__, logging.FATAL,
'Session rollback because of exception')
self.logger_handler_instance.log(__name__, logging.FATAL, e)
session.rollback()
raise
finally:
session.close()
async def init_models(self):
async with self.engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
await conn.run_sync(Base.metadata.create_all)
background_thread.py
class BackgroundRunnable:
def __init__(self):
self.instance_manger = None
self.core_process_instance = None
self.conf_reader_instance = None
self.process_id = None
self.process_name = "BTC"
def initialize(self, instance_manager: InstanceManager):
self.instance_manger = instance_manager
return self
def set_process_info(self, process_name):
self.process_id = os.getpid()
self.process_name = process_name
async def run_main(self):
self.instance_manger.logger_handler_instance.write_log(__name__, logging.INFO,
"Background Thread is start")
self.session: AsyncSession = next(self.instance_manger.db_instance.get_db_session())
results = await CryptoCoinService(CryptoCoinRepository(self.session)).get_coin(
self.instance_manger.config_reader_instance.BTC_INFO['BTC_COIN'])
print(results)
crypto_coin_repository.py
"""Repositories module."""
from contextlib import AbstractContextManager
from typing import Callable
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import class_mapper, Session
from db.models.models import CryptoCoinModel
class CryptoCoinRepository:
def __init__(self, session: AsyncSession) -> None:
self.session = session
async def get_all(self, coin) -> bool:
results = await self.session.execute(
select(CryptoCoinModel._id).where(CryptoCoinModel._symbol == coin))
results = results.fetchall()
if len(results) == 0:
return False
else:
return True
def serialize(self, model):
"""Transforms a model into a dictionary which can be dumped to JSON."""
# first we get the names of all the columns on your model
columns = [c.key for c in class_mapper(model.__class__).columns]
# then we return their values in a dict
return dict((c, getattr(model, '_' + c)) for c in columns)
class NotFoundError(Exception):
symbol: str
def __init__(self):
super().__init__(f'{self._symobl} not found,please add this coin to db')
class CryptoCoinNotFoundError(NotFoundError):
# entity_name: str = 'User'
pass
main.py
from fastapi import APIRouter, Depends, Request, Response, FastAPI, status
from fastapi.responses import JSONResponse
from sqlalchemy.ext.asyncio import AsyncSession
from coin_server.background_thread import BackgroundRunnable
from coin_server.core_process import CoreProcess
from core.instance_manager import InstanceManager
from db.database import DBManager
from db.repository.crypto_coin_repository import CryptoCoinRepository
from db.services.crypto_coin_service import CryptoCoinService
deposit_Router = APIRouter()
instance_manager = InstanceManager()
instance_manager.initialize()
db_instance = DBManager()
db_instance.initialize(instance_manager.config_reader_instance, instance_manager.logger_handler_instance)
#deposit_Router.post('/')
async def index(request: Request, session: AsyncSession = Depends(db_instance.get_db_session)):
results = await CryptoCoinService(CryptoCoinRepository(session)).get_coin()
print(results)
deposit_app = FastAPI()
#deposit_app.on_event('startup')
async def app_startup():
background_runnable = BackgroundRunnable()
background_runnable.initialize(instance_manager)
asyncio.create_task(background_runnable.run_main())
# asyncio.create_task(BackgroundRunnable().initialize(instance_manager).run_main())
deposit_app.include_router(deposit_Router)

FastAPI, Pytest TypeError: object dict can't be used in 'await' expression

I am writing an API using FastAPI when I run with uvicorn everything is normal, I get the error when I want to run a test using the FastAPI TestClient.
This is the error:
async def get_user_id(conn, user):
collection = conn.CIA.get_collection("Employees_Info")
user = await collection.find_one({'name':user},{"_id":1, "name":0, "password":0})
TypeError: object dict can't be used in 'await' expression
db\db.py:12: TypeError
project structure:
APP
|--__init__.py
|--run.py
|--main.py
|--test
|--test_app.py
|--routes
|--router.py
|--models
|--models.py
|--db
|--db_conn.py
|--db.py
|--auth_jwt
|--jwt_auth.py
|--auth
|--auth.py
This is the code of the test, I am using mongomock, I don't know if this will be the root of the problem:
import collections
from fastapi.testclient import TestClient
from fastapi import status
from main import app
from mongoengine import connect, disconnect, get_connection
from db.db_conn import db
client = TestClient(app)
connect('mongoenginetest', host='mongomock://localhost', alias='testdb')
db.client = get_connection('testdb')
db.client["CIA"]
db.client["Employees_Info"]
db.client.CIA.Employees_Info.insert_one({"name": "user_Name","password": "week"})
def test_ping():
response = client.get("/")
assert response.status_code == status.HTTP_200_OK
assert response.json() == {"message": "Conectado"}
def test_login():
data = {"username":'user_name', 'password':'week'}
response = client.post("/login", data=data)
assert response.headers['Content-Type'] == 'application/json'
assert response.status_code == status.HTTP_200_OK
db.client.disconnect()
I tried performing the Async test according to the FastAPI documentation but it doesn't work either, if I use the "normal" database the test works.
router.py
#router.post("/login", tags=["user"], response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(),
db: AsyncIOMotorClient = Depends(get_database)):
authenticate_user_id = await authenticate_user(db, form_data.username, form_data.password)
if not authenticate_user_id:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token = create_access_token(data={"user_id": str(authenticate_user_id["_id"])})
return {"access_token": access_token, "token_type": "bearer"}
auth.py
async def authenticate_user(conn:AsyncIOMotorClient, username, password):
user_id = await verify_user(conn, username)
if not user_id:
return False
if not await verify_password(conn, user_id, password):
return False
return user_id
async def verify_user(conn, user):
return await get_user_id(conn,user)
async def verify_password(conn, user_id, password):
return pbkdf2_sha256.verify(password, await get_password(conn, user_id))
db.py
async def get_user_id(conn, user):
collection = conn.CIA.get_collection("Employees_Info")
user = await collection.find_one({'name':user},{"_id":1, "name":0, "password":0})
print(type(user))
if user:
return user
async def get_password(conn, user_id):
collection = conn.CIA.get_collection("Employees_Info")
db = await collection.find_one(user_id)
if db:
return db['password']
Maybe you need install pytest-asyncio.Here more info https://fastapi.tiangolo.com/advanced/async-tests/
collection.find_one() is not a async function, so you are trying to await the result of the function which is a dict, that is why you are getting the error TypeError: object dict can't be used in 'await' expression you are awaiting a dict, not a coroutine which would be returned by an async function.
To fix you code just remove await from
db = await collection.find_one(user_id)
When you do that, you won't really need it to be a async function, so you can just define it regularly, but you will than have to change all the function calls and remove the await from them, otherwise you will get this error again
Full code:
db.py
def get_user_id(conn, user):
collection = conn.CIA.get_collection("Employees_Info")
user = collection.find_one({'name':user},{"_id":1, "name":0, "password":0})
print(type(user))
if user:
return user
def get_password(conn, user_id):
collection = conn.CIA.get_collection("Employees_Info")
db = collection.find_one(user_id)
if db:
return db['password']
auth.py
def authenticate_user(conn:AsyncIOMotorClient, username, password):
user_id = verify_user(conn, username)
if not user_id:
return False
if not verify_password(conn, user_id, password):
return False
return user_id
def verify_user(conn, user):
return get_user_id(conn,user)
def verify_password(conn, user_id, password):
return pbkdf2_sha256.verify(password, get_password(conn, user_id))
router.py
# This probably has to stay as an async function, I'm not sure how the module works
#router.post("/login", tags=["user"], response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(),
db: AsyncIOMotorClient = Depends(get_database)):
authenticate_user_id = authenticate_user(db, form_data.username, form_data.password)
if not authenticate_user_id:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token = create_access_token(data={"user_id": str(authenticate_user_id["_id"])})
return {"access_token": access_token, "token_type": "bearer"}
import collections
from fastapi.testclient import TestClient
from fastapi import status
from main import app
from mongoengine import connect, disconnect, get_connection
from db.db_conn import db
client = TestClient(app)
connect('mongoenginetest', host='mongomock://localhost', alias='testdb')
db.client = get_connection('testdb')
db.client["CIA"]
db.client["Employees_Info"]
db.client.CIA.Employees_Info.insert_one({"name": "user_Name","password": "week"})
def test_ping():
response = client.get("/")
assert response.status_code == status.HTTP_200_OK
assert response.json() == {"message": "Conectado"}
def test_login():
data = {"username":'user_name', 'password':'week'}
response = client.post("/login", data=data)
assert response.headers['Content-Type'] == 'application/json'
assert response.status_code == status.HTTP_200_OK
db.client.disconnect()

FASTApi authentication injection

My application has an AuthenticateService implemented as follows:
from domain.ports.repositories import ISalesmanRepository
from fastapi import HTTPException, status
from fastapi_jwt_auth import AuthJWT
from fastapi_jwt_auth.exceptions import JWTDecodeError
from shared.exceptions import EntityNotFound
from adapters.api.authentication.config import User
class AuthenticateService:
def __init__(self, user_repo: ISalesmanRepository):
self._repo = user_repo
def __call__(self, auth: AuthJWT) -> User:
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
auth.jwt_required()
user_id = auth.get_jwt_subject()
except JWTDecodeError:
raise credentials_exception
try:
user = self._repo.get_by_id(user_id)
return user
except EntityNotFound:
raise credentials_exception
So the behavior is basically:
Is jwt is valid, get user from repository and returns
Raise 401 if jwt is invalid
The problem is that in every controller implemented I have to repeat the process. I tried to implement a decorator that injects the user into the controller in case of success but I couldn't. I'm sure that the best way to implement this is to use fastAPI's Depends dependency injector.
Today, a controller looks something like this:
from typing import Optional
from adapters.api.services import authenticate_service, create_sale_service
from fastapi import APIRouter, Depends
from fastapi_jwt_auth import AuthJWT
from pydantic import BaseModel
router = APIRouter()
class Request(BaseModel):
code: str
value: float
date: str
status: Optional[str] = None
#router.post('/sale')
def create_sale(request: Request, auth: AuthJWT = Depends()):
user = authenticate_service(auth)
result = create_sale_service.handle(
{"salesman": user, "sale": request.dict()}
)
return result.dict()
How can I abstract my authentication so that my controllers look like any of the versions below:
# Option 1: decorator
#router.post('/sale')
#authentication_required
def create_sale(request: Request, user: User): # User is the `__call__` response from `AuthenticateService` class
result = create_sale_service.handle(
{"salesman": user, "sale": request.dict()}
)
return result.dict()
# Option 2:
#router.post('/sale')
def create_sale(request: Request, user: User = Depends(authenticate_service)): # Something like that, using the depends to inject User to me
result = create_sale_service.handle(
{"salesman": user, "sale": request.dict()}
)
return result.dict()
So I read a little more the Depends documentation and realize whats was going wrong with my attempt to inject the user on controller signature.
Right way to implement:
> AuthService class
class AuthenticateService:
def __init__(self, user_repo: ISalesmanRepository):
self._repo = user_repo
def __call__(self, auth: AuthJWT = Depends()) -> User:
...
authenticate_service = AuthenticateService(user_repository)
> Controller
#router.post('/sale')
def create_sale(request: Request, user: User = Depends(authenticate_service)):
result = create_sale_service.handle(
{"salesman": user, "sale": request.dict()}
)
return result.dict()

Aiopg cursor doesn't return fetchone()

I am stuck into a problem with aiopg. There are a one problem when i async with engine and try to return a result to endpoint in FastApi. Look down below for my db class.
class DBEngine:
def __init__(self, connection_url: str) -> None:
self.dsn = connection_url
self.engine: Optional[Engine] = None
async def connect(self) -> None:
self.engine = await create_engine(dsn=self.dsn, maxsize=100)
#property
def client(self) -> Engine:
if self.engine:
return self.engine
raise Exception("Not connected to database")
async def close(self) -> None:
if self.engine:
self.engine.close()
await self.engine.wait_closed()
As you can see there is a property, i use it in a code of method of a model class:
class Products(Base):
__tablename__ = "products"
UUID = sa.Column(
UUID(as_uuid=True),
primary_key=True,
default=uuid.uuid4,
unique=True,
nullable=False,
)
product_name = sa.Column(sa.String())
#classmethod
async def create_loan(
cls,
*,
product_name: str
) -> "Products":
query = cls.insert_query(
product_name=product_name
)
async with db_engine.client.acquire() as conn:
cursor = await conn.execute(query)
return await cursor.fetchone()
And finally i'll show an endpoint in fastapi:
#routes.post("/api/v1/loans")
async def create_loan(loan: Loan):
product = await Products.create_loan(product_name=loan.product_name)
return {"loan_id": product.UUID}
ERROR LOG:
**Traceback**
...
aiopg.sa.exc.ResourceClosedError: This result object does not return rows. It has been closed automatically.
Please tell me what's wrong, i spent so much time to solve this problem. Feel free to answer, thanks.

Python: New Object instance on every call of function

I'm trying to update a base class with a session token and user id for long polling.
Every time I call my function I create a new instance which calls a login function, that I don't want to happen.
I only want to call the login() method when the value is None
How do I return the instance of apiclient after the session token is set to use with the function for get_balance??
client.py
from __future__ import absolute_import, unicode_literals
import requests
import os
from matchbook import endpoints
class BaseClient(object):
def __init__(self, username, password=None, locale=None):
self.username = username
self.password = password
self.locale = locale
self.url_beta = 'https://beta.matchbook.com'
self.urn_main = '/bpapi/rest/'
self.session = requests.Session()
self.session_token = None
self.user_id = None
def set_session_token(self, session_token, user_id):
self.session_token = session_token
self.user_id = user_id
class APIClient(BaseClient):
def __init__(self, username, password=None):
super(APIClient, self).__init__(username, password)
self.login = endpoints.Login(self)
self.account = endpoints.Account(self)
def __repr__(self):
return '<APIClient [%s]>' % self.username
def __str__(self):
return 'APIClient'
get_bal.py
from client import APIClient
from celery import shared_task
def get_client():
apiclient = APIClient(username, password)
if apiclient.session_token is None:
apiclient.login()
session_token = apiclient.session_token
user_id = apiclient.user_id
apiclient.set_session_token(session_token,user_id)
else:
print('session token assigned',apiclient.session_token, apiclient.user_id)
return apiclient
#shared_task
def get_balance():
apiclient = get_client() *to call instance after login is set*
r = apiclient.account.get_account()
print(r)
You are creating a new instance of APIClient each time you call get_client(), which is what happens each time get_balance() get called.
You need to maintain an instance of the APIClient outside of the function scope for you to carry over your program and update your get_balance() to not call get_client() each time:
def get_balance(apiclient):
r = apiclient.account.get_account()
print(r)
def main():
apiclient = get_client()
get_balance(apiclient) # pass instance of APIClient in as an argument
Another note in your get_client() function, since both of the if conditions are the opposite of each other, just wrap them in an if... else block:
def get_client():
apiclient = APIClient(username, password)
if apiclient.session_token is None:
apiclient.login()
session_token = apiclient.session_token
user_id = apiclient.user_id
apiclient.set_session_token(session_token,user_id)
else:
print('session token assigned',apiclient.session_token, apiclient.user_id)
return apiclient
All that said, a much more OOP way would be to bake the get_balance() into the APIClient as an instance method so you don't even need to worry about the instance:
class APIClient(BaseClient):
...
def get_balance(self):
print(self.account.get_account())
# Then just call the get_balance() anywhere:
apiclient = get_client()
apiclient.get_balance()

Categories