terça-feira, 27 de setembro de 2011

Como ordenar uma listagem de produtos pelos mais recentes, deixando os esgotados pro fim?

Recentemente tive que fazer a ordenação descrita no título deste post.

A solução encontrada foi a utilização da poderosa ferramenta extra que o django oferece em seu ORM.

Tinha a seguinte situação:

models.py

class Produto(models.Model):
     ...
     ...
     estoque = models.IntegerField()
     ...
     ...


Meu campo estoque gerencia a quantidade disponível de cada produto na loja. 

Fiz da seguinte forma no meu arquivo view.py:

def lista_produto(request):
     ...
     ...
     produtos = Produto.objecst.all().extra(select={'esgotado': "( if (produtos_produto.estoque > 0,0,-10000) )+produtos_produto.id"}).extra( order_by = ['-esgotado'] )
     ...
     ...

Juntamente com todos os campos da classe Produto, crio um campo a mais para o retorno, sendo este chamado de esgotado. Para este campo será executado a query passada a seguir para o banco de dados:

 if (produtos_produto.estoque > 0,0,-10000) )+produtos_produto.id

Caso o estoque for maior que 0, o IF irá retornar 0, caso contrário será retornado -10000. O valor negativo aqui é arbitrário, podendo este ser qualquer valor, desde que seu módulo seja pelo menos igual ao maior id do banco.



Ao valor do IF é adiconado o ID do produto corrente.

Desta forma todos os produtos que não tiverem estoque, será trazido neste campo um valor negativo visto que a conta ficará -10000 + ID,  e os que possuírem algum estoque, será 0 + ID.

Finalmente, passamos a nova ordenção, esgotado DESC, também pelo extra:
.extra( order_by = ['-esgotado'] )

Assim, todos os produtos com estoque ficarão com um valor positivo para o valor esgotado, e os que não tiverem, será um valor negativo, ficando por últimos com a ordenação decrescente.

hasta!


segunda-feira, 26 de setembro de 2011

Enviando emails no Django através de um pool de contas autenticadas

Recentemente passei por um problema com relação ao envio de emails. O cliente possuia as constas hospedadas em um servidor de emails que limitava o envio diário. Logo, com todos os contatos, logs de erros, logs de controle, etc sendo enviados por uma mesma conta, tinha dias que excedia tal limite.

Para que isso não voltasse a ocorrer, a idéia era ter uma lista de possíveis conexões, para que em caso de falha de autenticação ou limite, utilizasse a outra. Logo veio o problema. No settings.py apenas há espaço para uma autenticação de email.

Então foi que fiz o seguinte:

send_email_by_pool.py ( na raiz do projeto )


import random
import smtplib
from django.conf import settings
from django.core.mail import send_mail, get_connection

def send_mail_by_pool(subject, message, from_email=None, recipient_list=None, fail_silently=False,
auth_user=None, auth_password=None, connection=None):
"""
Seleciona a conexao atual usando um pool de conexoes
"""
global _valid_connections

ret = 0
while _valid_connections and ret == 0:
try:
connection = connection or choose_connection()
from_email = from_email or connection.username

auth_user = auth_user or connection.username
auth_password = auth_password or connection.password

ret = send_mail(subject, message, from_email, recipient_list, fail_silently, auth_user, auth_password, connection)

except (smtplib.SMTPAuthenticationError, smtplib.SMTPSenderRefused):
if not connection:
raise

_valid_connections.remove(connection._pool_name)

return ret

_valid_connections = settings.EMAIL_CONNECTIONS.keys()
def choose_connection():
global _valid_connections
chosen = random.choice(_valid_connections)
conn_settings = settings.EMAIL_CONNECTIONS[chosen]
connection = get_connection(**conn_settings)
connection._pool_name = chosen
return connection

no settings.py:



EMAIL_CONNECTIONS = {
'default': {
'backend': 'django.core.mail.backends.smtp.EmailBackend',
'host': 'smtp.seu_servidor.com.br',
'username': 'seu_email@ seu_servidor.com.br',
'password': 'sua_senha',
'use_tls': False, # para o caso de usar TLS, colocar True
'fail_silently': False,
},
'second': {
'backend': 'django.core.mail.backends.smtp.EmailBackend',
'host': 'smtp.seu_servidor.com.br',
'username': 'seu_email2@ seu_servidor.com.br',
'password': 'sua_senha2',
'use_tls': False, # para o caso de usar TLS, colocar True
'fail_silently': False,
},
}

E na sua view:


from send_email_by_pool import *

subject = "Testando envio"
message = "oi"
from_email = None
recipient_list = ['zejuniortdr@gmail.com',]
fail_silently = False
auth_user = None
auth_password = None 
connection = None

ret = send_mail_by_pool(subject, message, from_email, recipient_list, fail_silently, auth_user, auth_password, connection)



Desta forma, o envio ocorrerá passando por tantas contas quantas forem necessárias até enviar, desde que haja uma conta autorizada e que não tenha excedido o limite.

hasta!



sexta-feira, 23 de setembro de 2011

Upload em Python

Em alguns caso é necessário fazer um upload manual no python.

Para tal, fiz o seguinte:



def upload(request):
target = os.path.join(settings.MEDIA_ROOT, 'uploads/anuncios/logo/2010')
if request.method == 'POST':
if request.FILES.get('logo_file'):
f = request.FILES['logo_file']
try:
destination = open('%s/%s' % (target, f.name), 'wb+')
for chunk in f.chunks():
destination.write(chunk)
destination.close()
except:
retorno = "{error:'100', msg:'Problema: Erro de permissão para escrever em arquivo'}"
return HttpResponse(retorno)

retorno = "{error:'', msg:'Sucesso', arquivo:'%s'}" % (f.name)
return HttpResponse(retorno)
else:
retorno = "{error:'200', msg:'Problema: Sem FILES'}"
return HttpResponse(retorno)
else:
retorno = "{error:'300', msg:'Problema: Sem Post'}"
return HttpResponse(retorno)