Eu tenho um modelo que representa pinturas que apresento no meu site. Na página principal, eu gostaria de mostrar alguns deles: o mais novo, um que não foi visitado por mais tempo, o mais popular e o aleatório.
Estou usando o Django 1.0.2.
Embora os três primeiros sejam fáceis de usar usando modelos de django, o último (aleatório) me causa alguns problemas. Eu posso ofc codificá-lo na minha opinião, para algo como isto:
number_of_records = models.Painting.objects.count()
random_index = int(random.random()*number_of_records)+1
random_paint = models.Painting.get(pk = random_index)
Não parece algo que eu gostaria de ter - isso faz parte da abstração do banco de dados e deve estar no modelo. Além disso, aqui eu preciso cuidar dos registros removidos (o número de todos os registros não me cobrirá todos os valores-chave possíveis) e provavelmente muitas outras coisas.
Alguma outra opção como eu posso fazer isso, preferencialmente de alguma forma dentro da abstração do modelo?
fonte
Respostas:
O uso
order_by('?')
matará o servidor db no segundo dia de produção. Uma maneira melhor é algo como o descrito em Obtendo uma linha aleatória de um banco de dados relacional .fonte
model.objects.aggregate(count=Count('id'))['count']
overmodel.objects.all().count()
.all()[randint(0, count - 1)]
com efeito. Talvez você deva se concentrar em identificar qual parte da resposta está errada ou fraca, em vez de redefinir "um por um erro" para nós e gritar com os eleitores tolos. (Talvez seja que ele não está usando.objects
?)Basta usar:
Está documentado na API QuerySet .
fonte
random.choice(Model.objects.all())
?As soluções com order_by ('?') [: N] são extremamente lentas, mesmo para tabelas de tamanho médio, se você usa o MySQL (não conhece outros bancos de dados).
order_by('?')[:N]
será traduzido paraSELECT ... FROM ... WHERE ... ORDER BY RAND() LIMIT N
consulta.Isso significa que, para cada linha da tabela, a função RAND () será executada, a tabela inteira será classificada de acordo com o valor dessa função e os primeiros N registros serão retornados. Se suas mesas são pequenas, tudo bem. Mas na maioria dos casos, essa é uma consulta muito lenta.
Eu escrevi uma função simples que funciona mesmo se os ID tiverem buracos (algumas linhas foram excluídas):
É mais rápido que order_by ('?') Em quase todos os casos.
fonte
Aqui está uma solução simples:
fonte
Você pode criar um gerente em seu modelo para fazer esse tipo de coisa. Para entender primeiro o que um gerente é, o
Painting.objects
método é um gerente que contémall()
,filter()
,get()
, etc. Criar o seu próprio gerente permite que você pré-filtro resultados e ter todos esses mesmos métodos, bem como seus próprios métodos personalizados, o trabalho sobre os resultados .Edição : eu modifiquei meu código para refletir o
order_by['?']
método. Observe que o gerente retorna um número ilimitado de modelos aleatórios. Por isso, incluí um pouco de código de uso para mostrar como obter apenas um modelo.Uso
Por fim, você pode ter muitos gerentes em seus modelos, portanto, fique à vontade para criar um
LeastViewsManager()
ouMostPopularManager()
.fonte
As outras respostas são potencialmente lentas (usando
order_by('?')
) ou usam mais de uma consulta SQL. Aqui está um exemplo de solução sem pedido e apenas uma consulta (assumindo o Postgres):Esteja ciente de que isso gerará um erro de índice se a tabela estiver vazia. Escreva para você uma função auxiliar independente de modelo para verificar isso.
fonte
count()
antecipadamente e dispensar a consulta bruta.Apenas uma ideia simples de como faço:
fonte
Apenas para observar um caso especial (bastante comum), se houver uma coluna de incremento automático indexada na tabela sem exclusões, a maneira ideal de fazer uma seleção aleatória é uma consulta como:
que assume essa coluna chamada id para a tabela. No django, você pode fazer isso:
em que você deve substituir appname pelo nome do aplicativo.
Em geral, com uma coluna de identificação, o order_by ('?') Pode ser feito muito mais rapidamente com:
fonte
Recomenda-se
obter uma linha aleatória de um banco de dados relacionalComo usar o django orm para fazer algo assim, o seu servidor db ficará irritado, especialmente se você tiver uma tabela de big data: |
E a solução é fornecer um Model Manager e gravar a consulta SQL manualmente;)
Atualização :
Outra solução que funciona em qualquer back-end de banco de dados, mesmo que não seja rel, sem escrever de forma personalizada
ModelManager
. Obtendo objetos aleatórios de um Queryset no Djangofonte
Convém usar a mesma abordagem usada para provar qualquer iterador, especialmente se você planeja experimentar vários itens para criar um conjunto de amostras . @ MatijnPieters e @DzinX pensam muito nisso:
fonte
OFFSET
), isso é desnecessariamente ineficiente.Uma abordagem muito mais fácil para isso envolve simplesmente filtrar o conjunto de registros de interesse e usar
random.sample
para selecionar quantos você quiser:Observe que você deve ter algum código para verificar
my_queryset
se não está vazio;random.sample
retornaValueError: sample larger than population
se o primeiro argumento contiver muito poucos elementos.fonte
Queryset
(pelo menos com Python 3.7 e Django 2.1); você deve convertê-lo em uma lista primeiro, o que obviamente recupera todo o conjunto de consultas.Oi, eu precisava selecionar um registro aleatório de um conjunto de consultas com o tamanho que eu também precisava informar (por exemplo, uma página da web produziu o item descrito e os registros restantes)
demorou metade do tempo (0,7s vs 1,7s) que:
Suponho que ele evite puxar toda a consulta antes de selecionar a entrada aleatória e torne meu sistema responsivo o suficiente para uma página que é acessada repetidamente para uma tarefa repetitiva em que os usuários desejam ver a contagem de itens_contagem.
fonte
Método para chave primária de incremento automático sem exclusões
Se você possui uma tabela em que a chave primária é um número inteiro seqüencial sem intervalos, o seguinte método deve funcionar:
Este método é muito mais eficiente do que outros métodos aqui que iteram por todas as linhas da tabela. Embora exija duas consultas ao banco de dados, ambas são triviais. Além disso, é simples e não requer definição de classes extras. No entanto, sua aplicabilidade é limitada a tabelas com uma chave primária de incremento automático, em que as linhas nunca foram excluídas, de modo que não haja lacunas na sequência de IDs.
No caso em que as linhas foram excluídas de forma que sejam lacunas, esse método ainda poderá funcionar se for tentado novamente até que uma chave primária existente seja selecionada aleatoriamente.
Referências
fonte
Eu tenho uma solução muito simples, faça gerente personalizado:
e depois adicione o modelo:
Agora, você pode usá-lo:
fonte
order_by('?').first()
mais de 60 vezes.