Tengo un modelo que representa pinturas que presento en mi sitio. En la página web principal me gustaría mostrar algunos de ellos: el más nuevo, uno que no fue visitado por la mayoría del tiempo, el más popular y uno aleatorio.
Estoy usando Django 1.0.2.
Si bien los primeros 3 de ellos son fáciles de extraer utilizando modelos django, el último (aleatorio) me causa algunos problemas. Desde mi punto de vista puedo codificarlo en algo como esto:
number_of_records = models.Painting.objects.count()
random_index = int(random.random()*number_of_records)+1
random_paint = models.Painting.get(pk = random_index)
No parece algo que me gustaría tener en mi opinión, esto es completamente parte de la abstracción de la base de datos y debería estar en el modelo. Además, aquí necesito cuidar los registros eliminados (entonces el número de todos los registros no me cubrirá todos los valores clave posibles) y probablemente muchas otras cosas.
¿Alguna otra opción para hacerlo, preferiblemente de alguna manera dentro de la abstracción del modelo?
fuente
Respuestas:
El uso
order_by('?')
matará al servidor db el segundo día de producción. Una mejor manera es algo como lo que se describe en Obtener una fila aleatoria de una base de datos relacional .fuente
model.objects.aggregate(count=Count('id'))['count']
más?model.objects.all().count()
.all()[randint(0, count - 1)]
en efecto. Tal vez debería concentrarse en identificar qué parte de la respuesta es incorrecta o débil, en lugar de redefinir "por error" para nosotros y gritar a los votantes tontos. (¿Tal vez es que no está usando.objects
?)Simplemente use:
Está documentado en la API de QuerySet .
fuente
random.choice(Model.objects.all())
?Las soluciones con order_by ('?') [: N] son extremadamente lentas incluso para tablas medianas si usa MySQL (no sabe sobre otras bases de datos).
order_by('?')[:N]
será traducido aSELECT ... FROM ... WHERE ... ORDER BY RAND() LIMIT N
consulta.Significa que para cada fila de la tabla se ejecutará la función RAND (), luego se clasificará toda la tabla de acuerdo con el valor de esta función y luego se devolverán los primeros N registros. Si sus mesas son pequeñas, está bien. Pero en la mayoría de los casos, esta es una consulta muy lenta.
Escribí una función simple que funciona incluso si los ID tienen agujeros (algunas filas se eliminaron):
Es más rápido que order_by ('?') En casi todos los casos.
fuente
Aquí hay una solución simple:
fuente
Puede crear un administrador en su modelo para hacer este tipo de cosas. Para entender primero qué es un gerente es, el
Painting.objects
método es un gerente que contieneall()
,filter()
,get()
, etc. La creación de su propio gestor le permite comprobar la validez de los resultados de filtro y tienen todos estos mismos métodos, así como sus propios métodos personalizados, el trabajo sobre los resultados .EDITAR : modifiqué mi código para reflejar el
order_by['?']
método. Tenga en cuenta que el administrador devuelve un número ilimitado de modelos aleatorios. Debido a esto, he incluido un poco de código de uso para mostrar cómo obtener un solo modelo.Uso
Por último, puede tener muchos gerentes en sus modelos, así que siéntase libre de crear un
LeastViewsManager()
oMostPopularManager()
.fuente
Las otras respuestas son potencialmente lentas (usando
order_by('?')
) o usan más de una consulta SQL. Aquí hay una solución de muestra sin ordenar y solo una consulta (suponiendo Postgres):Tenga en cuenta que esto generará un error de índice si la tabla está vacía. Escríbete una función auxiliar agnóstica de modelo para verificar eso.
fuente
count()
avance y prescindir de la consulta sin procesar.Solo una simple idea de cómo lo hago:
fuente
Solo para notar un caso especial (bastante común), si hay una columna de incremento automático indexada en la tabla sin eliminaciones, la forma óptima de hacer una selección aleatoria es una consulta como:
eso supone una columna llamada id para la tabla. En django puedes hacer esto:
en el que debe reemplazar appname con el nombre de su aplicación.
En general, con una columna de identificación, order_by ('?') Se puede hacer mucho más rápido con:
fuente
Esto es muy recomendable
Obtener una fila aleatoria de una base de datos relacionalDebido a que usar django orm para hacer algo así, su servidor db se enojará especialmente si tiene una tabla de datos grandes: |
Y la solución es proporcionar un administrador de modelos y escribir la consulta SQL a mano;)
Actualización :
Otra solución que funciona en cualquier backend de base de datos, incluso los que no son rel sin escribir de forma personalizada
ModelManager
. Obtener objetos aleatorios de un conjunto de consultas en Djangofuente
Es posible que desee utilizar el mismo enfoque que usaría para muestrear cualquier iterador, especialmente si planea muestrear múltiples elementos para crear un conjunto de muestra . @MatijnPieters y @DzinX piensan mucho en esto:
fuente
OFFSET
), esto es innecesariamente ineficiente.Un enfoque mucho más fácil para esto implica simplemente filtrar hasta el conjunto de registros de interés y usar
random.sample
para seleccionar tantos como desee:Tenga en cuenta que debe tener algún código para verificar que
my_queryset
no esté vacío;random.sample
devuelveValueError: sample larger than population
si el primer argumento contiene muy pocos elementos.fuente
Queryset
(al menos con Python 3.7 y Django 2.1); primero debe convertirlo en una lista, que obviamente recupera todo el conjunto de consultas.Hola, necesitaba seleccionar un registro aleatorio de un conjunto de consultas cuya longitud también necesitaba informar (es decir, la página web produjo el elemento descrito y dichos registros quedaron)
tomó la mitad de tiempo (0.7s vs 1.7s) que:
Supongo que evita desplegar toda la consulta antes de seleccionar la entrada aleatoria e hizo que mi sistema responda lo suficiente para una página a la que se accede repetidamente para una tarea repetitiva en la que los usuarios desean ver la cuenta regresiva de item_count.
fuente
Método para incrementar automáticamente la clave primaria sin eliminaciones
Si tiene una tabla donde la clave primaria es un entero secuencial sin espacios, entonces el siguiente método debería funcionar:
Este método es mucho más eficiente que otros métodos aquí que iteran a través de todas las filas de la tabla. Si bien requiere dos consultas a la base de datos, ambas son triviales. Además, es simple y no requiere definir ninguna clase adicional. Sin embargo, su aplicabilidad se limita a las tablas con una clave primaria de incremento automático donde las filas nunca se han eliminado, de modo que no hay espacios en la secuencia de identificadores.
En el caso de que se hayan eliminado filas que son espacios, este método aún podría funcionar si se vuelve a intentar hasta que se seleccione aleatoriamente una clave primaria existente.
Referencias
fuente
Obtuve una solución muy simple, haga un administrador personalizado:
y luego agregue el modelo:
Ahora puedes usarlo:
fuente
order_by('?').first()
más de 60 veces.