Let's say we have such situation:
from django_tenants.utils import schema_context
def do_something(context):
print("do_something")
def my_callable():
tenant = "db_tenant"
with schema_context(tenant):
context = {"a": 1, "b": 2}
do_something(context)
my_callable()
And question is: It's possible to access current tenant name in do_something function without passing it as parameter or store it as global variable
I found a solution but i don't know if it's stable. So, current tenant name (or schema_name) can be accessed through django.db connection as follow:
from django.db import connection
schema_name = connection.schema_name
No this is not possible, or at least will require some magical engineering to do so.
I'm assuming the only reason you wouldn't want to pass it as a parameter is because other things may be calling do_something as well that wouldn't be passing tenant as a parameter. In this case do:
def do_something(context, tentant=None):
if tenant:
print (tenant)
else:
print ("do_something")
Now you can call do_something with do_something(context, tenant='Bob') or do_something(context) and either will be fine.
Related
I have a server which can accept 0, 1 or many of the following url arguments:
/api/cases?id={id}&name={name}&owner={owner}&status={status}
So these, amongst other, are correct:
/api/cases?owner=me
/api/cases
/api/cases?name=bob&status=waiting
Currently, my code looks like this
routes = [(r'/cases?([^/]+)', MyHandler)]
tornado.web.Application.__init__(self, routes, settings={})
class MyHandler(APIHandler):
ACCEPTED_URL_ARGS = ["id", "name", "owner", "status"]
def get(self, i):
for key in self.request.arguments:
if key not in self.ACCEPTED_URL_ARGS:
# error
Is there a better way to check for the url arguments?
What you have is correct. In Tornado there is no other way to verify that you only got the arguments that you expect than to iterate over self.request.arguments.
As Ben says, this is the correct way to do it in Tornado. That being said, the better place for your test would be the prepare method; also, a strictly more "pythonic" approach would be to use sets:
class MyHandler(APIHandler):
ACCEPTED_URL_ARGS = {"id", "name", "owner", "status"}
def prepare(self):
unwanted_args = self.ACCEPTED_URL_ARGS - set(self.request.arguments)
if unwanted_args:
# error
I'm trying to find a way to store a function in a Django Model.
My use case is an emailer system with planned and conditional sending.
"send this email tomorrow if John has not logged in".
Ideally I would like to create emails with a test function field.
def my_test(instance):
if (now() - instance.user.last_login) > timedelta(days=1):
return False
return True
Email(send_later_date=now() + timedelta(days=1),
conditional=my_test)
Here is what I'm thinking about
import importlib
Email(send_later_date=now() + timedelta(days=1),
conditional='my_app.views.my_test')
class Email(models.Model):
send_later_date = models.DateTimeField(null=True, blank=True)
conditional = models.TextField()
def send_email(self):
if self.send_later_date < now():
if not self.conditional:
function_send_email()
else:
function_string = self.conditional
mod_name, func_name = function_string.rsplit('.',1)
mod = importlib.import_module(mod_name)
func = getattr(mod, func_name)
if func():
function_send_email()
How would you do that? I was thinking of storing the function name in a Text field. How would you run it once needed? ImportLib seems interesting...
Storing the function name is a valid aproach, since it is actually the one used by the pickle module. This has the benefit (in your case) that if you update the code of a function, it will automatically apply to existing e-mails (but be careful with backward-compatibility).
For convenience and safety (especially if the function name could come from user inputs), you may want to keep all such functions in the same module, and store only the function name in DB, not the full import path. So you could simply import the function repository and read it with getattr. I imagine you will also want to give parameters to these functions. If you don't want to restrict yourself to a given number / order / type of arguments, you could store in DB a dictionary as a JSON string, and expand it with the ** operator.
import my_app.func_store as store
import json
func = getattr(store, self.conditional)
params = json.loads(self.conditional_params)
if func(**params):
function_send_email()
I have a test class and a setup function that looks like this:
#pytest.fixture(autouse=True, scope='function')
def setup(self, request):
self.client = MyClass()
first_patcher = patch('myclass.myclass.function_to_patch')
first_mock = first_patcher.start()
first_mock.return_value = 'foo'
value_to_return = getattr(request, 'value_name', None)
second_patcher = patch('myclass.myclass.function_two')
second_mock = second_patcher.start()
second_mock.return_value = value_to_return
#could clean up my mocks here, but don't care right now
I see in the documentation for pytest, that introspection can be done for a module level value:
val = getattr(request.module, 'val_name', None)
But, I want to be able to specify different values to return based on the test I am in. So I am looking for a way to introspect the test_function not the test_module.
http://pytest.org/latest/fixture.html#fixtures-can-introspect-the-requesting-test-context
You can use request.function to get to the test function. Just follow the link on the b wepage you referenced to see what is available on the test request object :)
Maybe the documentation has changed since the time of the accepted answer.
At least for me it was not clear how to
Just follow the link
So I thought I'd update this thread with the link itself:
https://pytest.org/en/6.2.x/reference.html#request
Edit December 2021
Even when the link is correct now I think this statement from the pytest documentation is just not correct:
Fixture functions can accept the request object to introspect the “requesting” test function ...
While I found some examples for getting attributes of the module I did not find a single working example of introspecting the test function that requests the fixture. May be related to collection and runtime order.
What really helped me to get the desired behavior was to use the factory idiom a little further down in the pytest documentation:
https://pytest.org/en/6.2.x/fixture.html#factories-as-fixtures
Set up the fixture factory
#pytest.fixture(scope='function')
def getQueryResult() -> object:
def _impl(_mrId: int = 7622):
return QueryResult(_mrId)
return _impl
Usage
# Concrete value
def test_foo(getQueryResult):
queryResult = getQueryResult(4711)
...
# Default value
def test_bar(getQueryResult):
queryResult = getQueryResult()
...
I am accessing the class from the code api_service.py, which can be found here. When I call the first function, I have no problem, because no variables are passed:
from api_service import ApiService
import json
def main():
api_key = *removed*
access_token = *removed*
calling = ApiService(api_key,access_token)
survey_list = calling.get_survey_list()
But when I use the same type of routine as above to call a function from ApiService that requires a variable, I'm told that I should pass an object.
survey_details = calling.get_survey_details("1234")
survey_details = json.loads(json.dumps(survey_details))
print survey_details
The specific error message:
{u'status': 3, u'errmsg': u"Value '1234' for field '_data' is not of type object"}
Details for the get_survey_details aspect of the SurveyMonkey API are here, although I think a python-guru can solve this without knowing about the API.
This is a javascript/json object:
{field:'value'}
You have passed a string which, doesn't count as an "object" for these purposes.
Note that the error message is being generated by the service you are accessing. This question would be better directed to the creator of the service.
I have a class that is taking in an Id and trying to update the variable current_account but when I print out the details of the current_account it hasn't updated.
Anyone got any ideas for this? New to python so might be doing something stupid that I can't see.
class UserData:
def __init__(self, db_conn=None):
if None == db_conn:
raise Exception("DB Connection Required.")
self.db = db_conn
self.set_my_account()
self.set_accounts()
self.set_current_account()
def set_current_account(self, account_id=None):
print account_id
if None == account_id:
self.current_account = self.my_account
else:
if len(self.accounts) > 0:
for account in self.accounts:
if account['_id'] == account_id:
self.current_account = account
print self.current_account['_id']
else:
raise Exception("No accounts available.")
Assume that set_my_account() gets a dictionary of account data and that set_accounts() get a list of dictionaries of account data.
So when I do the following:
user_data = UserData(db_conn=db_conn)
user_data.set_current_account(account_id=account_id)
Where db_conn is a valid database connection and account_id is a valid account id.
I get the following out of the above two lines.
None
518a310356c02c0756764b4e
512754cfc1f3d16c25c350b7
So the None value is from the declaration of the class and then the next two are from the call to set_current_account(). The first id value is what I'm trying to set. The second id value is what was already set from the class __init__() method.
There were a lot of redundancies an un-Pythonic constructions. I cleaned up the code to help me understand what you trying to do.
class UserData(object):
def __init__(self, db_conn):
self.db = db_conn
self.set_my_account()
self.set_accounts()
self.set_current_account()
def set_current_account(self, account_id=None):
print account_id
if account_id is None:
self.current_account = self.my_account
else:
if not self.accounts:
raise Exception("No accounts available.")
for account in self.accounts:
if account['_id'] == account_id:
self.current_account = account
print self.current_account['_id']
user_data = UserData(db_conn)
user_data.set_current_account(account_id)
You used default arguments (db_conn=None) when a call without an explicit argument is invalid. Yes, you can now call __init__(None) but you could also call __init__('Nalum'); you can't protect against everything.
By moving the "No accounts" exception the block fast-fails and you save one level of indention.
The call UserData(db_conn=db_conn) is valid but unecessarily repetitive.
Unfortunately, I still can't figure out what you are trying to accomplish and this is perhaps the largest flaw. Variable names are terribly important for help the reader (which may be the future you) make sense of code. current_account, my_account, account_id and current_account['_id'] so obscure the intention that you should really consider more distinct, informative names.
Figured out what it was.
The data was being changed else where in the code base. It is now working as expected.
Thanks guys for pointing out the Python centric things that I was doing wrong, good to get it.