django-viewflow, rest api saving foreign key says "<ProcessName> has no <ForeignModel>" - django-viewflow

I have the following models setup:
class Cluster(models.Model):
name = models.CharField(...)
...
class ResourceRequest(Process):
cluster = models.ForeignKey('Cluster')
def clean(self, ...):
if self.cluster.name == 'abc':
...
And when I try to post to:
http://pmas-local:8000/workflow/api/tasks/vm_request/resourcerequest/start/
it complains that ResourceRequest has no cluster.
Stacktrace shows if self.cluster.name == 'abc': caused the problem.

The error is not related to django-viewflow. That's standard django error for a foreign key field, which means that self.cluster is None.

Related

Repeated checking of ID path parameter in FastAPI

I have the following route specs:
GET /councils/{id}
PUT /councils/{id}
DELETE /councils/{id}
In all three routes, I have to check in the database whether the council with the id exists, like this:
council = crud.council.get_by_id(db=db, id=id)
if not council:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail='Council not found'
)
Which adds to a lot of boilerplate in the code. Is there any way of reducing this? I have thought of creating a dependency but then I have to write different dependency function for different models in my database. Is there any standard practice for this?
Thanks.
Using a dependency is the way to go - it allows you to extract the logic around "get a valid council from the URL"; you can generalize it further if you want to map /<model>/<id> to always retrieving something from a specific model; however - you might want to validate this against a set of possible values to avoid people trying to make you load random Python identifiers in your models class.
from fastapi import Path
def get_council_from_path(council_id: int = Path(...),
db: Session = Depends(get_db),
):
council = crud.council.get_by_id(db=db, id=id)
if not council:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail='Council not found'
)
return council
#app.get('/councils/{council_id}')
def get_council(council: Council = Depends(get_council_from_path)):
pass
You can generalize the dependency definition to make it reusable (I don't have anything available to test this right now, but the idea should work):
def get_model_from_path(model, db: Session = Depends(get_db)):
def get_model_from_id(id: int = Path(...)):
obj = model.get_by_id(db=db, id=id)
if not council:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail='{type(model)} not found'
)
return get_model_from_id
#app.get('/councils/{id}')
def get_council(council: Council = Depends(get_model_from_path(crud.council)):
pass
Here is one solution that works. FastAPI supports classes as dependencies. Therefore I can have a class like this:
class DbObjIdValidator:
def __init__(self, name: str):
if name not in dir(crud):
raise ValueError('Invalid model name specified')
self.crud = getattr(crud, name)
def __call__(self, db: Session = Depends(get_db), *, id: Any):
db_obj = self.crud.get_by_id(db=db, id=id)
if not db_obj:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail='not found'
)
return db_obj
Considering crud module imports the crud classes for all the ORM models. My example is closely following the fastAPI cookiecutter project by Tiangolo, and the crud design pattern he followed.
Now in order to use it, for example in councils_route.py, I can do the following:
router = APIRouter(prefix='/councils')
council_id_dep = DbObjIdValidator('council') # name must be same as import name
#router.get('/{id}', response_model=schemas.CouncilDB)
def get_council_by_id(
id: UUID,
db: Session = Depends(get_db),
council: Councils = Depends(council_id_dep)
):
return council

Using hydra.main on main method

