django-neomodel icon indicating copy to clipboard operation
django-neomodel copied to clipboard

Signal when creating a new relationship?

Open adriancarayol opened this issue 8 years ago • 16 comments

Is there a way to use django-signals every time a new relationship is created?

Thanks.

adriancarayol avatar Jun 08 '17 17:06 adriancarayol

Solved.

class **FriendRel**(StructuredRel):
    since = DateTimeProperty(default=lambda: datetime.now(pytz.utc))
    met = StringProperty()

class Person(StructuredNode):
    name = StringProperty()
    friends = RelationshipTo('Person', 'FRIEND', model=**FriendRel**)

adriancarayol avatar Jun 10 '17 11:06 adriancarayol

Nice one. I am interested know how this works?

On 10 Jun 2017 12:43, "Adrián" [email protected] wrote:

Solved.

`class FriendRel(StructuredRel): since = DateTimeProperty(default=lambda: datetime.now(pytz.utc)) met = StringProperty()

class Person(StructuredNode): name = StringProperty() friends = RelationshipTo('Person', 'FRIEND', model=FriendRel)`

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/robinedwards/django-neomodel/issues/8#issuecomment-307560141, or mute the thread https://github.com/notifications/unsubscribe-auth/AAJ0YchND5rTumEQKneSCgdkVkWP3WUtks5sCoFPgaJpZM4N0Zea .

robinedwards avatar Jun 10 '17 11:06 robinedwards

One solution would be to extend StructuredRel, like that:

class DjangoRel(StructuredRel):
    def __init__(self, *args, **kwargs):
        super(DjangoRel, self).__init__(*args, **kwargs)

    __all_properties__ = ()

    @classproperty
    def _meta(self):
        if hasattr(self.Meta, 'unique_together'):
            raise NotImplementedError('unique_together property not supported by neomodel')

        opts = Options(self.Meta, app_label=self.Meta.app_label)
        opts.contribute_to_class(self.__class__, self.__class__.__name__)

        for key, prop in self.__all_properties__:
            opts.add_field(DjangoField(prop, key), getattr(prop, 'private', False))

        return opts

    def pre_save(self):
        if getattr(settings, 'NEOMODEL_SIGNALS', True):
            self._creating_node = getattr(self, 'id', None) is None
            signals.pre_save.send(sender=self.__class__, instance=self)

    def post_save(self):
        if getattr(settings, 'NEOMODEL_SIGNALS', True):
            created = None
            #delattr(self, '_creating_node')
            signals.post_save.send(sender=self.__class__, instance=self, created=created)

    def pre_delete(self):
        if getattr(settings, 'NEOMODEL_SIGNALS', True):
            signals.pre_delete.send(sender=self.__class__, instance=self)

    def post_delete(self):
        if getattr(settings, 'NEOMODEL_SIGNALS', True):
            signals.post_delete.send(sender=self.__class__, instance=self)

class FriendRel(DjangoRel):
    weight = IntegerProperty(default=0)

    class Meta:
        app_label = 'django_rel'

It may not be an elegant solution, but it works (at least in post_save).

I'm still working, I hope to improve it soon.

adriancarayol avatar Jun 10 '17 12:06 adriancarayol

I think overwriting the "disconnect" method of "RelationshipManager" can be implemented "disconnect-signal".

Something like that:

in relationship_manager.py:

@check_source
    def disconnect(self, node):
        """
        Disconnect a node

        :param node:
        :return:
        """
        rel = _rel_helper(lhs='a', rhs='b', ident='r', **self.definition)
        q = "MATCH (a), (b) WHERE id(a)={self} and id(b)={them} " \
            "MATCH " + rel + " DELETE r"
        self.source.cypher(q, {'them': node.id})
        signals.post_disconnect.send(sender=None, uid=node.uid, title=node.title)

in signals.py:

from django.dispatch import Signal
post_disconnect = Signal(providing_args=["uid", "title"])

in views.py

def disconnect_handler(sender, **kwargs):
    """signal intercept for post_disconnect"""
    uid = kwargs['uid']
    ...

post_disconnect.connect(disconnect_handler)

adriancarayol avatar Jun 10 '17 14:06 adriancarayol

Cool I can see how this would be useful.

I guess there is the edge case of disconnect though when a connected node gets deleted? Also how would you handle nodes that were connected or disconnected elsewhere in your code base via a manual cypher query?

