Mostrando postagens com marcador Dicas. Mostrar todas as postagens
Mostrando postagens com marcador Dicas. Mostrar todas as postagens

sexta-feira, 9 de junho de 2017

Como pegar o ID do último commit no GIT para forçar atualização de arquivos

Normalmente os navegadores modernos fazem cache dos arquivos para diminuir banda utilizada e aumentar o desempenho.

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.

No mobile ainda é mais custoso, haja visto que muitos usuários não sabem como limpar o cache e forçar o recarregamento.

Para isso, podemos "mudar" a url do arquivo com cache, para forçá-lo a atualizar e com isso obtermos o resultado desejado.

No seu arquivo de tags, inclua a simple_tag abaixo:

@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


E no seu template:

{% load site_tags %}
<link href="styles.css?v={% git_ver %}" rel="stylesheet">

hasta!

segunda-feira, 26 de setembro de 2016

Manter apenas um usuário logado por credenciais

Neste 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.

No seu models.py da app Cadastro adicione o seguinte atributo:

class Cadastro(models.Model):
 user = models.OneToOneField(User, null=True, blank=True)
 ...
 session_key = models.CharField(max_length=100, null=True)
 ...


E na sua views de login edite conforme a necessidade, incluíndo as linhas abaixo:
...
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)
...

hasta!

terça-feira, 6 de setembro de 2016

Validação customizada usando o ModelForm

Sempre existe alguma regra de validação particular de algum form. Seja um campo obrigatório caso outro seja preenchido, etc.

Vejamos como fazer isso de um jeito bem fácil:


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('campo3', u'Mensagem de erro do campo3')

Acima vemos duas possibilidades:

  • Validar um campo específico, através do método clean_<campo>
  • Validar o form todo, testando todos campos juntos com o método clean

hasta!

sexta-feira, 12 de agosto de 2016

Plugins Úteis: django-image-cropping - Como incoporar uma ferramenta de recorte para fotos no admin


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?

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?

Bom, pra isso, apresento-lhes o impressionante : django-image-cropping.


Vejamos como utilizar:

1. Primeiramente, instale ele no seu virtualenv:

pip install django-image-cropping

2. Se não tiver o easy_thumbnails instalado, instale:

pip install easy_thumbnails 

3. Adicione os dois no INSTALLED_APPS:

INSTALLED_APPS = [
    ...
    'easy_thumbnails',
    'image_cropping',
    ...
]

4. Adicone também no settings.py:

from easy_thumbnails.conf import Settings as thumbnail_settings
THUMBNAIL_PROCESSORS = (
    'image_cropping.thumbnail_processors.crop_corners',
) + thumbnail_settings.THUMBNAIL_PROCESSORS

5. No seu model que quiser a função de recorte no admin deixe como a seguir:

from image_cropping import ImageRatioField

class SeuModel(models.Model):
    ...
    imagem = models.ImageField(upload_to="/caminho/para/upload/",)
    cropping = ImageRatioField('imagem', '900x900')
    ...


Mude a proporção do atributo cropping de acordo com sua necessidade.

6. No admin.py:

from django.contrib import admin
from image_cropping import ImageCroppingMixin

class SeuModelAdmin(ImageCroppingMixin, admin.ModelAdmin):
    pass

admin.site.register(SeuModel, SeuModelAdmin)

7. No template que for exibir a foto recortada utilize a tag provida pelo plugin:

{% cropped_thumbnail instancia_do_seu_model "cropping" [scale=INT|width=INT|height=INT|max_size="INTxINT"] %}

EX:
<img cropping="" scale="0.5" src="{% cropped_thumbnail instancia_do_seu_model " />


8. Dá inclusive para utilizar esse plugins para Inlines:


from image_cropping import ImageCroppingMixin
class ImagemInline(ImageCroppingMixin, admin.TabularInline):
 model = Imagem
 extra = 0



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.


Fonte e mais informações: https://pypi.python.org/pypi/django-image-cropping


hasta!

sexta-feira, 8 de abril de 2016

SyntaxError: expected expression, got '<' /admin/jsi18n/ (line 1) interpolate is not defined

SyntaxError: expected expression, got '<'  <!DOCTYPE html> /admin/jsi18n/ (line 1) 
interpolate is not defined

Esse erro apareceu quando fiz a utilização do SelectFilter2 para um form no front.

Ele é responsável pela ferramenta de filter_horizontal / filter_vertical do admin, e deveria ser exibido assim:




E com o erro era exibido assim:





Para resolver esse problema:


Adicione no seu urls.py:
...
url(r'^jsi18n/$', 'django.views.i18n.javascript_catalog'),
...


E no seu template que for utilzar o widget:

<script type="text/javascript" src="/jsi18n/" > </script>

sexta-feira, 25 de setembro de 2015

Plugins Úteis: django-geoposition - Como incorporar o google maps dentro do admin para geolocalização

