shanemgrey
shanemgrey

Reputation: 2378

Django Filtering results inside ManyToManyField

I need to filter the results of a ManyToManyField before serializing it.

Example:

A user should see details on a record only if 'expiration_date' hasn't passed for 'Authorized Program' on file for the 'Individual' who is the subject of the record.

My model and serializer currently show a list of ALL related Authorized Program records.

Question:

How can I filter that list to only non-expired records before returning as a list in the parent 'individual' record?

Note: This is not a question about filtering the parent queryset based on the M2M field. It's about filtering the contents of the M2M field before returning the parent.

Currently, the serializer simply skips the 'active_authorized_program_list' field.

class Individual(models.Model):
    name = models.CharField(max_length=128, blank=True, default='')
    dob = models.DateField(null=True)
    authorized_program_list = models.ManyToManyField(
        'Program',
        related_name='individual_authorized_program_list',
        through='ReleaseAuthorization',
        blank=True
    )

    @property
    def current_age(self):
        if self.dob:
            today = datetime.date.today()
            return (today.year - self.dob.year) - int(
                (today.month, today.day) <
                (self.dob.month, self.dob.day))
        else:
            return 'Unknown'

    @property
    def active_authorized_program_list(self):
        return (
            self
                .authorized_program_list
                .get_queryset()
                .filter(expiration_date__lte=datetime.Date())
        )

class Program(models.Model):
    name = models.CharField(max_length=128, blank=True, default='')
    individual_list = models.ManyToManyField(
        'individual.Individual',
        related_name='program_individual_list',
        through='hub_program.ReleaseAuthorization',
        blank=True
    )
    active = models.BooleanField(default=True)

class ReleaseAuthorization(models.Model):
    individual = models.ForeignKey(
        Individual,
        related_name="release_authorization_individual",
        on_delete=models.CASCADE
    )
    program = models.ForeignKey(
        Program,
        related_name="release_authorization_program",
        on_delete=models.CASCADE
    )
    expiration_date = models.DateField()


class IndividualSerializer(serializers.ModelSerializer):
    class Meta:
        model = Individual
        fields = (
            'name',
            'dob',
            'current_age',
            'authorized_program_list',
            'active_authorized_program_list',
        )

Serialized Result is missing the last line for 'active_authorized_program_list':

{
    "name": "Individual One",
    "dob": "1974-10-11",
    "current_age": 43,
    "authorized_program_list": [1,2,3]
}

Should also have:

"active_authorized_program_list": [1,2]

Upvotes: 0

Views: 69

Answers (1)

neverwalkaloner
neverwalkaloner

Reputation: 47364

Try to specify type of field in serializer as PrimaryKeyRelatedField:

class IndividualSerializer(serializers.ModelSerializer):
    active_authorized_program_list = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Individual
        fields = (
            'name',
            'dob',
            'current_age',
            'authorized_program_list',
            'active_authorized_program_list',
        )

Also you can generate list in serializer directly using SerializerMethodField:

class IndividualSerializer(serializers.ModelSerializer):
    active_authorized_program_list = SerializerMethodField()

    class Meta:
        model = Individual
        fields = (
            'name',
            'dob',
            'current_age',
            'authorized_program_list',
            'active_authorized_program_list',
        )

    def get_active_authorized_program_list(self, obj):
        return (
            self
            .authorized_program_list
            .filter(expiration_date__lte=datetime.Date()).values_list('id', flat=True)
         )

Upvotes: 1

Related Questions