【问题标题】:Django: Creating a non-destinct union of two querysetsDjango:创建两个查询集的非目的地联合
【发布时间】:2014-11-11 11:05:03
【问题描述】:

我正在用 django 编写一个会计应用程序,有 Orders,其中有一个创建发票的日期和一个可选的创建贷方票据的日期。

class Order(models.Model):
    date_invoice_created = models.DateTimeField(null=True, blank=True)
    date_credit_note_created = models.DateTimeField(null=True, blank=True)

我目前正在为我们的会计师开发视图,她希望在管理面板的不同行中同时显示发票和贷项通知单,并按各自的创建日期排序。

所以基本上我想在不同的行中显示相同的模型两次,按不同的字段排序。在 SQL 中,这将类似于:

SELECT id, create_date FROM (
    SELECT id, date_invoice_created AS create_date, 'invoice' AS type FROM order
        UNION
    SELECT id, date_credit_note_created AS create_date, 'creditnote' AS type FROM order
) ORDER BY create_date

不要介意我的 SQL-fu 不是最新的,但我想你明白我的意思。

所以我试图让 django 为我做这件事,通过覆盖第二个查询集中的日期,因为 django 不支持两个 extra'd 查询集的联合:

invoices = Order.objects.filter(date_invoice_created__isnull=False)
credit_notes = Order.filter_valid_orders(qs
    ).filter(
        date_credit_note_created__isnull=False
    ).extra(
        select={'date_invoice_created': 'date_credit_note_created'}
    )
return (invoices | credit_notes).order_by('date_invoice_created')

不幸的是,union 的按位或运算始终确保 ID 是不同的,但我真的不希望它们不同。如何实现具有重复行的联合?

【问题讨论】:

  • 在我看来您正在进行一些过早的优化。如果这是两个不同的列表,彼此独立显示,那么您为什么要如此努力地在单个查询中获取它们?
  • 因为我想保留contrib.admin 接口提供的所有功能,它使用查询集来发挥它的魔力。
  • 这可能是你想要的:stackoverflow.com/questions/431628/…
  • 谢谢,但itertools.chain 与查询集接口不兼容,因此不适用于我的情况。我需要一个查询集,因为我想覆盖 django.contrib.admin.ModelAdminqueryset 方法 ...

标签: python django union django-queryset


【解决方案1】:

我现在使用 SQL 视图找到了解决问题的方法。

我创建了一个新的迁移(使用南),其中包含问题中提到的上述 SQL 查询作为视图,它返回所有行两次,每行分别带有 create_datetype 用于贷记单和发票。

accounting/migrations/00xx_create_invoice_creditnote_view.py:

class Migration(SchemaMigration):

    def forwards(self, orm):
        query = """
          CREATE VIEW invoiceoverview_invoicecreditnoteunion AS
            SELECT * FROM (
                SELECT  *, 
                        date_invoice_created AS create_date,
                        'invoice' AS type 
                    FROM accounting_order
                    WHERE date_invoice_created NOT NULL
                UNION
                SELECT  *,
                        date_credit_note_created AS date,
                        'creditnote' AS type
                    FROM accounting_order
                    WHERE date_credit_note_created NOT NULL
            );
        """
        db.execute(query)


    def backwards(self, orm):
        query = """
          DROP VIEW invoiceoverview_invoicecreditnoteunion;
        """
        db.execute(query)

    # ...
    # the rest of the migration model
    # ...

然后我为这个视图创建了一个新模型,它有 Meta managed = False 以便 django 使用该模型而不关心它的创建。它具有与原始 Order 模型相同的字段,但还包括 SQL 视图中的两个新字段:

invoiceoverview/models.py:

class InvoiceCreditNoteUnion(models.Model):
    """ This class is a SQL-view to Order, so that the credit note and
    invoice can be displayed independently.

    """
    class Meta:
        managed = False  # do not manage the model in the DB
    # fields of the view
    date = models.DateTimeField() 
    type = models.CharField(max_length=255)

    # ...
    # all the other fields of the original Order
    # ...

现在我可以将此模型用于contrib.admin.ModelAdmin,并通过检查type 字段来显示相应的内容。例如:

class InvoiceAdmin(admin.ModelAdmin):
    list_display = ['some_special_case']

    def some_special_case(self, obj):
        if obj.type == 'creditnote':
            return obj.credit_note_specific field
        else:
            return obj.invoice_specific_field

admin.site.register(InvoiceCreditNoteUnion, InvoiceAdmin)

这最终允许我使用管理面板提供的所有其他功能,例如覆盖queryset 方法、排序等。

【讨论】:

    猜你喜欢
    • 2011-05-23
    • 2021-11-10
    • 2014-05-31
    • 2021-09-03
    • 2023-03-27
    • 2017-12-04
    • 2014-02-23
    • 1970-01-01
    • 2013-04-18
    相关资源
    最近更新 更多