Precisa incorporar um mapa com base em um endereço no administrativo para exibir no front? Tarefa simples pro django-geoposition (https://github.com/philippbosch/django-geoposition).

A instalação é simples:
pip install django-geoposition

Coloque a linha abaixo no seu arquivo settings.py, em INSTALLED_APPS:
INSTALLED_APPS = (
    ...
    'geoposition',
    ...
)

Outra configuração que vale a pena fazer, é incluir no seu arquivo settings.py as linhas abaixo para definir o zoom:
GEOPOSITION_MAP_OPTIONS = {
    'minZoom': 15,
    'maxZoom': 18,
}

No seu arquivo models.py, deixe como a seguir:
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 

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.
# 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)

No arquivo css adicionado (geoposition_override.css), 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.

Crie um arquivo dentro de <STATIC_DIR>/admin/css, chamado geoposition_override.cssm, e nele coloque simplemente a linha abaixo:

.geoposition-search input{display:none;}

Agora, crie um js chamado geoposition_override.js na pasta <STATIC_DIR>/admin/js com as linhas abaixo:
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);
  };
 });
});

E o resultado:

hasta!

sexta-feira, 7 de agosto de 2015

Autenticação sem o request.user.get_profile


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.

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.

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.

Cenário: Imagine que temos uma app cadastros que possui uma classe Cadastro e esta tem um vínculo com a classe User do Django, para fazer uso do sistema de autenticação.

Remova a setting AUTH_PROFILE_MODULE:


Este passo é fácil. Encontre o AUTH_PROFILE_MODULE no seu arquivo settings.py e remova  linha.

# No seu settings.py:
AUTH_PROFILE_MODULE = 'cadastros.cadastro'     # remova esta linha


Mude seu modelo que possui o vínculo com a classe User do Django:


Antes:

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

Depois:

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


Aplique as migrações para concluir esta etapa:

python manage.py makemigrations cadastros
python manage.py migrate cadastros


Atualize seu código:


Antes:
cadastro = request.user.get_profile()

Depois:
cadastro = request.user.cadastro


hasta!

terça-feira, 21 de julho de 2015

django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.

Precisa escrever um script em python e fazer uso do Django para automatizar algo pela cron?

Na versão atual do django, quando fazemos isso igual nas versões mais antigas, tipo 1.4 até 1.6, recebmos o erro:

django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.


Para resolver isso, deixe seu script python como a seguir:

#!/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()



hasta!

quarta-feira, 21 de janeiro de 2015

Como criar um PNG com marca d'água e texto dinâmico

A necessidade: Criar uma ferramenta para upload de uma imagem e gerar uma saída com uma marca d'água e um texto dinâmico. Saída era esperada em PNG para melhor renderização no Facebook.

Pra isso, você vai precisar de:
  • PIL==1.1.7
  • django-easy-thumbnails-watermark==0.6.7

Crie uma classe no models.py para tal:
class Visita(models.Model):
 """(Visita description)"""
 data = models.DateField(auto_now_add=True)
 linha1 = models.CharField(max_length=255)
 linha2 = models.CharField(max_length=255, null=True, blank=True,)
 imagem = models.ImageField(upload_to="uploads/visitas/imagem/%Y",help_text="800x671")

 class Meta:
  verbose_name, verbose_name_plural = u"Visita à Fábrica" , u"Visitas à Fábrica"
  ordering = ('-data',)

 def __unicode__(self):
  return u"%s" % self.data


 def gerar(self):
  return 'Gerar' % self.id
 gerar.allow_tags = True
 gerar.is_safe = True

Aqui criei um modelo que tem uma data, por questão de organização, duas linhas para texto que serão inseridas na imagem e a imagem em si.

No urls.py, crie uma entrada para chamada da view:
url(r'^visitas/(?P\d+)/$', 'imagens_facebook.views.visitas', name="visitas"),

E da view abaixo:
def visitas(request, vid):
 img_obj = get_object_or_404(Visita, id=vid)
 img = '{0}/{1}'.format(settings.MEDIA_ROOT, img_obj.imagem)
 IMAGE = Image.open('{0}/{1}'.format(settings.MEDIA_ROOT, img_obj.imagem))
 WM = {
  'image':'{0}/site/img/visita.png'.format(settings.STATIC_PATH,),
  'position': 'BL',
  'opacity': 1,
  'scale': 1,
  'tile': False,
  'greyscale': False,
  'rotation': 0,
 }
 WK_DONE = watermark_processor(IMAGE, WM )
 path = '{0}/watermarks/visitas/{1}'.format(settings.MEDIA_ROOT, 'imagem.png')
 WK_DONE.save(path, "PNG")
 img = Image.open('{0}/watermarks/visitas/{1}'.format(settings.MEDIA_ROOT, 'imagem.png'))
 draw = ImageDraw.Draw(img)
 font = ImageFont.truetype("{0}/fonts/trebuc.ttf".format(settings.STATIC_PATH),20)
 draw.text((60, 465), img_obj.linha1.upper() ,(55,185,115),font=font)
 if img_obj.linha2:
  draw.text((60, 490), img_obj.linha2.upper() ,(55,185,115),font=font)
 img.save('{0}/watermarks/visitas/{1}'.format(settings.MEDIA_ROOT, 'imagem.png'))
 return force_download(request, path)