Summary of the problem:
I'm running a requests call on an API endpoint, whose request params are hidden in a config file and I decided to try out hydra to retrieve those params [Reason being the request params do change as I'm working on collecting custom dataset using RapidAPI]
I have created a class called QueryParamsLocations which implements the getter methods to fetch the parameters to be later used by run_query method.
class QueryParamsLocations(QueryParams):
#hydra.main(config_path='configs', config_name='location_query')
def get_params_query_string(self, cfg: DictConfig) -> dict():
return {
'query': cfg.location_params.query,
'locale': cfg.location_params.locale,
'currency': cfg.location_params.currency
}
#hydra.main(config_path='configs', config_name='location_query')
def get_url(self, cfg: DictConfig) -> str():
return cfg.urls.location_url
#hydra.main(config_path='configs', config_name='location_query')
def get_headers(self, cfg: DictConfig) -> dict():
return {
'X-RapidAPI-Host': cfg.headers.x_rapidapi_host,
'X-RapidAPI-Key': cfg.headers.x_rapidapi_key
}
class QueryParams is an abstract class which has these 3 getter templates. run_query method is an external call to run the request.
#hydra.main(config_path='configs', config_name='location_query')
def run_query(cfg: DictConfig) -> None:
try:
LoggerFactory.get_logger('logs/logger.log', 'INFO').info('Running query for location')
qpl = QueryParamsLocations()
response = requests.request("GET", qpl.get_url(cfg), headers=qpl.get_headers(cfg), params=qpl.get_params_query_string(cfg))
print(response.json())
except Exception as e:
LoggerFactory.get_logger('logs/logger.log',
'ERROR').error(f'Error in running query: {e}')
run_query()
While running run_query without if name == 'main': and with it as well , the following error is encountered :
[2022-05-16 13:43:32,614][logs/logger.log][ERROR] - Error in running query: **decorated_main()** takes from 0 to 1 positional arguments but 2 were given
Although newer version of hydra (I'm using hydra-core==1.1.2) uses two arguments while creating cfg object however , I'm not sure as to whether there's other way of handling this as such.
Also, by searching through other threads, following was also tried - Compose API
however, from the docs, it requires an override parameter , which is not needed atm.
Would like to know if any other approach can be tried out. Happy to provide more details if needed.
Definitely use the Compose API and not hydra.main() for this use case.
You can just pass an empty array for your override list if you have nothing to override.

Python Meta Class Inheritance

What am I trying to do?
I am trying to create a base factory boy class with the class meta set as follows:
class DBFactoryBase(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
sqlalchemy_session = common_session
sqlalchemy_session_persistence = 'flush'
I then want to inherit from this and use it in my factories instead of factory.alchemy.SQLAlchemyModelFactory:
class SomeFactory(DBFactoryBase):
class Meta:
model = AnSQLAlchemyModel
fake_line_1 = "A'tuin"
fake_line_2 = 'The currently untitled'
fake_line_3 = '42'
Essentially I want to be able to extend the class Meta instead of overriding it and by updating the base model session I want the other models to inherit that session.
Why am I trying to do this?
I am doing this for convenience, so I don't have to redefine these with all of my factory classes. I am also doing it so that I can override the session whilst running pytest using the below fixture. I don't want my factories defined inside fixtures because I also want to use the same factories to create demo data. Thus I need to pass them a none-test DB session and making the factories as fixtures will stop me from using them in regular scripts.
#pytest.fixture(scope="function")
def test_db(engine_factory, enable_override):
engine = engine_factory
testing_session_local = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base.metadata.drop_all(bind=engine)
Base.metadata.create_all(bind=engine)
api.dependency_overrides[get_db] = enable_override
session: Session = testing_session_local()
DBFactoryBase.sqlalchemy_session = session # <-- Please see here
yield session
# Rollback the session => no changes to the database
session.rollback()
# Remove it, so that the next test gets a new Session()
Base.metadata.drop_all(bind=engine)
If this were a Django-based Model I could access the parent's class meta as an attribute using DBFactoryBase.Meta and pass this into the class meta like below. I am not entirely sure how this works neither do I know how to do this when the original class is not a Django Model.
class SomeFactory(DBFactoryBase):
class Meta(DBFactoryBase.Meta):
model = AnSQLAlchemyModel
fake_line_1 = "A'tuin"
fake_line_2 = 'The currently untitled'
fake_line_3 = '42'

Automatic method delegation in Python

I have a rather contrived code here :
backend_data = {
"admins": ["Leo", "Martin", "Thomas", "Katrin"],
"members": [
"Leo",
"Martin",
"Thomas",
"Katrin",
"Subhayan",
"Clemens",
"Thoralf"
],
"juniors": ["Orianne", "Antonia", "Sarah"]
}
class Backend:
def __init__(self, data):
self.backend_data = data
def get_all_admins(self):
return self.backend_data.get("admins")
def get_all_members(self):
return self.backend_data.get("members")
def get_all_juniors(self):
return self.backend_data.get("juniors")
class BackendAdaptor:
# Does some conversion and validation
def __init__(self, backend):
self.backend = backend
def get_all_admins(self):
return (admin for admin in self.backend.get_all_admins())
def get_all_members(self):
return (member for member in self.backend.get_all_members() if member not in self.backend.get_all_admins())
def get_all_juniors(self):
return (junior for junior in self.backend.get_all_juniors())
if __name__ == "__main__":
backend = Backend(data=backend_data)
adaptor = BackendAdaptor(backend=backend)
print(f"All admins are : {list(adaptor.get_all_admins())}")
print(f"All members are : {list(adaptor.get_all_members())}")
print(f"All juniors are : {list(adaptor.get_all_juniors())}")
So the BackendAdaptor class basically would be used to do some validation and conversion of the data that we get from the Backend .
The client should only be asked to interact with the API of the BackendAdaptor which is exactly similar to that of Backend . The adaptor class sits in middle and gets data from Backend does some validation if required and the gives back the data to client.
The issue is that the validation on the data that is getting returned from the Backend is not done for every method(For ex: there is validation done on get_all_members but not on get_all_admins and also not on get_all_juniors). The method just gives back a generator on whatever data it gets from Backend.
As is the case now i still have to implement a one liner methods for them .
Is there a way in Python to avoid this ? I am thinking in lines of magic methods like __getattribute__ ? But i have no idea on how to do this for methods.
So the best case scenario is this:
I implement the methods for which i know that i have to do some validation on Backend data
For the rest of the methods it is automatically delegated to Backend and then i just return a generator from what i get back
Any help would be greatly appreciated.
You can implement __getattr__. It is only called if a non-existing attribute is accessed. This will return some generic function with the desired functionality.
class BackendAdaptor:
def __init__(self, backend):
self.backend = backend
def __getattr__(self, name):
if not hasattr(self.backend, name):
raise AttributeError(f"'{name}' not in backend.")
return lambda: (i for i in getattr(self.backend, name)())
def get_all_members(self):
return (member for member in self.backend.get_all_members() if member not in self.backend.get_all_admins())

How to define database 'driver' using Groovy sql module

I am trying to connect to an Oracle Sql Developer DB using Groovys sql class, which requires 4 pieces of information: (db url, username, password, db driver) I have all information needed except the driver. I have tried using oracle.jdbc.driver.OracleDrive and have set my GROOVY_HOME as : Variable: %GROOVY_HOME%\lib Value: C:\Oracle_SQL_DEVELOPER\sqldeveloper
I am receiving the following error :
Caused by: java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver
I have referenced a few answers here on StackOverflow but haven't had any luck setting up a connection. Here are the links I've read and tried:
Unable to connect to oracle database from groovy
SQLException: No suitable Driver Found for jdbc:oracle:thin:#//localhost:1521/orcl
I have also looked and researched the Groovy documentation but it is not clear on how to define the driver: https://groovy-lang.org/databases.html#_connecting_with_a_datasource
Any help would be much appreciated. Here is the code for reference:
import groovy.sql.Sql
class EstablishConnection {
def static url = 'url'
def static user = 'user'
def static password = 'pass'
def static driver = 'oracle.jdbc.driver.OracleDriver'
def static sql = Sql.newInstance(url, user, password, driver)
}
EstablishConnection.sql.eachRow('select * from ACCOUNT where CONSTI_ID = \'12345678\';'){
row ->
def a = row[0]
}

Resources