Home > Software engineering >  Filter a queryset using a function in Django
Filter a queryset using a function in Django

Time:08-24

I have this models.py file in my Django project

from django.db import models
from django.urls import reverse
from django.utils.timezone import now

class Campaign(models.Model):
    class Meta:
        db_table = 'campaign'

    campaign_id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255, default='')
    topic = models.CharField(max_length=255, default='')
    sender = models.CharField(max_length=255, default='')
    start_date = models.DateTimeField(default=now)
    is_active = models.BooleanField(default=False)
    draft = models.BooleanField(default=True)
    content = models.TextField(default='')
    footer = models.CharField(max_length=255, default='')
    unsubscribe_message = models.CharField(max_length=255, default='')

    @property
    def get_completion_percent(self):
        return 70

And I'm trying to use the get_completion_percent function in my views.py as a filter right here

def finished_ads_view(request):
    queryset = Campaign.objects.filter(get_completion_percent=100)

    context = {
        'object_list': queryset,
    }

    return render(request, 'campaigns_in_progress.html', context)

But the error I'm getting is this:

FieldError at /finished_campaigns/ Cannot resolve keyword 'get_completion_percent' into field. Choices are: campaign_id, content, draft, footer, is_active, name, sender, start_date, topic, unsubscribe_message

Can anyone help me make this work?

CodePudding user response:

You can't filter a property.

If you need a percentage you can use .annotate() and filter the field.

Campaign.objects.annotate(percentage=70).filter(percentage=100)

If you need to make operation on fields of the same record, you can get the values with F() expression

CodePudding user response:

I try to give a solution. It's not tested, so you should try it and let me know if it works. I am trying to understand which is your final goal and, as far as I understand, is to display something if it reaches the completion percentage of 100. I would add another model like this:

class Percentage(models.Model):
    campaign= models.ForeignKey(
        Campaign, on_delete=models.CASCADE, null=True, blank=True)
    percentage= models.IntegerField(default=0, blank=True, null=True) #I don't know if you need integer or float

With this foreign key, you can now do the calculations (in the view I suppose) to get the percentage. An example would be:

from django.db.models import Sum # remember this import

def finished_ads_view(request, pk):
    campaign = Campaign.objects.filter(campaign_id=pk)
    percentages = Percentage.objects.filter(campaign=campaign).aggregate(Sum(percentage))
    if percentages == 100:
         context = {
             'object_list': percentages,
         } #this context is idented inside the if statement
    else:
         percentages = 'Not completed' # or whatever you want display if the percentage is not 100
         context = {
                 'object_list': percentages,
             }
        

    return render(request, 'campaigns_in_progress.html', context)

It's just a try, let me know if it works. I didn't test the code so there might be some mistakes but I'd do something like that

  • Related