1

Let's assume that we the following ndb model:

class MyModel(ndb.Model):
    x = ndb.StringProperty()
    y = ndb.StringProperty()
    z = ndb.StringProperty(repeated=True)

We have a method that creates a query for the above model, executes it and fetch the results. However, we want this query to be modified my other functions. Specifically, we have the following:

def method_a():
    qry = MyModel.query() 
    values = {'query':qry}
    method_b(**values)
    entities = qry.fetch()

def method_b(**kwargs):
    k = ['a', 'b', 'c']
    qry = kwargs['query']
    qry.filter(MyModel.z.IN(k))

The problem is that the Query object is immutable, and thus it cannot be modified by method_b. Also, based on the specific architecture of the code, we cannot have method_b to return the new Query to method_a.

Any ideas on how to achieve the aforementioned functionality in another way??

Update: Please check the architecture of my code as presented below:

First, in a configuration file we specify a list of modules and if they are enabled or not. These modules affect the filters of the query we want to execute.

testparams = {
    'Test1': True,
    'Test2': True,
    'Test3': False,
    'Test4': True
}

Then, we have a method somewhere in the code that makes a query after the appropriate modules have been executed. Thus, it seems like this:

def my_func():
    qry = MyEntity.query()

    # modules
    query_wrapper = [qry]
    values = {'param':'x', 'query_wrapper':query_wrapper} #other values also
    execute_modules(**values)
    # get query and add some more things, like ordering
    entities = query_wrapper[0].fetch()

The execute_modules function is the following:

def execute_modules(**kwargs):
    for k in config.testparams:
        if config.testparams[k]:
            if kwargs['param'] == 'x':
                (globals()[k]).x(**kwargs)
            elif kwargs['param'] == 'y':
                (globals()[k]).y(**kwargs)

Finally, an indicative module is similar to the following:

class Test1():
    @classmethod
    def x(cls, *args, **kwargs):
        qry = kwargs['query_wrapper'][0]
        # do some stuff like adding filters
        kwargs['query_wrapper'][0] = qry

Any proposals to modify this architecture to a better approach?

Thanos Makris
  • 3,115
  • 2
  • 17
  • 26
  • "we cannot have method_b to return the new Query to method_a" - why not? It's difficult to provide helpful answers if you have invisible constraints that your sample code doesn't demonstrate. – Nick Johnson Oct 04 '12 at 10:04
  • It was an constraint based on the architecture of my code, that's why I didn't put in the sample code. The whole point of the question was to find an alternative to modify the immutable ndb.Query object. – Thanos Makris Oct 04 '12 at 10:11
  • But there may well be a better solution if you show us how your code is structured. Wrapping an immutable object in a mutable one to 'pass by reference' is a nasty, nasty hack. – Nick Johnson Oct 04 '12 at 10:12
  • @NickJohnson Please check the updated information that presents the architecture of my code. Any proposals are more than welcome! – Thanos Makris Oct 13 '12 at 08:07
  • Looking at your code, why not make the `execute_modules` function take an explicit query object, which it passes to each module executed, replacing it with the module's return value? – Nick Johnson Oct 14 '12 at 19:44

3 Answers3

1

I'm not aware of a way to do this without having method_b either return or change a referenced parameter. You should use a technique to pass a variable by reference, like passing a class with parameters.

Community
  • 1
  • 1
mjibson
  • 16,852
  • 8
  • 31
  • 42
1

You can pass in the args in a refrence object such as a dict/list:

def modify_query(kwargs):
  kwargs['qry'] = kwargs['qry'].filter(MyModel.z.IN(k))

qry = MyModel.query()
kwargs = {'qry': qry}
modify_query(kwargs)
result = kwargs['qry'].fetch()

It should be noted that this is an extremly dirty way to accomplish what you want to accomplish. Similarly, if you pass in a list with say one object, then you can modify the contents of said list (through assignment) to modify the object:

def modify_query(list_object):
  list_object[0] = list_object[0].filter(...)
someone1
  • 3,570
  • 2
  • 22
  • 35
0

You can do some hack for replace it object by other. For example:

def f(args):
  qry = args[0]
  qry_new = qry.filter(Model.a == 2)
  args[0] = qry_new

qry = Model.query()
args = [qry]
f(args)
qry = args[0]
Rekby
  • 679
  • 1
  • 6
  • 13