Linha a linha temos:

  1. Definição da View "visitas" que recebe o request e o id da visita, identificado por "vid"
  2. Obtenção do objeto visita com o id informado
  3. Definição do caminho de onde está a imagem que foi feita upload
  4. Abertura da imagem como objeto da Classe Image
  5. Definição da WaterMark com o parâmetros necessários:
    1. caminho da marca d'agua
    2. Posição que será inserida, BL para bottom left
    3. Opacidade =1 para não ter transparencia, do contrario coloque valores entre 0 e 1.
    4. Escala 1 para não alterar o tamanho
    5. Tile é falso para não esticar imagem utilizada como marca d'agua em toda a imagem definida no upload.
    6. Escala de cinza falso para sair colorido
    7. Sem rotação para sair em orientação normal.
  6. Criação da imagem com marca d'agua utilizando o watermark_processor
  7. Definição de onde será gerado o arquivo
  8. Ação de salvar para gerar o arquivo em png
  9. A partir desta linha é onde será escrito o texto dinamico. Aqui abrimos a imagem gerada no passo anterior.
  10. Criação de um objeto draw da classe ImageDraw.
  11. Definição da fonte a ser utilizada. Aqui será necessário copiar o arquivo da fonte para seu projeto.
  12. A escrita do texto proprimente dita com os respectivos parâmetros: posição(left, top), texto a ser inserido, cor em RGB e caminho da font definida no passo anterior
  13. Apenas um if para testar se há uma segunda linha no texto.
  14. Se houver repete a mesma ação do passo 12, passando o texto diferente.
  15. Ação de salvar a imagem para gerar o arquivo físico.
  16. View para forçar o download do arquivo gerado. Esta view pode ser vista aqui: http://djangoweb.blogspot.com.br/2011/10/forcando-download-de-arquivos-no-django.html

hasta!


quinta-feira, 15 de janeiro de 2015

Tradução de maneira descomplicada com o Model Translation


Todo mundo já deve ter passado por um projeto multi-idioma. Se ainda não, recomendo mesmo assim a leitura deste post. Por participar de algo assim, pude aprender mais algumas coisas interessantes.

Aqui seguem alguns passos que podem auxiliar e muito a confecção de um projeto multi-idioma

1. Primeiro de tudo, defina quais idiomas o projeto terá. 

Isso se faz com algumas configurações no settings.py:

LANGUAGE_CODE = 'pt-br'
TIME_ZONE = 'America/Sao_Paulo'
USE_I18N = True
USE_L10N = True
ugettext = lambda s: s
LANGUAGES = (
    ('pt-BR', ugettext(u'Português')),
    ('en-US', ugettext(u'Inglês')),
    ('es-ES', ugettext(u'Espanhol')),
)
USE_TZ = False
LOCALE_PATHS = (
   os.path.join( PROJECT_PATH, '../locale' ),
)
  • LANGUAGE_CODE: Aqui é definido a linguagem inicial do projeto. Valor padrão: 'en-us'. Veja a lista de outras línguas aqui: http://www.i18nguy.com/unicode/language-identifiers.html
  • TIME_ZONE: Define o fuso horário do projeto. Valor padrão: 'America/Chicago' 
  • USE_I18N: Flag que determina se o sistema de tradução do Django deve ser habilitado ou não. Deve ser True para multi-idiomas e False, por questões de otimização, quando tiver apenas um idioma. Valor padrão: False 
  • USE_L10N: Um booleano que especifica se a formatação localizada de dados será ativado por padrão ou não. Se esta é definida como True , por exemplo Django irá exibir números e datas usando o formato da localidade atual. Valor padrão: True 
  • LANGUAGES: Tupla com as linguagens que serão utilizadas no site. 
  • USE_TZ: Determina se o django usará o datetime com base no fuso ou não.Valor padrão: False 
  • LOCALE_PATH: Tupla com os diretórios onde serão gerados os arquivos para tradução.

2. Instale o django-modeltranslation

$ pip install django-modeltranslation

No settings, certifique-se que o modeltranslation está acima do admin do Django, pra não ter problemas, coloque ele como a primeira linha do INSTALLED_APPS:
INSTALLED_APPS = (
    # TRANSLATION
    'modeltranslation',
    ...
)

Cerfitique-se que o Locale Middleware está instalado:
MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

E inclua a url de tradução no urls.py:
...
(r'^i18n/', include('django.conf.urls.i18n')),
...

Na aplicação que terá conteúdos dinâmicos para cada idioma deixe os arquivos como abaixo:

models.py
# coding: utf-8
from django.db import models

# Create your models here.

class Conteudo(models.Model):
	"""(Conteudo description)"""
	titulo = models.CharField(u'Título', max_length=255)
	texto = models.TextField()
	slug = models.SlugField(verbose_name=u'Slug / URL')

	class Meta:
		verbose_name, verbose_name_plural = u"Conteúdo" , u"Conteúdos"
		ordering = ('titulo',)

	def __unicode__(self):
		return u"%s" % self.titulo

admin.py
# Register your models here.

from django.contrib import admin
from .models import *
from modeltranslation.admin import TranslationAdmin

class ConteudoAdmin(TranslationAdmin):
	search_fields = ('titulo', 'texto',)
	list_display = ('titulo', 'texto')
	prepopulated_fields = {"slug": ("titulo",)}
	save_on_top = True

admin.site.register(Conteudo, ConteudoAdmin)

