Feature: parent subscriptions
This commit is contained in:
parent
b615899121
commit
598baa5fa1
@ -31,6 +31,9 @@ An object allowing a User to be notified of any change on a resource or a contai
|
||||
| -------- | ---------- | ------- | ------------------------------------------------------------ |
|
||||
| `object` | `URLField` | | ID of the resource or the container to watch |
|
||||
| `inbox` | `URLField` | | ID of the inbox to notify when the resource or the container change |
|
||||
| `field` | `CharField` | | (optional) if set, then object['field'] will be sent in the notification, not object |
|
||||
|
||||
For convenience, when you create a subscription on an object, DjangoLDP-Notification will parse the object for any one-to-many nested field relations. It will then create nested-subscriptions, i.e. a subscription on the nested field which sends an update to the same inbox, passing the parent model. If this behaviour is undesired you can delete the `Subscription` instance
|
||||
|
||||
# Middlewares
|
||||
|
||||
|
@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2020-06-02 10:35
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('djangoldp_notification', '0005_auto_20200505_1733'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='parent',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='djangoldp_notification.Subscription'),
|
||||
),
|
||||
]
|
24
djangoldp_notification/migrations/0007_auto_20200604_1055.py
Normal file
24
djangoldp_notification/migrations/0007_auto_20200604_1055.py
Normal file
@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2020-06-04 10:55
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('djangoldp_notification', '0006_subscription_parent'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='subscription',
|
||||
name='parent',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='subscription',
|
||||
name='field',
|
||||
field=models.CharField(blank=True, help_text='if set to a field name on the object model, the field will be passed instead of the object instance', max_length=255, null=True),
|
||||
),
|
||||
]
|
@ -7,7 +7,7 @@ from django.db import models
|
||||
from django.db.models.signals import post_save, post_delete
|
||||
from django.dispatch import receiver
|
||||
from django.template import loader
|
||||
from django.urls import NoReverseMatch
|
||||
from django.urls import NoReverseMatch, get_resolver
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from djangoldp.fields import LDPUrlField
|
||||
from djangoldp.models import Model
|
||||
@ -53,6 +53,8 @@ class Notification(Model):
|
||||
class Subscription(Model):
|
||||
object = models.URLField()
|
||||
inbox = models.URLField()
|
||||
field = models.CharField(max_length=255, blank=True, null=True,
|
||||
help_text='if set to a field name on the object model, the field will be passed instead of the object instance')
|
||||
|
||||
def __str__(self):
|
||||
return '{}'.format(self.object)
|
||||
@ -62,30 +64,42 @@ class Subscription(Model):
|
||||
authenticated_perms = ["add", "view", "delete"]
|
||||
permission_classes = [SubscriptionsPermissions]
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# save subscriptions for nested fields
|
||||
if self.pk is None and not self.is_backlink and self.object.startswith(settings.SITE_URL):
|
||||
|
||||
@receiver(post_save, sender=Subscription, dispatch_uid="nested_subscriber_check")
|
||||
def create_nested_subscribers(sender, instance, created, **kwargs):
|
||||
# save subscriptions for one-to-many nested fields
|
||||
if created and not instance.is_backlink and instance.object.startswith(settings.SITE_URL):
|
||||
try:
|
||||
# object is a WebID.. convert to local representation
|
||||
local = Model.resolve(self.object.replace(settings.SITE_URL, ''))[0]
|
||||
# object is a WebID.. convert to local representation
|
||||
local = Model.resolve(instance.object.replace(settings.SITE_URL, ''))[0]
|
||||
nested_fields = Model.get_meta(local, 'nested_fields', [])
|
||||
|
||||
for nested_field in nested_fields:
|
||||
nested_url = str(self.object) + '1/' + nested_field + '/'
|
||||
|
||||
# we have the nested_url, but we want the model contained within's container
|
||||
nested_container = Model.resolve(nested_url)[0]
|
||||
try:
|
||||
field = local._meta.get_field(nested_field)
|
||||
nested_container = field.related_model
|
||||
nested_container_url = Model.absolute_url(nested_container)
|
||||
|
||||
# check a Subscription on this pair doesn't exist already
|
||||
existing_subscriptions = Subscription.objects.filter(object=nested_container_url, inbox=self.inbox)
|
||||
if field.one_to_many:
|
||||
# get the nested view set
|
||||
nested_url = str(instance.object) + '1/' + nested_field + '/'
|
||||
view, args, kwargs = get_resolver().resolve(nested_url.replace(settings.SITE_URL, ''))
|
||||
# get the reverse name for the field
|
||||
field_name = view.initkwargs['nested_related_name']
|
||||
|
||||
if field_name is not None and field_name != '':
|
||||
# check that this nested-field subscription doesn't already exist
|
||||
existing_subscriptions = Subscription.objects.filter(object=nested_container_url, inbox=instance.inbox,
|
||||
field=field_name)
|
||||
# save a Subscription on this container
|
||||
if not existing_subscriptions.exists():
|
||||
Subscription.objects.create(object=nested_container_url, inbox=self.inbox, is_backlink=True)
|
||||
Subscription.objects.create(object=nested_container_url, inbox=instance.inbox, is_backlink=True,
|
||||
field=field_name)
|
||||
except:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
|
||||
super(Subscription, self).save(*args, **kwargs)
|
||||
|
||||
|
||||
# --- SUBSCRIPTION SYSTEM ---
|
||||
@ -94,6 +108,7 @@ class Subscription(Model):
|
||||
def send_notification(sender, instance, **kwargs):
|
||||
if sender != Notification:
|
||||
threads = []
|
||||
recipients = []
|
||||
try:
|
||||
url_container = settings.BASE_URL + Model.container_id(instance)
|
||||
url_resource = settings.BASE_URL + Model.resource_id(instance)
|
||||
@ -102,11 +117,21 @@ def send_notification(sender, instance, **kwargs):
|
||||
|
||||
# dispatch a notification for every Subscription on this resource
|
||||
for subscription in Subscription.objects.filter(models.Q(object=url_resource) | models.Q(object=url_container)):
|
||||
if not instance.is_backlink:
|
||||
if not instance.is_backlink and subscription.inbox not in recipients and \
|
||||
(not subscription.is_backlink or not kwargs.get("created")):
|
||||
# I may have configured to send the subscription to a foreign key
|
||||
if subscription.field is not None and len(subscription.field) > 1:
|
||||
instance = getattr(instance, subscription.field, instance)
|
||||
try:
|
||||
url_resource = settings.BASE_URL + Model.resource_id(instance)
|
||||
except NoReverseMatch:
|
||||
continue
|
||||
|
||||
process = Thread(target=send_request, args=[subscription.inbox, url_resource, instance,
|
||||
kwargs.get("created", False)])
|
||||
process.start()
|
||||
threads.append(process)
|
||||
recipients.append(subscription.inbox)
|
||||
|
||||
|
||||
def send_request(target, object_iri, instance, created):
|
||||
|
Loading…
Reference in New Issue
Block a user