EMiDU
EMiDU

Reputation: 684

Django - how to assign custom object as model property and get that model instance in that object?

I want to separate some logic from model and group it in a property, just like Django does with model managers (object property). I fact, something like ForeignKey but without database representation.

I have something like this:

class Remote(object):
    def __init(self, *args, **kwargs)
        self.post = ... # how to get to post instance?

    def synchronize(self):
       # this function requires Post object access
       print(self.post.name)

class Post(models.Model):
    name = models.CharField(max_length=100)
    remote = Remote()

...

for post in Post.objects.all():
    post.remote.synchronize()

Question

How to modify above code to get access to Post object in Remote object?

Additional question

Is it possible to determine if Remote object has been called from Post instance (post.remote... – like above) or Post class (Post.remote...)?

Upvotes: 2

Views: 1173

Answers (1)

Olzhas Arystanov
Olzhas Arystanov

Reputation: 986

What you want here can be achieved with descriptors.

In order for it to work, you need to define a __get__ method in your class that you want to be accessible as an attribute of another class.

A simple example for your case will look like this:

class Remote:
    def __init__(self, post)
        self.post = post

    def synchronize(self):
       print(self.post.name)


class RemoteDescriptor:
    def __get__(self, obj):
        if not obj:
            return self
        remote = getattr(obj, '_remote', None)
        if not remote:
            remote = Remote(obj)
            obj._remote = remote
        return remote


class Post(models.Model):
    name = models.CharField(max_length=100)
    remote = RemoteDescriptor()

Explanation:

In the above code, every time you call an attribute remote of your Post model, __get__ method of the RemoteDescriptor will be invoked. First check for obj is to make sure that descriptor is called from other object, not directly. Two classes Remote and RemoteDescriptor are needed here in order for you to be able to add custom methods inside your descriptor accessible using dot (e.g. post.remote.calculate())

Note also that I am placing the instance of Remote to the dict of Post on first invokation and on all subsequent calls, object will be returned from there.

You should also check a great article on descriptors on RealPython.

Upvotes: 3

Related Questions