translation.py
# translation.py

from modeltranslation.translator import translator, TranslationOptions
from .models import *

class ConteudoOptions(TranslationOptions):
    fields = ('titulo', 'texto',)
translator.register(Conteudo, ConteudoOptions)


Quando usar a migração (South para Django<1.7, ou nativo para Django>=1.7), serão gerados no banco os campos definidos no translation.py para cada idioma, isto é, o resultado será uma tabela com:
  • titulo_pt_br
  • texto_pt_br
  • titulo_en_us
  • texto_en_us
  • titulo_es_es
  • texto_es_es
  • slug

E o admin já trará todos eles com uma marcação de idioma através de [<idioma>] nas labels de cada campo.


Observação Importante: 

Caso não haja necessidade de traduzir a url que ficará no navegador, não inclua o slug no translation.py (recomendo que não inclua! Insista, convença seu chefe!). Isso facilita e muito quando o usuario troca de idioma para redirecioná-lo para a mesma página ao invés de mandá-lo de volta pra home do site e/ou fazer qualquer gambiarra para redirecioná-lo corretamente.

3. Nos templates, fica muito simples:

{{ obj.titulo }}

Aqui o próprio Model Translation irá verificar qual o é o idioma atual e exibir o valor correto. Sem IFs e sem duplicação de templates.

4. Para conteúdos estáticos, o processo continua o mesmo com a geração dos .po

Carrege o módulo i18n nos templates que for traduzir:
{% load i18n %}

Marque nos templates estáticos onde sofrerá tradução com as tags trans e blocktrans
{% trans "Exemplo de texto a ser traduzido nos seus htmls" %}

Execute o comando makemessages para gerar as mensagens:
$ python manage.py makemessages --locale=en --ignore=templates/admin --ignore=project/settings.py

Serão gerados arquivos .po para tradução no LOCALE_PATH definido nas settings com a seguinte estrutura:
  • locale
    • en
      • LC_MESSAGES
        • django.po
    • es
      • LC_MESSAGES
        • django.po


Nestes arquivos, serão armazenados os termos a serem traduzidos para envio para a pessoa responsável para tradução, e eles se parecem com isso:
#: templates/atendimento/atendimento.html:21
#: templates/atendimento/sucesso.html:21 templates/curriculos/sucesso.html:21
#: templates/curriculos/trabalhe-conosco.html:25
msgid "Dados enviados com sucesso"
msgstr ""

No exemplo acima, as linhas iniciadas com # indicam o caminho dos arquivos em tem o termo contido no msgid em comum, e no msgstr é onde deve ser preenchido com a tradução para o idioma correspondente.

