tag:blogger.com,1999:blog-75297270494084006122024-03-05T08:53:15.641-03:00djangowebDicas e tutoriais úteis sobre Python e DjangoJosé Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.comBlogger113125tag:blogger.com,1999:blog-7529727049408400612.post-46036380629554848482017-12-28T15:37:00.000-02:002017-12-28T15:37:24.183-02:00Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or ROW_FORMAT=COMPRESSEDAltere o ROW_FORMAT conforme abaixo:<br />
<br />
<pre class="prettyprint">ALTER TABLE table_name ROW_FORMAT=DYNAMIC</pre>
<br />
Hasta!
José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-34589144056583658442017-12-21T16:25:00.001-02:002017-12-21T16:25:15.997-02:00[CKEditor] ModuleNotFoundError: No module named 'django.core.urlresolvers'No django 2.0, o reverse mudou de pacote.<br />
<br />
Altere no arquivo <b>[SEU_ENV]/lib/python3.6/site-packages/ckeditor_uploader/widgets.py </b>a linha:<br />
<br />
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Monaco; color: #f4f4f4; background-color: #000000; background-color: rgba(0, 0, 0, 0.85)}
span.s1 {font-variant-ligatures: no-common-ligatures}
</style>
<br />
<pre class="prettyprint">#from django.core.urlresolvers import reverse</pre>
<div>
<br /></div>
Para:<br />
<br />
<pre class="prettyprint">from django.urls import reverse</pre>
<br />
hasta!
José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-56411840490350531552017-11-01T09:26:00.000-02:002017-11-01T09:26:47.906-02:00Limpar a lista de conexões recentes do SSH no New Remote ConnectionEm uma janela no terminal, digite:
<br />
<br />
<pre class="preetyprint">defaults write "com.apple.Terminal" PreviousCommands '()'</pre>
<div>
<span style="background-color: white; color: #333333; font-family: "myriad set pro" , "helvetica neue" , "helvetica" , "arial" , sans-serif; font-size: 18px;"><br /></span>
<span style="background-color: white; color: #333333; font-family: "myriad set pro" , "helvetica neue" , "helvetica" , "arial" , sans-serif; font-size: 18px;">Hasta!</span></div>
José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-42905339078396911972017-09-14T16:36:00.000-03:002017-09-14T16:39:21.305-03:00Django + Masonry + Imagesloaded + Twitter Style PaginationO que é o Masonry?<br />
<br />
Como o proprio <a href="https://masonry.desandro.com/" target="_blank">site</a> do plugin explica:<br />
<blockquote class="tr_bq">
Masonry is a JavaScript grid layout library. It works by placing elements in optimal position based on available vertical space, sort of like a mason fitting stones in a wall. You’ve probably seen it in use all over the Internet.</blockquote>
<br />
O objetivo no final deste post é ter um layout organizado pelo plugin, com registros paginados e requisições via ajax para obter os registros das páginas seguintes, utilizando uma Class Based View no Django.<br />
<br />
Requisitos:<br />
<br />
<ul>
<li>Django==1.11.1+</li>
<li><a href="https://masonry.desandro.com/" target="_blank">Masonry</a></li>
<li><a href="https://imagesloaded.desandro.com/" target="_blank">ImagesLoaded</a></li>
</ul>
<br />
<br />
<br />
Bom, primeiramente faremos a View:<br />
<br />
<pre class="prettyprint">class PostListView(View):
def get(self, request, *args, **kwargs):
posts = Post.objects.all()
RPP = 18
PAGINA = 1
TOTAL = posts.count()
if request.GET.get('page'):
PAGINA = int(request.GET.get('page'))
page_template = 'blog/paginacao.html'
template = 'blog/list.html'
if request.is_ajax():
template = page_template
tem_mais = posts[PAGINA*RPP+1:]
tem_mais = tem_mais.count()
posts = posts[(PAGINA-1)*RPP:PAGINA*RPP]
if posts.count() == 0:
return HttpResponse('')
VARS = {
'posts':posts,
'pag':PAGINA,
'tem_mais': tem_mais,
'total': TOTAL,
}
return render(request, template, VARS)</pre>
<br />
<br />
Serão necessários dois templates:<br />
<br />
<b>blog/list.html</b><br />
<pre class="prettyprint">{% extends "base.html" %}
{% block scripts %}
<script src="{% static "site/vendor/masonry/jquery.masonry.pkgd.min.js" %}" type="text/javascript"></script>
<script src="{% static "site/vendor/masonry/imagesloaded.pkgd.min.js" %}" type="text/javascript"></script>
<script src="{% static "site/src/js/layout.js" %}" type="text/javascript"></script>
<script src="{% static "site/src/js/blog.list.js" %}" type="text/javascript"></script>
{% endblock scripts %}
{% block conteudo %}
<!-- Masonry Grid -->
<div class="masonry-grid">
<div class="masonry-grid-sizer col-xs-6 col-sm-4 col-md-3 col-lg-2"></div>
{% for p in posts %}
<!-- Post Item -->
<div class="masonry-grid-item col-xs-6 col-sm-4 col-md-3 col-lg-2">
</div>
<!-- End Post Item -->
{% endfor %}
</div>
<!-- End Masonry Grid -->
<div class='loading-bg'>
<div class='loading'></div>
</div>
{% endblock conteudo %}
</pre>
<br />
<b>blog/paginacao.html</b>
<br />
<pre class="prettyprint"><!-- Masonry Grid -->
<div class="masonry-grid">
<div class="masonry-grid-sizer col-xs-6 col-sm-4 col-md-3 col-lg-2"></div>
{% for p in posts %}
<!-- Post Item -->
<div class="masonry-grid-item col-xs-6 col-sm-4 col-md-3 col-lg-2">
</div>
<!-- End Post Item -->
{% endfor %}
</div>
<!-- End Masonry Grid -->
<div class='loading-bg'>
<div class='loading'></div>
</div>
</pre>
<br />
<b>layout.js:</b><br />
<pre class="prettyprint">var Layout = function () {
'use strict';
var mobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
// handle on page scroll
var handleHeaderOnScroll = function() {
if ($(window).scrollTop() > 60) {
$('body').addClass('page-on-scroll');
} else {
$('body').removeClass('page-on-scroll');
}
}
// handle carousel
var handleCarousel = function() {
var $item = $('.carousel .item');
var $wHeight = $(window).height();
$item.eq(0).addClass('active');
$item.height($wHeight);
$item.addClass('full-screen');
$('.carousel img').each(function() {
var $src = $(this).attr('src');
var $color = $(this).attr('data-color');
$(this).parent().css({
'background-image' : 'url(' + $src + ')',
'background-color' : $color
});
$(this).remove();
});
$(window).on('resize', function (){
$wHeight = $(window).height();
$item.height($wHeight);
});
}
if($(window).width() > 992) {
$('.screen__height').height($(window).height()/1.3);
$(window).resize(function(){
$('.screen__height').height($(window).height()/1.3);
});
}
// handle group element heights
var handleHeight = function() {
$('[data-auto-height]').each(function() {
var parent = $(this);
var items = $('[data-height]', parent);
var height = 0;
var mode = parent.attr('data-mode');
var offset = parseInt(parent.attr('data-offset') ? parent.attr('data-offset') : 0);
items.each(function() {
if ($(this).attr('data-height') == "height") {
$(this).css('height', '');
} else {
$(this).css('min-height', '');
}
var height_ = (mode == 'base-height' ? $(this).outerHeight() : $(this).outerHeight(true));
if (height_ > height) {
height = height_;
}
});
height = height + offset;
items.each(function() {
if ($(this).attr('data-height') == "height") {
$(this).css('height', height);
} else {
$(this).css('min-height', height);
}
});
if(parent.attr('data-related')) {
$(parent.attr('data-related')).css('height', parent.height());
}
});
}
return {
init: function () {
handleHeaderOnScroll(); // initial setup for fixed header
handleCarousel(); // initial setup for carousel
handleHeight(); // initial setup for group element height
// handle minimized header on page scroll
$(window).scroll(function() {
handleHeaderOnScroll();
});
}
};
}();
$(document).ready(function() {
Layout.init();
});
</pre>
<br />
<b>blog.list.js</b><br />
<pre class="prettyprint">jQuery(document).ready(function($) {
var carregando = false;
$(window).on('scroll', function(event) {
event.preventDefault();
/* Act on the event */
if($(window).scrollTop() + $(window).height() > $(document).height() - 100 && carregando == false && parseInt($('#tem_mais').val()) > 0) {
// console.log("near bottom!");
carregando = true
retrive_posts();
}
});
$('body').on('click', '.next', function(event) {
event.preventDefault();
carregando = true;
/* Act on the event */
retrive_posts();
});
function retrive_posts(){
var pag = parseInt($('#pag').val())+1;
$('.loading-bg').show();
$.get('?page='+pag, function(data) {
/*optional stuff to do after success */
// console.log(data);
// console.log(data == '');
// console.log($('#tem_mais').length);
console.log($('#tem_mais').val());
if(data){
$('#pag').val(parseInt(pag));
// console.log(data);
var aux = $('<div/>');
$(aux).html(data);
var tem_mais = $(aux).find('.tem_mais')[0];
$('#tem_mais').val($(tem_mais).val());
if (parseInt($('#tem_mais').val()) == 0) {
$('button.next').hide();
}
var $container = $('.masonry-grid');
$container.imagesLoaded( function() {
$container.masonry({
itemSelector: '.masonry-grid-item', // use a separate class for itemSelector, other than .col-
columnWidth: '.masonry-grid-sizer',
percentPosition: true,
transitionDuration: 0,
});
});
// console.log($(aux).find('.masonry-grid-item.appended'));
$(aux).find('.masonry-grid-item.appended').each(function(index, el) {
$(el).css({ opacity: 0 });
// console.log($(el));
$(el).imagesLoaded(function(){
// show elems now they're ready
$(el).css({ opacity: 1 });
$container.append( $(el) );
$container.masonry( 'appended', $(el), true );
$(el).removeClass('.appended')
});
});
carregando = false;
$('.loading-bg').hide();
}else{
$('button.next').hide();
}
});
}
});
</pre>
José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-3315085583162365782017-06-09T11:44:00.000-03:002017-08-04T09:10:44.777-03:00Como pegar o ID do último commit no GIT para forçar atualização de arquivosNormalmente os navegadores modernos fazem cache dos arquivos para diminuir banda utilizada e aumentar o desempenho.<br />
<br />
Isso é sempre uma boa, a não ser quando vc precisa forçá-lo a atualizar, principalmente quando alguma mudança é feita em CSS e JS.<br />
<br />
No mobile ainda é mais custoso, haja visto que muitos usuários não sabem como limpar o cache e forçar o recarregamento.<br />
<br />
Para isso, podemos "mudar" a url do arquivo com cache, para forçá-lo a atualizar e com isso obtermos o resultado desejado.<br />
<br />
No seu arquivo de tags, inclua a simple_tag abaixo:<br />
<br />
<pre class="prettyprint">@register.simple_tag
def git_ver():
'''
Retrieve and return the latest git commit hash ID and tag as a dict.
'''
import os, subprocess
from django.conf import settings
git_dir = os.path.dirname(settings.PROJECT_PATH)
try:
# Date and hash ID
head = subprocess.Popen(
"git --git-dir={dir}/.git rev-parse HEAD".format(dir=git_dir),
shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
version = head.stdout.readline().strip().decode('utf-8')
git_string = "{v}".format(v=version)
except:
git_string = u'unknown'
return git_string
</pre>
<br />
<br />
E no seu template:<br />
<br />
<pre class="prettyprint">{% load site_tags %}
<link href="styles.css?v={% git_ver %}" rel="stylesheet">
</pre>
<br />
hasta!José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-77390783814606818792017-05-05T09:31:00.003-03:002017-05-05T09:31:55.199-03:00'Settings' object has no attribute 'TEMPLATE_DEBUG'Para quem atualizou a versão do Django para 1.11 e utiliza a sorl-thumbnail, a versão disponível desse plugin no PyPI ainda não contempla a setting TEMPLATE como um dicionário, ocasionando o erro do título deste post pela falta da setting TEMPLATE_DEBUG.<br />
<br />
Caso opte por colocar no arquivo settings.py a setting obsoleta, será gerado o warning abaixo:<br />
<br />
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Monaco; color: #afad24; background-color: #000000; background-color: rgba(0, 0, 0, 0.85)}
span.s1 {font-variant-ligatures: no-common-ligatures}
</style>
<br />
<div class="p1">
<span class="s1" style="background-color: black; color: yellow;">?: (1_8.W001) The standalone TEMPLATE_* settings were deprecated in Django 1.8 and the TEMPLATES dictionary takes precedence. You must put the values of the following settings into your default TEMPLATES dict: TEMPLATE_DEBUG.</span></div>
<br />
A solução (provisória) é instalar a Sorl direto do github onde este problema foi resolvido até que ela seja atualizada no PyPI.<br />
<br />
<br />
Execute comando abaixo para a instalação:<br />
<br />
<pre class="prettyprint">pip install -e git+https://github.com/mariocesar/sorl-thumbnail.git#egg=sorl-thumbnail</pre>
<br />
O comando acima irá desinstalar a versão que ainda apresenta este problema e baixará a versão presente no git.<br />
<br />
Atualize também seu arquivo requirements.txt com o comando acima (sem o pip install) para usar em produção.<br />
<br />
hasta!José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-67926184172083730152017-04-10T10:11:00.000-03:002017-04-10T10:11:03.571-03:00from django.utils.importlib import import_module - Django 1.9.XO erro:<br />
<br />
<pre class="prettyprint">from django.utils.importlib import import_module
ImportError: No module named importlib
</pre>
<br />
Para resolver edite o arquivo que está gerando o erro, no meu caso<b> django-1.9.12/lib/python2.7/site-packages/sorl/thumbnail/helpers.py </b>substitua:<br />
<br />
Isto<br />
<pre class="prettyprint">from django.utils.importlib import import_module</pre>
<br />
Por:<br />
<pre class="prettyprint">from importlib import import_module</pre>
<br />
hasta!José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-20126223078433619182016-09-26T09:21:00.001-03:002016-09-26T09:21:18.017-03:00Manter apenas um usuário logado por credenciaisNeste post vou mostrar como manter apenas um usuário logado por credenciais. Desta forma quando ele logar com as mesmas credenciais em outra máquina, a sessão anterior é deslogada.<br />
<br />
No seu models.py da app Cadastro adicione o seguinte atributo:<br />
<br />
<pre class="preetyprint">class Cadastro(models.Model):
user = models.OneToOneField(User, null=True, blank=True)
...
session_key = models.CharField(max_length=100, null=True)
...
</pre>
<br />
<br />
E na sua views de login edite conforme a necessidade, incluíndo as linhas abaixo:
<br />
<pre class="preetyprint">...
from django.contrib.auth import authenticate, logout, login as authlogin
from django.contrib.sessions.backends.db import SessionStore
...
u = authenticate(username=email, password=senha)
if u is not None:
if u.is_active:
if u.cadastro.session_key: # check if user has session_key. This will be true for users logged in on another device
try:
s = SessionStore.objects.get(session_key=u.cadastro.session_key)
except Session.DoesNotExist:
pass
else:
s.delete() # delete the old session_key from db
# set new session_key for user instance
u.cadastro.session_key = request.session.session_key
u.cadastro.save() # save the user
u.save()
authlogin(request, u)
...
</pre>
<br />
hasta!José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-81372053273431181432016-09-20T14:04:00.000-03:002016-09-20T14:05:11.236-03:00Como ler feed de redes sociais: Facebook, Twitter, Instagram<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkOa2c3WObZ5IF8F6sl8grvKSwTlTDK30yvCq6UOMqbhISZCqd4LeMPm_NQsrIreQ_PXpXMVr9LE_cSY21crUWGjuTmP5qrSTXn-oE_Xnuz0Y5fvLsT1Ts8FWcwMLOEMK6nmMUxiUtZ_Zn/s1600/feed.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkOa2c3WObZ5IF8F6sl8grvKSwTlTDK30yvCq6UOMqbhISZCqd4LeMPm_NQsrIreQ_PXpXMVr9LE_cSY21crUWGjuTmP5qrSTXn-oE_Xnuz0Y5fvLsT1Ts8FWcwMLOEMK6nmMUxiUtZ_Zn/s1600/feed.jpg" /></a></div>
<br />
<br />
A ideia é simples: gostaria de postar algo nas redes sociais e seria fantástico se esses posts fossem incorporados no site.<br />
<br />
Bom, tem como.<br />
<br />
Primeiro vamos a estrutura:<br />
<br />
<br />
<ol>
<li>Crie uma app para armazenar esse conteúdo:<br /><pre class="prettyprint">python manage.py startapp redes_sociais</pre>
</li>
<li><b>models.py</b>:<br /><pre class="prettyprint">from django.db import models
# Create your models here.
REDES_SOCIAIS_C = (
('FACEBOOK', 'Facebook'),
('TWITTER', 'Twitter'),
('INSTAGRAM', 'Instagram'),
)
class Post(models.Model):
"""(Post description)"""
texto = models.TextField()
imagem = models.ImageField(upload_to=U.retira_acento,null=True,blank=True,)
imagem_src = models.TextField(null=True,blank=True)
redesocial = models.CharField(max_length=255, choices=REDES_SOCIAIS_C)
pid = models.CharField(max_length=255)
data = models.DateTimeField(null=True, blank=True)
link = models.CharField(max_length=255, null=True, blank=True)
ativo = models.BooleanField(default=True)
class Meta:
verbose_name, verbose_name_plural = u"Post" , u"Posts"
ordering = ('-data',)
def __unicode__(self):
return u"%s" % self.texto
def get_imagem(self):
if self.imagem:
return self.imagem
elif self.imagem_src:
return self.imagem_src
return None</pre>
</li>
<li><b>admin.py</b>:<br /><pre class="prettyprint"># coding: utf-8
from django.contrib import admin
from .models import *
class PostAdmin(admin.ModelAdmin):
search_fields = ('texto',)
list_display = ('texto', 'redesocial', 'pid','link' ,'data','ativo')
list_filter = ['redesocial','data','ativo']
list_editable = ['ativo',]
readonly_fields = ['texto', 'redesocial', 'pid','link' ,'data','imagem','imagem_src']
save_on_top = True
fieldsets = (
(u'Ativo', {'fields': ('ativo',)}),
(u'Infos', {'fields': ('redesocial', 'texto', 'pid', 'link')}),
(u'Data', {'fields': ('data',)}),
(u'Imagem', {'fields': ('imagem','imagem_src')}),
)
admin.site.register(Post, PostAdmin)
</pre>
</li>
<li>Fiz dois campos para imagens para dar prioridade de edição sobre o campo do tipo file, uma vez que o facebook retorna o caminho da imagem com mais de 255 chars para um varchar normal. O método get_imagem é o responsável por definir qual imagem exibir.</li>
</ol>
<br />
<h2>
Instagram:</h2>
<div>
<ol>
<li>Instale o app em seu celular e crie uma conta.</li>
<li>Acesse: https://www.instagram.com/developer/ e registre uma aplicação</li>
<ol>
<li>Na aba Details preencha as informações obrigagtórias</li>
<li>Na aba Security coloque uma url válida em <b>Valid redirect URIs. </b>Esta urla lhe dará o access token depois da autorização</li>
<li>Desabilite a opção: <b>Disable implicit OAuth</b></li>
<li>Acesse https://www.instagram.com/oauth/authorize/?client_id=[<b>CLIENT_ID</b>]&redirect_uri=[<b>URI_DO_PASSO 2.2]</b>&response_type=code</li>
<li>Você será redirecionado para a URI informada com o seu access token no get:</li>
<ol>
<li>EX: [<b>URI_DO_PASSO 2.2]</b>/?code=rx1p450j0gz2ahgq8ufn0mvw50mvv47e</li>
</ol>
</ol>
<li>Configure seu settings.py com as informações abaixo:<br />
<pre class="prettyprint"># INSTAGRAM
INSTAGRAM_USER_ID = '[USER_ID DO FEED]'
INSTAGRAM_CLIENT_ID = '[CLIENT_ID DO PASSO 2]'
INSTAGRAM_CLIENT_SECRET = '[CLIENT_SECRET DO PASSO 2]'
INSTAGRAM_ACCESS_TOKEN = '[TOKEN OBTIDO NO PASSO 5.1]'
</pre>
</li>
<li>Para o [USER_ID DO FEED] utilizei um site que fornece de maneira fácil: https://smashballoon.com/instagram-feed/find-instagram-user-id/</li>
<li>Instale o python-instagrampy no seu ENV:<br /><pre class="prettyprint">pip install python-instagram==1.3.2</pre>
</li>
<li>Crie um arquivo chamado feed_instagram.py na mesma pasta do settings.py com o seguinte conteúdo:<br /><pre class="prettyprint">#!/usr/bin/env python
# coding: utf-8
from os.path import abspath, dirname
from datetime import datetime, date, timedelta
import sys, os, commands, time
SETTINGS_DIRECTORY = dirname(dirname(abspath(__file__)))
sys.path.insert(0, SETTINGS_DIRECTORY)
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from django.conf import settings
from django.db.models import Count, Min, Sum, Avg
from django.template.defaultfilters import slugify
import re, json
def log(texto):
print "===================================="
print texto
print "===================================="
def carga():
log("INICIO DA ROTINA")
from redes_sociais.models import Post
from instagram.client import InstagramAPI
# AUTH REQUIRED
access_token = settings.INSTAGRAM_ACCESS_TOKEN
client_secret = settings.INSTAGRAM_CLIENT_SECRET
api = InstagramAPI(access_token=access_token, client_secret=client_secret)
recent_media, next_ = api.user_recent_media(user_id=settings.INSTAGRAM_USER_ID, count=10)
for media in recent_media:
#print dir(media)
#print media.caption.text
post = Post.objects.filter(pid=media.id)
texto = media.caption.text if media.caption else None
try:
if post:
post = post[0]
post.texto = u'{0}'.format(texto)
post.data = media.created_time
post.imagem = media.get_standard_resolution_url()
else:
post = Post(
redesocial = 'INSTAGRAM',
pid = media.id,
texto = u'{0}'.format(texto),
data = media.created_time,
link = media.link,
imagem = media.get_standard_resolution_url(),
)
post.save()
except Exception, e:
log(u"Erro ao inserir [{0}]: {1}".format(media.id, media.get_standard_resolution_url()))
log(e)
# AUTH NON REQUIRED
# api = InstagramAPI(client_id=settings.INSTAGRAM_CLIENT_ID, client_secret=settings.INSTAGRAM_CLIENT_SECRET)
# popular_media = api.media_popular(count=20)
# for media in popular_media:
# print media.images['standard_resolution'].url
log("FIM DA ROTINA")
if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
carga()
</pre>
</li>
</ol>
<br />
<h2>
Twitter</h2>
</div>
<div>
<ol>
<li>Crie um aplicativo em: https://apps.twitter.com/</li>
<li>Após criado, na aba settings tem as informações necessárias para configuração:</li>
<ol>
<li>Access Token<span class="Apple-tab-span" style="white-space: pre;"> </span>[TOKEN]</li>
<li>Access Token Secret<span class="Apple-tab-span" style="white-space: pre;"> </span>[TOKEN_SECRET]</li>
<li>Access Level<span class="Apple-tab-span" style="white-space: pre;"> </span>Read and write</li>
<li>Owner<span class="Apple-tab-span" style="white-space: pre;"> </span>[SEU USUARIO]</li>
<li>Owner ID<span class="Apple-tab-span" style="white-space: pre;"> </span>[SEU ID]</li>
</ol>
<li>Configure seu settings.py com as informações abaixo:</li>
<ol>
<li><pre class="prettyprint"># TWITTER
TWITTER_USERNAME = 'zejuniortdr'
TWITTER_CONSUMER_KEY = '0aYPbXNNNYvhGyPg9CYufUncN'
TWITTER_CONSUMER_SECRET = 'Lf6MfaSRp3qQaAGlbrVyt0hyo3659obj5bL2W6tiO8PWEAE0vU'
TWITTER_ACCESS_TOKEN = '30026596-WKqQtcC3AmboRBOqp9uE1cEPfJkZIfeNB8mdR1nVK'
TWITTER_ACCESS_SECRET = '9j7D0yzgvIybg6vnB9iqac1354Shv21YE2OwSO1oUarTw'
</pre>
</li>
</ol>
<li>Instale o python-instagrampy no seu ENV:<br /><pre class="prettyprint">pip install tweepy==3.3.0</pre>
</li>
<li>Crie um arquivo chamado feed_twitter.py na mesma pasta do settings.py com o seguinte conteúdo:<br /><pre class="prettyprint">#!/usr/bin/env python
# coding: utf-8
from os.path import abspath, dirname
from datetime import datetime, date, timedelta
import sys, os, commands, time
SETTINGS_DIRECTORY = dirname(dirname(abspath(__file__)))
sys.path.insert(0, SETTINGS_DIRECTORY)
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from django.conf import settings
import re
def log(texto):
print "===================================="
print texto
print "===================================="
def carga():
log("INICIO DA ROTINA")
from redes_sociais.models import Post
import tweepy
from tweepy import OAuthHandler
consumer_key = settings.TWITTER_CONSUMER_KEY
consumer_secret = settings.TWITTER_CONSUMER_SECRET
access_token = settings.TWITTER_ACCESS_TOKEN
access_secret = settings.TWITTER_ACCESS_SECRET
auth = OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_secret)
api = tweepy.API(auth)
username = settings.TWITTER_USERNAME
user = tweepy.API(auth).get_user(username)
# log(dir(user))
status = api.user_timeline(screen_name=username, count=200, include_entities=True)
for s in status:
imagem = None
if 'media' in s.entities.keys():
imagem = s.entities['media'][0]['media_url']
post = Post.objects.filter(pid=s.id)
try:
if post:
post = post[0]
post.texto = u'{0}'.format(s.text)
post.data = s.created_at
post.imagem = imagem
else:
post = Post(
redesocial = 'TWITTER',
pid = s.id,
texto = u'{0}'.format(s.text),
data = s.created_at,
link = 'https://twitter.com/{0}/status/{1}'.format(username, s.id),
imagem = imagem,
)
post.save()
except Exception, e:
log(u"Erro ao inserir [{0}]: {1}".format(status.id, status.text))
log("FIM DA ROTINA")
if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
carga()
</pre>
</li>
</ol>
<h2>
Facebook</h2>
</div>
<div>
<ol>
<li>Crie um app com uma conta de desenvolvedor onde esta pessoa seja um dos administradores da Fan Page que quer extrair o feed: https://developers.facebook.com/apps</li>
<li>Configure seu settings.py com as informações abaixo:<br />
<pre class="prettyprint"># FACEBOOK
FACEBOOK_PAGE_ID = [ID DA PAGINA QUE QUER O FEED DE POSTAGENS] # must be integer
FACEBOOK_APP_ID = [ID DA APLICACAO CRIADA] # must be integer
FACEBOOK_APP_SECRET = "[APP SECRET DA APLICACAO CRIADA]"
</pre>
</li>
<li>Instale as dependencias do Facebook no seu ENV:<br /><pre class="prettyprint">facebook-sdk==2.0.0
facepy==1.0.8</pre>
</li>
<li>Crie um arquivo chamado feed_facebook.py na mesma pasta do settings.py com o seguinte conteúdo:<br /><pre class="prettyprint">#!/usr/bin/env python
# coding: utf-8
from os.path import abspath, dirname
from datetime import datetime, date, timedelta
import sys, os, commands, time
SETTINGS_DIRECTORY = dirname(dirname(abspath(__file__)))
sys.path.insert(0, SETTINGS_DIRECTORY)
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from django.conf import settings
from django.db.models import Count, Min, Sum, Avg
from django.template.defaultfilters import slugify
import re, json
def log(texto):
print "===================================="
print texto
print "===================================="
def carga():
log("INICIO DA ROTINA")
from redes_sociais.models import Post
from facepy import utils, GraphAPI
import facebook
app_id = settings.FACEBOOK_APP_ID
page_id = settings.FACEBOOK_PAGE_ID
app_secret = settings.FACEBOOK_APP_SECRET
oath_access_token = utils.get_application_access_token(app_id, app_secret)
a = GraphAPI(oath_access_token)
response = a.get('{0}/posts?fields=id,picture,message,link,full_picture,created_time,object_id'.format(page_id))
for r in response['data']:
post_id = None
post_message = None
post_link = None
post_full_picture = None
post_created_time = None
for k, v in r.items():
if k == 'id':
post_id = v
elif k == 'message':
post_message = v
elif k == 'link':
post_link = v
elif k == 'full_picture':
post_full_picture = v
elif k == 'created_time':
post_created_time = v
post = Post.objects.filter(pid=post_id)
try:
if post:
post = post[0]
post.texto = u'{0}'.format(post_message)
post.pid = u'{0}'.format(post_id)
post.data = post_created_time
post.imagem_src = post_full_picture
post.link = u'{0}'.format(post_link)
else:
post = Post(
texto = u'{0}'.format(post_message),
redesocial = 'FACEBOOK',
pid = u'{0}'.format(post_id),
imagem_src = post_full_picture,
data = post_created_time,
link = u'{0}'.format(post_link),
)
post.save()
except Exception, e:
log(u"Erro ao inserir [{0}]: {1} - {2}".format(post_id, post_message, e))
log("FIM DA ROTINA")
if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
carga()
</pre>
</li>
</ol>
<br />
Agora para executar as rotinas, basta estar com seu ENV ativado e rodar os comandos:<br />
<pre class="prettyprint">python app/feed_instagram.py
python app/feed_twitter.py
python app/feed_facebook.py</pre>
<br />
Após rodar as rotinas vai ter em sua base de dados os post das três redes sociais. Na view que for fazer o render, basta utilizar o ORM para obter os registros.<br />
<br />
Espero ter ajudado mais gente.<br />
<br />
Nota: do Google Plus está em desenvolvimento, mas é a documentação mais enjoada de conseguir informação. Se alguém tiver algum script pronto que faça o que estes acima se propõe a fazer seria de grande ajuda para complementar este post, com os devidos créditos, lógico.<br />
<br />
<br />
hasta!<br />
<br />
<br /></div>
José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-41566779172487839412016-09-06T17:41:00.000-03:002016-09-06T17:45:34.237-03:00Validação customizada usando o ModelFormSempre existe alguma regra de validação particular de algum form. Seja um campo obrigatório caso outro seja preenchido, etc.<br />
<br />
Vejamos como fazer isso de um jeito bem fácil:<br />
<br />
<br />
<pre class="prettyprint">class SeuModelForm(forms.ModelForm):
campo1 = forms.CharField(required=False, widget=forms.TextInput(),)
campo1a = forms.BooleanField(required=False, widget=forms.CheckboxInput(),)
campo2 = forms.CharField(required=False, widget=forms.TextInput(),)
campo2a = forms.BooleanField(required=False, widget=forms.CheckboxInput(),)
campo3 = forms.BooleanField(required=False, widget=forms.CheckboxInput(),)
class Meta:
model = SeuModel
fields = '__all__'
#def clean_aceito(self):
# campo3 = self.cleaned_data['campo3']
# if not campo3:
# raise forms.ValidationError("Mensagem de erro do campo3")
# return campo3
def clean(self):
cleaned_data = super(SeuModelForm, self).clean()
campo1 = cleaned_data.get("campo1")
campo1a = cleaned_data.get("campo1a")
campo2 = cleaned_data.get("campo2")
campo2a = cleaned_data.get("campo2a")
campo3 = cleaned_data['campo3']
if not campo1 and not campo1a:
self.add_error('campo1', u'Mensagem de erro do campo1')
if not campo2 and not campo2a:
self.add_error('campo2', u'Mensagem de erro do campo2')
if not campo3:
self.add_error('<span style="background-color: white; color: #222222; font-size: 13.2px; line-height: 18.48px;">campo3</span>', u'Mensagem de erro do campo3')</pre>
<br />
Acima vemos duas possibilidades:<br />
<br />
<ul>
<li>Validar um campo específico, através do método <b>clean_<campo></b></li>
<li>Validar o form todo, testando todos campos juntos com o método <b>clean</b></li>
</ul>
<div>
<b><br /></b></div>
<div>
hasta!</div>
José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-11083733148510141812016-08-19T11:24:00.000-03:002016-09-06T17:32:19.354-03:00Erros Frequentes: Django==1.9.8 + django-ckeditor-updated==4.4.4 No module named utilPeguei o seguinte erro:<br />
<br />
<br />
<pre class="prettyprint">Traceback (most recent call last):
File " in="" line="" manage.py="" module=""> execute_from_command_line(sys.argv)
File "/var/www/SEU_SITE/ENV/local/lib/python2.7/site-packages/django/core/management/__init__.py", </pre>
<pre class="prettyprint">line 353, in execute_from_command_line
utility.execute()
File "/var/www/SEU_SITE/ENV/local/lib/python2.7/site-packages/django/core/management/__init__.py", </pre>
<pre class="prettyprint">line 327, in execute
django.setup()
File "/var/www/SEU_SITE/ENV/local/lib/python2.7/site-packages/django/__init__.py", line 18, in setup
apps.populate(settings.INSTALLED_APPS)
File "/var/www/SEU_SITE/ENV/local/lib/python2.7/site-packages/django/apps/registry.py", line 108, in populate
app_config.import_models(all_models)
File "/var/www/SEU_SITE/ENV/local/lib/python2.7/site-packages/django/apps/config.py", </pre>
<pre class="prettyprint">line 202, in import_models
self.models_module = import_module(models_module_name)
File "/usr/lib/python2.7/importlib/__init__.py", line 37, in import_module
__import__(name)
File "/var/www/SEU_SITE/app/blog/models.py", line 11, in <module>
from ckeditor.fields import RichTextField
File "/var/www/SEU_SITE/ENV/local/lib/python2.7/site-packages/ckeditor/fields.py", line 4, in <module>
from ckeditor.widgets import CKEditorWidget
File "/var/www/SEU_SITE/ENV/local/lib/python2.7/site-packages/ckeditor/widgets.py", line 10, in <module>
from django.forms.util import flatatt
ImportError: No module named util
</module></module></module></pre>
<br />
Para resolver, basta editar o arquivo <b>/var/www/SEU_SITE/ENV/local/lib/python2.7/site-packages/ckeditor/widgets.py</b> como abaixo:<br />
<b><br /></b>
<b><br /></b>
<br />
<b>Troque a linha:
</b><br />
<pre class="prettyprint">django.forms.util import flatatt</pre>
<br />
<b>Por:
</b><br />
<pre class="prettyprint">django.forms.utils import flatatt</pre>
José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-36457785662130795052016-08-12T14:02:00.001-03:002016-08-12T14:08:42.663-03:00Plugins Úteis: django-image-cropping - Como incoporar uma ferramenta de recorte para fotos no admin<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZFCm3ijq2dNDy2ULxDgLu3RnWCio8Azth1HU13SQglyY7LnH42u8sH7ts0Ktd4DplaqkjCJ3c7xpqHLyC9endrasiI8eJa0KrfeB1HOYAGZrNCM-dGBeig6zPepzHzaQXUtUDdXyDjzET/s1600/django_image_cropping_example+%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZFCm3ijq2dNDy2ULxDgLu3RnWCio8Azth1HU13SQglyY7LnH42u8sH7ts0Ktd4DplaqkjCJ3c7xpqHLyC9endrasiI8eJa0KrfeB1HOYAGZrNCM-dGBeig6zPepzHzaQXUtUDdXyDjzET/s1600/django_image_cropping_example+%25281%2529.png" /></a></div>
<br />
Imagine que temos um projeto onde o usuário, responsável por alimentar os conteúdos, não tem a mínima noção do tamanho da imagem necessária para não destruir o layout, e nenhum pouco de boa vontade para ler a especificação do tamanho no help_text?<br />
<br />
Claro que sempre dá pra fazer o recorte automático com o sorl-thumbnail, mas e se o usuário quiser escolher como recortar a foto?<br />
<br />
Bom, pra isso, apresento-lhes o impressionante :<b> django-image-cropping.</b><br />
<b><br /></b>
<b><br /></b>
Vejamos como utilizar:<br />
<br />
<h3>
1. Primeiramente, instale ele no seu virtualenv:</h3>
<pre class="prettyprint">pip install django-image-cropping</pre>
<br />
<h3>
2. Se não tiver o easy_thumbnails instalado, instale:</h3>
<pre class="prettyprint">pip install easy_thumbnails </pre>
<br />
<h3>
3. Adicione os dois no INSTALLED_APPS:</h3>
<pre class="prettyprint">INSTALLED_APPS = [
...
'easy_thumbnails',
'image_cropping',
...
]</pre>
<br />
<h3>
4. Adicone também no settings.py:</h3>
<pre class="prettyprint">from easy_thumbnails.conf import Settings as thumbnail_settings
THUMBNAIL_PROCESSORS = (
'image_cropping.thumbnail_processors.crop_corners',
) + thumbnail_settings.THUMBNAIL_PROCESSORS</pre>
<br />
<h3>
5. No seu model que quiser a função de recorte no admin deixe como a seguir:
</h3>
<pre class="prettyprint">from image_cropping import ImageRatioField
class SeuModel(models.Model):
...
imagem = models.ImageField(upload_to="/caminho/para/upload/",)
cropping = ImageRatioField('imagem', '900x900')
...
</pre>
<br />
Mude a proporção do atributo cropping de acordo com sua necessidade.<br />
<br />
<h3>
6. No admin.py:</h3>
<pre class="prettyprint">from django.contrib import admin
from image_cropping import ImageCroppingMixin
class SeuModelAdmin(ImageCroppingMixin, admin.ModelAdmin):
pass
admin.site.register(SeuModel, SeuModelAdmin)</pre>
<br />
<h3>
7. No template que for exibir a foto recortada utilize a tag provida pelo plugin:</h3>
<pre class="prettyprint">{% cropped_thumbnail instancia_do_seu_model "cropping" [scale=INT|width=INT|height=INT|max_size="INTxINT"] %}</pre>
<br />
EX:<br />
<pre class="prettyprint"><img cropping="" scale="0.5" src="{% cropped_thumbnail instancia_do_seu_model " /></pre>
<br />
<br />
<h3>
8. Dá inclusive para utilizar esse plugins para Inlines:</h3>
<br />
<pre class="prettyprint">from image_cropping import ImageCroppingMixin
class ImagemInline(ImageCroppingMixin, admin.TabularInline):
model = Imagem
extra = 0
</pre>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgI5qUxiJJbph4bPTkHGjWRwhCxnz5yfTeg8_qipnBfhJ7wRHMkURCT1FwoWMlox7hF6vxsqa8EMeoN_PwOrF1hvNykQ3b48JP1ZEFX4tqgYg0Ro7NYjMVR2ddena9LW_uC9dUxrkQXrLqJ/s1600/Screen+Shot+2016-08-12+at+14.00.24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="280" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgI5qUxiJJbph4bPTkHGjWRwhCxnz5yfTeg8_qipnBfhJ7wRHMkURCT1FwoWMlox7hF6vxsqa8EMeoN_PwOrF1hvNykQ3b48JP1ZEFX4tqgYg0Ro7NYjMVR2ddena9LW_uC9dUxrkQXrLqJ/s640/Screen+Shot+2016-08-12+at+14.00.24.png" width="640" /></a></div>
<br />
<br />
Desta maneira, ao fazer o upload, o usuário poderá selecionar a área de recorte diretamente no admin, respeitando a proporção imposta no models.py e sem destruir o layout.<br />
<br />
<br />
Fonte e mais informações: <a href="https://pypi.python.org/pypi/django-image-cropping">https://pypi.python.org/pypi/django-image-cropping</a><br />
<br />
<br />
hasta!<br />
<br />José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-90819312518482688002016-04-20T15:21:00.000-03:002016-04-20T15:22:18.729-03:00Como esconder alguns registros do changelist do admin do Django<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGbmUmDSe1XyYcLHWMyyTH3KhAsGtah2nVHTtV4dj5u-W2hLzoF-xuIhUqvFw_N1Ddf-VoRAPuySh-BQCd-OwqDBrA-763L5fjsPI5tGFMtNpISLTrMpCankeHQE2sfoHTUTjEECHKwBd1/s1600/maxresdefault.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGbmUmDSe1XyYcLHWMyyTH3KhAsGtah2nVHTtV4dj5u-W2hLzoF-xuIhUqvFw_N1Ddf-VoRAPuySh-BQCd-OwqDBrA-763L5fjsPI5tGFMtNpISLTrMpCankeHQE2sfoHTUTjEECHKwBd1/s1600/maxresdefault.png" /></a></div>
A pergunta é simples: Pra que eu um dia iria precisar esconder alguns registros do change list, se já estou no admin como superuser?<br />
<br />
Bom, imagine que seu cliente quer o acesso ao admin para gerenciar os conteúdos e também acesso a alguma seção do projeto que o login é necessário.<br />
<br />
Quão surpreso você ficaria se descobrisse, que aquele seu cadastro de teste onde vc conseguia entrar para ajudar, testar e acompanhar o andamento do projeto fosse removido?<br />
<br />
Isso, apesar de ridículo, não é de todo o impossível, e foi por passar por algo semelhante que escrevo este post.<br />
<br />
<br />
Para ocultar os registros de uma determinada classe, basta sobrescrever o método <b>get_queryset </b>do admin daquela classe no <b>admin.py:</b><br />
<br />
<pre class="prettyprint">class CadastroAdmin(admin.ModelAdmin):
form = CadastroForm
search_fields = ('nome',)
list_display = ('nome', 'email', 'unidade','ativo',)
list_filter = ['ativo','unidade']
list_editable = ['ativo',]
exclude = ('user',)
save_on_top = True
def get_queryset(self, request):
qs = super(CadastroAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.exclude(id__in=[13,14,15])
admin.site.register(Cadastro, CadastroAdmin)</pre>
<br />
No exemplo acima, quando o usuário logado for superuser do admin, este verá todos os registros, do contrário, a queryset excluirá dos resultados os cadastros com ids 13, 14 e 15.<br />
<br />
Com pequenos ajustes, este método tornar bastante útil para evitar dedos inquietos clicando onde não devem.<br />
<br />
<br />
hasta!José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-83681942320682742492016-04-08T11:45:00.000-03:002016-04-08T11:45:21.460-03:00SyntaxError: expected expression, got '<' <!DOCTYPE html> /admin/jsi18n/ (line 1) interpolate is not defined<b>SyntaxError: expected expression, got '<' <!DOCTYPE html> /admin/jsi18n/ (line 1) </b><br />
<b>interpolate is not defined</b><br />
<br />
Esse erro apareceu quando fiz a utilização do SelectFilter2 para um form no front.<br />
<br />
Ele é responsável pela ferramenta de filter_horizontal / filter_vertical do admin, e deveria ser exibido assim:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhz8LCuGm5kXLfEma3Eo8ja2UnkzOjH8-NEIau1pYYurP-JtKGwiHcOqfN7PrkaRCTz02uyI79GNHWrVM3r3EL-mr0gD1fIcC4aBwcNszGE09FK_BVGM8YofyChAcaXdSxLmJgbiduOOaZ/s1600/Screen+Shot+2016-04-08+at+11.32.57.png" imageanchor="1"><img border="0" height="248" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhz8LCuGm5kXLfEma3Eo8ja2UnkzOjH8-NEIau1pYYurP-JtKGwiHcOqfN7PrkaRCTz02uyI79GNHWrVM3r3EL-mr0gD1fIcC4aBwcNszGE09FK_BVGM8YofyChAcaXdSxLmJgbiduOOaZ/s640/Screen+Shot+2016-04-08+at+11.32.57.png" width="640" /></a><br />
<br />
<br />
E com o erro era exibido assim:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgICWNajvutfeNgUj9JqGRyZfgZr41WmXQoDZEJN1OVw1r1_giXxeGGWYlDLCdJvNHz-fAFyWdFM_ZQ9P7sLjLzJ8vN5UNhVEJkqqJ4TiQELKoeDRMitEHOrtQPRtcZT8t7mcDMM8NKr-TO/s1600/image005.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgICWNajvutfeNgUj9JqGRyZfgZr41WmXQoDZEJN1OVw1r1_giXxeGGWYlDLCdJvNHz-fAFyWdFM_ZQ9P7sLjLzJ8vN5UNhVEJkqqJ4TiQELKoeDRMitEHOrtQPRtcZT8t7mcDMM8NKr-TO/s400/image005.png" /></a><br />
<br />
<br />
<br />
Para resolver esse problema:<br />
<br />
<br />
Adicione no seu <b>urls.py:</b><br />
<pre class="prettyprint">...
url(r'^jsi18n/$', 'django.views.i18n.javascript_catalog'),
...
</pre>
<br />
<br />
E no seu template que for utilzar o widget:<br />
<br />
<pre class="prettyprint"><script type="text/javascript" src="/jsi18n/" > </script>
</pre>
José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-56835541720915066952016-02-01T11:54:00.002-02:002016-02-01T11:54:54.346-02:00Como utilizar o "Salvar e adicionar outro" do Admin no Front com Class-Based ViewsO botão de<b> Salvar e Adicionar outro</b> é muito útil no administrativo quando se faz necessário inserir uma lista de dados em algum modelo.<br />
<div>
<br /></div>
<div>
No front, quando o usuário tem permissão de inserir algo para persistir o banco, também é possível utilizar este recurso. Vejamos como:</div>
<div>
<br /></div>
<div>
<b>views.py</b></div>
<div>
<br /></div>
<pre class="prettyprint">class SuaClasseCreateView(CreateView):
form_class = SuaClasseForm
success_url = '/url-de-sucesso/'
template_name = 'form.html'
model = SuaClasse
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
if request.POST.get('_addanother'):
self.success_url = '/url-de-nova-insercao/'
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.save()
return HttpResponseRedirect(self.success_url)
def form_invalid(self, form):
return self.render_to_response(
self.get_context_data(form=form)
)
class SuaClasseUpdateView(UpdateView):
form_class = SuaClasseForm
model = SuaClasse
success_url = '/url-de-sucesso/'
template_name = 'form.html'
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
form.instance = self.object
if form.is_valid():
if request.POST.get('_addanother'):
self.success_url = '/url-de-nova-insercao/'
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form.save()
return HttpResponseRedirect(self.success_url)
def form_invalid(self, form):
return self.render_to_response(
self.get_context_data(form=form)
)</pre>
<br />
As views acima funcionam para qualquer modelo, sem nenhuma alteração nos forms.py. No html, apenas é necessário incluir o botão de <b>Salvar e adicionar outro </b>como exemplo abaixo:<br />
<br />
<b>form.html</b><br />
<pre class="prettyprint"><input type="submit" name="_addanother" value="Salvar e adicionar outro(a)"></pre>
<br />
<br />
Quando o clique é feito neste botão, o value dele é enviado via post, e a tratativa disso está no método <b>post </b>da view. Quando não é clicado, o valor não é enviado, não redirecionando assim para uma nova tela com o formulário aberto.<br />
<br />
hasta!José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-12722119583135737872015-09-25T15:55:00.000-03:002015-09-25T16:06:06.791-03:00Plugins Úteis: django-geoposition - Como incorporar o google maps dentro do admin para geolocalização<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4vWeioT1DIiw7ONFGv2R0PuBRYM8FPR77h7bvBA9iDLflj3CB3uv-aqZGKc70zJvaFUy4pVyao_QAmYO97nqQwGWOTPeuQ7Znlq-CjiRkfVHZhrOfA_woUP42tz5sZgq1ukhC5aPL8RQF/s1600/maps.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4vWeioT1DIiw7ONFGv2R0PuBRYM8FPR77h7bvBA9iDLflj3CB3uv-aqZGKc70zJvaFUy4pVyao_QAmYO97nqQwGWOTPeuQ7Znlq-CjiRkfVHZhrOfA_woUP42tz5sZgq1ukhC5aPL8RQF/s640/maps.jpg" width="640" /></a></div>
Precisa incorporar um mapa com base em um endereço no administrativo para exibir no front? Tarefa simples pro django-geoposition (<a href="https://github.com/philippbosch/django-geoposition">https://github.com/philippbosch/django-geoposition</a>).<br />
<br />
A instalação é simples:<br />
<pre class="prettyprint">pip install django-geoposition</pre>
<br />
Coloque a linha abaixo no seu arquivo <b>settings.py</b>, em <b>INSTALLED_APPS</b>:
<br />
<pre class="prettyprint">INSTALLED_APPS = (
...
'geoposition',
...
)</pre>
<br />
Outra configuração que vale a pena fazer, é incluir no seu arquivo <b>settings.py</b> as linhas abaixo para definir o zoom:<br />
<pre class="prettyprint">GEOPOSITION_MAP_OPTIONS = {
'minZoom': 15,
'maxZoom': 18,
}</pre>
<br />
No seu arquivo <b>models.py, </b>deixe como a seguir:<br />
<pre class="prettyprint">from geoposition.fields import GeopositionField
from localflavor.br.br_states import STATE_CHOICES
class SuaClasse(models.Model):
"""(Unidade description)"""
endereco = models.CharField(max_length=255, verbose_name=u'Endereço', help_text='Para uma melhor localização no mapa, preencha sem abreviações. Ex: Rua Martinho Estrela, 1229')
bairro = models.CharField(max_length=255,)
cidade = models.CharField(max_length=255,help_text="Para uma melhor localização no mapa, preencha sem abreviações. Ex: Belo Horizonte")
estado = models.CharField(max_length=2, null=True, blank=True,choices=U.STATE_CHOICES)
position = GeopositionField(verbose_name=u'Geolocalização', help_text="Não altere os valores calculados automaticamente de latitude e longitude")
class Meta:
verbose_name, verbose_name_plural = u"Sua Classe" , u"Suas Classes"
ordering = ('endereco',)
def __unicode__(self):
return u"%s" % self.endereco
</pre>
<br />
No administrativo será gerado tudo automático, mas existem algumas melhorias que podem ser feitas pra ficar ainda mais bacana. Inclua o arquivo css e js abaixo que em seguida explico como faremos.<br />
<pre class="prettyprint"># coding: utf-8
from django import forms
from django.contrib import admin
from .models import *
class SuaClasseForm(forms.ModelForm):
class Media:
css = {
'all': ('admin/css/geoposition_override.css',)
}
js = ('admin/js/geoposition_override.js',)
class SuaClasseAdmin(admin.ModelAdmin):
form = SuaClasseForm
search_fields = ('endereco', 'cidade',)
list_display = ('endereco', 'cidade','estado','bairro')
list_filter = ['estado',]
save_on_top = True
admin.site.register(SuaClasse, SuaClasseAdmin)</pre>
<br />
No arquivo css adicionado (<b>geoposition_override.css</b>), iremos ocultar o campo de busca do mapa, e utilizaremos nossos próprios atributos da classe para tal, mesmo porquê, o atributo position irá só gravar em banco a latitude e longitude e não o endereço pesquisado.<br />
<br />
Crie um arquivo dentro de <b><STATIC_DIR>/admin/css</b>, chamado<b> geoposition_override.cssm, </b>e nele coloque simplemente a linha abaixo:<br />
<br />
<pre class="prettyprint">.geoposition-search input{display:none;}</pre>
<br />
Agora, crie um js chamado<b> geoposition_override.js </b>na pasta <b><STATIC_DIR>/admin/js </b>com as linhas abaixo:<br />
<pre class="prettyprint">django.jQuery(document).ready(function($) {
$('#id_position_0, #id_position_1').attr('readonly', 'readonly');
$('#id_endereco, #id_cidade, #id_estado').blur(function(event) {
/* Act on the event */
if ($('#id_endereco').val()!='' && $('#id_cidade').val()!='' && $('#id_estado').val()!='') {
$('.geoposition-search input').val($('#id_endereco').val()+' ' +$('#id_cidade').val()+' '+$('#id_estado').val());
// TRIGGER DO ENTER PARA EXECUTAR A BUSCA
var e = $.Event("keydown");
e.which = 50; // # Some key code value
$(".geoposition-search input").trigger(e);
};
});
});</pre>
<br />
E o resultado:
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVSGly8dIyp3NpcZZVIZN5u-bte_QcirlUditc6CS9cP7jU7gWaT71saOWPynEtArJkz7YeO_6VD7xXMZwoUm1MO7CrOquk2uW1hYrzPikCLdUaXq9Rm06WH_k4GqI7nM_LsRIZ_G3VlYN/s1600/Capturar.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="538" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVSGly8dIyp3NpcZZVIZN5u-bte_QcirlUditc6CS9cP7jU7gWaT71saOWPynEtArJkz7YeO_6VD7xXMZwoUm1MO7CrOquk2uW1hYrzPikCLdUaXq9Rm06WH_k4GqI7nM_LsRIZ_G3VlYN/s640/Capturar.JPG" width="640" /></a></div>
<br />
hasta!<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-80125530226935772282015-09-02T11:24:00.001-03:002015-09-02T11:39:24.120-03:00Desmistificando Forms: CreateView e UpdateView com o trabalho pesado<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitTbfovEXoZHBFEzacsA69feCSDJ91AswsATJXSdc3LdxJ63wIZ_X9pz3hZdCTPoU21Pes_OuMRRdYTH4_W18cus8N5ahuH7wc4wLXCUzyka3UVNP2ZPeNhHZKttMwfL6SEJpzPzODAgU-/s1600/p-form.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitTbfovEXoZHBFEzacsA69feCSDJ91AswsATJXSdc3LdxJ63wIZ_X9pz3hZdCTPoU21Pes_OuMRRdYTH4_W18cus8N5ahuH7wc4wLXCUzyka3UVNP2ZPeNhHZKttMwfL6SEJpzPzODAgU-/s640/p-form.jpg" width="100%" /></a></div>
<h3>
</h3>
<h3>
<br /></h3>
<h3>
O básico do CreateView</h3>
<div>
Começando do básico, o exemplo abaixo já quebra um galho enorme quando de trata de formulários simples, como por exemplo o Contato de algum site.<br />
<br />
<b>forms.py</b></div>
<pre class="prettyprint"># coding: utf-8
from django import forms
from .models import Contato
class ContatoForm(forms.ModelForm):
nome = forms.CharField(widget=forms.TextInput(attrs={'class' : 'required',}), label="Nome")
email = forms.EmailField(widget=forms.TextInput(attrs={'class' : 'required',}), label="E-mail")
texto = forms.CharField(widget=forms.Textarea(attrs={'class' : 'required',}), label="Texto")
class Meta:
model = Contato
fields = '__all__'
def dados(self):
return {'form':self.cleaned_data, 'data':datetime.now()}</pre>
<br />
Até aqui sem muitas dificuldades né? Vemos a definição da classe para gerenciamento do form, com alguns atributos. Com o <b>fields = '__all__' </b>não era necessário definir os atributos, mas isso é obrigatório caso queira adicionar uma classe, como por exemplo o <b>required</b> ou outro atributo qualquer, por exemplo um placeholder, etc.<br />
<div>
<b><br /></b>
<b>views.py</b></div>
<pre class="prettyprint"># Create your views here.
# coding: utf-8
from django.views.generic.edit import CreateView
from .forms import ContatoForm
from .models import Contato
class ContatoView(CreateView):
form_class = ContatoForm
success_url = '/contato/sucesso/'
template_name = 'contato/contato.html'
model = Contato
def get_context_data(self, **kwargs):
kwargs.update({
'menu':'contato',
'title': 'Contato',
})
return kwargs</pre>
<br />
A view também é bem tranquila. Herdando as características da CreateView, exige apenas algumas informações para resolver o problema de inserção e validação dos campos de forma elegante:<br />
<br />
<ul>
<li>form_class: Classe do Form definida no forms.py</li>
<li>success_url: URL para qual será redirecionado após o sucesso da inserção</li>
<li>template_name: Html do form.</li>
<li>model: Classe definida no models.py</li>
</ul>
<div>
<br /></div>
<div>
<b>contato.html</b></div>
<pre class="prettyprint"><form method="post" action="">
{% csrf_token %}
{{ form.errors}}
{{ form.as_p }}
<input class="btn" type="submit" value="Enviar">
</form>
</pre>
<br />
Acima temos um exemplo bem sucinto de como montar um form de forma bem automágica e podemos observar algumas coisas interessantes com o exemplo acima:<br />
<br />
<ul>
<li>A CreateView trabalha com o post para própria página (mesma url) por isso no action do form não tem nenhuma informação. </li>
<li>A validação do CSRF já é nativa da CreateView, a unica coisa que precisa e incluir o token no html dentro da tag <b>form</b>.</li>
<li><b>{{ form.errors }} </b>vai gerar uma <b><ul> </b>com a tag <b>errorlist </b>onde cada <b><li> </b>será responsável por lista os erros de um campo específico e<b> </b>terá uma <ul> também com a classe <b>errorlist </b>uma lista de <b><li> </b>para todos os erros deste respectivo campo.</li>
</ul>
<div>
Outra coisa legal de fazer é definir no html os campos separamente para um tratamento de erro e layout melhor apresentados. Isso pode ser feito assim:</div>
<div>
<br /></div>
<pre class="prettyprint"><p>{{ form.email }}</p>
<div class="error">{{ form.email.errors }}</div>
</pre>
<br />
<br />
<h3>
E o UpdatView?</h3>
Um formulário de contato não sofrerá update bem possivelmente. Mas apenas para ilustar usaremos o e mesmo exemplo.<br />
<br />
<br />
<b>views.py</b><br />
<pre class="prettyprint">class ContatoUpdateView(UpdateView):
form_class = ContatoForm
model = Contato
success_url = '/contato/sucesso/'
template_name = 'contato/contato.html'
def get_context_data(self, **kwargs):
kwargs.update({
'menu':'contato',
'title': 'Contato',
'update':True,
})
return kwargs</pre>
<div>
<br /></div>
Como assim só isso? Pois é. Só isso. E sim o HTML é exatemente o mesmo. Se usar algum recurso para gerar o form como o {{ <b>form.as_p }}, {{ form.as_table }} </b>é só isso e o mesmo html acima. Caso defina os campos individualmente, lembre-se de colocar <b>{{ form.id }} </b>para validar a instancia que está sendo editada.<br />
<div>
<br /></div>
<div>
<br /></div>
<div>
Eu já mostrei como fazer para utilizar os inlines junto com a CreateView neste post: <a href="http://djangoweb.blogspot.com.br/2013/08/como-utilizar-os-inlines-no-front-para.html">http://djangoweb.blogspot.com.br/2013/08/como-utilizar-os-inlines-no-front-para.html</a>. Agora vamos a algo mais divertido. </div>
<div>
<br /></div>
<div>
Que tal criarmos um exemplo mais complexo, onde não terá apenas uma, mas sim várias classes inline, com o CreateView e UpdateView?</div>
<div>
<br /></div>
<div>
<br /></div>
<h3>
<b>Multiplos inlines</b></h3>
<div>
<b><br /></b>
<b>forms.py</b></div>
<pre class="prettyprint"># coding: utf-8
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.forms.models import inlineformset_factory
from django import forms
from .models import *
from util import util as U
class ModeloForm(forms.ModelForm):
class Meta:
model = Modelo
fields = '__all__'
def dados(self):
return {'form':self.cleaned_data, 'data':datetime.now()}
class ModeloInline1Form(forms.ModelForm):
class Meta:
model = ModeloInline1
fields = '__all__'
ModeloInline1FormSet = inlineformset_factory(Modelo, ModeloInline1, extra=0, min_num=1, form=ModeloInline1Form, fields='__all__')
class ModeloInline2Form(forms.ModelForm):
class Meta:
model = ModeloInline1
fields = '__all__'
ModeloInline2FormSet = inlineformset_factory(Modelo, ModeloInline2, extra=0, min_num=1, form=ModeloInline1Form, fields='__all__')
</pre>
<b><br /></b>
<b><br /></b>
<b>views.py</b><br />
<pre class="prettyprint">class ModeloCreateView(CreateView):
form_class = ModeloForm
success_url = '/home/'
template_name = 'modelo/form.html'
model = Modelo
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
inline1_form = ModeloInline1FormSet()
inline2_form = ModeloInline2FormSet()
return self.render_to_response(self.get_context_data(form=form, inline1_form = inline1_form , inline2_form = inline2_form ))
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
inline1_form = ModeloInline1FormSet(self.request.POST)
inline2_form = ModeloInline1FormSet(self.request.POST)
if (form.is_valid() and inline1_form .is_valid() and
inline2_form .is_valid()):
return self.form_valid(form, inline1_form , inline2_form )
else:
return self.form_invalid(form, inline1_form , inline2_form )
def form_valid(self, form, inline1_form, inline2_form ):
self.object = form.save()
inline1_form.instance = self.object
inline1_form.save()
inline2_form.instance = self.object
inline2_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, inline1_form, inline2_form ):
return self.render_to_response(
self.get_context_data(
form=form,
inline1_form=inline1_form,
inline2_form=inline2_form)
)
def get_context_data(self, **kwargs):
kwargs.update({})
return kwargs
class ModeloUpdateView(UpdateView):
form_class = ModeloForm
model = Modelo
success_url = '/home/'
template_name = 'modelo/form.html'
def get(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
# Render form
inline1_form = ModeloInline1FormSet(instance=self.object)
inline2_form = ModeloInline2FormSet(instance=self.object)
return self.render_to_response(self.get_context_data(form=form,inline1_form=inline1_form,inline2_form=inline2_form))
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
inline1_form = ModeloInline1FormSet(self.request.POST, instance=self.object)
inline2_form = ModeloInline2FormSet(self.request.POST, instance=self.object)
if (form.is_valid() and inline1_form.is_valid() and inline2_form.is_valid()):
return self.form_valid(form, inline1_form, inline2_form)
else:
return self.form_invalid(form, inline1_form, inline2_form)
def form_valid(self, form, inline1_form , inline2_form ):
self.object = form.save()
inline1_form.instance = self.object
inline1_form.save()
inline2_form.instance = self.object
inline2_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, inline1_form , inline2_form ):
return self.render_to_response(
self.get_context_data(
form=form,
inline1_form=inline1_form,
inline2_form=inline2_form)
)
def get_context_data(self, **kwargs):
kwargs.update({
'update':True,
})
return kwargs</pre>
<br />
Nos exemplos acima, o formulário html pode ser o mesmo como no exemplo do Contato. Podemos também utilizar o recurso do inline visto no post cima citado para inserção de quantos inlines forem necessários com o mesmo recurso do admin para sempre adicionar mais um conforme necessidade.<br />
<br />
Um jeito mais elegante de usar isso é usar um template vinculado ao js, com o plugin underscore disponível aqui: <a href="http://underscorejs.org/">http://underscorejs.org/</a><br />
<br />
<pre class="prettyprint"><script type="text/javascript" src="/static/site/js/plugins/underscore/underscore-min.js"></script>
<script type="text/html" id="modeloinline1-template">
<div class="bloco-modeloinline1 clearfix">
<div class="col-lg-12">
<div class="form-group">
<label for="">Exemplo de Textarea</label>
<textarea rows="5" name="modeloinline1_set-<%= id %>-descricao" id="id_modeloinline1_set-<%= id %>-descricao" cols="40" class="form-control"></textarea>
</div>
</div>
</div>
</script>
django.jQuery('.container-modeloinline1').on('click', '.btn-adicionar-modeloinline1', function(ev) {
ev.preventDefault();
django.jQuery(this).parent().find('button').hide();
var count = django.jQuery('.container-modeloinline1').children().length;
var tmplMarkup = django.jQuery('#modeloinline1-template').html();
var compiledTmpl = _.template(tmplMarkup, { id : count });
django.jQuery('.modeloinline1-form"').append(compiledTmpl);
// update form count
django.jQuery('#id_modeloinline1_set-TOTAL_FORMS').attr('value', count+1);
});
</script>
<form role="form" action="" method="post" class=''>
{{ modeloinline1.management_form }}
<div class="container-modeloinline1-form">
{% for form in inline1_form %}
{% if update %}
{{ form.id }}
{{ form.avaliacao }}
{% endif %}
{% if form.errors %}
<div class="alert alert-danger">
Por favor corriga os <strong>erros abaixo</strong> para prosseguir:
{{ form.errors}}
</div>
{% endif %}
</div>
</form>
<button class="btn btn-primary btn-sm btn-adicionar-modeloinline1" type="button">Adicionar outro Modeloinline1</button>
</pre>
José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-91690411343763442412015-08-20T10:07:00.002-03:002015-08-20T10:07:41.455-03:00Como usar o filter_horizontal/filter_vertical fora do admin do Django<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0g2_sJ6BHdZ3jN-dBPpGyanq55fEZhyphenhypheni2oukB8AwubIUoVYhJ0Z1X03jnIs0N-QrXS79HvihpqtvlLZrOaB3y3l2z-iTkGUhvmPpfxuLduckJx0bZjKMi3qTTsW2NpuzaZztF7xKY6Mty/s1600/filter_horizontal.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0g2_sJ6BHdZ3jN-dBPpGyanq55fEZhyphenhypheni2oukB8AwubIUoVYhJ0Z1X03jnIs0N-QrXS79HvihpqtvlLZrOaB3y3l2z-iTkGUhvmPpfxuLduckJx0bZjKMi3qTTsW2NpuzaZztF7xKY6Mty/s640/filter_horizontal.JPG" width="880" /></a></div>
<br />
Tem um ManyToManyField em sua classe e deseja usar o fantástico recurso do filter_horizontal/filter_vertical em um formulário no front? Vou lhe dizer como.<br />
<br />
No html, deixe como a seguir:<br />
<br />
<pre class="prettyprint">{% load static from staticfiles %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" href="/static/admin/css/widgets.css">
</head>
<body>
<form action="" method="post">
<!-- Não esqueca de colcoar o atributo multiple="multiple" -->
<select name="seu_campo" id="id_seu_campo" multiple="multiple">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
</select>
<input type="submit">
</form>
<script src="{% static "site/js/jquery.js" %}"></script>
<script type="text/javascript" src="{% static "admin/js/jquery.init.js" %}"></script>
<script type="text/javascript" src="/admin/jsi18n/"></script>
<script type="text/javascript" src="{% static "admin/js/core.js" %}"></script>
<script type="text/javascript" src="{% static "admin/js/SelectBox.js" %}"></script>
<script type="text/javascript" src="{% static "admin/js/SelectFilter2.js" %}"></script>
<script type="text/javascript">
// PARAMETROS DO SELECT FILTER:
// ID do seu elemento select
// NAME do seu elemento select
// 0 - para filter_horizontal / 1 para filter_vertical
// Caminho do admin para source de imagens utilizadas
addEvent(window, "load", function(e) {SelectFilter.init("id_seu_campo", "seu_campo", 0, "/static/admin/"); });
</script>
</body>
</html></pre>
hasta!José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-92201262074891971982015-08-14T11:25:00.000-03:002015-08-14T11:27:20.394-03:00Plugins Úteis: django-auditlog - Ótima solução para registro de logs<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqGGdzVjJZT3HpQgvmyON-Db_idUlnedwoSrm70wK0zalK0hOFIovPAlvO9q4Sk5sYX1cqI8LdaHfVnq4z9FNRLy8lOKdOQ_B7CuOUKI-TivPIDxxm643KAxZOYAhPOwCbf47x1_Crl4Pc/s1600/audit2.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqGGdzVjJZT3HpQgvmyON-Db_idUlnedwoSrm70wK0zalK0hOFIovPAlvO9q4Sk5sYX1cqI8LdaHfVnq4z9FNRLy8lOKdOQ_B7CuOUKI-TivPIDxxm643KAxZOYAhPOwCbf47x1_Crl4Pc/s1600/audit2.jpg" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
Precisa de um registro de logs para todas as alterações realizadas em um sistema? Isso normalmente é necessário para sistemas que sofrerão auditoria.<br />
<br />
O caminho das pedras é duro, alterar todas as views, fazer os registros manualmente, coonsiderar se quer apenas registrar qual atributo foi alterado ou pior, registrar qual era a informação antiga e qual é a informação nova de cada atributo. Trabalhoso? Muito.<br />
<br />
Eis que surge o django-auditlog (<a href="https://github.com/jjkester/django-auditlog">https://github.com/jjkester/django-auditlog</a>). Com ele o log fica bem completo e é muito fácil de configurar.<br />
<br />
Você vai precisar de:<br />
<ul>
<li>Python 2.7 ou 3.4 </li>
<li>Django 1.7 ou 1.8 </li>
</ul>
<br />
<h4>
Vamos a um passo a passo bem simples para colocar o plugin pra funcionar:</h4>
<br />
<b>1. Instale ele no seu virtualenv:</b><br />
<pre class="prettyprint">pip install django-auditlog</pre>
<br />
<b>2. Adicione ele na setting INSTALLED_APPS:</b><br />
<pre class="prettyprint">...
'auditlog'
...</pre>
<br />
<b>3. Rode o comando migrate para criar as tabelas necessárias no banco:</b><br />
<pre class="prettyprint">manage.py migrate</pre>
<br />
<b>4. Adicione a linha abaixo na setting MIDDLEWARE_CLASSES:</b><br />
<pre class="prettyprint">...
'auditlog.middleware.AuditlogMiddleware'
...
</pre>
<br />
<b>5. Pronto, agora é só registrar os modelos que quer manter o histórico de mudanças como no examplo abaixo:</b><br />
<pre class="prettyprint">from auditlog.registry import auditlog
from django.db import models
class MyModel(models.Model):
pass
# Model definition goes here
auditlog.register(MyModel)
</pre>
<br />
<br />
Depois de tudo configurado, terá sido criado uma tabela no seu banco de dados com o nome de <b>auditlog_logentry, </b> com os seguintes campos:<br />
<br />
<ul>
<li>id</li>
<li>object_pk</li>
<li>object_id</li>
<li>object_repr</li>
<li>action</li>
<li>changes</li>
<li>timestamp</li>
<li>actor_id</li>
<li>content_type_id</li>
<li>remote_addr</li>
<li>additional_data</li>
</ul>
<br />
<div>
<br /></div>
Algumas considerações importantes:<br />
<br />
<ul>
<li><b>object_pk </b>e <b>object_id </b>armezenam a chave primária e o id do registro em questão. Caso sua classe não possua uma chave primária definida, estes dois atributos terão o mesmo valor: a chave primária <b>ID </b>criada automaticamente pelo Django na ausencia de uma definida.</li>
<li><b>object_repr </b>será o valor definido no método <b><i>__unicode__ </i></b>da classe do objeto em questão.</li>
<li><b>action </b>será um valor númerico (0,1 ou 2), onde o mais valor é considerado a atividade mais intrusiva no banco, logo em ordem temos: Inserção (0), Alteração (1) e Remoção (2)</li>
<li><b>changes </b>armazena as mudanças feitas na forma de um dicionário, onde cada chave será o atributo alterado e terá uma lista com dois valores associado, o antigo e o novo. Ex:</li>
<ul>
<li><i>{"ultima_troca_senha": ["2015-01-13", "2015-08-13"], "senha": ["1234567", "123456"]}</i></li>
</ul>
<li><b>timestamp </b>registrará o momento da mudança no formato <i><b>datetime, </b>yyyy-mm-dd h:i:s</i></li>
<li><b>actor_id </b>armazena o id do usuário logado que fez a alteração. Caso haja em algum momento alteração do model registrado e o usuário não estiver logado, este atributo será nulo. Este campo faz referência ao model <b>User </b>utilizado pelo django, na tabela <b>auth_user.</b></li>
<li><b>content_type_id </b>armazena o id do content_type, fazendo referência ao model ContentType utilizado pelo django, na tabela <b>django_content_type.</b></li>
<li><b>remote_addr </b>registra o ip do usuário que fez a mudança</li>
<li><b>additional_data </b>armazenará informações adicionais, sendo nulo no registro automático exemplificado acima.</li>
</ul>
<br />
<br />
<br />
Para mais infrmações, consulte a documentação: <a href="http://django-auditlog.readthedocs.org/en/latest/">http://django-auditlog.readthedocs.org/en/latest/</a><br />
<br />
hasta!José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-43689076650594028952015-08-12T15:44:00.000-03:002015-08-12T15:44:24.509-03:00Criando um middleware para controlar acesso ao site via IP<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjudDUbtUhLXb3o0QTpGLgyoNP5eg6ruPpMsZAkLXS1YcF7xVS-mWoxrWyMHvkol4r6eHopnI3Vof_wsiqjnWomSccm81q6HKcPbevU4ktemGpYH51JdubVYPTJSiPVGJVM50bWpqcLhYi/s1600/middlewarePic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjudDUbtUhLXb3o0QTpGLgyoNP5eg6ruPpMsZAkLXS1YcF7xVS-mWoxrWyMHvkol4r6eHopnI3Vof_wsiqjnWomSccm81q6HKcPbevU4ktemGpYH51JdubVYPTJSiPVGJVM50bWpqcLhYi/s1600/middlewarePic.png" /></a></div>
<br />
Para tal, criarei uma classe para gerenciar IPs permitidos via administrativo. Crie uma nova app no seu projeto e dê o nome de <b><i>permissoes.</i></b><br />
<b><i><br /></i></b>
Deixe os arquivos como a seguir:<br />
<br />
<b><i>models.py</i></b><br />
<br />
<pre class="prettyprint"># coding: utf-8
from django.db import models
# Create your models here.
class IP(models.Model):
"""(IP description)"""
ip = models.GenericIPAddressField()
descricao = models.CharField(max_length=255, null=True, blank=True,verbose_name=u'Descrição')
data = models.DateTimeField(auto_now=True)
class Meta:
verbose_name, verbose_name_plural = u"IP" , u"IPs"
ordering = ('-data',)
def __unicode__(self):
return u"%s" % self.ip</pre>
<br />
<br />
<b><i>admin.py</i></b><br />
<br />
<pre class="prettyprint"># coding: utf-8
from django.contrib import admin
from .models import *
class IPAdmin(admin.ModelAdmin):
search_fields = ('ip', 'descricao',)
list_display = ('ip', 'descricao','data',)
list_filter = ['data',]
save_on_top = True
admin.site.register(IP, IPAdmin)</pre>
<br />
<br />
Adicione sua app no <i style="font-weight: bold;">INSTALLED_APPS</i>:<br />
<br />
<pre class="prettyprint">...
'permissoes',
...
</pre>
<br />
<br />
E por fim, não menos importante, o middleware. Crie um arquivo chamado middleware.py e coloque no mesmo diretorio do settings.py com o conteúdo abaixo:<br />
<br />
<pre class="prettyprint"># coding: utf-8
from django.conf import settings
from django.http import HttpResponseRedirect
from permissoes.models import IP
class NeedToLoginMiddleware(object):
def process_request(self, request):
# pega o IP do usuário
ip = request.META['REMOTE_ADDR']
# verifica se o ip consta na base de ips permitidos
ip_permitido = IP.objects.filter(ip=ip).count()
# lista de urls que não serão validadas
ALLOWED_URLS = [
'%s' % settings.LOGIN_URL,
'/admin/',
]
for a in ALLOWED_URLS:
try:
if request.META['PATH_INFO'].find(a) >= 0:
return None
except:
pass
# verifica se o ip está permitido
if ip_permitido:
return None
else:
# se o ip não estiver permitido, verifica se o usuário está autenticado
# e redireciona para url de login em caso negativo.
if not request.user.is_authenticated(): #if ip check failed, make authentication check
return HttpResponseRedirect(settings.LOGIN_URL)
return None</pre>
<br />
Adicone ele na sua setting <b><i>MIDDLEWARE_CLASSES</i></b>:<br />
<br />
<pre class="prettyprint">MIDDLEWARE_CLASSES = (
...
'app.middleware.NeedToLoginMiddleware',
)</pre>
<br />
hasta!José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-63660527094374312542015-08-07T10:31:00.000-03:002015-08-07T10:41:06.680-03:00Autenticação sem o request.user.get_profile<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghGkM1CmddpwSX_oOC-qwOFOSbV_T_FudwU2gBCKrWlxYT8MfvlyFH-Hgju3MTTpYU-1w_pl7Lg99YEIgSrko2yapQ9qPkL5sZAmhrxgP1SmyjEtjL8oRScamSs6gCl4Q6nTQROyuZGGt-/s1600/auth.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghGkM1CmddpwSX_oOC-qwOFOSbV_T_FudwU2gBCKrWlxYT8MfvlyFH-Hgju3MTTpYU-1w_pl7Lg99YEIgSrko2yapQ9qPkL5sZAmhrxgP1SmyjEtjL8oRScamSs6gCl4Q6nTQROyuZGGt-/s1600/auth.jpg" /></a></div>
<br />
Desde do Django 1.5, o framework possibilita a criação de um modelo de Usuário personalizado. Faço parte do time de pessoas que não concorda em adicionar campos não relacionados com a autenticação na classe de Usuário.<br />
<br />
Para resolver esse problema, já que a partir da versão 1.7 do django este método não é mais recomendado, faremos algumas modificações nos arquivos.<br />
<br />
Mudanças que tratrei aqui são fáceis de implementar e não irão gerar problemas com autenticação já existente e/ou migrações de banco.<br />
<br />
Cenário: Imagine que temos uma app <b>cadastros</b> que possui uma classe <b>Cadastro</b> e esta tem um vínculo com a classe <b>User </b>do Django, para fazer uso do sistema de autenticação.<br />
<br />
<h3>
Remova a setting AUTH_PROFILE_MODULE:</h3>
<div>
<br />
Este passo é fácil. Encontre o AUTH_PROFILE_MODULE no seu arquivo settings.py e remova linha.</div>
<div>
<br /></div>
<pre class="prettyprint"># No seu settings.py:
AUTH_PROFILE_MODULE = 'cadastros.cadastro' # remova esta linha
</pre>
<div>
<br />
<br />
<h3>
Mude seu modelo que possui o vínculo com a classe User do Django:</h3>
</div>
<div>
<b><br /></b>
<b>Antes:</b><br />
<b><br /></b></div>
<pre class="prettyprint">from django.db import models
from django.contrib.auth.models import User
class Cadastro(models.Model):
"""modelo que representa informações adicionais sobre o usuário"""
user = models.ForeignKey(User, unique=True) # mude esta linha
# ... outros campos aqui
</pre>
<div>
<b><br /></b>
<b>Depois:</b><br />
<b><br /></b></div>
<pre class="prettyprint">from django.db import models
from django.contrib.auth.models import User
class Cadastro(models.Model):
"""modelo que representa informações adicionais sobre o usuário"""
user = models.OneToOneField(User) # nova linha
# ... outros campos aqui
</pre>
<div>
<br />
<br />
Aplique as migrações<b> </b>para concluir esta etapa:<br />
<br />
<pre class="prettyprint">python manage.py makemigrations cadastros
python manage.py migrate cadastros</pre>
<h3>
</h3>
<div>
<br /></div>
<h3>
Atualize seu código:</h3>
</div>
<div>
<b><br /></b>
<b>Antes:</b></div>
<pre class="prettyprint">cadastro = request.user.get_profile()</pre>
<div>
<b><br /></b>
<b>Depois:</b></div>
<pre class="prettyprint">cadastro = request.user.cadastro</pre>
<div>
<br />
<br />
hasta!</div>
José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-8637899010093766062015-07-29T15:16:00.005-03:002015-07-29T15:17:27.669-03:00Creating a ModelForm without either the 'fields' attribute or the 'exclude' attribute is prohibited;Para todos que pegarem o erro <b>Creating a ModelForm without either the 'fields' attribute or the 'exclude' attribute is prohibited</b>, basta fazer uma pequena alteração no <b>ModelForm</b><br />
para resolver.<br />
<br />
Altere seu forms.py de:<br />
<br />
<pre class="prettyprint">class BlocoForm(forms.ModelForm):
class Meta:
model = Bloco
def dados(self):
return {'form':self.cleaned_data, 'data':datetime.now()}
</pre>
<b><br /></b><b><br /></b>Para:<br />
<br />
<pre class="prettyprint">class BlocoForm(forms.ModelForm):
class Meta:
model = Bloco
fields = '__all__'
def dados(self):
return {'form':self.cleaned_data, 'data':datetime.now()}
</pre>
<br />
<br />
hasta!José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-10284043160261063222015-07-29T15:13:00.002-03:002015-07-29T15:13:36.731-03:00Calling modelformset_factory without defining 'fields' or 'exclude' explicitly is prohibited.Para todos que pegarem o erro <b>Calling modelformset_factory without defining 'fields' or 'exclude' explicitly is prohibited, </b>basta fazer uma pequena alteração no <b>inlineformset_factory</b><br />
para resolver.<br />
<br />
Altere seu forms.py de:<br />
<br />
<pre class="prettyprint">FotoFormSet = inlineformset_factory(Bloco, Foto, extra=1)</pre>
<b><br /></b>
<b><br /></b>
Para:<br />
<br />
<pre class="prettyprint">FotoFormSet = inlineformset_factory(Bloco, Foto, fields='__all__', extra=1)
</pre>
<br />
<br />
hasta!José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-85832501567242132752015-07-21T11:30:00.002-03:002015-07-21T11:30:47.829-03:00django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.Precisa escrever um script em python e fazer uso do Django para automatizar algo pela cron?<br />
<br />
Na versão atual do django, quando fazemos isso igual nas versões mais antigas, tipo 1.4 até 1.6, recebmos o erro:<br />
<br />
<b>django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.</b><br />
<b><br /></b>
<b><br /></b>
Para resolver isso, deixe seu script python como a seguir:<br />
<br />
<pre class="prettyprint">#!/usr/bin/env python
# coding: utf-8
from os.path import abspath, dirname
SETTINGS_DIRECTORY = dirname(dirname(abspath(__file__)))
sys.path.insert(0, SETTINGS_DIRECTORY)
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
import django
django.setup()
def sincorniza_busca():
from django.template.defaultfilters import striptags
# importe seus models aqui
from sua_app.models import SeuModel
# faça o que tiver que fazer aqui
if __name__ == '__main__':
sua_funcao()
</pre>
<br />
<br />
<br />
hasta!José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0tag:blogger.com,1999:blog-7529727049408400612.post-43962908972406576332015-06-12T10:27:00.000-03:002015-06-12T10:28:02.710-03:00AppRegistryNotReady: The translation infrastructure cannot be initialized before the apps registry is ready<b><br /></b>
Dê uma olhada em seu arquivo wsgi que carrega os módulos e apps. O erro acima é muito comum a partir do Django 1.7<br />
<b><br /></b>
<b>Troque:</b><br />
<pre class="prettyprint">module = django.core.handlers.wsgi:WSGIHandler()
</pre>
<br />
<br />
<b>Por:</b><br />
<pre class="prettyprint">module = get_wsgi_application()
</pre>
<br />
<br />
<b>Exemplo:</b><br />
<br />
<pre class="prettyprint">import sys, os
sys.path.append(os.getcwd())
# CONFIGURACAO PARA ENV
sys.path.insert(0,'<CAMINHO PARA SEU ENV>/ENV/bin')
sys.path.insert(0,'/<CAMINHO PARA SEU ENV>/ENV/lib/python2.7/site-packages')
# -------------------------
sys.path.insert(1, "/<CAMINHO PARA SEU PROJETO>/app")
os.environ['DJANGO_SETTINGS_MODULE'] = 'app.settings'
import django.core.handlers.wsgi
#application = django.core.handlers.wsgi.WSGIHandler()
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
</pre>
José Luishttp://www.blogger.com/profile/09964456499910011264noreply@blogger.com0