WTF is this context thing in CKAN
If you've taken a look at the ckan source code, you'll have come across ‘context’ as the first parameter in many of the functions. It basically contains all threadlocal information required for a function to execute. It's taken me far too long to understand why they exist and I currently think contexts in their current state are pointless.
I'm assuming contexts only contain model, session and user. I get why context exists, it basically comes down to whether
- you prefer passing parameters for the request/session/user around into functions, so that the parameters define exactly what the function needs to execute. If we do this though, these are ‘different’ to other parameters which is why they've been seperated into context/data_dict. Your functions do not have to refer to some magical global object to execute.
- Or whether you are happy using threadlocals, so that if your logic function, calls another function that calls another, that calls …, that calls function
f
,f
can easily get the request/session/user by calling the flaskg
object/pylonsg
thing to get the user without having to muddy up all the other previous function callers definitions. Your compromise is that you have a magical ‘global’ object (saymodel.Session
) but you can access it anywhere in the code safely without thinking about it too much.
There is no right answer, the guy who wrote flask wrote this blog post detailing his hate for thread locals before he wrote flask, where the decision was made to use them, I know that David R is not a fan of them
The problem I think we have, is that ckan straddles both sides and we end up with a worse, really confusing, solution. Currently in ckan, we use the scoped_session, which is a threadlocal based setup for sqlalchemy, So when you pass ckan.model
from your extension into an action function, unless theres is some additional voodoo(possibly vdm, i haven't looked at that code), then ckan.model.Session
and context['session']
are currently always the same. This is incredibly confusing, especially to new ckan developers.
so if context['session']
is the current sqlalchemy session, that we're passing around, then why are we passing context['model']
around? Well it's all to do with the model code. Take a look at model/package.py
and you'll see it using meta.Session.query
all over the shop. For example,
# ckan/model/package.py
import meta
class Package(...):
# ...
@classmethod
def get(cls, reference):
"""Returns a package object referenced by its id or name."""
query = meta.Session.query(cls).filter(cls.id == reference)
pkg = query.first()
if pkg == None:
pkg = cls.by_name(reference)
return pkg
In much of the logic layer you'll see that all the action functions begin
# ckan/logic/action/get.py
def package_show(context, data_dict):
model = context["model"]
session = context["session"]
pkg = model.Package.get(name_or_id)