I have a Django 3 application, using an LDAP service class, like this :
class LDAPService:
def init(self, host: str, user: str, password: str, ssl: bool = True):
...
def bind(): # The connection is done here, __init__ just sets values
....
def create_ou(base: str, ou_name: str):
....
Where (or when) should I initialize the service to use it in views ? The bind step takes about 2 seconds to apply, I can not do it on every request. How can I keep an instance of this class shared, and not done every single time ? I may have a solution using singleton, and/or initializing it in like settings files, but i think there is a better way to do it.
I know in production, there may be multiple workers, so multiple instances, but i am ok with it.
Another question: How can everything above be done, with connections credentials from a database model (so not at django startup, but at anytime)
I am totally new to the django ecosystem, the things i have found about a service layer were all about django models. I want to do the same interface i would do for models in a regular service layer, but working on something else than django models.
I think the LDAP connection itself should not be there, only the CRUD methods, but i do not know where to place it, and how to make django interact with.
Thanks in advance for your suggestions :)
You can use a memoized factory function:
def get_ldap_service() -> LDAPService:
if not hasattr(get_ldap_service, 'instance'):
get_ldap_service.instance = LDAPService(**input_from_somewhere)
return get_ldap_service.instance
This is cleaner than a Singleton and allows for easier testability of the service class.
Additionally, it might be a better design to send the low-level connection logic to another class, say
class LDAPConnection:
def __init__(self, host: str, user: str, password: str, ssl: bool = True):
...
and then your service layer would take that as a dependency at run-time (dependency injection)
class LDAPService:
def __init__(self, connection: LDAPConnection):
self.connection = connection
# CRUD operations
def create_ou(self, base: str, ou_name: str):
# Do operations via self.connection
...
This allows for different connections exposing the same interface.
You can build from these two ideas (dependency injection and caching) to get more complicated general structures in a maintainable way.