aambrozkiewicz
aambrozkiewicz

Reputation: 552

Django contraint filtered models to include set that matches first criteria

I want to iterate over user matching entities to avoid grouping in helper array first.

How from query like this I can make User.stone_set to contain only ones that matched the filter?

users = User.objects.filter(payment__due_day__lte=datetime.today()+3)

Now each user should have only payment instances that mateched the filter, but users.payment_set is a manager not really related to it.

Clarification

Here is my model:

class Payment(models.Model):
   owner = models.ForeignKey(User)
   due_day = models.IntegerField()

now, query like this: User.objects.filter(payment__due_day=10) will give me users that have payment due day 10. Now iterating over those users objects I want it to have only those payment I queried for the first time (only those that have payment with due_day = 10), without quering single user again for their payments.

Upvotes: 1

Views: 96

Answers (1)

Germano
Germano

Reputation: 2482

If I understand your question, in the end you want a queryset of Payment objects. Then, start from that model, not User.

payments = Payment.objects.filter(due_day=10).select_related('owner').order_by('owner')

After that, you can iterate over the queryset and get the owner (user) of each payment without any extra query, because select_related will do a SQL join for you in the background.

The order_by clause is needed if you have multiple payments for each user and you need to show that. In your template you can use the regroup built-in template tag.

For example:

# In your view (EXTREMELY simplified)
def my_view(request):
    payments = Payment.objects.filter(due_day=10).select_related('owner').order_by('owner')
    return render_to_response('template.html', {'payments': payments})

# In your template
{% regroup payments by owner as payment_list %}
<ul>
    {% for payment in payment_list %}
    <li>{{ payment.grouper }} <!-- this is the owner -->
        <ul>
        {% for item in payment.list %}
          <li>{{ item.due_day }}</li>
        {% endfor %}
        </ul>
    </li>
    {% endfor %}
</ul>

If, instead, you want to achieve that in bare python, the best solution is to use itertools.groupby (which is, in fact, used by the regroup tag).

A quick and untested example:

from itertools import groupby
from operator import attrgetter

payments = Payment.objects.filter(due_day=10).select_related('owner').order_by('owner')

for owner, grouped_payments in groupby(payments, attrgetter('owner')):
    do_stuff_with(owner)
    do_some_other_stuff_with(list(grouped_payments))

Be sure to read the docs.

Upvotes: 3

Related Questions