Skip to content
Snippets Groups Projects
models.py 3.69 KiB
Newer Older
  • Learn to ignore specific revisions
  • import uuid
    
    from django.contrib.postgres.fields import JSONField
    from django.contrib.contenttypes.fields import GenericForeignKey
    from django.contrib.contenttypes.models import ContentType
    
    from django.db import models, transaction
    
    from django.db.models import Lookup
    from django.db.models.fields import Field
    
    from django.utils import timezone
    from django.urls import reverse
    
    from funkwhale_api.federation import utils as federation_utils
    
    
    
    @Field.register_lookup
    class NotEqual(Lookup):
        lookup_name = "ne"
    
        def as_sql(self, compiler, connection):
            lhs, lhs_params = self.process_lhs(compiler, connection)
            rhs, rhs_params = self.process_rhs(compiler, connection)
            params = lhs_params + rhs_params
            return "%s <> %s" % (lhs, rhs), params
    
    class LocalFromFidQuerySet:
        def local(self, include=True):
            host = settings.FEDERATION_HOSTNAME
            query = models.Q(fid__startswith="http://{}/".format(host)) | models.Q(
                fid__startswith="https://{}/".format(host)
            )
            if include:
                return self.filter(query)
            else:
                return self.filter(~query)
    
    
    
    class MutationQuerySet(models.QuerySet):
        def get_for_target(self, target):
            content_type = ContentType.objects.get_for_model(target)
            return self.filter(target_content_type=content_type, target_id=target.pk)
    
    
    class Mutation(models.Model):
        fid = models.URLField(unique=True, max_length=500, db_index=True)
        uuid = models.UUIDField(unique=True, db_index=True, default=uuid.uuid4)
        created_by = models.ForeignKey(
            "federation.Actor",
            related_name="created_mutations",
            on_delete=models.SET_NULL,
            null=True,
            blank=True,
        )
        approved_by = models.ForeignKey(
            "federation.Actor",
            related_name="approved_mutations",
            on_delete=models.SET_NULL,
            null=True,
            blank=True,
        )
    
        type = models.CharField(max_length=100, db_index=True)
        # None = no choice, True = approved, False = refused
        is_approved = models.NullBooleanField(default=None)
    
        # None = not applied, True = applied, False = failed
        is_applied = models.NullBooleanField(default=None)
        creation_date = models.DateTimeField(default=timezone.now, db_index=True)
        applied_date = models.DateTimeField(null=True, blank=True, db_index=True)
        summary = models.TextField(max_length=2000, null=True, blank=True)
    
        payload = JSONField()
        previous_state = JSONField(null=True, default=None)
    
        target_id = models.IntegerField(null=True)
        target_content_type = models.ForeignKey(
            ContentType,
            null=True,
            on_delete=models.CASCADE,
            related_name="targeting_mutations",
        )
        target = GenericForeignKey("target_content_type", "target_id")
    
        objects = MutationQuerySet.as_manager()
    
        def get_federation_id(self):
            if self.fid:
                return self.fid
    
            return federation_utils.full_url(
                reverse("federation:edits-detail", kwargs={"uuid": self.uuid})
            )
    
        def save(self, **kwargs):
            if not self.pk and not self.fid:
                self.fid = self.get_federation_id()
    
            return super().save(**kwargs)
    
        @transaction.atomic
        def apply(self):
            from . import mutations
    
            if self.is_applied:
                raise ValueError("Mutation was already applied")
    
            previous_state = mutations.registry.apply(
                type=self.type, obj=self.target, payload=self.payload
            )
            self.previous_state = previous_state
            self.is_applied = True
            self.applied_date = timezone.now()
            self.save(update_fields=["is_applied", "applied_date", "previous_state"])
            return previous_state