0

I need to create a business query model, in which I need to create a circular dependency, I am using a look a like design of django models to implement it,

#Modeule a.py

import b
class A:
    b_obj = B()
    a_property_1 = ObjectAttribute(b_obj.b_property_1) # a_property_1 is dependent on b_property_1
    a_property_2 = ObjectAttribute(b_obj.b_property_2)


#Module b.py

import a
class B:
        a_obj = A()
        b_property_1 = ObjectAttribute(a_obj.a_property_1)
        b_property_2 = ObjectAttribute(a_obj.a_property_2)

When I execute the above program, it will throw an error, name 'B' is not defined on executing a.py and viceversa.

After that, I did a bit research on this to figure out and findout django models already implemented something like this via ForeignKey

https://docs.djangoproject.com/en/dev/ref/models/fields/#foreignkey

All I need to implement the my ForeignKey module, can some one please help me in understanding the logic and writing the code in below format.

#Modeule a.py

import b
class A:
    b_obj = MyForeignKey('B')
    a_property_1 = ObjectAttribute(b_obj.b_property_1) # a_property_1 is dependent on b_property_1
    a_property_2 = ObjectAttribute(b_obj.b_property_2)


#Module b.py

import a
class B:
        a_obj = MyForeignKey('A')
        b_property_1 = ObjectAttribute(a_obj.a_property_1)
        b_property_2 = ObjectAttribute(a_obj.a_property_2)
Aman Agarwal
  • 727
  • 6
  • 15
  • A side note: in Python 2 always inherit from "object" - you classes inherit form "nothng" (as in `class A:`) - in Python 2 this yields old-sytles classes that do not implement some of the modern mechanisms for attribute fetching, among other incompabilities that could hit you by surprise. – jsbueno Sep 08 '14 at 17:47

1 Answers1

0

There are some ways to do that. One of which would be for your foreign Key to be made as proxy classes to the actuall classes, that on instantiating, just annotate the class model, and forhe next subsequent attribute access instantiate the proxied-to class, and keep its reference, Subsequent attributes would just be redirected to the underlying class.

One mechanism that allows such hooks to be executed on attribute fecth (remebering that in Pyhton a class "method" is just a callable attribute - so it works for methods as well), is to implement the __getattribute__ method.

Let's supose you have a "models" module (or other kind of registry) wher all your models are referenced, after creation -- your code could look more or less like this:

import models

class MyForeignKey(objec):
    def __init__(self, model_name, *args, **kw):
        self._model_name = model_name
        self._args = args
        self._kw = kw

    def _instantiate(self):
        self._object = getattr(models, self._model_name)(*self._args, **self._kw)

    def __getattribute__(self, attr):
        if attr in ("_model_name", "_args", "_kw", "_object", "_instantiate"):
            return object.__getattribute__(self, attr)
        if not hasattr(self, "_object"):
            self._instantiate()
        return getattr(self._object, attr)

    def __setattr__(self, attr, value):
        if attr in ("_model_name", "_args", "_kw", "_object"):
            return object.__setattr__(self, attr, value)
        if not hasattr(self, "_object"):
            self._instantiate()
        return setattr(self._object, attr, value)

Note that (a) your models have to inherit from "object" like I commented in the question and (b) - this is ot complete if you implement "dunder" methods (python double underscore methods) to override behavior on any of the models - in that case, you have to set the appropriate te dunder methods to do the proxying as well.

jsbueno
  • 99,910
  • 10
  • 151
  • 209