Django:创建两个查询集的非destinct联合

时间:2014-09-17 12:03:45

标签: python django union django-queryset

我正在django写一个会计应用程序,有Order个,其中包含创建发票的日期和创建信用票据的可选日期。

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查询集的并集: / p>

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')

不幸的是,联合的逐位或操作总是确保ID是不同的,但我真的希望它们不是。如何实现具有重复行的联合?

1 个答案:

答案 0 :(得分:0)

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

我创建了一个新的迁移(使用south),其中包含上述问题中提到的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-View中的两个新字段:

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字段来显示appripriate内容。 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方法,排序等。