Inlines en el admin Django

Intermedio
Django
Django
Actualizado: 18/04/2026

TabularInline

TabularInline muestra los modelos relacionados en formato de tabla, compacto y eficiente para muchos campos:

from django.contrib import admin
from .models import Pedido, LineaPedido, ImagenProducto, Producto

class LineaPedidoInline(admin.TabularInline):
    model = LineaPedido
    fields = ['producto', 'cantidad', 'precio_unitario', 'subtotal']
    readonly_fields = ['subtotal']
    extra = 1         # Número de formularios vacíos adicionales
    min_num = 1       # Mínimo requerido
    max_num = 50      # Máximo permitido

    def subtotal(self, obj):
        return f'{obj.precio_unitario * obj.cantidad:.2f} €'
    subtotal.short_description = 'Subtotal'

@admin.register(Pedido)
class PedidoAdmin(admin.ModelAdmin):
    list_display = ['id', 'cliente', 'estado', 'total', 'fecha_creacion']
    inlines = [LineaPedidoInline]

Diagrama conceptual de Inlines en el admin Django

StackedInline

StackedInline muestra cada modelo relacionado como un formulario apilado, más espacioso y adecuado para modelos con muchos campos:

class ImagenProductoInline(admin.StackedInline):
    model = ImagenProducto
    fields = ['imagen', 'alt_text', 'es_principal', 'orden']
    extra = 0
    max_num = 10
    can_delete = True

    class Media:
        css = {'all': ['admin/css/inlines.css']}

@admin.register(Producto)
class ProductoAdmin(admin.ModelAdmin):
    inlines = [ImagenProductoInline]

Inline con validación personalizada

from django.forms import BaseInlineFormSet

class LineaPedidoFormSet(BaseInlineFormSet):
    def clean(self):
        super().clean()
        total = sum(
            form.cleaned_data.get('precio_unitario', 0) * form.cleaned_data.get('cantidad', 0)
            for form in self.forms
            if form.cleaned_data and not form.cleaned_data.get('DELETE')
        )
        if total > 50000:
            from django.core.exceptions import ValidationError
            raise ValidationError('El importe total del pedido no puede superar 50.000 €.')

class LineaPedidoInline(admin.TabularInline):
    model = LineaPedido
    formset = LineaPedidoFormSet
    extra = 1

Inline con relación ManyToMany mediante through

Cuando una relación ManyToMany tiene un modelo intermedio (through), se puede editar ese modelo intermedio como inline:

class ProductoEtiquetaInline(admin.TabularInline):
    model = Producto.etiquetas.through  # Modelo intermedio
    extra = 1
    verbose_name = 'Etiqueta'
    verbose_name_plural = 'Etiquetas'

@admin.register(Producto)
class ProductoAdmin(admin.ModelAdmin):
    exclude = ['etiquetas']  # Excluir el widget M2M por defecto
    inlines = [ProductoEtiquetaInline]

ReadOnlyInline (Django 5.0+)

Desde Django 5.0 existe ReadOnlyTabularInline y ReadOnlyStackedInline para mostrar modelos relacionados sin posibilidad de edición:

class HistorialPrecioInline(admin.TabularInline):
    model = HistorialPrecio
    fields = ['precio_anterior', 'precio_nuevo', 'fecha_cambio', 'modificado_por']
    readonly_fields = ['precio_anterior', 'precio_nuevo', 'fecha_cambio', 'modificado_por']
    extra = 0
    can_delete = False

    def has_add_permission(self, request, obj=None):
        return False  # No permitir añadir registros de historial manualmente
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Django es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Django

Explora más contenido relacionado con Django y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Crear inlines con TabularInline para mostrar modelos relacionados en formato tabla. Usar StackedInline para mostrar modelos relacionados como formularios apilados. Configurar extra, min_num y max_num para controlar el número de formularios inline. Definir readonly_fields y fields en inlines para controlar qué se muestra. Usar inlines con relaciones ManyToMany mediante through models.