Administrador de Django: relación directa con el segundo nivel

Tengo un modelo de Invoice tres niveles que me gustaría mostrar en el área de administración de Django … de una manera ” especial “.

Permítanme darles un poco de antecedentes:

Cada Invoice está conformada por varias SubInvoice y cada SubInvoice ellas SubInvoice una SubInvoice de los Products comprados por un cliente.

Lógicamente hablando, sería algo como esto (con suerte las obras de arte ascii)

 +---------- Invoice id=3 -----------+ | Full total: $100.00 | | | | +----- Sub Invoice id=1 -----+ | | | Subtotal $70 | | | | | | | | Item 1 in SubInv.1 | | | | Item 2 in SubInv.1 | | | | Item 3 in SubInv.1 | | | |____________________________| | | | | +----- Sub Invoice id=2 -----+ | | | Subtotal $30 | | | | | | | | Item 1 in SubInv.2 | | | | Item 2 in SubInv.2 | | | |____________________________| | | | |___________________________________| 

Los modelos se ven más o menos (se han simplificado para esta pregunta) como:

     class Invoice(models.Model): full_total = DecimalField(...) # has a .sub_invoices RelatedManager through a backref from SubInvoice class SubInvoice(models.Model): sub_total = DecimalField(...) invoice = ForeignKey('server.Invoice', related_name='sub_invoices') # has an .items RelatedManager through a backref from InvoiceItem class InvoiceItem(models.Model): sub_invoice = ForeignKey('server.SubInvoice', related_name='items') product = ForeignKey('server.Product', related_name='+') quantity = PositiveIntegerField(...) price = DecimalField(...) 

    Ahora, soy consciente de que anidar dos niveles de relaciones en Django Admin es muy complejo, y no estoy tratando de anidar el objeto InvoiceItem en la SubInvoiceSubInvoice y anidar ese en la Invoice . Eso sería genial, pero estoy dispuesto a renunciar a eso debido a las dificultades de las líneas anidadas. No: lo que me gustaría hacer es mostrar la Invoice y, como una inline , sus Items , “saltando” a través de la Invoice.sub_invoices__items . Realmente no me importa mucho la información que se muestra en la (s) SubInvoiceSubInvoice (s), pero sí me importa la información en la Invoice y en los artículos de InvoiceItems .

    Lo que quiero decir es que, básicamente, me gustaría (o “podría vivir con” , más bien) si la vista del administrador de la Invoice parece a lo siguiente:

     +---------- Invoice id=3 -----------+ | Full total: $100.00 | | | | +----------------------------+ | | | | | | | Item 1 in SubInv.1 | | | | Item 2 in SubInv.1 | | | | Item 3 in SubInv.1 | | | | Item 1 in SubInv.2 | | | | Item 2 in SubInv.2 | | | |____________________________| | | | |___________________________________| 

    ( InvoiceItems como una línea de la (s) Invoice (s) sin mostrar ninguna información sobre las SubInvoices en ella)

    He intentado lo siguiente en el admin.py :

     class InvoiceItemInline(admin.StackedInline): fk_name = 'sub_invoice__invoice' model = InvoiceItem class InvoiceAdmin(admin.ModelAdmin): inlines = (InvoiceItemInline,) 

    Pero eso me da un error:

    : (admin.E202) 'server.InvoiceItem' has no field named 'sub_invoice__invoice'.

    También he intentado directamente esto:

     class InvoiceItemInline(admin.StackedInline): model = InvoiceItem class InvoiceAdmin(admin.ModelAdmin): inlines = (InvoiceItemInline,) 

    Pero entonces (este que esperaba) produce este error:

    : (admin.E202) 'server.InvoiceItem' has no ForeignKey to 'server.Invoice'.

    ¿Hay alguna manera de lograr esto? Gracias de antemano.

    PD:

    A partir de ahora, tengo una solución “parcheada” que parece ser la forma canónica:

    • Registre el modelo de Invoice .
    • Registre un admin.ModelAdmin línea para la SubInvoiceSubInvoice (esta línea estará “integrada” en el ModelAdmin de la Invoice ).
    • También registre la SubInvoice en el administrador, para que podamos calcular un enlace a su vista de administrador.
    • Agregue una vista en línea de InvoiceItems a la vista de la InvoiceItems SubInvoice mencionada SubInvoice .
    • Agregue un enlace a la vista de administración de la (s) SubInvoiceSubInvoice (s) en la Invoice

    Más o menos lo que se describe en esta otra respuesta SO .

    Pero el problema con este enfoque es que no me permite ver la Invoice y sus InvoiceItems Invoice un vistazo (veo la factura, con sub_invoices en ella, y luego dentro de las sub_invoices en línea, hay un enlace a los Elementos de Factura que tengo hacer clic en para ver los artículos). Sería genial si pudiera deshacerme de la necesidad de ese enlace.

    Esto es lo que tengo ahora, básicamente:

     +---------- Invoice id=3 -----------+ | Full total: $100.00 | | | | +----- Sub Invoice id=1 -----+ | +--- Sub Invoice id=1 ---+ | | Subtotal $70 | | | Item 1 in SubInv.1 | | | | | | Item 2 in SubInv.1 | | | Click for items ==============> | Item 3 in SubInv.1 | | |____________________________| | |________________________| | | | +----- Sub Invoice id=2 -----+ | | | Subtotal $30 | | +--- Sub Invoice id=2 ---+ | | | | | Item 1 in SubInv.2 | | | Click for items ==============> | Item 2 in SubInv.2 | | |____________________________| | |________________________| | | |___________________________________| 

    Creo que su problema podría resolverse utilizando el ManyToManyField + a through . (Esto es un ejemplo)

     #models.py class Invoice(models.Model): full_total = DecimalField(...) # has a .sub_invoices RelatedManager through a backref from SubInvoice class SubInvoice(models.Model): sub_total = DecimalField(...) invoice = ManyToManyField( 'server.Invoice', through='server.InvoiceItem', through_fields=('sub_invoice', 'invoice')) # has an .items RelatedManager through a backref from InvoiceItem class InvoiceItem(models.Model): sub_invoice = ForeignKey('server.SubInvoice') invoice = ForeignKey('server.Invoice') product = ForeignKey('server.Product', related_name='+') quantity = PositiveIntegerField(...) price = DecimalField(...) #admin.py from django.contrib import admin from .models import InvoiceItem, Invoice, SubInvoice class InvoiceItemInline(admin.TabularInline): model = InvoiceItem extra = 1 class InvoiceAdmin(admin.ModelAdmin): inlines = (InvoiceItemInline,) admin.site.register(Invoice, InvoiceAdmin) admin.site.register(SubInvoice, InvoiceAdmin) 

    Recomendaría trabajar con clases para este en su views.py y usar inlineformset_factory en su forms.py para esto. jquery-formset utiliza la biblioteca jquery-formset en tu interfaz, ya que se ve bastante bien juntos.

    NOTA: También puede usar on_delete=models.CASCADE si lo desea en InvoiceItem en las teclas foráneas, por lo que si se elimina uno de esos elementos, entonces InvoiceItem también se eliminará, o models.SET_NULL , según lo que prefiera.

    Espero que esto te pueda ayudar.