Python Django:创建两个查询集的非目标并集

Python Django:创建两个查询集的非目标并集,python,django,union,django-queryset,Python,Django,Union,Django Queryset,我正在用django编写一个会计应用程序,其中有订单s,有创建发票的日期和创建信用票据的可选日期 class Order(models.Model): date_invoice_created = models.DateTimeField(null=True, blank=True) date_credit_note_created = models.DateTimeField(null=True, blank=True) 我目前正在为我们的会计师开发视图,她希望在管理面板中的

我正在用django编写一个会计应用程序,其中有
订单
s,有创建发票的日期和创建信用票据的可选日期

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不支持两个额外的查询集的并集:

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的按位or操作总是确保ID是不同的,但我真的不希望它们是不同的。如何实现具有重复行的并集?

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

我已经创建了一个新的迁移(使用south),其中包含问题中提到的上述SQL查询作为视图,它返回所有行两次,每个行分别为贷记单和发票返回
create\u date
type

accounting/migrations/00xx\u create\u invoice\u creditnote\u 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
    # ...
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
    # ...
然后,我为这个视图创建了一个新模型,它具有
Meta
managed=False
,这样django就可以使用这个模型,而不必关心它的创建。它具有与原始
订单
模型相同的所有字段,但还包括SQL视图中的两个新字段:

invoiceoverview/models.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
    # ...
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
字段来显示适当的内容。e、 g:

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
方法、排序等。

在我看来,您正在进行一些过早的优化。如果这是两个不同的列表,彼此独立显示,为什么您要在一个查询中如此努力地获取它们?因为我想保留
contrib.admin
界面提供的所有功能,该界面使用querysets发挥其魔力。这可能是您想要的:谢谢,但是
itertools.chain
与queryset接口不兼容,因此在我的例子中不起作用。我需要一个queryset,因为我想覆盖
django.contrib.admin.ModelAdmin
queryset
方法。。。