Django Order QuerySet by Min Value from Child Table

Django Order QuerySet by Min Value from Child Table

我正尝试在 Django 中创建一个我正在努力解决的特定复杂查询。 我正在使用 pagination 在索引页上显示蛋糕列表。我想按不同的属性对它们进行排序。下面是我的索引页面的截图。

Name (A-Z) & Name (Z-A) 是通过简单地按 'name' 或 '-name' 对 Cake 查询集进行排序来实现的,我从页面顶部。

cakes_list = Cake.objects.all().order_by('name')

但是我很难按最低价格订购查询集。 每个蛋糕都有不同的尺寸,价格也不同(蛋糕之间的尺寸和价格不同)。这些存储在 Dimension 中,外键指向它们所属的 Cake。 我想找出每个蛋糕最便宜的选择,并根据它(最低价格升序和降序)订购我在分页中使用的蛋糕列表。

我还创建了一个方法 from_price,其中 returns 那个蛋糕的价格。我在我的模板中使用它来显示每个蛋糕的名称和最低价格。但我似乎无法将其应用到我的排序中。

感谢您帮助我创建查询或类似查询,让我可以根据每个蛋糕的最低价格对我的所有蛋糕进行排序。我刚刚开始学习 Django,所以我目前的实现可能并不理想。

vault/models.py:

class Cake(models.Model):
    name = models.CharField(max_length=200)

    def from_price(self):
        temp = self.dimension_set.aggregate(Min('price')).get('price__min')
        if not temp:
            temp = 0
        return temp

class Dimension(models.Model):
    cake = models.ForeignKey(Cake, on_delete=models.CASCADE)
    dimension = models.CharField(max_length=50)
    price = models.DecimalField(max_digits=6, decimal_places=2)

vault/views.py

def index(request):
    #from the form on the index page
    order_by = request.POST.get('order_by')
    if not order_by:
        order_by = 'name'

    cakes_list = Cake.objects.all().order_by(order_by)
    paginator = Paginator(cakes_list, 5)

    page_number = request.GET.get('page', 1)
    page_obj = paginator.get_page(page_number)
    return render(request, 'vault/index.html', {'page_obj': page_obj, 'order_by': order_by})


vault/templates/vault/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

{# Form for cake sorting dropdown #}
<form action="{% url 'vault:index' %}" method="post">
    {% csrf_token %}
    <label for="order_by">Order by:</label>
    <select name="order_by" id="order_by">
        <option {% if order_by == "name" %} selected="selected" {% endif %} value="name">Name (A-Z)</option>
        <option {% if order_by == "-name" %} selected="selected" {% endif %} value="-name">Name (Z-A)</option>
        {% comment %}
        New options for ordering by price
        <option {% if order_by == "" %} selected="selected" {% endif %} value="name">Price from (Low to High)</option>
        <option {% if order_by == "" %} selected="selected" {% endif %} value="-name">Price from (High to Low)</option>
        {% endcomment %}
    </select>
    <input type="submit" value="Select">
</form>

{# Code for printing the list of cakes #}
{% if page_obj %}
    <ul>
        {% for cake in page_obj %}
            <li>
                <a href="{% url 'vault:detail' cake.id %}">
                    {{ cake.name }}
                    {% if cake.from_price %}
                        - from &#163;{{ cake.from_price|floatformat:'2' }}
                    {% endif %}
                </a>
            </li>
        {% endfor %}
    </ul>
{% else %}
    <p>No cakes are available</p>
{% endif %}

{# Code for the pagination navigation elements #}
<div class="pagination">
    <span class="step-links">
        {% if page_obj.has_previous %}
            <a href="?page=1">&laquo; first</a>
            <a href="?page={{ page_obj.previous_page_number }}">previous</a>
        {% endif %}
        <span class="current">
            Page {{  page_obj.number }} of {{ page_obj.paginator.num_pages }}.
        </span>

        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}">next</a>
            <a href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
        {% endif %}
    </span>

</div>
</body>
</html>

尝试在订购之前使用注释:

cakes_list = Cake.objects.annotate(
    max_price=Max('dimension__price'),
).order_by('max_price')

如果您需要每个 Cake 记录的最低价格值,那么可能最简单的方法是使用 subquery:

from django.db.models import OuterRef, Subquery

sub_q = Dimension.objects.filter(cake=OuterRef('id')).order_by('price')
qs = Cake.objects.annotate(min_price=Subquery(sub_q.values('prize')[:1]).order_by('min_price')