Stevy
Stevy

Reputation: 3387

Django change field for each object in ManyToManyField

I have two models with a ManyToMany relationship like this:

# models.py

class Fileset(models.Model):
    fileset_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
    name = models.CharField(max_length=50)
    in_content_build = models.BooleanField(default=False)


class ContentBuild(models.Model):
    content_build_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
    filesets = models.ManyToManyField(Fileset, related_name='content_filesets')

When a ContentBuild is created I want all the Filesets that are in this ContentBuild to have their in_content_build field set to True.

I tried to achieve this by using a post_save signal but I have no idea how to get all the related Filesets.

My attempt at a signal:

# signals.py

@receiver(post_save, sender=ContentBuild)
def set_fileset_as_deployed(sender, instance, **kwargs):
    try:
        content_build = ContentBuild.objects.get(content_build_uuid=instance.content_build_uuid)
        for fileset in content_build.filesets:
        fileset.in_content_build = True
        fileset.save()
    except ContentBuild.DoesNotExist:
        pass

How would I be able to set in_content_build to True for all Filesets in the created ContentBuild instance?

Upvotes: 0

Views: 42

Answers (3)

schillingt
schillingt

Reputation: 13731

You can't really do what you want in a signal because post_save will be sent before the filesets is set. You should handle this wherever you are creating the instance rather than trying to do it within a signal.

content_build = create_content_build() # Or whatever you're using.
content_build.file_sets.set([some_fileset_queryset])
# If you can do it here, then do:
some_fileset_queryset.update(in_content_build=True)

# Otherwise:
content_build.file_sets.all().update(in_content_build=True)

Another thing to point out is that you're breaking normalization with this field. You could filter down to filesets that are associated with a ContentBuild with:

Fileset.objects.filter(
    content_filesets__isnull=False,
)

Upvotes: 1

Lu Chinke
Lu Chinke

Reputation: 683

Try:

content_build = ContentBuild.objects.get(content_build_uuid=instance.content_build_uuid)
content_build.filesets.all().update(in_content_build=True)

Upvotes: 0

Amine Messaoudi
Amine Messaoudi

Reputation: 2279

You can set in_content_build to be True by default

in_content_build = models.BooleanField(default=True)

UPDATE : If I understand you correctly, you are using in_content_build to check if a FileSet belongs to one of the ContentBuilds. If that's the case, you don't need that field at all. You can just use a function that will perform the check

class Fileset(models.Model):
    fileset_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
    name = models.CharField(max_length=50)

    def is_in_content_build(self):
        for cb in ContentBuild.objects.all():
            for fs in cb.filesets.all():
                if self.fileset_uuid == fs.fileset_uuid
                    return True
        return False


class ContentBuild(models.Model):
    content_build_uuid = models.UUIDField(primary_key=True, 
    default=uuid.uuid4, editable=False, db_index=True)
    filesets = models.ManyToManyField(Fileset, related_name='content_filesets')

Upvotes: 1

Related Questions