Django 跨多个模型关系进行过滤

问题描述 投票:0回答:1

我的模型的简化版本如下:

class Order (models.Model):
    customer = models.ForeignKey("Customer", on_delete=models.RESTRICT)
    request_date = models.DateField()
    price = models.DecimalField(max_digits=10, decimal_places=2)

    @property
    def agent_name(self):
        assignment = Assignment.objects.get(assig_year = self.request_date.year, customer = self.customer)
        if assignment is not None:
            return assignment.sales_agent.name + ' ' + assignment.sales_agent.surname
        else:
            return 'ERROR'

class Company (models.Model):
    pass

class Customer (Company):
    pass

class Assignment (models.Model):
    assig_year = models.PositiveSmallIntegerField()
    customer = models.ForeignKey("Customer", on_delete=models.CASCADE)
    sales_agent = models.ForeignKey("Agent", on_delete=models.CASCADE)

class Employee (models.Model):
    name = models.CharField(max_length=32)
    surname = models.CharField(max_length=32)

class Agent (Employee):
    pass

在我的一种观点中,我通过列出相应的销售代理、客户、日期和价格来显示所有订单,如下所示:

def GetOrders(request):
    orders = Order.objects.order_by('-request_date')
    
    template = loader.get_template('orders.html')
    context = {
        'orders' : orders,
    }
    return HttpResponse(template.render(context,request))

orders.html 看起来像这样:

<!DOCTYPE html>
<html>
  <head>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
  </head>
  <body>
    <main>
      <table>
        <thead>
          <th>Agent</th>
          <th>Customer</th>
          <th>Date</th>
          <th>Price</th>
        </thead>
        <tbody>
          {% for x in orders %}
            <td>{{ x.agent_name }}</td>
            <td>{{ x.customer.name }}</td>
            <td>{{ x.request_date }}</td>
            <td>{{ x.price }}</td>
            </tr>
          {% endfor %}
        </tbody>
      </table>
    </main>
  </body>
</html>

现在我想在 html 中添加一些过滤功能,以便仅选择我感兴趣的销售代理,但这是我的第一个 Django 项目,我不知道如何处理我刚接触的所有关系通过以检查销售代理的名称。我尝试利用 agent_name 属性,如下所示:

<!DOCTYPE html>
<html>
  <head>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
  </head>
  <body>
    <main>
      <div class="filters">
        <form action="" method="GET">
          <div class="row">
            <div class="col-xl-3">
              <label>Agent:</label>
              <input type="text" class="form-control" placeholder="Name" name="name" {% if name %} value = "{{ name }}" {% endif %}>
            </div>
            <div class="col-xl-2" style="padding-top: 2%;">
              <button type="submit" class="btn custom-btn">Filter</button>
            </div>
          </div>
        </form>
      </div>
      <p/>
      <table>
        <thead>
          <th>Agent</th>
          <th>Customer</th>
          <th>Date</th>
          <th>Price</th>
        </thead>
        <tbody>
          {% for x in orders %}
            <td>{{ x.agent_name }}</td>
            <td>{{ x.customer.name }}</td>
            <td>{{ x.request_date }}</td>
            <td>{{ x.price }}</td>
            </tr>
          {% endfor %}
        </tbody>
      </table>
    </main>
  </body>
</html>

我的观点现在变成这样:

def GetOrders(request):
    orders = Order.objects.order_by('-request_date')

    com = request.GET.get('name')
    if com != '' and com is not None:
        orders = orders.filter(Q(agent_name__icontains=com))
    
    template = loader.get_template('orders.html')
    context = {
        'orders' : orders,
    }
    return HttpResponse(template.render(context,request))

但似乎我不能将它用作过滤条件,因为它不是真正的模型字段,并且我得到一个 FieldError 返回(“无法将关键字“agent_name”解析为字段”)。

有什么想法吗?

django sqlite django-models django-views
1个回答
0
投票

简短的回答是:您不能按照描述执行此操作,因为 @property 函数是在执行查询后通过 python 设置的,即它使用返回的查询作为函数的数据。

此外,您的属性还会对每个 agent_name 进行 sql 调用。 对于一个代理来说不是问题,但是循环 50 个代理会效率很低。

订单和分配之间似乎存在某种关系,因此您可以通过外键将它们连接起来,就像处理订单和客户一样。 然后将一个新字段注释到查询集中

from django.db.models.functions import Concat
from django.db.models import Value


com = request.GET.get('name')
if com != '' and com is not None:
    #start definition of orders with ( so we can break up the line for readability
    orders = (
        Order.objects.annotate(
            #Combine firstname, space, and last name into new field
            full_name = Concat(
                'order__assignment__sales_agent__name',
                 Value(' '),
                 'order__assignment__sales_agent__surname'
             )
         )
         #filter on our annotated field from the form
         .filter(full_name__icontains=com)
         .order_by('-request_date')
     )
© www.soinside.com 2019 - 2024. All rights reserved.