Vez ou outra é necessário cadastrar a cidade e o estado em um formulário de algum projeto, seja para cadastro de perfis, pedidos, etc.
Quando tem endereço envolvido ainda é mais fácil, pois existem diversos webservices que fornecem o nome da forma correta, como consta nos correios. O problema acontece quando desejamos apenas cidade e estado.
Sempre aparece alguns "espertinhos" (pra não dizer outra coisa), que escrevem sua cidade cada hora de um jeito, abreviando das mais diversas maneiras, e com isso, o banco fica uma bagunça quando precisa-se filtrar ou tirar um relatório baseado em cidades.
Por isso, resolvi montar um banco com base nas informações dispostas no site do IBGE (http://www.cidades.ibge.gov.br/xtras/home.php - atualizado em 2010) para ajudar com esta tarefa.
Espero que ajude outros programadores.
Segue o link: https://github.com/zejuniortdr/cidades-br
hasta!
Django 1.9
Quem sou eu
Pesquisar
Marcadores
Admin
(18)
ajax
(2)
auditoria
(1)
autenticação
(1)
change list
(1)
CKEditor
(3)
Class Based Views
(4)
ColorPicker
(1)
contrab
(2)
Dicas
(64)
django
(2)
Django 1.3
(11)
Django 1.4
(10)
Django 1.6
(6)
Django 1.7
(4)
Django 1.8
(10)
Django 1.9
(4)
Django 2.0
(1)
Django/Python
(58)
E-mail
(1)
Erros Frequentes
(19)
Facebook API
(2)
Flash
(1)
Formset
(3)
Function Based Views
(1)
Git
(2)
ImagesLoaded
(1)
Inlines
(3)
Instagram API
(1)
Instalação
(10)
Interface
(1)
Javascript
(1)
JSON
(1)
Lion
(1)
Login único
(1)
MAC
(2)
maps
(1)
Masonry
(1)
Middleware
(2)
ModelForm
(4)
MySQL
(5)
normalização
(1)
ordenação
(1)
OSX
(1)
paginação
(2)
Parser
(2)
PDF
(2)
plugins-uteis
(6)
RegEx
(1)
RSS
(1)
slugify
(1)
South
(2)
SSH
(1)
Terminal
(1)
Tradução
(2)
tutorial
(10)
Twitter API
(1)
Twitter Style Pagination
(1)
upload
(1)
Vagrant
(2)
Validação
(2)
Virtualenv
(4)
Watermark
(3)
Widgets
(1)
sexta-feira, 6 de dezembro de 2013
quinta-feira, 7 de novembro de 2013
Como instalar o MySQL no OS X 10.9 Mavericks
Estava ocorrendo o erro:
EnvironmentError: mysql_config not found
ao tentar instalar o mysql com o comando:
com o ENV ativo, então resolvi reinstalar o MySQL Achei um tutorial (disponivel neste link) com 1 linha de comando que ajudou muito:
Pro caso de sair do ar, replico o conteúdo aqui:
Para instalar abra o terminal e execute o comando:
Siga os passos conforme forem aparecendo. Quando pedir a senha, coloque a senha do usuário do mac, será gerado uma senha nova para o usuario root do mysql e ela estará disponivel tanto no terminal quanto num arquivo direto na Mesa.
Se por ventura quando for rodar o syncdb, pegar o erro:
Library not loaded: libmysqlclient.18.dylib
Reason: image not found
faça um link simbolico para essa lib com o comando:
EnvironmentError: mysql_config not found
ao tentar instalar o mysql com o comando:
pip install mysql-python
com o ENV ativo, então resolvi reinstalar o MySQL Achei um tutorial (disponivel neste link) com 1 linha de comando que ajudou muito:
bash <(curl -Ls http://git.io/eUx7rg)
Pro caso de sair do ar, replico o conteúdo aqui:
#!/bin/bash ############################################# # AUTHOR: JONATHAN SCHWENN @JONSCHWENN # # MAC MINI VAULT - MAC MINI COLOCATION # # MACMINIVAULT.COM - @MACMINIVAULT # # VERSION 1.05 RELEASE DATE OCT 11 2013 # # DESC: THIS SCRIPT INSTALLS MySQL on OSX # ############################################# #REQUIREMENTS: # OS X 10.7 or newer ############################################# # CHECK FOR OS X 10.7+ if [[ $(sw_vers -productVersion | grep '10.[7-9]') ]] then # CHECK FOR EXISTING MySQL if [[ -d /usr/local/mysql && -d /var/mysql ]] then echo "It looks like you already have MySQL installed..." echo "This script will most likely fail unless MySQL is completley removed" echo "..." while true; do read -p "DO YOU WANT TO CONTINUE? [y/N]" yn case $yn in [Yy]* ) break;; [Nn]* ) exit ;; * ) echo "Please answer yes or no.";; esac done fi # LOOKS GOOD, LETS GRAB MySQL AND GET STARTED ... echo "Downloading MySQL Installers ... may take a few moments" curl -s -o ~/Downloads/MySQL.dmg http://cdn.mysql.com/Downloads/MySQL-5.6/mysql-5.6.14-osx10.7-x86_64.dmg hdiutil attach -quiet ~/Downloads/MySQL.dmg cd /Volumes/mysql-5.6.14-osx10.7-x86_64/ echo "..." echo "..." echo "Installing MySQL, administrator password required ..." sudo installer -pkg mysql-5.6.14-osx10.7-x86_64.pkg -target / echo "..." echo "..." echo "Installing MySQL start up items..." sudo installer -pkg MySQLStartupItem.pkg -target / echo "..." echo "..." echo "Click Install to install the MySQL preferance pane" echo "..." echo "..." # MOVING PREFPANE TO DOWNLOADS FOLDER SO IT CAN STILL BE INSTALLED # AFTER THE SCRIPT COMPLETES AND REMOVES THE INSTALLER FILES # AS SCRIPT DOESN'T WAIT FOR USER TO CLICK "INSTALL" FOR PREFPANE cp -R MySQL.prefPane ~/Downloads/MySQL.prefpane open ~/Downloads/MySQL.prefPane/ echo "..." sleep 15 sudo /usr/local/mysql/support-files/mysql.server start echo "export PATH=$PATH:/usr/local/mysql/bin" >> ~/.bash_profile sudo mkdir /var/mysql; sudo ln -s /tmp/mysql.sock /var/mysql/mysql.sock if [[ $(sudo /usr/local/mysql/support-files/mysql.server status | grep "SUCCESS") ]] then mypass="$(cat /dev/urandom | base64 | tr -dc A-Za-z0-9_ | head -c8)" echo $mypass > ~/Desktop/MYSQL_PASSWORD echo "Setting MySQL root Password to $mypass" echo "Placing password on desktop..." /usr/local/mysql/bin/mysql -uroot -e "GRANT ALL ON *.* TO 'root'@'localhost' IDENTIFIED BY '$mypass' WITH GRANT OPTION;" echo "..." echo "..." cd ~/ hdiutil detach -quiet /Volumes/mysql-5.6.14-osx10.7-x86_64/ sleep 2 rm ~/Downloads/MySQL.dmg # NEW MY.CNF PERFORMANCE OPTION START echo "BASE PERFORMANCE MY.CNF IS JUST A GENERIC SUGGESTION FOR PERFORMANCE" echo "YOUR RESULTS MAY VARY AND YOU MAY WANT TO FURTHER TUNE YOUR MY.CNF SETTINGS" echo "BASE PERFORMANCE MY.CNF INCREASES BUFFERS/MEMORY USAGE" echo "8GB+ RAM IS RECOMMENDED FOR BASE PERFORMANCE MY.CNF" echo "..." sudo cp /usr/local/mysql/my.cnf /usr/local/mysql/mmv.cnf sudo tee -a /usr/local/mysql/mmv.cnf > /dev/null << EOF # CUSTOMIZED BY MMVMySQL SCRIPT - JUST GENERIC SETTINGS # DO NOT TREAT AS GOSPEL skip-external-locking key_buffer_size = 384M max_allowed_packet = 1M table_open_cache = 512 sort_buffer_size = 2M read_buffer_size = 2M read_rnd_buffer_size = 8M myisam_sort_buffer_size = 64M thread_cache_size = 8 query_cache_size = 32M thread_concurrency = 4 EOF while true; do read -p "DO YOU WANT TO LOAD A BASE PERFORMANCE MY.CNF FILE? [y/N]" cnf case $cnf in [Yy]* ) sudo cp /usr/local/mysql/mmv.cnf /etc/my.cnf; sudo /usr/local/mysql/support-files/mysql.server restart; break ;; [Nn]* ) break;; * ) echo "Please answer yes or no.";; esac done # NEW MY.CNF PERFORMANCE OPTION END # NEW SEQUEL PRO INSTALL OPTION START while true; do read -p "DO YOU WANT TO AUTOMATICALLY INSTALL SEQUEL PRO? [Y/n]" sp case $sp in [Yy]* ) curl -s -o ~/Downloads/SequelPro.dmg https://sequel-pro.googlecode.com/files/sequel-pro-1.0.2.dmg; hdiutil attach -quiet ~/Downloads/SequelPro.dmg;cp -R /Volumes/Sequel\ Pro\ 1.0.2/Sequel\ Pro.app/ /Applications/Sequel\ Pro.app/; hdiutil detach -quiet /Volumes/Sequel\ Pro\ 1.0.2/;sleep 5; rm ~/Downloads/SequelPro.dmg; echo "Sequel Pro is now in your Applications folder!"; break ;; [Nn]* ) break;; * ) echo "Please answer yes or no.";; esac done # NEW SEQUEL PRO INSTALL OPTION END echo " " echo " " echo "ALL DONE! Install Sequel Pro or phpmyadmin to administer MySQL" echo "Log off and log back in for 'mysql' to be recognized as a command in terminal" else "SORRY, MySQL IS NOT RUNNING ... THERE MUST BE A PROBLEM" fi else echo "ERROR: YOU ARE NOT RUNNING OS X 10.7 OR NEWER" exit 1 fi
Para instalar abra o terminal e execute o comando:
bash <(curl -Ls http://git.io/eUx7rg)
Siga os passos conforme forem aparecendo. Quando pedir a senha, coloque a senha do usuário do mac, será gerado uma senha nova para o usuario root do mysql e ela estará disponivel tanto no terminal quanto num arquivo direto na Mesa.
Se por ventura quando for rodar o syncdb, pegar o erro:
Library not loaded: libmysqlclient.18.dylib
Reason: image not found
faça um link simbolico para essa lib com o comando:
sudo ln -s /usr/local/mysql/lib/libmysqlclient.18.dylib /usr/lib/libmysqlclient.18.dylib
Marcadores:
Dicas,
Django/Python,
Erros Frequentes,
Instalação,
MySQL,
Virtualenv
sexta-feira, 23 de agosto de 2013
Ordenando por um campo calculado por uma def no admin
Seguindo o exemplo anterior, Como utilizar os inlines no front para os ModelForms (class based view) , imagine que gostaríamos de exibir quantas fotos cada cadastro postou. Fácil né?
Basta fazer uma def no models.py como abaixo e depois colocá-la no list_display do admin.py:
Mas e se, quiséssemos ordenar no admin por este valor? Saber em ordem quais os cadastros que mais enviaram fotos? Por ser uma def, o django não ordena automaticamente. Pra isso temos que fazer alguns ajustes. Primeiro de tudo, apague esta def do seu model caso tenha feito.
No admin de seu model no arquivo admin.py, deixe como abaixo:
depois basta adicionar a def qtde_fotos no list_display.
hasta!
Basta fazer uma def no models.py como abaixo e depois colocá-la no list_display do admin.py:
def qtde_fotos(self): return self.foto_set.all().count()
Mas e se, quiséssemos ordenar no admin por este valor? Saber em ordem quais os cadastros que mais enviaram fotos? Por ser uma def, o django não ordena automaticamente. Pra isso temos que fazer alguns ajustes. Primeiro de tudo, apague esta def do seu model caso tenha feito.
No admin de seu model no arquivo admin.py, deixe como abaixo:
def queryset(self, request): qs = super(CadastroAdmin, self).queryset(request) qs = qs.annotate(qtde=Count('foto')) return qs def qtde_fotos(self, inst): return inst.qtde qtde_fotos.admin_order_field = 'qtde'
depois basta adicionar a def qtde_fotos no list_display.
hasta!
Marcadores:
Admin,
change list,
Dicas,
Django 1.4,
Django/Python,
ordenação
Como utilizar os inlines no front para os ModelForms (class based view)
Assim como prometido, mesmo que com uma "pequena" demora, segue a continuação do post
Como utilizar os inlines no front para os ModelForms (function based view). Faremos algo bem similar com alguns detalhes a mais.
hasta!
Como utilizar os inlines no front para os ModelForms (function based view). Faremos algo bem similar com alguns detalhes a mais.
Objetivo é fornecer um formulário de cadastro e este pode fazer uploads de fotos. Quando o email já existir no banco de dados, faremos que as fotos do segundo envio seja direcionadas para o primeiro cadastro, do contrário, uma nova instancia para todos os objetos envolvidos.
models.py
from django.db import models # Create your models here. class Cadastro(models.Model): """(Cadastro description)""" nome = models.CharField(max_length=255) email = models.EmailField(verbose_name='E-mail') telefone = models.CharField(max_length=20) aceite = models.BooleanField(default=True) data = models.DateField(auto_now_add=True) hora = models.TimeField(auto_now_add=True) class Meta: verbose_name, verbose_name_plural = u"Cadastro" , u"Cadastros" ordering = ('-data',) def __unicode__(self): return "%s" % self.nome class Foto(models.Model): """(Foto description)""" cadastro = models.ForeignKey(Cadastro) foto = models.ImageField(upload_to="uploads/fotos/foto/%Y") data = models.DateField(auto_now_add=True) hora = models.TimeField(auto_now_add=True) aprovado = models.BooleanField(default=False) class Meta: verbose_name, verbose_name_plural = u"Foto" , u"Fotos" ordering = ('-data',) def __unicode__(self): return "%s" % self.id def thumb_admin(self): from sorl.thumbnail import get_thumbnail im = get_thumbnail(self.foto, '120x120', crop='center', quality=99) if im: return '<a href="http://www.blogger.com/media/%s" onclick="window.open(this.href);return false;"><img alt="Imagem" src="%s" /></a>' % (self.foto, str(im.url)) return 'Sem foto' thumb_admin.is_safe = True thumb_admin.allow_tags = True thumb_admin.short_description = 'Thumb'
admin.py
from django import forms from django.contrib import admin from .models import * class FotoInline(admin.TabularInline): model = Foto readonly_fields = ['foto',] extra = 5 class CadastroAdmin(admin.ModelAdmin): inlines = [ FotoInline, ] search_fields = ('nome', 'email',) list_display = ('nome', 'email','telefone','data', 'hora',) list_filter = ['data',] date_hierarchy = 'data' readonly_fields = ['nome', 'email','telefone','aceite'] save_on_top = True admin.site.register(Cadastro, CadastroAdmin) class FotoAdmin(admin.ModelAdmin): list_display = ('cadastro', 'data', 'hora','thumb_admin','aprovado') list_filter = ['data','aprovado'] date_hierarchy = 'data' list_editable = ['aprovado'] readonly_fields = ['cadastro','foto',] save_on_top = True admin.site.register(Foto, FotoAdmin)
forms.py
# coding: utf-8 from django import forms from django.utils.translation import ugettext_lazy as _ from .models import * from util import util as U from django.forms.models import inlineformset_factory class CadastroForm(forms.ModelForm): class Meta: model = Cadastro class FotoForm(forms.ModelForm): aprovado = forms.BooleanField(initial=False, required=False) aceite = forms.BooleanField(initial=True) class Meta: model = Foto def dados(self): return {'form':self.cleaned_data, 'data':datetime.now()} def clean(self): cleaned_data = super(FotoForm, self).clean() #if cleaned_data.get("senha") != cleaned_data.get("confirme"): # self._errors["confirme"] = self.error_class(['Senha nao confere']) return self.cleaned_data FotoFormSet = inlineformset_factory(Cadastro, Foto, extra=5)
urls.py
# coding: utf-8 from django.conf.urls.defaults import patterns, include, url from . import views urlpatterns = patterns('fotos.views', url(r'^$', views.CadastroView.as_view(), name='cadastro_form'), )
form.html
<html> <head> <meta charset="utf-8"> <title></title> </head> <body> <form action="" method="post" enctype="multipart/form-data"> {% csrf_token %} {{ form.errors }} <p>Nome:{{ form.nome }}</p> <p>Telefone: {{ form.telefone }}</p> <p>E-mail: {{ form.email }}</p> <p>Foto: {{ form.foto }}</p> <p>Aceite: {{ form.aceite }}</p> {{ foto_formset.management_form }} {% for form in foto_formset %} {{ form.id }} <div class="form-row inline {{ foto_formset.prefix }}"> {{ form.foto.label_tag }} {{ form.foto }} {{ form.foto.errors }} </div> {% endfor %} <input type="submit" name="" value="Enviar"> </form> </body> </html>
views.py
class CadastroView(CreateView): #template_name = 'form.html' template_name = 'index.html' model = Cadastro form_class = CadastroForm success_url = '/envie/?sucesso=1#sucesso' def form_valid(self, form): context = self.get_context_data() foto_form = context['foto_formset'] email = form.cleaned_data['email'] if foto_form.is_valid(): print "erros: %s" % foto_form.errors if Cadastro.objects.filter(email=email): foto_form.instance = Cadastro.objects.get(email=email) else: self.object = form.save() foto_form.instance = self.object foto_form.save() return HttpResponseRedirect('/envie/?sucesso=1#sucesso') else: addDebug("erros: %s" % foto_form.errors) return self.render_to_response(self.get_context_data(form=form)) def form_invalid(self, form): return self.render_to_response(self.get_context_data(form=form)) def get_context_data(self, **kwargs): context = super(CadastroView, self).get_context_data(**kwargs) r = self.request.POST f = self.request.FILES if r and f: context['foto_formset'] = FotoFormSet(self.request.POST,f) else: context['foto_formset'] = FotoFormSet() return context
cadastro.js
jQuery(document).ready(function($) { $('.bt-mais').click(function(event) { if($('.enviar-foto-fake').length < 5){ $('.enviar-foto-fake').last().clone().insertAfter($('.enviar-foto-fake').last()); $('.enviar-foto-fake').last().find('div p.nome-foto').text('Você pode enviar até cinco fotos'); $('.enviar-foto-fake').last().find('input').attr({ id: 'id_foto_set-'+($('.enviar-foto-fake').length-1)+'-foto', name: 'foto_set-'+($('.enviar-foto-fake').length-1)+'-foto' }); $('#id_foto_set-TOTAL_FORMS').val($('.enviar-foto-fake').length); } if ($('.enviar-foto-fake').length == 5) { $(this).hide(); }; }) });
hasta!
Marcadores:
Class Based Views,
Dicas,
Django 1.4,
Django/Python,
Formset,
Inlines,
ModelForm,
tutorial
Gerando imagens com marca d'agua com django-watermark 0.1.6-pre1
Algumas vezes é necessário que seja feita um tratamento em imagens, para que estas apresentem o logo ou alguma outra informação. Dependendo da utilização, pode simplesmente colocar as imagens como backgrounds de divs com position absolute dentro de um container. Esta abordagem tem dois pontos negativos:
Então qual a correta forma de solucionar o problema? django-watermark.
Execute o syncdb para criar a tabela deste app.
python manage.py syncdb
position - Este é bastante personalizável. Primeiro, você pode exibirsua marca d'água em um canto suas imagens usando um dos BR, BL, TR, e TL. Estes representam 'bottom-right', 'bottom-left', 'top-right' e 'top-left', respectivamente.
Alternativamente, você pode usar o posicionamento relativo ou absoluto para a marca d'água. Posicionamento relativo usa porcentagens; posicionamento absoluto utiliza pixels exatos. Você pode misturar e combinar estes dois modos de posicionamento, mas você não pode misturar e combinar relativa / absoluta com o posicionamento canto. Quando se utiliza o posicionamento relativo / absoluto, o valor para o parâmetro de posição é XxY, onde X é o valor da esquerda e o símbolo Y representa o valor superior. Os valores de esquerda e superior devem ser separados com um x minúsculo.
Se você quiser sua imagem marca d'água para mostrar-se no centro de qualquer imagem, você pode utilizar como posição = 50% x50% ou mesmo position = C. Se você queria que a marca d'água para mostrar-se a meio caminho entre as margens esquerda e direita da imagem e 100 pixels da parte superior, você utilizaria = 50% x100.
Finalmente, você pode dizer ao filtro para gerar uma posição para sua marca de água de forma dinâmica. Para fazer isso, use position = R.
opacity - Este parâmetro permite que você especifique a transparência da marca d'água aplicada. O valor deve ser um número inteiro entre 0 e 100, onde 0 é totalmente transparente e 100 é totalmente opaco. Por padrão, a opacidade é fixado em 50%.
tile - Se você quer sua marca d'água para azulejo por toda a imagem, basta especificar um parâmetro como tile= 1. Por padrão, o tile é fixado em 0.
scale - Se você gostaria de ter a marca d'água tão grande quanto possível a imagem de destino e totalmente visível, você pode querer usar scale = F. Se você quiser especificar um fator de escala particular, é só usar algo como scale = 1.43.
greyscale - Se você quer sua marca d'água a ser em tons de cinza, você pode especificar o parâmetro greyscale = 1 e todas saturações de cores vão embora.
rotation - Definir este parâmetro para qualquer inteiro entre 0 e 359 (na verdade, qualquer número inteiro deve funcionar, mas para sua própria sanidade, eu recomendo manter o valor entre 0 e 359). Se você quiser que a rotação ser aleatória, use rotation= R em vez de um número inteiro.
obscure - Defina esse parâmetro como 0 para fazer o nome do arquivo da imagem original do visível para o usuário. O padrão é 1 (ou verdadeiro) para ocultar o nome do arquivo original.
quality - Manter esta a um número inteiro entre 0 e 100 para especificar a qualidade da imagem resultante. O padrão é 85.
random_position_once - Defina como 0 ou 1 para especificar o comportamento de posicionamento aleatório para marca d'água da imagem. Quando ajustado para 0, a marca d'água será colocado aleatoriamente em cada solicitação. Quando ajustado para 1, a marca d'água será posicionado aleatoriamente na primeira solicitação, e os pedidos subsequentes usarão a imagem produzida. O padrão é True (posicionamento aleatório só acontece no primeiro pedido).
- Alguém com o mínimo de conhecimento conseguirá isolaras imagens através do código fonte e obter a imagem original sem a marca facilmente.
- Caso seja necessário integração com algum plugin de compartilhamento de conteúdo em redes sociais, por exemplo, a imagem que o plugin pega também é a original.
Então qual a correta forma de solucionar o problema? django-watermark.
Instalação
Para instalação basta utilizar o pip ou easy_install:easy_install django-watermark pip install -U django-watermark
Configuração
Para configurá-lo em seu projeto, adicone o aplicativo no INSTALLED_APPS no settings.pyINSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', ... 'watermarker', ... )
Execute o syncdb para criar a tabela deste app.
python manage.py syncdb
Opções:
position - Este é bastante personalizável. Primeiro, você pode exibirsua marca d'água em um canto suas imagens usando um dos BR, BL, TR, e TL. Estes representam 'bottom-right', 'bottom-left', 'top-right' e 'top-left', respectivamente.
Alternativamente, você pode usar o posicionamento relativo ou absoluto para a marca d'água. Posicionamento relativo usa porcentagens; posicionamento absoluto utiliza pixels exatos. Você pode misturar e combinar estes dois modos de posicionamento, mas você não pode misturar e combinar relativa / absoluta com o posicionamento canto. Quando se utiliza o posicionamento relativo / absoluto, o valor para o parâmetro de posição é XxY, onde X é o valor da esquerda e o símbolo Y representa o valor superior. Os valores de esquerda e superior devem ser separados com um x minúsculo.
Se você quiser sua imagem marca d'água para mostrar-se no centro de qualquer imagem, você pode utilizar como posição = 50% x50% ou mesmo position = C. Se você queria que a marca d'água para mostrar-se a meio caminho entre as margens esquerda e direita da imagem e 100 pixels da parte superior, você utilizaria = 50% x100.
Finalmente, você pode dizer ao filtro para gerar uma posição para sua marca de água de forma dinâmica. Para fazer isso, use position = R.
opacity - Este parâmetro permite que você especifique a transparência da marca d'água aplicada. O valor deve ser um número inteiro entre 0 e 100, onde 0 é totalmente transparente e 100 é totalmente opaco. Por padrão, a opacidade é fixado em 50%.
tile - Se você quer sua marca d'água para azulejo por toda a imagem, basta especificar um parâmetro como tile= 1. Por padrão, o tile é fixado em 0.
scale - Se você gostaria de ter a marca d'água tão grande quanto possível a imagem de destino e totalmente visível, você pode querer usar scale = F. Se você quiser especificar um fator de escala particular, é só usar algo como scale = 1.43.
greyscale - Se você quer sua marca d'água a ser em tons de cinza, você pode especificar o parâmetro greyscale = 1 e todas saturações de cores vão embora.
rotation - Definir este parâmetro para qualquer inteiro entre 0 e 359 (na verdade, qualquer número inteiro deve funcionar, mas para sua própria sanidade, eu recomendo manter o valor entre 0 e 359). Se você quiser que a rotação ser aleatória, use rotation= R em vez de um número inteiro.
obscure - Defina esse parâmetro como 0 para fazer o nome do arquivo da imagem original do visível para o usuário. O padrão é 1 (ou verdadeiro) para ocultar o nome do arquivo original.
quality - Manter esta a um número inteiro entre 0 e 100 para especificar a qualidade da imagem resultante. O padrão é 85.
random_position_once - Defina como 0 ou 1 para especificar o comportamento de posicionamento aleatório para marca d'água da imagem. Quando ajustado para 0, a marca d'água será colocado aleatoriamente em cada solicitação. Quando ajustado para 1, a marca d'água será posicionado aleatoriamente na primeira solicitação, e os pedidos subsequentes usarão a imagem produzida. O padrão é True (posicionamento aleatório só acontece no primeiro pedido).
Utilização
Sua utilização também é bastante simples. No template que precisar gerar a marca d'agua carrege o filter da aplicação com o comando:{% load watermarker %}
Exemplos
{{ image_url|watermark:"My Watermark,position=br,opacity=35" }}
Procura uma marca d'água com o nome "My Watermark", coloque-o no canto inferior direito da imagem de destino, utilizando um nível de transparência de 35%.
{{ image_url|watermark:"Your Watermark,position=tl,opacity=75" }}
Procura uma marca d'água com o nome "Your Watermark", coloque-o no canto superior esquerdo da imagem de destino, utilizando um nível de transparência de 75%.
{{ image_url|watermark:"The Watermark,position=43%x80%,opacity=40" }}
Procura uma marca d'água com o nome "The Watermark", coloca-lo em 43% em relação ao eixo x e de 80% no eixo dos y da imagem do alvo, a um nível de 40% de transparência.
{{ image_url|watermark:"The Watermark,position=R,opacity=10,rotation=45" }}
Procura uma marca d'água com o nome "The Watermark", gera aleatoriamente uma posição para ele, a um nível de 10% de transparência, rotação de 45 graus.
{{ image_url|watermark:"w00t,opacity=40,tile=1" }}
Procura uma marca d'água chamado "w00t", telhas de TI em toda a imagem de destino inteira, a um nível de 40% de transparência.
Marcadores:
Dicas,
Django 1.4,
Django/Python,
Watermark
sexta-feira, 21 de junho de 2013
Exibir e ordenar por um campo FK de outra classe no change list
Comumente utilizamos classe com FKs e no change list do admin existe a possibilidade de fazer filtros por esses campos da seguinte forma:
Sabia que tem como fazer isso ser exibido também no list_display?
Se tentarmos utilizar da mesma forma surge o seguinte erro:
ImproperlyConfigured at /admin/seu-app/sua-classe/
Sua-ClasseAdmin.list_display[4], 'suafk__seuatributo' is not a callable or an attribute of 'Sua-ClasseAdmin' or found in the model 'Sua-Classe'.
O numero 4 indica a posição que colocou o atributo a ser exibido, e pode variar de acordo com a posição que colocar.
hasta!
list_filter = ['suafk__seuatributo']
Sabia que tem como fazer isso ser exibido também no list_display?
Se tentarmos utilizar da mesma forma surge o seguinte erro:
ImproperlyConfigured at /admin/seu-app/sua-classe/
Sua-ClasseAdmin.list_display[4], 'suafk__seuatributo' is not a callable or an attribute of 'Sua-ClasseAdmin' or found in the model 'Sua-Classe'.
O numero 4 indica a posição que colocou o atributo a ser exibido, e pode variar de acordo com a posição que colocar.
Mas então como fazer isso? Simples:
No seu models.py crie um método dentro da classe que tem a FK do atributo que deseja listar da seguinte forma:
def seu_metodo(self): return self.suafk.seuatributo seu_metodo.admin_order_field = 'suafk__seuatributo'
Agora basta chamar o metodo recem criado no list_display:
list_display = ('seu_metodo')
hasta!
Marcadores:
Admin,
Dicas,
Django 1.4,
Django/Python
sexta-feira, 24 de maio de 2013
Como utilizar os inlines no front para os ModelForms (function based view)
Para isso é preciso de dois models, um model Pai que conterá N instancias de objetos de uma classe Filho, como por exemplo a relação de Conteúdo para Fotos. Assumindo este exemplo, deixe os arquivos como a seguir:
models.py
forms.py
views.py
conteudo_template.html
Para as class-based views, mostrarei como fazer no próximo post. hasta!
models.py
from django.db import models # Create your models here. class Conteudo(models.Model): """(Conteudo description)""" # ... # SEUS ATRIBUTOS AQUI # ... class Meta: verbose_name, verbose_name_plural = u"Conteúdo" , u"Conteúdos" class Foto(models.Model): """(Foto description)""" conteudo= models.ForeignKey(Conteudo, verbose_name=u'Conteúdo') imagem = models.ImageField(upload_to="uploads/conteudos/imagem/%Y") class Meta: verbose_name, verbose_name_plural = u"Foto" , u"Fotos" ordering = ('id',) def __unicode__(self): return "%s" % str(self.imagem)
forms.py
from django.forms.models import inlineformset_factory from models import Conteudo, Foto class ConteudoForm(forms.ModelForm): class Meta: model = Conteudo FotoFormSet = inlineformset_factory(Conteudo, Foto, extra=1)
views.py
from models import * from django.shortcuts import render_to_response, get_object_or_404, redirect from django.template import Context, loader, RequestContext from django.core.context_processors import csrf from forms import ConteudoForm, FotoFormSet def conteudo_enviar(request): ret = '' if request.POST: c = {} c.update(csrf(request)) r = request.POST f = request.FILES form = ConteudoForm(r,f) formset = FotoFormSet(r,f) if form.is_valid() and request.FILES: try: form.save() formset.instance = get_object_or_404(Conteudo, id=form.instance.id) formset.save() ret = u'Conteúdo enviado com sucesso.' # ZERANDO O FORM form = ConteudoForm() formset = FotoFormSet() except: ret = u'Erro ao enviar o conteúdo' elif request.FILES: for field in form: if field.errors: ret += "%s: %s" % (field.label, striptags(str(field.errors)) ) else: ret += "Imagem: pelo menos uma imagem do conteúdo deve ser enviada" else: form = ConteudoForm() formset = FotoFormSet() VARS = { 'form':form, 'formset':formset, 'ret':ret, } return render_to_response('conteudo_template.html', VARS, context_instance=RequestContext(request))
conteudo_template.html
<script type="text/javascript" src="http://underscorejs.org/underscore-min.js"></script> <script type="text/html" id="foto-template"> <div id="foto-<%= id %>" class="imagem"> <input id="id_foto_set-<%= id %>-id" type="hidden" name="foto_set-<%= id %>-id"> <label for="id_foto-<%= id %>-title">Imagem:</label> <input id="id_foto_set-<%= id %>-imagem" class="file" type="file" name="foto_set-<%= id %>-imagem"> <input type="hidden" name="foto-<%= id %>-acao" id="id_foto-<%= id %>-acao"> <input type="hidden" name="foto-<%= id %>-id" id="id_foto-<%= id %>-id"> {% if not request.META.HTTP_USER_AGENT|isIE %} <div class="filename"></div> <div class="button-container"> <input type="button" name="filebutton" class="filebutton" value="Procurar"> </div> {% endif %} </div> </script> <script type="text/javascript"> jQuery(document).ready(function($) { // Stuff to do as soon as the DOM is ready; $('.add-foto').click(function(ev){ ev.preventDefault(); var count = $('.fotos').children().length; var tmplMarkup = $('#foto-template').html(); var compiledTmpl = _.template(tmplMarkup, { id : count }); $('div.fotos').append(compiledTmpl); // update form count $('#id_foto_set-TOTAL_FORMS').attr('value', count+1); console.log(count+1); }); }); </script> {{ formset.management_form }} <div class="fotos"> {% for foto_form in formset %} <div id="foto-{{ forloop.counter0 }}" class="imagem"> {{ foto_form.id }} {% if request.META.HTTP_USER_AGENT|isIE %} {# IE HACK PARA INPUT FILE #} {{ foto_form }} {# <input type="file" id="id_imagem" name="imagem[]" class="file" style="right:0; height: 50px; width:478px; position:absolute;opacity:1.0;filter:alpha(opacity=100);"> #} {% else %} {{ foto_form }} <div class="filename"></div> <div class="button-container"> <input type="button" name="filebutton" class="filebutton" value="Procurar"> </div> {% endif %} </div> {% endfor %} </div> <div class="form-actions"> <button class="medium green add-foto">Adicionar mais fotos</button> </div>
Para as class-based views, mostrarei como fazer no próximo post. hasta!
Marcadores:
Dicas,
Django 1.4,
Formset,
Inlines,
ModelForm
sexta-feira, 17 de maio de 2013
Como criar uma aba no Facebook e pegar os dados dos usuário no aplicativo
Extraído de: http://blog.hubspot.com/blog/tabid/6307/bid/26330/How-to-Create-Custom-Tabs-for-Facebook-Business-Pages.aspx
Volte para suas configurações de aplicativos em developers.facebook.com, desça até a opção "Selecionar como seu aplicativo se integra com Facebook" seção, e escolha "Page Tab", nomeie do seu guia que você gostaria, então copie a URL da página que você criou no passo 4, e cole a URL em "Página Tab URL".
Para se certificar de que todos podem ver o sua nova guia da página do Facebook, você também precisará fornecer uma URL segura. Esta deve ser a mesma URL que você forneceu para o separador de página, mas com "https://" em vez de "http://" na frente. Se o seu site não suporta https, o aplicativo ainda funciona para quem tem navegação segura desativada em suas configurações do Facebook, mas você deve olhar para obter um certificado SSL.
Salve as alterações que você fez para o app.
Passo 1: Faça login como Desenvolvedor
Faça login como desenvolvedor no Facebook em : https://developers.facebook.com/appsPasso 2: Crie e nomeie seu novo aplicativo
Clique em "Criar um novo aplicativo" no canto superior esquerdo. Você precisa fornecer um "Display Name", que é como seu "app", ou guia, será chamado. Você também precisa criar um "namespace", que é basicamente apenas um ID único para o seu aplicativo.
Passo 3: Escolha uma imagem e atualizar informações básicas para a App
Escolha uma imagem e um ícone para o seu guia personalizado. Lembre-se, isso vai exibir no topo da sua página do Facebook, então pense nisso como um call-to-action. Escolha uma imagem que vai receber seus visitantes ao clicar!
Atualize suas informações básicas com o nome de domínio e categoria. Você também pode fazer upload de uma imagem personalizada para o seu aplicativo, clicando em "ícone de edição."
Passo 4: Criar o conteúdo que será exibido dentro da aba do aplicativo
Agora crie uma página da web fora do Facebook. Isto é o que será exibido dentro de sua guia personalizado Facebook. Para garantir o conteúdo da página exibida corretamente no Facebook, certifique-se que a largura da sua página web esteja configurado para 100%, 520px ou 810px. Certifique-se de todas as imagens, vídeos, etc, que você incluir em sua página são menos do que qualquer 520px ou 810px, dependendo de quão grande você escolheu para fazer o conteúdo da sua guia.
Para se certificar de que todos podem ver o sua nova guia da página do Facebook, você também precisará fornecer uma URL segura. Esta deve ser a mesma URL que você forneceu para o separador de página, mas com "https://" em vez de "http://" na frente. Se o seu site não suporta https, o aplicativo ainda funciona para quem tem a navegação segura desativada em suas configurações do Facebook, mas você deve obter um certificado SSL.
Salve as alterações que você fez para o app.
Passo 5: Diga Facebook o conteúdo que você deseja exibir no seu guia Personalizar
Volte para suas configurações de aplicativos em developers.facebook.com, desça até a opção "Selecionar como seu aplicativo se integra com Facebook" seção, e escolha "Page Tab", nomeie do seu guia que você gostaria, então copie a URL da página que você criou no passo 4, e cole a URL em "Página Tab URL".
Para se certificar de que todos podem ver o sua nova guia da página do Facebook, você também precisará fornecer uma URL segura. Esta deve ser a mesma URL que você forneceu para o separador de página, mas com "https://" em vez de "http://" na frente. Se o seu site não suporta https, o aplicativo ainda funciona para quem tem navegação segura desativada em suas configurações do Facebook, mas você deve olhar para obter um certificado SSL.
Salve as alterações que você fez para o app.
Passo 6: Adicione o seu guia para sua página do Facebook
Este passo pode ser um pouco complicado, então leia com atenção. Para instalar sua guia nova em sua página de negócios, você precisa visitar um link com vários parâmetros de URL personalizada. O link é:
http://www.facebook.com/dialog/pagetab?app_id=YOUR_APP_ID&next=YOUR_URL
Você vai precisar de substituir os termos em negrito na URL acima. "YOUR_APP_ID" e "YOUR_URL" com algumas informações que o Facebook oferece para o seu aplicativo, o ID do seu aplicativo e a url do seu site.
Passo 7: Página que será renderizada na aba e channel file
Channel File
Crie um arquivo com o conteúdo abaixo que será utilizado na página da sua aba:
<?php $cache_expire = 60*60*24*365; header("Pragma: public"); header("Cache-Control: maxage=".$cache_expire); header('Expires: '.gmdate('D, d M Y H:i:s', time()+$cache_expire).' GMT'); ?> <script src="//connect.facebook.net/en_US/all.js"></script>Se não tiver trabalhando com php, pode criar um arquivo apenas com o script do facebook, o php acima serve apenas para controle de cache, ou pode fazer da forma que sua linguagem assim permitir.
Conteúdo da aba:
Na sua página que se hospedará externamente ao facebook, insira os códigos abaixo, subistituindo os devidos valores:Antes de fechar a tag HEAD:
<meta property="og:title" content="TITULO" /> <meta property="og:url" content="<?=$url?>/" /> <meta property="og:type" content="company" /> <meta property="og:site_name" content="TITULO DO SITE" /> <meta property="og:description" content="DESCRICAO" /> <meta property="fb:app_id" content="ID DO APLICATIVO" /> <meta property="og:image" content="https://URL DA THUMB/thumb.jpg" />
Logo depois da abertura da tag BODY:
<!-- Facebook JS SDK --> <div id="fb-root"></div> <script> // Additional JS functions here window.fbAsyncInit = function() { FB.init({ appId : 'ID DO APLICATIVO', // App ID channelUrl : '//CHANNEL FILE', // Channel File ex: 'SEU SITE/channel.php' status : true, // check login status cookie : true, // enable cookies to allow the server to access the session xfbml : true // parse XFBML }); // Additional init code here FB.getLoginStatus(function(response) { if (response.status === 'connected') { // connected //alert('usuario já autorizou a aplicacao'); //console.log(JSON.stringify(response)); FB.api('/me', function(response){ //DADOS ESTARÃO TODOS NO RESPONSE // Ex: // NOME = response.name }); } else if (response.status === 'not_authorized') { // not_authorized login(); } else { // not_logged_in login(); } }); }; function login() { FB.login(function(response) { if (response.authResponse) { // connected FB.api('/me', function(response) { location.reload(); }); } else { // cancelled //alert("Usuário Cancelou"); } },{scope: 'email'}); } function postToWall() { FB.login(function(response) { if (response.authResponse) { FB.ui({ method: 'feed', name: 'NOME', link: 'LINK DO SEU APLICATIVO', // EX: https://www.facebook.com/SUA PAGINA/app_(ID DO APLICATIVO)'; picture: 'URL DA FOTO DO POST', caption: 'LEGENDA', description: 'DESCRICAO/TEXTO DO POST' }, function(response) { if (response && response.post_id) { //alert('Post was published.'); } else { //alert('Post was not published.'); } }); } else { //alert('User cancelled login or did not fully authorize.'); } }, {scope: 'publish_stream'}); return false; } // Load the SDK Asynchronously (function(d){ var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0]; if (d.getElementById(id)) {return;} js = d.createElement('script'); js.id = id; js.async = true; js.src = "//connect.facebook.net/en_US/all.js"; ref.parentNode.insertBefore(js, ref); }(document));
Considerações:
- O que acontece na verdade depois de tudo isso é que sua página será executada dentro de um iframe do facebook, e graças a isso, é possível fazer tudo que uma página normal faz: upload, persistência em banco de dados, ajax, etc.
- O SSL se faz ncessário, pois caso não exista, apenas usuários que desabilitaram esta opção nas configurações do facebook poderão ver o aplicativo, sendo que muitos não fazem ideia de onde isso fica, e nem o que faz.
- A função de login, solicita além dos dados publicos (nome, lista de amigos, etc) o eemail da pessoa. Para mais permissões, consulte https://developers.facebook.com/docs/reference/login/#permissions
quarta-feira, 8 de maio de 2013
GIT - Visão geral e comandos úteis
Git é um sistema de controle de versão distribuído grátis e de código aberto, desenhado para lidar com tudo, desde pequenos até grandes projetos com rapidez e eficiência.
Para instalar:
Ou também pode ser usado os famosos apt-get, yum, ports, etc.
Fluxo
Seu repositório consiste em três "árvores" mantidas pelo git. A primeira delas é sua Working Directory que contém os arquivos vigentes que estão na máquina. A segunda é a Index que funciona como uma área temporária, que contém as mudanças propostas pelos comandos "add" e finalmente a HEAD que aponta para o último commit (confirmação) que você fez.
Comandos mais utilizados:
- git init
- Para criar um novo repositório
- git clone usuário@servidor:/caminho/para/o/repositório
- Para clonar um repositório em um servidor remoto
- git add /caminho/para/o/arquivo
- Para adicionar um arquivo que foi criado e ainda não está sob controle do git
- git add .
- Para adicionar todos os arquivos, novos e modificados
- git commit -m 'comentario sobre sua mudança'
- Para dar o commit nas alterações propostas pelo add
- git pull origin master
- Fazer o download das ultima revisão dos arquivos do repositorio (Head)
- git push origin master
- Enviar a árvore do repositório (Head) as mudanças do commit
- git status
- Verificar o status de cada arquivo/pasta no repositório
- git remote add origin <servidor>
- Se você não clonou um repositório existente e quer conectar seu repositório a um servidor remoto, você deve adicioná-lo
Na prática
- git clone usuário@servidor:/caminho/para/o/repositório
- git pull origin master
- Crie/modifique os arquivos
- git add .
- git commit -m 'comentario sobre sua mudança'
- git push origin master
Links Úteis
sexta-feira, 19 de abril de 2013
Gerando um PDF para download ou arquivo a partir de um Template em Django
Você vai precisar dos seguintes aplicativos instalados no ENV:
Crie um arquivo chamado report.py na raiz do seu projeto com o conteúdo abaixo:
Crie a view que será responsável pela chamada da geração do PDF:
Caso seu template possua alguma imagem, lembre-se de colocar nos src das tags img apenas o caminho a partir do caminho definido na def fetch_resorces do arquivo report.py
Agora basta criar a url para a chamada da view:
hasta!
- html5lib==0.95
- pisa==3.0.33
- reportlab==2.7
Crie um arquivo chamado report.py na raiz do seu projeto com o conteúdo abaixo:
# -*- coding: utf-8 -*- from django import http from django.conf import settings from django.http import HttpResponse from django.template import Context from django.template import RequestContext from django.template.loader import get_template from django.template.loader import render_to_string import cgi, os import cStringIO as StringIO import ho.pisa as pisa def fetch_resources(uri, rel): path = os.path.join(settings.STATIC_PATH, uri.replace(settings.MEDIA_URL, "")) return path def write_to_pdf(template_src, context_dict, filename): template = get_template(template_src) context = Context(context_dict) html = template.render(context) result = StringIO.StringIO() pdf = pisa.pisaDocument(StringIO.StringIO(html.encode("UTF-8")), result, link_callback=fetch_resources) if not pdf.err: response = http.HttpResponse(mimetype='application/pdf') response['Content-Disposition'] = 'attachment; filename=%s.pdf' % filename response.write(result.getvalue()) # OPCIONAL, CASO QUEIRA GERAR O ARQUIVO EM DISCO f = open('%s/uploads/projetos/%s.pdf' % (settings.MEDIA_ROOT, filename), 'w+') f.write(result.getvalue()) f.close() return response return http.HttpResponse('Problema ao gerar PDF: %s' % cgi.escape(html))
Crie a view que será responsável pela chamada da geração do PDF:
from app.report import write_to_pdf def projeto2pdf(request, slug=None): projeto = get_object_or_404(Projeto, slug=slug) response = write_to_pdf('template_pdf.html', {'projeto': projeto}, projeto.slug) return response
Caso seu template possua alguma imagem, lembre-se de colocar nos src das tags img apenas o caminho a partir do caminho definido na def fetch_resorces do arquivo report.py
... <img src="site/img/sua_imagem.png" alt="imagem" width="176" height="103"> ...
Agora basta criar a url para a chamada da view:
...url(r'^projeto2pdf/(?P<slug>[-\w]+)/$', 'projeto2pdf', name='projeto2pdf'), ...
hasta!
domingo, 7 de abril de 2013
Enviando email multi-formato (txt, html) com EmailMultiAlternatives
# ENVIANDO EMAIL DE CADASTRO from django.template import Context, loader, RequestContext from django.core.mail import EmailMultiAlternatives tHtml = loader.get_template('email/cadastro.html') tTxt = loader.get_template('email/cadastro.txt') c = Context({ 'email': self.email, 'senha': self.senha, }) destinatario = [] destinatario.append(self.email) subject = "PROMOÇÃO INOVE SUA COZINHA com WICKBOLD" emailFrom = "" text_content = tTxt.render(c) html_content = tHtml.render(c) msg = EmailMultiAlternatives(subject, text_content, emailFrom, destinatario) msg.attach_alternative(html_content, "text/html") try: msg.send() except: pass
quinta-feira, 4 de abril de 2013
NoReverseMatch Exception Value: u"'" is not a registered namespace
Nas versões mais novas do Django (1.4+), o comportamento da tag {% url %} utilizada nos templates mudou, e para continuar utilizando, agora com suporte aos NAMESPACES, deve-se fazer conforme abaixo:
No urls.py utilize namespaces conforme abaixo:
Certifique-se que no template que deseja utilizar a tag {% url %} tenha o import abaixo:
Para utilizar a tag {% url %} em seus links, utilize a nova sintaxe:
hasta!
No urls.py utilize namespaces conforme abaixo:
url(r'^', include('appviews.urls', namespace='appsite')),
Certifique-se que no template que deseja utilizar a tag {% url %} tenha o import abaixo:
{% load url from future %}
Para utilizar a tag {% url %} em seus links, utilize a nova sintaxe:
<a href="{% url '<NAMESPACE>:<URL_NAME>' %}">Seu link</a>
hasta!
Marcadores:
Dicas,
Django 1.4,
Django/Python,
Erros Frequentes
terça-feira, 8 de janeiro de 2013
Autenticação do Django utilizando old_password do mysql
No mesmo diretório do seu arquivo settings.py crie um arquivo chamado hashers.py com o seguinte conteúdo:
A função de login fica como de costume:
Da mesma forma pode ser feito com outros hashers. Basta adicioná-los ao arquivo hashers.py e listá-los na ordem desejada no settings como vimos acima.
Uma coisa notada analisando os algoritmos de hash disponível no Django (disponíveis em django.contrib.auth.hashers), o password gerado automático que é salvo no banco possui 3 partes separadas pelo simbolo "$":
hasta!
from django.contrib.auth.hashers import BasePasswordHasher, mask_hash from django.utils.datastructures import SortedDict from django.utils.crypto import constant_time_compare from django.utils.translation import ugettext_noop as _ class MySQLOldPasswordHasher(BasePasswordHasher): """ Classe para criptografar as senhas utilizando o modelo da funcao old_password do mysql 4.x Class to encrypt the passwords using the model of mysql 4.x old_password function """ algorithm = "mysql_old_password" def salt(self): return '' def encode(self, password, salt): nr = 1345345333 add = 7 nr2 = 0x12345671 for c in (ord(x) for x in password if x not in (' ', '\t')): nr^= (((nr & 63)+add)*c)+ (nr << 8) & 0xFFFFFFFF nr2= (nr2 + ((nr2 << 8) ^ nr)) & 0xFFFFFFFF add= (add + c) & 0xFFFFFFFF password = "%08x%08x" % (nr & 0x7FFFFFFF,nr2 & 0x7FFFFFFF) return "%s$%s" % (self.algorithm, password) def verify(self, password, encoded): encoded_2 = self.encode(password, '') return constant_time_compare(encoded, encoded_2) def safe_summary(self, encoded): return SortedDict([ (_('algorithm'), self.algorithm), (_('hash'), mask_hash(encoded, show=3)), ])
Adicone esse novo hasher no início da setting PASSWORD_HASHERS no seu arquivo settings.py:
PASSWORD_HASHERS = ( 'app.hashers.MySQLOldPasswordHasher', 'django.contrib.auth.hashers.BCryptPasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.SHA1PasswordHasher', 'django.contrib.auth.hashers.MD5PasswordHasher', 'django.contrib.auth.hashers.CryptPasswordHasher', )
A função de login fica como de costume:
from django.contrib.auth import authenticate, logout, login as authlogin from django.core.context_processors import csrf def logar(request): if request.POST: c = {} c.update(csrf(request)) r = request.POST usuario = r.get('usuario') senha = r.get('senha') u = authenticate(username=usuario, password=senha) if u is not None: c = u.get_profile() if c: if c.ativo: if u.is_active: authlogin(request, u) # USUARIO LOGADO COM SUCESSO return HttpResponse('1') # USUARIO INATIVO return HttpResponse('-3') # USUARIO NAO ENCONTRADO / SENHA INCORRETA return HttpResponse('0') # REQUISICAO SEM POST return HttpResponse('-1')
Da mesma forma pode ser feito com outros hashers. Basta adicioná-los ao arquivo hashers.py e listá-los na ordem desejada no settings como vimos acima.
Uma coisa notada analisando os algoritmos de hash disponível no Django (disponíveis em django.contrib.auth.hashers), o password gerado automático que é salvo no banco possui 3 partes separadas pelo simbolo "$":
- Algoritmo de criptografia utilizado
- Número de iterações
- Hash da senha
No exemplo acima que a função old_password() do mysql não possui iterações, a segunda parte é omitda.
hasta!
Assinar:
Postagens (Atom)