简体   繁体   中英

DJANGO: Sorting sets of sets

I have this models (simplified):

#models.py

class Expression(models.Model):
    text = models.CharField(max_length=254)

class Country(models.Model):
    name = models.CharField(max_length=100)

class Definition(models.Model):
    expression = models.ForeignKey(Expression)
    country = models.ForeignKey(Country)
    text = models.CharField(max_length=254)

class Vote(models.Model):
    definition = models.ForeignKey(Definition)

And this view

#views.py

def index(request):
    expressions = Expression.objects.all()
    return render(request, 'expression_index.html', { 'expressions':expressions)

So it will show the last 10 created expressions.

Then in the template I have this:

#index.html

{% for expression in expressions %}
    {{ expression }}
    {% for definition in expression.definition_set.all %}
        <ul>
            <li>{{ definition }}</li>
        </ul>
    {% endfor %}
{% endfor %}

Every definition has several votes. Every vote is a single row so we can do:

definition.votes_set.count()

How can I achieve to display them like this:

The top definition of every country alphabetically. Each country appears only with one definition.

Lets say Germany has two definitions for expression "A" and Denmark has three definitions for the same expression it will show only two definitions: the one with the most votes.

I hope I'm making sense.

Thanks

I think something like this should work (untested)

from django.db.models import Count
{{ expression.definition_set.annotate(Count('votes_set')) }}
{% for definition in expression.definition_set.order_by('country','votes_set__count') %}

This queryset will sort alphabetically by country, then each country from it's top voted definition to least.

expression = Expression.objects.get(text__exact="A")
definitions = Definition.objects.filter(expression=expression).annotate(num_votes=Count('vote')).order_by("country__name", "-num_votes")

If i try to keep only the top definition of each country as you wanted, and set .distinct("country") at the end of the queryset, it will throw this error:

NotImplementedError at  annotate() + distinct(fields) is not implemented.

So another solution would be:

import operator

expression = Expression.objects.get(text__exact="A")

# Get top definitions sorted by num of votes and convert the values to list.
top_definitions = list(Definition.objects.filter(expression=expression).annotate(num_votes=Count('vote')).order_by("num_votes").values("id", "country", "country__name", "expression", "text", "num_votes"))

# Remove dublicate countries and leave the only the top voted definition.
definitions = list({v['country']: v for v in top_definitions}.values())

# Sort alphabetically by country.
definitions.sort(key=operator.itemgetter('country__name'))

return render(request, 'expression_index.html', {'definitions': definitions, 'expression': expression})

template:

<h1>{{ expression.text }}</h1>
<ul>
    {% for definition in definitions %}
            <li>{{ definition.country__name }} - {{ definition.text }} - {{ definition.num_votes }}</li>
    {% endfor %}
</ul>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM