2008-11-23 16 views
11

En mi caso específico, tengo dos tipos de "mensajes" que necesito recuperar y paginar.Usando django ¿cómo puedo combinar dos consultas de modelos separados en una consulta?

Vamos a omitir los detalles, y sólo decir que el primer tipo se encuentra en un modelo llamado Msg1 y el otro se llama Msg2

Los campos de estos dos modelos son completamente diferentes, los únicos campos que son comunes a la dos modelos son "fecha" y "título" (y por supuesto, id).

Puedo obtener Msg1.objects.all() y Msg2.objects.all(), pero ¿puedo combinar estas dos consultas en una consulta, ordenarla por fecha y paginarla?

Necesito preservar la naturaleza perezosa de la consulta.

La solución trivial es list(query) ambas consultas y combinarlas en una lista de Python. pero esto es ineficiente por razones obvias.

Miré las referencias de django en modelos y dp-api, pero no parece que haya una forma de combinar consultas de diferentes modelos/tablas en una sola.

+0

"ineficiente por razones obvias" ¿De verdad? ¿Tienes métricas? Lo pregunto porque no hay una razón obvia de por qué sería ineficiente. –

+0

Creo que porque una vez que lista (consulta) obtiene todos los resultados, y quiere dejar eso lo más tarde posible. –

+0

ineficaz porque golpea la base de datos para TODOS los elementos, (podría ser 1000) mientras que solo muestra 20 por página o así .. – hasen

Respuesta

11

Yo sugeriría que utilice Model inheritance.

Cree un modelo base que contenga la fecha y el título. La subclase Msg1 y Msg2 se desactivan como se describe. Haga todas sus consultas (para llenar una página) usando el modelo base y luego cambie al tipo derivado en el último momento.

Lo mejor de la herencia es que django le permite utilizar el modelo base en claves externas de otros modelos, para que pueda hacer toda su aplicación más flexible. Debajo del capó, solo hay una tabla para el modelo base con una tabla por submodelo que contiene claves uno a uno.

+1

¿Qué sucede si mi clase base es abstracta? ¿Puedes mostrar algún ejemplo de código? –

2

"combinar estas dos consultas en una consulta, ordenarla por fecha y paginarla?"

  1. Esa es la unión de SQL. Deje el ORM de Django y use una unión SQL. No es brillantemente rápido porque SQL tiene que crear un resultado temporal, que ordena.

  2. Crea el resultado temporal, que se puede ordenar. Como una lista tiene un método de clasificación, deberá fusionar los dos resultados en una lista.

  3. Escriba un algoritmo de combinación que acepte dos conjuntos de consultas, paginating los resultados.


Editar. Aquí hay un algoritmo de fusión.

def merge(qs1, qs2): 
    iqs1= iter(qs1) 
    iqs2= iter(qs2) 
    k1= iqs1.next() 
    k2= iqs2.next() 
    k1_data, k2_data = True, True 
    while k1_data or k2_data: 
     if not k2_data: 
      yield k1 
      try: 
       k1= iqs1.next() 
      except StopIteration: 
       k1_data= False 
     elif not k1_data: 
      yield k2 
      try: 
       k2= iqs2.next() 
      except StopIteration: 
       k2_data= False 
     elif k1.key <= k2.key: 
      yield k1 
      try: 
       k1= iqs1.next() 
      except StopIteration: 
       k1_data= False 
     elif k2.key < k1.key: # or define __cmp__. 
      yield k2 
      try: 
       k2= iqs2.next() 
      except StopIteration: 
       k2_data= False 
     else: 
      raise Exception("Wow...") 

Puede doblar en la paginación:

def paginate(qs1, qs2, start=0, size=20): 
    count= 0 
    for row in merge(qs1, qs2): 
     if start <= count < start+size: 
      yield row 
     count += 1 
     if count == start+size: 
      break 
+0

Si tuviera que seguir este camino, debería realizar el ordenamiento antes de la unión en la instrucción SQL (porque SQL puede usar índices para acelerar el orden) y con suerte puede limitar los resultados intermedios antes de la unión, reduciendo el tamaño de cualquier información temporal. –

+0

Sin embargo, como una Unión puede ser * cualquier * combinación de tablas, cálculos, literales, etc., una unión * debe * crear y ordenar un resultado intermedio. Tal vez pueda optimizar, pero la versión general * debe * funcionar. –

Cuestiones relacionadas