On 10 June 2017 at 15:06, Adrián [email protected] wrote:

I think overwriting the "disconnect" method of "RelationshipManager" can be implemented "disconnect-signal".

Something like that:

in relationship_manager.py:


@check_source
def disconnect(self, node):
"""
Disconnect a node

    :param node:
    :return:
    """
    rel = _rel_helper(lhs='a', rhs='b', ident='r', **self.definition)
    q = "MATCH (a), (b) WHERE id(a)={self} and id(b)={them} " \
        "MATCH " + rel + " DELETE r"
    self.source.cypher(q, {'them': node.id})
    signals.post_disconnect.send(sender=None, uid=node.uid, title=node.title)

**in signals.py:**


from django.dispatch import Signal
post_disconnect = Signal(providing_args=["uid", "title"])

**in views.py**


def disconnect_handler(sender, **kwargs):
"""signal intercept for post_disconnect"""
uid = kwargs['uid']
...

post_disconnect.connect(disconnect_handler)

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<https://github.com/robinedwards/django-neomodel/issues/8#issuecomment-307567188>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAJ0YZT7TVPU2BvKrEd0aryNeMZyEW_rks5sCqLTgaJpZM4N0Zea>
.

robinedwards avatar Jun 10 '17 14:06 robinedwards

I think that post_save is called when save() method of RelationRel is called.

connect() method call save()?

adriancarayol avatar Jun 10 '17 16:06 adriancarayol

Nope the connect() method executes a cypher query under the hood for speed.

On 10 June 2017 at 17:12, Adrián [email protected] wrote:

I think that post_save is called when save() method of RelationRel is called.

connect() method call save()?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/robinedwards/django-neomodel/issues/8#issuecomment-307574575, or mute the thread https://github.com/notifications/unsubscribe-auth/AAJ0Ycg3t10jKZvdamFgwRx737JJic9tks5sCsBZgaJpZM4N0Zea .

robinedwards avatar Jun 10 '17 16:06 robinedwards

Uhm... When i "connect" a new relation post_save is called, but, when i "disconnect" pre/post_delete isnt called.

adriancarayol avatar Jun 10 '17 16:06 adriancarayol

Aha!

I forgot:

https://github.com/robinedwards/neomodel/blob/master/neomodel/relationship_manager.py#L104

I guess for delete to work we need to inflate the model before deleting it which would mean an extra query..

On 10 June 2017 at 17:51, Adrián [email protected] wrote:

Uhm... When i "connect" a new relation post_save is called, but, when i "disconnect" pre/post_delete isnt called.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/robinedwards/django-neomodel/issues/8#issuecomment-307576939, or mute the thread https://github.com/notifications/unsubscribe-auth/AAJ0YZ3Lq7GShCCxab-9M4zyecKKV2I4ks5sCsmsgaJpZM4N0Zea .

robinedwards avatar Jun 10 '17 16:06 robinedwards

I think it would be very useful to use signals in the operations (like connect or disconnect) of relationships.

adriancarayol avatar Jun 10 '17 18:06 adriancarayol

Agreed, as long as the edge cases are well documented.

I guess it should be a specific signal 'connect' and 'disconnect' as we cant piggy back save and delete.

Another thing is there shouldnt be delete signals on StructuredRel as I believe I removed the delete method.. This needs to be more clear in the docs.

robinedwards avatar Jun 10 '17 19:06 robinedwards

Completely agree with you.

Specific signal for 'connect' and 'disconnect' is the best option.

adriancarayol avatar Jun 10 '17 19:06 adriancarayol

Any progress on this?

aaronst avatar Aug 12 '17 16:08 aaronst

@aaronst Unfortunately, I could not have time to do it

adriancarayol avatar Aug 14 '17 10:08 adriancarayol

@adriancarayol in your pre_save, is id referring to the internal id of the node, or did you explicitly set id = UniqueIdProperty()?

So as I understand it, we can use your DjangoRel class for listeners on creating connections, but deleting connections is still up in the air? thank you for your efforts =)

ghost avatar Apr 19 '18 01:04 ghost

@laynetrain i refer id to the internal id of the node 👍

I would like to take time out within a few weeks to complete this problem

adriancarayol avatar Apr 19 '18 11:04 adriancarayol