示例#1
0
def datastream(cls):
    """
    Class decorator to track model changes to datastream

    Usage

    @datastream
    class MyModel(Model):
        ...
        def iter_changed_datastream(self, changed_fields=None):
           yield <datastream name>, <object id>
           ...
           yield <datastream name>, <object id>
    """
    if hasattr(cls, "iter_changed_datastream"):
        if is_document(cls):
            mongo_signals.post_save.connect(_on_document_change, sender=cls)
            mongo_signals.pre_delete.connect(_on_document_change, sender=cls)
        else:
            django_signals.post_save.connect(_on_model_change, sender=cls)
            django_signals.pre_delete.connect(_on_model_change, sender=cls)
    return cls
示例#2
0
    def model(cls, m_cls):
        """
        Decorator to denote models with labels.
        Contains field validation and `effective_labels` generation.

        Usage:
        ```
        @Label.model
        class MyModel(...)
        ```

        Adds pre-save hook to check and process `.labels` fields. Raises ValueError
        if any of the labels is not exists.

        Target model may have `iter_effective_labels` method with following signature:
        ```
        def iter_effective_labels(self) -> Iterable[List[str]]
        ```
        which may yield a list of effective labels from related objects to form
        `effective_labels` field.

        :param m_cls: Target model class
        :return:
        """
        def default_iter_effective_labels(instance) -> Iterable[List[str]]:
            yield instance.labels or []

        def on_pre_save(sender, instance=None, document=None, *args, **kwargs):
            instance = instance or document
            # Clean up labels
            labels = Label.merge_labels(
                default_iter_effective_labels(instance))
            instance.labels = labels
            # Build and clean up effective labels
            can_expose_label = getattr(sender, "can_expose_label",
                                       lambda x: True)
            labels_iter = getattr(sender, "iter_effective_labels",
                                  default_iter_effective_labels)
            instance.effective_labels = [
                ll.name for ll in Label.objects.filter(
                    name__in=Label.merge_labels(labels_iter(instance)))
                if can_expose_label(ll)
            ]
            # Validate all labels
            all_labels = set(instance.labels) | set(instance.effective_labels)
            can_set_label = getattr(sender, "can_set_label", lambda x: True)
            for label in Label.objects.filter(name__in=list(all_labels)):
                if not can_set_label(label):
                    # Check can_set_label method
                    raise ValueError(f"Invalid label: {label.name}")
                all_labels.discard(label.name)
            if all_labels:
                raise ValueError(f"Invalid labels: {', '.join(all_labels)}")

        # Install handlers
        if is_document(m_cls):
            from mongoengine import signals as mongo_signals

            mongo_signals.pre_save.connect(on_pre_save,
                                           sender=m_cls,
                                           weak=False)
        else:
            from django.db.models import signals as django_signals

            django_signals.pre_save.connect(on_pre_save,
                                            sender=m_cls,
                                            weak=False)
        return m_cls