Caso a pessoa responsável pela tradução não conseguir/quiser editar direto os .po, existem alguns apps que podem ser utilizados para facilitar esse processo, como o Rosetta (mais informações em: https://github.com/mbi/django-rosetta) por exemplo. Este cria uma interface com base no bom e velho admin provido pelo Django.

Depois de tudo preenchido, basta compilar as mensagens com o comando compilemessages:
$ python manage.py compilemessages --locale=en
$ python manage.py compilemessages --locale=es


5. Para trocar de idioma no site, basta um form:


{% get_available_languages as LANGUAGES %}
{% get_current_language as LANGUAGE_CODE %}

<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{{ redirect_to }}" />
<select name="language">
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
    {{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
<input type="submit" value="Go" />
</form>


Em {{ redirect_to }}, se o slug não tiver sido traduzido e cada conteúdo tiver uma única url, aqui vc pode por a url atual, {{ request.META.PATH_INFO }},  e quando o usuário trocar de idioma, já vai ser redirecionado para a mesma página que estava visualizando.



Referências:


hasta!

terça-feira, 16 de dezembro de 2014

Erros para instalar dependências do virtualenv no Mac OSX Lion

Tentando instalar as dependências do ambiente virutal para trabalhar com Django no OSX Lion me deparei com os seguinte erros:

error command 'llvm-gcc-4.2' failed with exit status 1
mysql_config not found

Além do claro, clássico trabalho para instalar a PIL com suporte a JPEG.

Resolvi as coisas com o a mesma solução: BREW (http://brew.sh/)

Depois de instalar o brew, desinstale PIL e Pillow, caso estejam instaladas  execute os comandos:

brew install libjpeg
brew install mysql
pip install PIL
pip install Pillow


hasta!

segunda-feira, 15 de dezembro de 2014

Apps não aparecem no administrativo mesmo como superuser autenticado

Criou os apps e tem certeza que as classes no admin.py estão corretas?

Caso tenha, e/ou esteja fazendo um downgrade do django 1.7 pro 1.6, e estava funcionando antes, certifique-se que colocou a linha abaixo no urls.py antes do urlpatterns:


...
admin.autodiscover()
...


hasta!

sábado, 22 de novembro de 2014

Definindo uma linguagem inicial para o projeto, não importando a linguagem setada pelo navegador

Eis o middleware que faz a mágica:


class ForceDefaultLanguageMiddleware(object):
    """
    Ignorar cabeçalhos HTTP Accept-Language

    Isso vai forçar as máquinas I18N para escolher sempre settings.LANGUAGE_CODE
    como o idioma inicial padrão, a menos que um outro é definido através de sessões ou cookies

    Deve ser instalado * antes * qualquer middleware que verifica request.META [' HTTP_ACCEPT_LANGUAGE '] ,
    como por exemplo:  django.middleware.locale.LocaleMiddleware
    """

    def process_request(self, request):
        if request.META.has_key('HTTP_ACCEPT_LANGUAGE'):
            del request.META['HTTP_ACCEPT_LANGUAGE']


e no settings.py, basta colocar o middleware antes do LocaleMiddleware como dito acima:

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'settings.middleware.ForceDefaultLanguageMiddleware',
    'django.middleware.locale.LocaleMiddleware',
)


hasta!

terça-feira, 23 de setembro de 2014

Como exibir o conteúdo html de um atributo que usa CKEDITOR dentro do readonly fields no admin

Muito específico esta necessidade? Também achava. Até precisar dela.

Imagine que tenha um form no front onde o usuário irá postar conteúdos html. No admin, conteúdos de usuários serão moderados, contudo não poderão ser editados. Nesse ponto o readonly fields ajuda muito. Mas como exibir o conteúdo html (safe) ao invés das tags?

Simples.

No seu admin.py faça da seguinte forma:


from django.contrib import admin
from .models import *


class ConteudoAdmin(admin.ModelAdmin):
 search_fields = ('nome', 'email','titulo','texto')
 list_display = ('nome', 'email','titulo','arquivo', 'data', 'hora')
 list_filter = ['data',]
 date_hierarchy = 'data'
 readonly_fields = ['nome', 'email','titulo', 'texto_html','arquivo']
 exclude = ['texto',]
 save_on_top = True

 def texto_html(self, obj):
  return u'{0}'.format(obj.texto)
 texto_html.allow_tags = True
 texto_html.short_description = 'Texto'


admin.site.register(Conteudo, ConteudoAdmin)


Note que criei um método para retornar o atributo texto do meu objeto, e permiti ao método que retornasse tags html com o allow_tags = True. Para não ficar repetido, exclui o atributo original do admin com o exclude.

hasta!

quarta-feira, 3 de setembro de 2014

Django 1.7 - Getting Started!

Saiu a versão tão esperada do Django, a 1.7.

Algumas novidades importantes antes de começarmos:

  • Suporte ao python 3.3.
  • Retirou o suporte ao python 2.6.
  • Sistema de migração de banco nativo ( integração com South ).

Vou tentar registrar aqui o que tive que fazer para iniciar um projeto e conforme for surgindo as novidades vou atualizando o post.


Como instalar uma nova versão do Python:



Bom pra começar, eu tinha o python 2.6 e por isso precisei atualizar. 

O ambiente que utilizo é windows 7 e uso uma máquina virtual com o Vagrant para rodar os projetos. Para futuras referencias abaixo, tudo será feito dentro do terminal do vagrant. Para mais informações: 

Para este início, vou instalar a versão 2.7.8 do python. Para isso faça o download em algum lugar do source e instale com os comandos abaixo:
wget https://www.python.org/ftp/python/2.7.8/Python-2.7.8.tgz
tar -xvf Python-2.7.8.tgz
cd Python-2.7.8/
./configure
make
sudo make install

Caso não tenha o make instalado, era o meu caso, utilize o comando abaixo e posteriormente a instalação conclua os passos acima.
sudo apt-get install make


Como criar o virtualenv com o python recém instalado:



Para criar o ENVo comando é o mesmo visto nos links acima:
virtualenv --no-site-packages --unzip-setuptools django17 --python=/usr/local/bin/python2.7

Aqui tive outro problema: A versão do virtualenv que tinha, não era compativel com o python 2.7. Portanto tive que atualizar  o virtualenv também:
sudo pip install virtualenv --upgrade

Depois de instalado, o comando ficou:
virtualenv-2.6 --no-site-packages --unzip-setuptools django17 --python=/usr/local/bin/python2.7

No comando acima vale prestar atenção na versão do virtualenv chamado e no parâmetro python passado ao sistema indicar o caminho do python desejado.

Com o ENV chamado django17 criado, vamos agora instalar as dependências que nosso projeto precisa, primeiramente ligue o env:
. django17/bin/activate

E instale o que desejar usando o pip, como por exemplo:
pip install django
pip install MySQL-python
pip install sorl-thumbnail==11.12.1b

A versão da sorl.thumbnail foi especificada pois a final ainda não tem compatibilidade com o Django 1.7, por isso instalei a beta.




Ajustando o settings para rodar o projeto



Achei que o settings.py nativo veio bem mais enxuto, e por hora mudarei apenas duas configurações:
LANGUAGE_CODE = 'pt-br'
TIME_ZONE = 'America/Sao_Paulo'

E no fim do arquivo adicionarei a importação das configurações que farei a sobreposição para rodar o projeto local:
try:
 from local_settings import *
except:
 pass


Criei um arquivo chamado local_settings.py no mesmo diretório do settings.py com as seguintes configurações:
# coding: utf-8
SITE_URL = 'http://localhost:8000'
DATABASES = {
 'default': {
  'ENGINE': 'django.db.backends.mysql', 
  'NAME': 'django17',        
  'USER': 'root',    
  'PASSWORD': '',    
  'HOST': '',    
  'PORT': '',    
 }
}

DEBUG = True
LOCAL = True
USE_TZ = False

if not DEBUG:
 ALLOWED_HOSTS = ['localhost',]

Ajuste neste arquivo as configurações necessárias, como DEBUG, acesso ao banco, etc.

Não se esqueça de criar uma database com o nome especificado a seu gosto no SGBD de sua preferência.


Criação das tabelas:



Para criação das tabelas padrões do django, utilize o comando abaixo. Ele será usado sempre que for instalar uma aplicação, tendo esta migrações ou não.
./manage.py migrate




Criar o super usuário do Admin:



Agora nesta versão,  o migrate (antigo syncdb) não cria mais um super usuário para o administrativo. Você deve fazer isso manualmente com o comando:
./manage.py createsuperuser

E preencha conm seu usuário, email e senha, da mesma forma que era feito antes.


Como realizar migrações dos models com makemigrations / migrate



Pra quem já utilizava o South , não terá muita dificuldade em mudar para o sistema de migração nativo que a versão 1.7 ofecere.

Para uma nova aplicação ou qualquer mudança feita no models de uma aplicação o procedimento é o mesmo:
./manage.py makemigrations debug


E depois rode novamente o migrate, passando ou não sua app:
./manage.py migrate debug




Para rodar o projeto:



Para rodar o projeto execute o comando abaixo. Ele rodará servidor e porta padrão do ambiente desenvolvimento: localhost:8000. Para mudar o servidor e/ou porta basta especificar o desejado a frente do parâmetro.
./manage.py runserver




Novidades do admin:



  • Agora é possivel alterar algumas configurações do admin sem ter que sobrepor templates. Em qualquer arquivo admin.py dentro da sua aplicação utilize o código abaixo:
# coding: utf-8

from django.contrib import admin

admin.site.site_header = u"Administração do Site"
admin.site.index_title = u"Administração do Site"
admin.site.site_title = u"Site de Administração do Django"



  • Agora os botões com cantos arredondados utilizam a propriedade border-radius do CSS ao invés de GIFs.
  • No changelist, agora as células tem uma classe CSS chamada field-<field_name> para facilitar customizações.
  • Além da sintaxe admin.site.register existente , você pode usar o decorador novo register() para registrar um ModelAdmin.
  • Agora é possível  especificar ModelAdmin.list_display_links = None desativar links na grade página de listagem.

Chamar métodos customizados do Queryset pelo Manager


class FoodQuerySet(models.QuerySet):
    def pizzas(self):
        return self.filter(kind='pizza')

    def vegetarian(self):
        return self.filter(vegetarian=True)

class Food(models.Model):
    kind = models.CharField(max_length=50)
    vegetarian = models.BooleanField(default=False)
    objects = FoodQuerySet.as_manager()

Food.objects.pizzas().vegetarian()


Veja estas e outras dicas interessantes sobre tudo que mudou para esta versão no link oficial:
https://docs.djangoproject.com/en/1.7/releases/1.7/

hasta!

quinta-feira, 14 de agosto de 2014

Gerando um PDF em Django sem template e forçando seu donwload

Você vai precisar dos seguintes aplicativos instalados no ENV:
  • reportlab==2.7

Faça uma view conforme abaixo:

#############################################################
# PDF
#############################################################
class PDFView(View):

 def post(self, request, *args, **kwargs):
  # IMPORTS NECESSARIOS
  from reportlab.pdfgen import canvas
  from reportlab.lib.pagesizes import letter, landscape, A4
  from reportlab.lib.units import inch, cm
  from reportlab.lib.colors import HexColor
  from django.conf import settings
  import os

  # VALIDAÇÃO DO CSRF_TOKEN
  c = {}
  c.update(csrf(request))
  
  # NOME DO ARQUIVO
  nome_arquivo = "1.pdf"
  arquivo = '%s/uploads/%s' % (settings.MEDIA_ROOT, nome_arquivo)

  if os.path.isfile(arquivo):
   # REDIRECT CASO NÃO ENCONTRAR O ARQUIVO
   return redirect('/')

  imagem =  '%s/%s' % (settings.MEDIA_ROOT, "sua_imagem_aqui.jpg")

  # CRIAÇÃO DO PDF EM MODO PAISAGEM , TAMANHO A4
  c = canvas.Canvas(arquivo, pagesize=landscape(A4))
  c.drawImage(
   '%s' % imagem,
   0,
   0,
   29.7*cm,
   21*cm
  )

  # DEFINIÇÃO DE FONTE, FAMILIA, TAMANHO E COR
  c.setFont("Helvetica", 16)
  c.setFillColor(HexColor(0x094d8a))

  # ESCRITA DO TEXTO POR CIMA DA IMAGEM COM ALINHAMENTO
  c.drawString(18.4*cm, 14*cm, "%s" % participante.nome)

  # GERAÇÃO E CRIACAO DO PDF
  c.showPage()
  c.save()

  # REDIRECT PARA VIEW QUE FORCARÁ O DOWNLOAD
  return redirect('certificados:download',participante.id)

 def get(self, request, *args, **kwargs):
  return redirect('/')


#############################################################
# DOWNLOAD
#############################################################
class ForceDownload(View):
 def get(self, request, *args, **kwargs):
  from django.http import HttpResponse
  from os import path
  
  import mimetypes

  arquivo = '%s/uploads/%s.pdf' % (settings.MEDIA_ROOT, kwargs['pk'])

  if not (path.exists(arquivo)):
   raise Http404

  mimetype, encoding = mimetypes.guess_type(arquivo)

  if mimetype is None:
   mimetype = 'application/force-download'

  file = arquivo.split("/")[-1]

  response = HttpResponse(open(arquivo, 'r').read())
  response['Content-Type'] = mimetype
  response['Pragma'] = 'public'
  response['Expires'] = '0'
  response['Cache-Control'] = 'must-revalidate, post-check=0, pre-check=0'
  response['Content-Disposition'] = 'attachment; filename=%s' % file
  response['Content-Transfer-Encoding'] = 'binary'
  response['Content-Length'] = str(path.getsize(arquivo))
  return response # Create your views here.


E no urls deixe da seguinte forma:


# coding: utf-8
from django.conf.urls import patterns, include, url
from . import views

urlpatterns = patterns('',

 ############################################################################
 # DOWNLOADS
 ############################################################################
 url(r'^downloads/(?P<pk>\d+)/$', views.ForceDownload.as_view(), name='download'),

 ############################################################################
 # CERTIFICADO
 ############################################################################
 url(r'^(?P<slug>[-\w]+)/$', views.PDFView.as_view(), name='pdf_detail'),


)

hasta!

quarta-feira, 2 de julho de 2014

Como fazer a contrab trabalhar pra você

Normalmente quando é necessário uma tarefa repetitiva e que possui um certo grau de integração com outros serviços, utilizo o recurso da contrab.

No python basta criar seu arquivo e definir nele o que será feito. Em seguida basta agendar a tarefa na crontab de acordo com a periodicidade necessária.

Para listar as tarefas agendadas na cron basta executar o comando:
crontab -l

Para criar/editar tarefas basta executar o comando:
crontab -e

Vale ressaltar que caso nao goste do editor que a cron utiliza, pode trocar por algum  de sua preferencia, como o nano, basta executar o seguinte comando:

export VISUAL=nano

Para definir o tempo que você pode fornecer valores para minutos, horas, dias do mes, meses e dias da semana. Nesta ordem. Também é possível definir * para qualquer um destes parametros de forma a referenciar qualquer valor.

Outra coisa que vale dizer é : Se vc precisa executar uma rotina que precisa de mais de um comando , certifique-se que todas as linhas sejam aglutinadas em apenas uma, através do &&.

Exemplo:

# m h  dom mon dow   command

0 * * * 1,2,3,4,5 . /ENV/bin/activate && python carga.py && deactivate

O comando acima roda sempre que os minutos forem iguais a 0, para todas a horas, em todos os dias do mês, durante todos os meses e somente de segunda a sexta. Ele inicia o  ambiente virtual, executa o arquivo carga.py e desativa o ambiente virtual.

quarta-feira, 7 de maio de 2014

Paginação estilo Twitter com Django CBV e django-endless-pagination



Quem já não precisou fazer uma paginação ao estilo do twitter? Bom não é complicado. Pelo menos não com as dicas abaixo!

Requisitos necessários:

  • Python >= 2.6
  • Django >= 1.3
  • jQuery >= 1.7


Instalação:
pip install django-endless-pagination

Configuração:


Adicione a app na setting INSTALLED_APPS no settings.py:
...
'endless_pagination',
...

Adicione a linha abaixo na setting TEMPLATE_CONTEXT_PROCESSORS também no settings.py:
...
'django.core.context_processors.request',
...

Na views.py

from django.shortcuts import render, render_to_response, get_object_or_404
from django.template import Context, loader, RequestContext
from .models import Produto

class ProdutoTTPag(View):
 def get(self, request, *args, **kwargs):

  template = 'produtos/lista-produtos.html'
  page_template = 'produtos/paginacao.html'

  produtos = Produto.objects.filter(ativo=True)
  
  context = {
   'page_template': page_template,
   'produtos_list' : produtos,
  }

  if request.is_ajax():
   template = page_template

  return render_to_response(template, context, context_instance=RequestContext(request))


No urls.py

Adicione a linha abaixo:
...
url(r'^(?P[-\w]+)/$', views.ProdutoTTPag.as_view(), name='produtos'),
...


No template:

Aqui será necessário a criação de dois arquivos html. Um para ser de fato a lista (lista-produtos.html) e o outro para ser apenas os itens, para que o ajax possa trazê-los de forma correta (paginacao.html).

lista-produtos.html:
...
{% load endless %}
...

{% block extra_js %}
    <script src="{{ STATIC_URL }}endless_pagination/js/endless-pagination.js" type="text/javascript"></script>
    <script type="text/javascript">
     jQuery(document).ready(function($) {
      //$.endlessPaginate({
         //    paginateOnScroll: true,
         //    paginateOnScrollChunkSize: 5,
         //    paginateOnScrollMargin: 200
         //});

      $.endlessPaginate({});
     });
    </script>
{% endblock extra_js %}

...
<section class="list-products">
 <ul>
  {% include page_template %}
 </ul>
...
</section>

Aqui a parte comentada serve para trazer os resultados das outras páginas conforme o scroll da página.

paginacao.html:

{% load endless %}

{% paginate 9 produtos_list %}
{% for p in produtos_list %}
 <li>
  {# HTML PARA ITEM DO PRODUTO #}
 </li>
{% endfor %}
{% show_more %}

Mais informações em:

  1. http://django-endless-pagination.readthedocs.org/en/latest/index.html
  2. https://github.com/frankban/django-endless-pagination
  3. https://pypi.python.org/pypi/django-endless-pagination/2.0


hasta!

sábado, 26 de abril de 2014

Instalação do Vagrant no Windows com instalador MSI

Instalar o vagrant no windows agora ficou muito mais fácil. Diferente de todos os passos vistos em Instalação do Vagrant no windows, agora tem um instalador.

Tentei instalar seguindo os passos do tutorial anterior e recebi uma mensagem de aviso dizendo que o Vagrant agora não era mais recomendado baixar como uma ruby gem, e esta não terá mais suporte nem atualizações. Ainda é possível instalar segundo os passos da mensagem, mas este não é o objetivo deste tutorial.

Vamos lá.

  1. Baixe e instale o Oracle Virtual Box no link https://www.virtualbox.org/wiki/Downloads
  2. Adicione o caminho da instalação no PATH do windows.
  3. Baixe e instale o instalador para sua windows no link http://www.vagrantup.com/downloads
  4. Crie uma pasta chamada vagrant-machine no disco C (C:) para facilitar o acesso via prompt.
  5. Abra o promt de comando (cmd) e acesse a pasta recém criada:
    1. cd c:\vagrant-machine
  6. Execute os comandos:
    1. vagrant init hashicorp/precise32
      1. Este comando criará o Vagrantfile
    2. vagrant up
      1. Este comando colocará a maquina virtual em execução
    3. sudo apt-get install libxml2-dev libxslt1-dev python-libxml2 python-setuptools git-core build-essential libxml2-dev libpcre3-dev libpcrecpp0 libssl-dev zlib1g-dev libgeoip-dev memcached libmemcached-dev python-mysqldb libmysqlclient16-dev python-virtualenv
    4. sudo apt-get update
    5. sudo apt-get install python-virtualenv
    6. sudo apt-get build-dep python-imaging
    7. sudo sh postinstall.sh
    8. Abra o arquivo Vagrantfile dentro de C:\vagrant-machine e descomente a linha
      1. config.vm.network "forwarded_port", guest: 80, host: 8080
    9. Adicione abaixo portas extras
      1. config.vm.network "forwarded_port", guest: 8000, host: 8000
      2. config.vm.network "forwarded_port", guest: 8001, host: 8001
      3. config.vm.network "forwarded_port", guest: 8002, host: 8002
    10. Adicione abaixo de
      # config.vm.synced_folder "../data", "/vagrant_data":
      1.  
        config.vm.synced_folder "../wamp/www", "/projetos"
    11. sudo apt-get install git
    12. Crie o ENV 
      1. virtualenv --no-site-packages --unzip-setuptools django16
    13. Ligar o ENV
      1. . django16/bin/activate
    14. Instale primeiro o driver do MySQL que é o que dá mais trabalho:
      1. sudo apt-get build-dep python-mysqldb
      2. sudo pip install MySQL-python
    15. Alguns aplicativos uteis:
      1. BeautifulSoup==3.2.1
      2. Django==1.6.3
      3. Fabric==1.8.3
      4. MySQL-python==1.2.5
      5. PIL==1.1.7
      6. Pillow==2.4.0
      7. South==0.8.4
      8. argparse==1.2.1
      9. distribute==0.6.24
      10. django-appconf==0.6
      11. django-ckeditor-updated==4.2.8
      12. django-compressor==1.3
      13. django-localflavor==1.0
      14. django-pagination==1.0.7
      15. django-simple-captcha==0.4.2
      16. django-subdomains==2.0.4
      17. easy-thumbnails==2.0.1
      18. ecdsa==0.11
      19. paramiko==1.12.3
      20. pycrypto==2.6.1
      21. simplejson==3.4.0
      22. six==1.6.1
      23. sorl-thumbnail==11.12.1b
      24. wsgiref==0.1.2
    16. Salve esta lista num arquivo txt(requirements.txt) e execute o comando:
      1. pip install -r requirements.txt

quarta-feira, 16 de abril de 2014

error: command 'cc' failed with exit status 1

Problemas para instalação das libs do python no ENV no Mavericks?

Com o ENV ligado, se a instalação de algum pacote apresentar o seguinte erro:

error: command 'cc' failed with exit status 1

Execute estas duas linhas no seu shell:

export CFLAGS=-Qunused-arguments

export CPPFLAGS=-Qunused-arguments


Essas exportações dizem ao compilador para ignorar argumentos não utilizados, em vez de reclamar sobre eles.

A razão parece ser que Python compila módulos usando as opções com que ele foi construído, exceto uma dessas opções não funciona mais em mavericks.

Agora é só instalar suas libs normalmente com o pip.


hasta!