diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml
new file mode 100644
index 000000000..e5d50d384
--- /dev/null
+++ b/.github/workflows/build-test.yml
@@ -0,0 +1,32 @@
+name: Build Test
+
+on:
+ push:
+ branches:
+ - '*'
+ - '!pelican'
+
+permissions:
+ contents: read
+
+jobs:
+ build:
+ name: Build
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout do repositório
+ uses: actions/checkout@v3
+ with:
+ submodules: recursive
+
+ - name: Configura python
+ uses: actions/setup-python@v4
+ with:
+ python-version: "2.7"
+ cache: pip
+
+ - name: Instala dependências
+ run: pip install -r requirements.txt
+
+ - name: Build do site
+ run: make publish
diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml
new file mode 100644
index 000000000..a7b555e78
--- /dev/null
+++ b/.github/workflows/github-pages.yml
@@ -0,0 +1,55 @@
+name: GitHub Pages
+
+on:
+ push:
+ branches:
+ - pelican
+
+permissions:
+ contents: read
+
+concurrency:
+ group: pages
+ cancel-in-progress: true
+
+jobs:
+ build:
+ name: Build
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout do repositório
+ uses: actions/checkout@v3
+ with:
+ submodules: recursive
+
+ - name: Configura python
+ uses: actions/setup-python@v4
+ with:
+ python-version: "2.7"
+ cache: pip
+
+ - name: Instala dependências
+ run: pip install -r requirements.txt
+
+ - name: Build do site
+ run: make publish
+
+ - name: Upload do site
+ uses: actions/upload-pages-artifact@v1
+ with:
+ path: output/
+
+ deploy:
+ name: Deploy
+ needs: build
+ permissions:
+ pages: write
+ id-token: write
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - name: Deploy no GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v1
diff --git a/.gitignore b/.gitignore
index e1673531f..14885e0ab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,23 @@ output/
# OS
Thumbs.db
+
+# Arquivos temporarios do Gedit
+*~
+
+# Cache files
+cache/*
+
+# Pycharm
+.idea
+
+# Virtualenv
+venv/
+
+MacOS Files
+.DS_Store
+
+# alias
+src
+
+.env
diff --git a/.travis.yml b/.travis.yml
index 82ab1cde9..87856a85d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,7 @@ language: python
python:
- 2.7
install:
-- pip install -r requirements.txt --use-mirrors
+- pip install -r requirements.txt
script:
- make publish
notifications:
@@ -16,5 +16,6 @@ env:
global:
- secure: fjtVZfyJ7SOg1Glo3T6zYyyzf3OcBiIKY3Lx2abwNt3jIrZalTAc/T2EpeRF+0cLp4NAhHGxylD/pQahWYAw30+SBD5wghd9iBpbJmTxyWHaM9zdLHiU5M3fuSeRwRdIVSG46tLv5wulCIBDubw/b32Ewc4VfEqHtjIahBuGvr4=
before_install:
-- git submodule update --init --recursive
+- git submodule update --remote --merge
after_success: bash deploy.sh
+
diff --git a/README.md b/README.md
index 1f25bc75a..74d1c4814 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
[pythonclub.com.br][0]
======================
+Duvidas sobre este projeto, deixe sua mensagem em [](https://gitter.im/pythonclub/pythonclub.github.io?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Blog colaborativo sobre tecnologias que envolvam a linguagem Python
@@ -10,9 +11,9 @@ Como Contribuir
* Faça um fork desse repositório, clicando no botão [![Fork][14]][15], na parte superior direita da pagina do Github
* Clone seu fork:
- ``git clone --recursive https://github.com/SEU_USUARIO_DO_GITHUB/pythonclub.github.io.git``
+ ``git clone --depth 1 --recursive https://github.com/SEU_USUARIO_DO_GITHUB/pythonclub.github.io.git``
-* Instale os requirements ``pip install -r requirements.txt``
+* Instale os requirements ``pip install -r requirements.txt`` - se você não tiver o pip instalado, instale-o: https://pip.pypa.io/en/latest/installing.html#install-pip
* Todas as publicações ficam na pasta ``content``, os textos podem ser escritos
no formato **[Markdown][4]** ou **[reStructuredText][5]**, fique a vontade
para usar o que você sentir mais afinidade, veja alguns **[exemplos][6]**.
@@ -79,17 +80,6 @@ Para finalizar o servidor use:
``./develop_server.sh stop``
-Futuras Publicações
--------------------
-
-Alguns dos contribuidores criaram o compromisso de publicar alguns artigos.
-
-Foi estabelecido um prazo maximo para a entrega dos artigos com o intuito de que o contribuidor realmente publique o artigo com o conteudo que ele mesmo definiu.
-
-Você pode ver a lista contendo os nomes dos artigos nesta planilha no [Google Drive][7].
-
-Quando tiver um assunto e uma data de entrega, adicione na planinha, ao finalizar o seu artigo, envie o pull request e atualize a planilha marcando que sua publicação já foi entregue.
-
[0]: http://pythonclub.com.br/
[1]: https://pages.github.com/
diff --git a/content/a-armadilha-dos-argumentos-com-valores-padrao.md b/content/a-armadilha-dos-argumentos-com-valores-padrao.md
new file mode 100644
index 000000000..7408ef65b
--- /dev/null
+++ b/content/a-armadilha-dos-argumentos-com-valores-padrao.md
@@ -0,0 +1,197 @@
+title: A armadilha dos argumentos com valores padrão
+Slug: a-armadilha-dos-argumentos-com-valores-padrao
+Date: 2015-06-07 11:00
+Tags: python,mutable,function,class,anti-pattern
+Author: Diego Garcia
+Email: drgarcia1986@gmail.com
+Github: drgarcia1986
+Site: http://www.codeforcloud.info
+Twitter: drgarcia1986
+Linkedin: drgarcia1986
+Category: anti-patterns
+
+
+
+
+
+
+Algo muito comum em várias linguagens de programação é a possibilidade de definir _valores default_ (valores padrão) para argumentos de funções e métodos, tornando a utilização desses opcional.
+Isso é ótimo, principalmente para manter retrocompatibilidade, porém, o python possui uma pequena armadilha que caso passe despercebida, pode causar sérios problemas, muitas vezes difíceis de serem detectados.
+Essa armadilha ocorre quando usamos valores de tipos `mutáveis` como valor default de argumentos.
+
+
+
+### O que são tipos mutáveis e imutáveis?
+Segundo a [documentação oficial do python](https://docs.python.org/3.4/reference/datamodel.html), o valor de alguns objetos pode mudar, esses objetos que podem ter seu valor alterado após serem criados são chamados de mutáveis, enquanto que os objetos que não podem ter seus valores alterados após serem criados são chamados de imutáveis (simples assim).
+
+* **Tipos mutáveis**:
+
+Listas, Dicionários e tipos definidos pelo usuário.
+
+* **Tipos imutáveis**:
+
+Numeros, Strings e Tuplas.
+
+> Apesar de serem imutáveis, a utilização de um valor mutável (uma lista por exemplo) dentro de uma tupla, pode causar o efeito _[tuplas mutáveis](http://pythonclub.com.br/tuplas-mutantes-em-python.html)_, onde visualmente o valor da tupla é alterado, mas por trás dos panos o valor da tupla não muda, o que muda é o valor do objeto pelo qual a tupla está se referenciando.
+
+### A armadilha
+Como disse no começo desse blogpost, é muito comum a utilização de valores default em agurmentos de funções e métodos, por essa razão, nos sentimos seguros em fazer algo desse tipo:
+
+```python
+def my_function(my_list=[]):
+ my_list.append(1)
+ print(my_list)
+```
+
+Porém, levando esse exemplo em consideração, o que irá acontecer se invocarmos essa função 3 vezes?
+
+```python
+>>> my_function()
+[1]
+>>> my_function()
+[1, 1]
+>>> my_function()
+[1, 1, 1]
+```
+Sim, o valor do argumento `my_list` mudou em cada vez que executamos a função sem passar algum valor para ele.
+
+### Por que isso acontece?
+Isso acontece porque o python processa os valores default de cada argumentos de uma função (ou método) quando essa for definida, após esse processamento o valor é atribuido ao objeto da função.
+Ou seja, por questões de optimização, seguindo nosso exemplo, o python não cria uma lista vazia para o argumento `my_list` a cada vez que a função `my_function` for invocada, ele reaproveita uma lista que foi criada no momento em que essa função foi importada.
+
+```python
+>>> my_function.func_defaults
+([],)
+>>> id(my_function.func_defaults[0])
+140634243738080
+>>> my_function()
+[1]
+>>> my_function.func_defaults
+([1],)
+>>> id(my_function.func_defaults[0])
+140634243738080
+>>> my_function()
+[1, 1]
+>>> my_function.func_defaults
+([1, 1],)
+>>> id(my_function.func_defaults[0])
+140634243738080
+```
+> Note que a identificação do argumento (no caso `my_list`) não muda, mesmo executando a função várias vezes.
+
+Outro exemplo seria utilizar o resultado de funções como valores default de argumentos, por exemplo, uma função com um argumento que recebe como default o valor de `datetime.now()`.
+
+```python
+def what_time_is_it(dt=datetime.now()):
+ print(dt.strftime('%d/%m/%Y %H:%M:%S'))
+```
+O valor do argumento `dt` sempre será o _datetime_ do momento em que o python carregou a função e não o _datetime_ de quando a função foi invocada.
+
+```python
+>>> what_time_is_it()
+07/06/2015 08:43:55
+>>> time.sleep(60)
+>>> what_time_is_it()
+07/06/2015 08:43:55
+```
+
+### Isso também acontece com classes?
+Sim e de uma forma ainda mais perigosa.
+
+```python
+class ListNumbers():
+ def __init__(self, numbers=[]):
+ self.numbers = numbers
+
+ def add_number(self, number):
+ self.numbers.append(number)
+
+ def show_numbers(self):
+ print(numbers)
+```
+Assim como no caso das funções, no exemplo acima o argumento `numbers` é definido no momento em que o python importa a classe, ou seja, a cada nova instância da classe `ListNumbers`, será aproveitada a mesma lista no argumento `numbers`.
+
+```python
+>>> list1 = ListNumbers()
+>>> list2 = ListNumbers()
+>>> list1.show_numbers()
+[]
+>>> list2.show_numbers()
+[]
+>>> list2.add_number(1)
+>>> list1.show_numbers()
+[1]
+>>> list2.show_numbers()
+[1]
+>>> list1.numbers is list2.numbers
+True
+```
+
+### Por que isso não acontece com Strings?
+Porque strings são `imutáveis`, o que significa que a cada alteração de valor em uma variavel que armazena uma strings, o python cria uma nova instância para essa variável.
+
+```python
+>>> a = 'foo'
+>>> id(a)
+140398402003832
+>>> a = 'bar'
+>>> id(a)
+140398402003872 # o penúltimo número muda :)
+```
+
+Em argumentos com valores default, não é diferente.
+
+```
+def my_function(my_str='abc'):
+ my_str += 'd'
+ print(my_str)
+```
+No exemplo acima, sempre que for executado o `inplace add` (`+=`) será criada outra váriavel para `my_str` sem alterar o valor default do argumento.
+
+```python
+>>> my_function()
+abcd
+>>> my_function.func_defaults
+('abc',)
+>>> my_function()
+abcd
+>>> my_function.func_defaults
+('abc',)
+```
+
+### Como se proteger?
+A maneira mais simples de evitar esse tipo de surpresa é utilizar um [valor sentinela](http://en.wikipedia.org/wiki/Sentinel_value) como por exemplo `None`, nos argumentos opcionais que esperam tipos mutáveis:
+
+```python
+def my_function(my_list=None):
+ if my_list is None:
+ my_list = []
+ my_list.append(1)
+ print(my_list)
+```
+
+Ou, para deixar o código ainda mais elegante, podemos simplificar a condicional com um simples `or`:
+
+```python
+def my_function(my_list=None):
+ my_list = my_list or []
+ my_list.append(1)
+ print(my_list)
+```
+> Obrigado [Bruno Rocha](http://pythonclub.com.br/author/bruno-cezar-rocha.html) pela sugestão.
+
+Pronto, sem surpresas e sem armadilhas :).
+
+```python
+>>> my_function()
+[1]
+>>> my_function()
+[1]
+>>> my_function()
+[1]
+```
+
+### Referências
+
+* [Fluent Python (Mutable types as parameter defaults: bad idea)](http://shop.oreilly.com/product/0636920032519.do)
+* [Python Anti-Patterns (Using a mutable default value as an argument)](http://docs.quantifiedcode.com/python-anti-patterns/correctness/mutable_default_value_as_argument.html)
diff --git a/content/abrangencia-de-listas-e-dicionarios.md b/content/abrangencia-de-listas-e-dicionarios.md
new file mode 100644
index 000000000..d025ac785
--- /dev/null
+++ b/content/abrangencia-de-listas-e-dicionarios.md
@@ -0,0 +1,131 @@
+Title: Abrangência de Listas e Dicionários
+Slug: abrangencia-de-listas-e-dicionarios-com-python
+Date: 2017-01-16 10:37:39
+Category: Python
+Tags: python,tutorial,list comprehensions
+Author: Michell Stuttgart
+Email: michellstut@gmail.com
+Github: mstuttgart
+Linkedin: mstuttgart
+Site: https://mstuttgart.github.io
+
+A utilização de listas em Python é algo trivial. A facilidade provida pela linguagem aliada a simplicidade da estrutura de dados *list* a torna, ao lado dos dicionários *dict*, uma das estrutura de dados mais utilizadas em Python. Aqui neste tutorial irei compartilhar algo que aprendi trabalhando com listas e dicionário em Python, mais especificamente no que diz respeito a *abrangência* de listas (e dicionários).
+
+## Abrangência de listas
+
+A abrangência de listas, ou do inglês *list comprehensions*, é um termo utilizado para descrever uma sintaxe compacta que o Python nos oferece para criamos uma lista baseada em outra lista. Pareceu confuso? Ok, vamos aos exemplos!
+
+### Exemplo 1
+Vamos supor que temos a seguinte lista de valores:
+
+```python
+valores = [1, 2, 3, 4, 5]
+```
+Queremos gerar uma outra lista contendo o dobro de cada um desses números, ou seja,
+
+```python
+[2, 4, 6, 8, 10]
+```
+Inicialmente, podemos montar o seguinte código como solução:
+
+```python
+# Recebe o nosso resultado
+valores_dobro = []
+
+for val in valores:
+ valores_dobro.append(val * 2)
+
+print(valores_dobro)
+
+>>>
+[2, 4, 6, 8, 10]
+
+```
+
+A solução acima é uma solução simples e resolve nosso problema, entretanto para algo tão simples precisamos de 4 linhas de código. Este exemplo é uma situação onde a *abrangência de lista* pode ser útil. Podemos compactar a criação da lista `valores_dobro` da seguinte maneira:
+
+```python
+valores_dobro = [valor*2 for valor in valores]
+```
+Bacana não? O exemplo seguinte podemos incrementar mais o exemplo acima.
+
+### Exemplo 2
+
+Vamos supor que desejamos criar uma lista onde apenas os valores pares (resto da divisão por 2 é zero) serão multiplicados por 2. Abaixo temos a nossa lista de valores:
+
+```python
+valores = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+```
+
+Assim como no exemplo anterior, podemos resolver utilizando um algoritmo básico.
+
+```python
+# Lista que recebera o nosso resultado
+valores_dobro = []
+
+for valor in valores:
+ if valor % 2 == 0:
+ valores_dobro.append(valor * 2)
+
+print(valores_dobro)
+
+>>>
+[4, 8, 12, 16, 20]
+
+```
+Podemos também resolver o mesmo problema utilizando as funções nativas *map* e *filter*:
+
+```python
+valores_dobro = map(lambda valor: valor * 2, filter(lambda valor: valor % 2 == 0, valores))
+```
+Muito mais complicada não é? Apesar de resolver nosso problema, expressões como a acima são difíceis de ler e até mesmo de escrever. Em casos como esse, podemos novamente compactar nosso algoritmo utilizando a *abrangência de lista*.
+
+```python
+valores_dobro = [valor * 2 for valor in valores if valor % 2 == 0]
+```
+Muito mais simples, não? Vamos para o próximo exemplo.
+
+### Exemplo 3
+
+De maneira semelhante a lista, nós também podemos aplicar a abrangência em lista e dicionários. Segue um exemplo onde temos o seguinte dicionário:
+
+```python
+ dicionario = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
+```
+
+Vamos criar um segundo dicionário contendo apenas as chaves que são consoantes, ou seja, `b`, `c`, `d` e `f`, sendo que o valor para cada uma dessas chaves deve ser o dobro do valor armazenado na respectiva chave do dicionário original. Complicado? Em outras palavras, o novo dicionário deve ficar assim:
+
+```python
+ novo_dicionario = {'b': 4, 'c': 6, 'd': 8, 'f': 12}
+```
+
+Utilizando um algoritmo genérico, podemos resolver o problema da seguinte maneira:
+
+```python
+novo_dicionario = {}
+
+for chave, valor in dicionario:
+ if chave in ['b', 'c', 'd', 'f']:
+ novo_dicionario[chave] = 2 * valor
+
+print(novo_dicionario)
+
+>>
+{'b': 4, 'c': 6, 'd': 8, 'f': 12}
+
+```
+Aplicando agora a abrangência, conseguimos compactar o código acima de maneira interessante:
+
+```python
+novo_dicionario = {chave: 2 * valor for chave, valor in dicionario.items() if chave in ['b', 'c', 'd', 'f']}
+```
+
+## Conclusão
+
+Chegamos ao final de mais um tutorial! Sempre temos de ter em mente que tão importante quanto escrever um código que funciona, é mantê-lo (seja por você ou por outro programador). Neste ponto, a abrangência de lista (e outras estruturas de dados) nos ajudam a escrever um código claro e fácil de dar manutenção.
+
+Até o próximo tutorial pessoal!
+
+## Referências
+
+* [Python eficaz: 59 maneiras de programar melhor em Python; Slatkin, Brett; Novatec Editora, 2016.](https://novatec.com.br/livros/python-eficaz/)
diff --git a/content/algoritmos_ordenacao_usando_python.rst b/content/algoritmos_ordenacao_usando_python.rst
new file mode 100644
index 000000000..71c714302
--- /dev/null
+++ b/content/algoritmos_ordenacao_usando_python.rst
@@ -0,0 +1,134 @@
+Algoritmos de Ordenação
+########################
+
+:date: 2018-11-29 13:10
+:tags: python, algoritmos
+:category: Python
+:slug: algoritmos-ordenacao
+:author: Lucas Magnum
+:email: lucasmagnumlopes@gmail.com
+:github: lucasmagnum
+:linkedin: lucasmagnum
+
+Fala pessoal, tudo bom?
+
+Nos vídeos abaixo, vamos aprender como implementar alguns dos algoritmos de ordenação usando Python.
+
+
+Bubble Sort
+===========
+
+Como o algoritmo funciona: Como implementar o algoritmo usando Python: `https://www.youtube.com/watch?v=Doy64STkwlI `_.
+
+
+.. youtube:: Doy64STkwlI
+
+Como implementar o algoritmo usando Python: `https://www.youtube.com/watch?v=B0DFF0fE4rk `_.
+
+.. youtube:: B0DFF0fE4rk
+
+Código do algoritmo
+
+.. code-block:: python
+
+ def sort(array):
+
+ for final in range(len(array), 0, -1):
+ exchanging = False
+
+ for current in range(0, final - 1):
+ if array[current] > array[current + 1]:
+ array[current + 1], array[current] = array[current], array[current + 1]
+ exchanging = True
+
+ if not exchanging:
+ break
+
+
+Selection Sort
+==============
+
+Como o algoritmo funciona: Como implementar o algoritmo usando Python: `https://www.youtube.com/watch?v=vHxtP9BC-AA `_.
+
+.. youtube:: vHxtP9BC-AA
+
+Como implementar o algoritmo usando Python: `https://www.youtube.com/watch?v=0ORfCwwhF_I `_.
+
+.. youtube:: 0ORfCwwhF_I
+
+Código do algoritmo
+
+.. code-block:: python
+
+ def sort(array):
+ for index in range(0, len(array)):
+ min_index = index
+
+ for right in range(index + 1, len(array)):
+ if array[right] < array[min_index]:
+ min_index = right
+
+ array[index], array[min_index] = array[min_index], array[index]
+
+
+Insertion Sort
+==============
+
+Como o algoritmo funciona: Como implementar o algoritmo usando Python: `https://www.youtube.com/watch?v=O_E-Lj5HuRU `_.
+
+.. youtube:: O_E-Lj5HuRU
+
+Como implementar o algoritmo usando Python: `https://www.youtube.com/watch?v=Sy_Z1pqMgko `_.
+
+.. youtube:: Sy_Z1pqMgko
+
+Código do algoritmo
+
+.. code-block:: python
+
+ def sort(array):
+ for p in range(0, len(array)):
+ current_element = array[p]
+
+ while p > 0 and array[p - 1] > current_element:
+ array[p] = array[p - 1]
+ p -= 1
+
+ array[p] = current_element
+
+
+Merge Sort
+==============
+
+Como o algoritmo funciona: Como implementar o algoritmo usando Python: `https://www.youtube.com/watch?v=Lnww0ibU0XM `_.
+
+.. youtube:: Lnww0ibU0XM
+
+
+Como implementar o algoritmo usando Python - Parte I: `https://www.youtube.com/watch?v=cXJHETlYyVk `_.
+
+.. youtube:: cXJHETlYyVk
+
+Código do algoritmo
+
+.. code-block:: python
+
+ def sort(array):
+ sort_half(array, 0, len(array) - 1)
+
+
+ def sort_half(array, start, end):
+ if start >= end:
+ return
+
+ middle = (start + end) // 2
+
+ sort_half(array, start, middle)
+ sort_half(array, middle + 1, end)
+
+ merge(array, start, end)
+
+
+ def merge(array, start, end):
+ array[start: end + 1] = sorted(array[start: end + 1])
+
diff --git a/content/aprendendo_e_ensinando_python.md b/content/aprendendo_e_ensinando_python.md
index 04836f15c..650ebb6e5 100644
--- a/content/aprendendo_e_ensinando_python.md
+++ b/content/aprendendo_e_ensinando_python.md
@@ -39,7 +39,7 @@ a semana e aos sábados e domingos eu produzia uma nova aula com novos desafios
e exercícios para quem estava acompanhando. Algo muito legal (ou nem tanto) que
eu fiz na época foi pedir para o pessoal me mandar os scripts que faziam, assim
eu podia corrigir e ajudar ainda mais. Talvez essa ideia não tenha sido tão boa
-pois, não tenho os números agora, mas na época das publicações eu chagava a
+pois, não tenho os números agora, mas na época das publicações eu chegava a
receber mais de 100 e-mails por semana com scripts de pessoas de todas as
partes do Brasil (Alguns de fora). Imagina o trabalhão que dava para conseguir
acompanhar a todos!
diff --git a/content/bot-telegram-e-web-scraping-parte1.md b/content/bot-telegram-e-web-scraping-parte1.md
new file mode 100644
index 000000000..a4bb33885
--- /dev/null
+++ b/content/bot-telegram-e-web-scraping-parte1.md
@@ -0,0 +1,114 @@
+Title: Bot telegram mais web scraping - parte 1
+Slug: bot-telegram-mais-web-scraping-parte-1
+Date: 2016-10-23 20:30
+Tags: python,blog,tutorial,aulas
+Author: Pedro Souza.
+About_author: Just another Programmer and Security Researcher, just a noob.,
+Email: souza.vipedro@gmail.com
+Github: Pedro-Souza
+Facebook: https://www.facebook.com/DeveloperPS
+Category: Python, Bot, Telegram, Scraping
+
+
+Irei separa o artigo em 2 partes para não ficar extenso. Nessa primeira
+parte irei falar um pouco como criar um bot no telegram e como
+programa-lo para nos responder.
+
+1 - Parte 1 - [**Bot simples.**]({filename}bot-telegram-e-web-scraping-parte1.md) (você está aqui)
+
+2 - Parte 2 - **Bot e Web Scraping**
+
+Primeiro de tudo precisamos cria o bot, para isso usamos o próprio bot
+do telegram que faz isso para gente. Para isso bastar iniciar uma
+conversa com o [@BotFather](https://web.telegram.org/#/im?p=@BotFather), ele irá nós da algumas
+opções:
+
+```
+/newbot - create a new bot
+/token - generate authorization token
+/revoke - revoke bot access token
+/setname - change a bot's name
+/setdescription - change bot description
+/setabouttext - change bot about info
+/setuserpic - change bot profile photo
+/setinline - change inline settings
+/setinlinegeo - toggle inline location requests
+/setinlinefeedback - change inline feedback settings
+/setcommands - change bot commands list
+/setjoingroups - can your bot be added to groups?
+/setprivacy - what messages does your bot see in groups?
+/deletebot - delete a bot
+/newgame - create a new game
+/listgames - get a list of your games
+/editgame - edit a game
+/deletegame - delete an existing game
+/cancel - cancel the current operation
+
+```
+
+As que nós interessa por enquanto são:
+
+```
+/newbot - Cria um novo bot.
+/setdescription - Adiciona uma descrição ao nosso bot.
+/setuserpic - Adiciona uma imagem ao nosso bot.
+```
+
+Feito isso agora temos um token, que iremos usar para dar funções e vida
+ao bot. Para isso iremos usar a lib telegram-bot, ela irá facilitar a
+nosso vida, assim não iremos precisar mexer diretamente com a API do
+telegram.
+
+### Instalando telegram-bot utilizando o pip
+
+```bash
+pip install python-telegram-bot
+```
+
+Agora com a biblioteca instalada iremos programar um mini bot para nós falar as horas.
+
+```python
+
+#!/usr/bin/env python3
+# -*- coding:utf-8 -*-
+
+from telegram.ext import Updater, CommandHandler
+from time import strftime
+
+up = Updater('Insira o token aqui.')
+
+
+def Horas(bot, update):
+
+ msg = "Olá {user_name} agora são: "
+ msg += strftime('%H:%M:%S')
+
+ bot.send_message(chat_id=update.message.chat_id,
+ text=msg.format(
+ user_name=update.message.from_user.first_name))
+
+
+up.dispatcher.add_handler(CommandHandler('horas', Horas))
+up.start_polling()
+
+```
+
+### Entendendo o código.
+
+1 - Importamos tudo que iremos utilizar.
+2 - Informamos o token do nosso bot.
+3 - Criamos uma função que pega a horas com strftime e responde no chat.
+4 - Criamos um comando para o nosso bot, no caso o /horas.
+5 - Startamos o bot.
+
+Feito isso quando mandar um /horas para o bot ele irá nos responder com: "Olá SeuNome agora são
+Horas."
+
+Caso você queira adicionar mais funções ao bot,
+[aqui](http://python-telegram-bot.readthedocs.io/en/latest/) está a documentação da biblioteca.
+
+Na próxima parte iremos escolher alguns site que fale sobre Python e fazer Scraping nele, assim
+sempre que ele tiver uma nova postagem nosso bot vai nós enviar uma mensagem informando.
+
+
+
diff --git a/content/bottle-framework-full-stack-sem-ser-o-django.md b/content/bottle-framework-full-stack-sem-ser-o-django.md
new file mode 100644
index 000000000..b33d48852
--- /dev/null
+++ b/content/bottle-framework-full-stack-sem-ser-o-django.md
@@ -0,0 +1,146 @@
+Title: Bottle Framework full stack sem Django
+Slug: bottle-framework-full-stack-sem-django
+Date: 2014-12-03 19:40
+Tags: bottle,python
+Author: Eric Hideki
+Email: eric8197@gmail.com
+Github: erichideki
+Site: http://ericstk.wordpress.com
+Twitter: erichideki
+Category: begginers, bottle, tutorial
+
+#Esse artigo foi originalmente traduzido de:
+
+[http://www.avelino.xxx/2014/12/bottle-full-stack-without-django]
+
+Este artigo é baseado em uma palestra que apresentei aqui no Brasil, seguem os [slides](https://speakerdeck.com/avelino/bottle-o-full-stack-sem-django)!
+
+
+
+Bottle é um micro framework web compatível com WSGI, depende apenas da biblioteca padrão do Python, sendo compatível com Python 2.6, 2.7, 3.2, 3.3 e 3.4, [sendo um arquivo único](https://github.com/defnull/bottle/blob/master/bottle.py). Ele foi criado pelo Marcel Hellkamp ([@defnull](https://github.com/defnull)) e mantido pela [comunidade](https://github.com/orgs/bottlepy/people) que mantém esse framework.
+
+[Django](https://www.djangoproject.com/) é um framework para rápido desenvolvimento na web, escrito em Python, no qual usa o padrão MTV(model-template-view), sendo pragmático. Foi originalmente criado como um sistema de gerenciamento de um site jornalístico na cidade de Lawrence, Kansas. Se tornou um projeto open-source e foi publicado sobre a licença BSD em 2005. O nome Django foi inspirado pelo músido de jazz Django Reinhardt. Django se tornou muito conhecido pelas suas baterias inclusas, i.e diversas bibliotecas distribuídas que se juntaram ao centro do framework para simplificar o trabalho (chamado "Full stack").
+
+Pragmatismo é o que contém a prática, considerações realistas, com objetivos bem definidos. Ser pragmático é ser prático tendo objetivos definidos. Em outras palavras, o time que desenvolve o Django toma algumas modelagens de arquitetura e quem usa Django segue essa arquitetura sem ser capaz de mudá-la facilmente.
+
+Isto é bom para um framework web que tem baterias inclusas? Depente, se você usa tudo o que o framework oferece, sim, mas nem todos os designs de aplicações são iguais.
+
+Muitos projetos não usam 80% do que Django oferece, nesses casos em que não usam mais que 50%, o custo que pagamos ao oferecer o Django a alguém é alto, já que temos definido a arquitetura, ou seja, perde-se a performance porque o Django tem diversos módulos que não serão usados e obrigatoriamente subirá alguns módulos que não iremos usar. Quando nós usamos um micro framework, fazemos toda a arquitetura da aplicação, então não temos previamente preparado a arquitetura para desenvolver o necessário, dedicando o tempo do time para definir a arquitetura da aplicação.
+
+Todos os pacotes que nós temos na biblioteca padrão do Python/Django podem ser substituídas usando um micro framework!
+
+* ORM - [SQLAlchemy](http://www.sqlalchemy.org/) to [bottle-sqlalchemy](https://github.com/iurisilvio/bottle-sqlalchemy)
+* Forms - [WTForms](https://wtforms.readthedocs.org/en/latest/)
+* Template Engine - [Jinja2](http://jinja.pocoo.org/docs/dev/), [mako](http://www.makotemplates.org/), etc
+* Migration - [Alembic](http://alembic.readthedocs.org/en/latest/)
+
+
+## SQLAlchemy
+
+O SQLAlchemy existe antes do Django, [sim, antes do Django](https://github.com/zzzeek/sqlalchemy/commit/ec052c6a1f1fb0236bd367c510d82f076cb67bc9) e desde 2005 temos um time focado no desenvolvimento da ORM, ao contrário do Django que dispṍe tempo cuidando do framework web + ORM (Eu acredito que eu não preciso falar com um desenvolvedor focado render mais do que um desenvolvedor não focado).
+
+Estrutura de um modelo:
+
+```python
+class Entity(Base):
+ __tablename__ = 'entity'
+ id = Column(Integer, Sequence('id_seq'), primary_key=True)
+ name = Column(String(50))
+
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "" % (self.id, self.name)
+```
+
+## WTForms
+
+A solução alternativa para aqueles que não usam Django e precisam trabalhar com formulários, nós temos o WTForms, que foi criado em [2008](https://github.com/wtforms/wtforms/commit/c0998bac1a4d5cd5fdf43a825529a64e24dea9a5) e atualizado ainda hoje!
+
+Estrutura de um formulário:
+
+```python
+class UserForm(Form):
+ name = TextField(validators=[DataRequired(), Length(max=100)])
+ email = TextField(validators=[DataRequired(), Length(max=255)])
+```
+
+## Mecanismo de template
+
+Jinja2 é um moderno e contém um design de template amigável para Python, modelado após os templates do Django. É rápido, amplamente usado e seguro com a opcional área restrita de template no ambiente de desenvolvimento.
+
+Estrutura de um template:
+
+```html
+{% block title %}{% endblock %}
+
+```
+
+## Migrações
+
+A utilização do Alembic começa com a criação do ambiente de migração. Este é o diretório de arquivos que especificam para uma particular aplicação. O ambiente de migração é criado apenas uma vez, e é mantido ao longo do desenvolvimento do código da aplicação.
+
+Estrutura de uma migração:
+
+```python
+revision = '1975ea83b712'
+down_revision = None
+
+from alembic import op
+import sqlalchemy as sa
+
+def upgrade():
+ pass
+
+def downgrade():
+ pass
+```
+
+Como criar a evolução e o rebaixamento:
+
+```python
+def upgrade():
+ op.create_table(
+ 'account',
+ sa.Column('id', sa.Integer, primary_key=True),
+ sa.Column('name', sa.String(50), nullable=False),
+ sa.Column('description', sa.Unicode(200)),
+ )
+
+def downgrade():
+ op.drop_table('account')
+```
+
+Estrutura de alteração de tabela:
+
+```python
+"""
+$ alembic revision -m "Add a column"
+"""
+
+revision = 'ae1027a6acf'
+down_revision = '1975ea83b712'
+
+from alembic import op
+import sqlalchemy as sa
+
+def upgrade():
+ op.add_column('account', sa.Column('last_transaction_date', sa.DateTime))
+
+def downgrade():
+ op.drop_column('account', 'last_transaction_date')
+```
+
+## Conclusão
+
+Exatamente o que você vê, tudo o que o Django contém temos fora do conjunto do Django. Eu não escreveria esse artigo para falar mal do Django, e sim mostrar que existem outras soluções para desenvolvimento full stack. Muitas pessoas usam o Django mas não entendem o ambiente Python, hoje o Django traz muitas coisas preparadas que fazem alguns desenvolvedores serem preguiçosos e não adquirir experiência em arquitetura de software.
+
+Venha ajudar o Bottle, somos uma comunidade em crescimento, para contribuir com o código do Bottle, olhe essa issue que nós abrimos. Em caso de dúvidas, nós temos uma lista de e-mail e um canal IRC.
+
+[Se envolva!](http://bottlepy.org/docs/dev/development.html#get-involved)
+[http://www.avelino.xxx/2014/12/bottle-full-stack-without-django]:http://www.avelino.xxx/2014/12/bottle-full-stack-without-django
diff --git a/content/changing-tuples-pt_BR.rst b/content/changing-tuples-pt_BR.rst
new file mode 100644
index 000000000..f8f5d567e
--- /dev/null
+++ b/content/changing-tuples-pt_BR.rst
@@ -0,0 +1,155 @@
+Tuplas mutantes em Python
+=========================
+
+
+:date: 2015-02-24 10:17
+:tags: python,tuplas
+:category: python-por-dentro
+:slug: tuplas-mutantes-em-python
+:author: Luciano Ramalho
+:email: luciano@ramalho.org
+:about_author: Ramalho é autor do livro Fluent Python (O'Reilly, 2014) e sócio/professor em Python.pro.br, oferecendo cursos in-company e também online. Foi diretor técnico do Brasil Online, primeiro portal da Abril na Web, lançado uma semana antes do UOL. Depois liderou times para os sites IDG Now, BOL, UOL, AOL Brasil e outros, usando Python desde 1998. Palestrante em eventos internacionais como PyCon US, OSCON e FISL, ajudou a criar a Associação Python Brasil e foi seu presidente. É membro da Python Software Foundation e fundador do Garoa Hacker Clube, o primeiro hackerspace do Brasil.
+:github: ramalho
+:site: https://adm.python.pro.br/
+:twitter: ramalhoorg
+:linkedin: lucianoramalho
+
+
+Por Luciano Ramalho, autor do livro `Fluent Python`_ (O'Reilly, 2014)
+
+ See also the original article in English: http://radar.oreilly.com/2014/10/python-tuples-immutable-but-potentially-changing.html
+
+
+Tuplas em Python têm uma característica surpreendente: elas são imutáveis, mas seus valores podem mudar. Isso pode acontecer quando uma ``tuple`` contém uma referência para qualquer objeto mutável, como uma ``list``. Se você precisar explicar isso a um colega iniciando com Python, um bom começo é destruir o senso comum sobre variáveis serem como caixas em que armazenamos dados.
+
+Em 1997 participei de um curso de verão sobre Java no MIT. A professora, Lynn Andrea Stein - uma premiada educadora de ciência da computação - enfatizou que a habitual metáfora de "variáveis como caixas" acaba atrapalhando o entendimento sobre variáveis de referência em linguagens OO. Variáveis em Python são como variáveis de referência em Java, portanto é melhor pensar nelas como etiquetas afixadas em objetos.
+
+Eis um exemplo inspirado no livro *Alice Através do Espelho e O Que Ela Encontrou Por Lá*, de Lewis Carroll.
+
+.. image:: {filename}images/ramalho/Tweedledum-Tweedledee_500x390.png
+ :alt: imagem Alice Através do Espelho e O Que Ela Encontrou Por Lá
+
+Tweedledum e Tweedledee são gêmeos. Do livro: “Alice soube no mesmo instante qual era qual porque um deles tinha 'DUM' bordado na gola e o outro, 'DEE'”.
+
+.. image:: {filename}images/ramalho/diagrams/dum-dee.png
+ :alt: exemplo 1
+
+Vamos representá-los como tuplas contendo a data de nascimento e uma lista de suas habilidades:
+
+.. code-block:: python
+
+ >>> dum = ('1861-10-23', ['poesia', 'fingir-luta'])
+ >>> dee = ('1861-10-23', ['poesia', 'fingir-luta'])
+ >>> dum == dee
+ True
+ >>> dum is dee
+ False
+ >>> id(dum), id(dee)
+ (4313018120, 4312991048)
+
+
+É claro que ``dum`` e ``dee`` referem-se a objetos que são iguais, mas que não são o mesmo objeto. Eles têm identidades diferentes.
+
+Agora, depois dos eventos testemunhados por Alice, Tweedledum decidiu ser um rapper, adotando o nome artístico T-Doom. Podemos expressar isso em Python dessa forma:
+
+.. code-block:: python
+
+ >>> t_doom = dum
+ >>> t_doom
+ ('1861-10-23', ['poesia', 'fingir-luta'])
+ >>> t_doom == dum
+ True
+ >>> t_doom is dum
+ True
+
+Então, ``t_doom`` e ``dum`` são iguais - mas Alice acharia tolice dizer isso, porque ``t_doom`` e ``dum`` referem-se à mesma pessoa: ``t_doom is dum``.
+
+.. image:: {filename}images/ramalho/diagrams/dum-t_doom-dee.png
+ :alt: exemplo 2
+
+Os nomes ``t_doom`` e ``dum`` são apelidos. O termo em inglês "alias" significa exatamente apelido. Gosto que os documentos oficiais do Python muitas vezes referem-se a variáveis como “nomes“. Variáveis são nomes que damos a objetos. Nomes alternativos são apelidos. Isso ajuda a tirar da nossa mente a ideia de que variáveis são como caixas. Qualquer um que pense em variáveis como caixas não consegue explicar o que vem a seguir.
+
+Depois de muito praticar, T-Doom agora é um rapper experiente. Codificando, foi isso o que aconteceu:
+
+.. code-block:: python
+
+ >>> skills = t_doom[1]
+ >>> skills.append('rap')
+ >>> t_doom
+ ('1861-10-23', ['poesia', 'fingir-luta 'rap'])
+ >>> dum
+ ('1861-10-23', ['poesia', 'fingir-luta 'rap'])
+
+T-Doom conquistou a habilidade ``rap``, e também Tweedledum — óbvio, pois eles são um e o mesmo. Se ``t_doom`` fosse uma caixa contendo dados do tipo ``str`` e ``list``, como você poderia explicar que uma inclusão à lista ``t_doom`` também altera a lista na caixa ``dum``? Contudo, é perfeitamente plausível se você entende variáveis como etiquetas.
+
+A analogia da etiqueta é muito melhor porque apelidos são explicados mais facilmente como um objeto com duas ou mais etiquetas. No exemplo, ``t_doom[1]`` e ``skills`` são dois nomes dados ao mesmo objeto da lista, da mesma forma que ``dum`` e ``t_doom`` são dois nomes dados ao mesmo objeto da tupla.
+
+Abaixo está uma ilustração alternativa dos objetos que representam Tweedledum. Esta figura enfatiza o fato de a tupla armazenar referências a objetos, e não os objetos em si.
+
+.. image:: {filename}images/ramalho/diagrams/dum-skills-references.png
+ :alt: exemplo 3
+
+O que é imutável é o conteúdo físico de uma tupla, que armazena apenas referências a objetos. O valor da lista referenciado por ``dum[1]`` mudou, mas a identidade da lista referenciada pela tupla permanece a mesma. Uma tupla não tem meios de prevenir mudanças nos valores de seus itens, que são objetos independentes e podem ser encontrados através de referências fora da tupla, como o nome ``skills`` que nós usamos anteriormente. Listas e outros objetos imutáveis dentro de tuplas podem ser alterados, mas suas identidades serão sempre as mesmas.
+
+Isso enfatiza a diferença entre os conceitos de identidade e valor, descritos em *Python Language Reference*, no capítulo `Data model`_:
+
+ Cada objeto tem uma identidade, um tipo e um valor. A identidade de um objeto nunca muda, uma vez que tenha sido criado; você pode pensar como se fosse o endereço do objeto na memória. O operador ``is`` compara a identidade de dois objetos; a função ``id()`` retorna um inteiro representando a sua identidade.
+
+Após ``dum`` tornar-se um rapper, os irmãos gêmeos não são mais iguais:
+
+.. code-block:: python
+
+ >>> dum == dee
+ False
+
+Temos aqui duas tuplas que foram criadas iguais, mas agora elas são diferentes.
+
+O outro tipo interno de coleção imutável em Python, ``frozenset``, não sofre do problema de ser imutável mas com possibilidade de mudar seus valores. Isso ocorre porque um ``frozenset`` (ou um ``set`` simples, nesse sentido) pode apenas conter referências a objetos ``hashable`` (objetos que podem ser usados como chave em um dicionário), e o valor destes objetos, por definição, nunca pode mudar.
+
+Tuplas são comumente usadas como chaves para objetos ``dict``, e precisam ser ``hashable`` - assim como os elementos de um conjunto. Então, as tuplas são ``hashable`` ou não? A resposta certa é **algumas** tuplas são. O valor de uma tupla contendo um objeto mutável pode mudar, então uma tupla assim não é ``hashable``. Para ser usada como chave para um ``dict`` ou elemento de um ``set``, a tupla precisa ser constituída apenas de objetos ``hashable``. Nossas tuplas de nome ``dum`` e ``dee`` não são ``hashable`` porque cada elemento contem uma referência a uma lista, e listas não são ``hashable``.
+
+Agora vamos nos concentrar nos comandos de atribuição que são o coração de todo esse exercício.
+
+A atribuição em Python nunca copia valores. Ela apenas copia referências. Então quando escrevi ``skills = t_doom[1]``, não copiei a lista referenciada por ``t_doom[1]``, apenas copiei a referência a ela, que então usei para alterar a lista executando ``skills.append('rap')``.
+
+Voltando ao MIT, a Profa. Stein falava sobre atribuição de uma forma muito cuidadosa. Por exemplo, ao falar sobre um objeto gangorra em uma simulação, ela dizia: “A variável ``g`` é atribuída à gangorra“, mas nunca “A gangorra é atribuída à variável ``g`` “. Em se tratando de variáveis de referência, é mais coerente dizer que a variável é atribuída ao objeto, e não o contrário. Afinal, o objeto é criado antes da atribuição.
+
+Em uma atribuição como ``y = x * 10``, o lado direito é computado primeiro. Isto cria um novo objeto ou retorna um já existente. Somente após o objeto ser computado ou retornado, o nome é atribuído a ele.
+
+Eis uma prova disso. Primeiro criamos uma classe ``Gizmo``, e uma instância dela:
+
+.. code-block:: python
+
+ >>> class Gizmo:
+ ... def __init__(self):
+ ... print('Gizmo id: %d' % id(self))
+ ...
+ >>> x = Gizmo()
+ Gizmo id: 4328764080
+
+Observe que o método ``__init__`` mostra a identidade do objeto tão logo criado. Isso será importante na próxima demonstração.
+
+Agora vamos instanciar outro ``Gizmo`` e imediatamente tentar executar uma operação com ele antes de atribuir um nome ao resultado:
+
+.. code-block:: python
+
+ >>> y = Gizmo() * 10
+ Gizmo id: 4328764360
+ Traceback (most recent call last):
+ ...
+ TypeError: unsupported operand type(s) for *: 'Gizmo' and 'int'
+ >>> 'y' in globals()
+ False
+
+Este trecho mostra que o novo objeto foi instanciado (sua identidade é ``4328764360``) mas antes que o nome ``y`` possa ser criado, uma exceção ``TypeError`` abortou a atribuição. A verificação ``'y' in globals()`` prova que não existe o nome global ``y``.
+
+Para fechar: sempre leia lado direito de uma atribuição primero. Ali o objeto é computado ou retornado. Depois disso, o nome no lado esquerdo é vinculado ao objeto, como uma etiqueta afixada nele. Apenas esqueça aquela idéia de variáveis como caixas.
+
+Em relação a tuplas, certifique-se que elas apenas contenham referências a objetos imutáveis antes de tentar usá-las como chaves em um dicionário ou itens em um ``set``.
+
+ Este texto foi originalmente publicado no `blog`_ da editora O'Reilly em inglês. A tradução para o português foi feita por Paulo Henrique Rodrigues Pinheiro. O conteúto é baseado no capítulo 8 do meu livro `Fluent Python`_. Esse capítulo, intitulado *Object references, mutability and recycling* também aborda a semântica da passagem de parâmetros para funções, melhores práticas para manipulação de parâmetros mutáveis, cópias rasas (*shallow copies*) e cópias profundas (*deep copies*), e o conceito de referências fracas (*weak references*) - além de outros tópicos. O livro foca em Python 3 mas grande parte de seu conteúdo se aplica a Python 2.7, como tudo neste texto.
+
+.. _blog: http://radar.oreilly.com/2014/10/python-tuples-immutable-but-potentially-changing.html
+.. _Fluent Python: http://shop.oreilly.com/product/0636920032519.do
+.. _Data Model: https://docs.python.org/3/reference/datamodel.html#objects-values-and-types
+
diff --git a/content/class-based-views-no-django.md b/content/class-based-views-no-django.md
new file mode 100644
index 000000000..e93d5cade
--- /dev/null
+++ b/content/class-based-views-no-django.md
@@ -0,0 +1,906 @@
+Title: Class Based Views no Django
+Slug: class-based-views-django
+Date: 2015-10-26 14:50
+Tags: python,blog,tutorial,django,cbv
+Author: Raphael Passini Diniz
+Email: raphapassini@gmail.com
+Github: raphapassini
+Bitbucket: raphapassini
+Twitter: raphapassini
+Category: Django
+
+Esse tutorial tem como objetivo explicar o básico sobre Class Based Views
+no Django. Por motivos de agilidade vou usar ``CBV`` para me referir as
+``Class Based Views``.
+
+Segundo a documentação do Django sobre CBV:
+
+> CBV's permitem você estruturar as suas views e reutilizar código aproveitando
+ heranças e mixins
+
+O Django já vem CBV's genéricas que atendem as necessidades da maioria das aplicações.
+Essas views genéricas são flexiveis o suficiente para você poder adaptá-las as
+suas necessidades.
+
+Nesse tutorial eu vou falar brevemente sobre os 4 grupos de CBV's que existem
+no Django atualmente:
+
+- [Base views](#base_views)
+ - [View](#view)
+ - [TemplateView](#template_view)
+ - [RedirectView](#redirect_view)
+- [Display views](#display_views)
+ - [DetailView](#detail_view)
+ - [ListView](#list_view)
+- [Editing views](#editing_views)
+ - [Model based Views](#model_view)
+ - [CreateView, UpdateView, DeleteView](#create_update_delete_view)
+- [Date views](#date_views)
+ - [ArchiveView](#archive_view)
+ - [YearView](#year_view)
+ - [MonthView](#month_view)
+ - [WeekView](#week_view)
+ - [DayView](#day_view)
+ - [TodayView](#today_view)
+ - [DateDetailView](#date_detail_view)
+- [Conclusão](#conclusao)
+- [Referências](#ref)
+
+ Antes de começarmos a falar sobre as CBV's vamos ver como apontar uma rota
+ do Django para uma CBV:
+
+```python
+
+from django.conf.urls import url
+from django.views.generic import TemplateView
+from meuapp.views import AboutView
+
+urlpatterns = [
+ url(http://github.com/r'%5Eabout/',%20AboutView.as_view()),
+]
+```
+
+## Base Views
+
+As classes listadas abaixo contém muito da funcionalidade necessária para criar
+views no Django. Essas classes são a base sob a qual as outras CBV's são
+construídas.
+
+### View
+
+A classe genérica *master*. **Todas** as outras classes herdam dessa classe.
+O fluxo básico de execução dessa classe quando recebe uma requisição é:
+
+1. ``dispatch()``
+2. ``http_method_not_allowed()``
+3. ``options()``
+
+A função ``dispatch()`` verifica se a classe tem um método com o nome do verbo
+HTTP usado na requisição. Caso não haja, um ``http.HttpResponseNotAllowed`` é
+retornado.
+
+Essa classe sempre responde a requisições com o verbo ``OPTIONS`` retornando
+nesse caso uma lista com os verbos suportados. A não ser que o método
+``options()`` seja sobrescrito.
+
+Um exemplo de implementação:
+
+```python
+
+from django.http import HttpResponse
+from django.views.generic import View
+
+class MyView(View):
+
+ def get(self, request, *args, **kwargs):
+ return HttpResponse('Hello, World!')
+```
+
+No exemplo acima a classe só responde a requisições do tipo ``GET`` e
+``OPTIONS``, todas as outras requisições retornam
+``http.HttpResponseNotAllowed``.
+
+### Template View
+
+Renderiza um template. O fluxo básico de execução dessa classe quando recebe
+uma requisição é:
+
+1. ``dispatch()``
+2. ``http_method_not_allowed()``
+3. ``get_context_data()``
+
+Quando você precisa apenas renderizar uma página para o usuário essa com certeza
+é a melhor CBV para o caso. Você pode editar o contexto que o template recebe
+sobrescrevendo a função ``get_context_data()``
+
+Um exemplo de implementação:
+
+```python
+
+from django.views.generic.base import TemplateView
+
+from articles.models import Article
+
+class HomePageView(TemplateView):
+
+ template_name = "home.html"
+
+ def get_context_data(self, **kwargs):
+ context = super(HomePageView, self).get_context_data(**kwargs)
+ context['latest_articles'] = Article.objects.all()[:5]
+ return context
+```
+
+No exemplo acima o template *home.html* será renderizado e vai receber como
+contexto uma variável chamada ``lastest_articles``.
+
+Uma coisa interessante é que o contexto da ``TemplateView`` é populado pelo
+[ContextMixin](https://docs.djangoproject.com/en/1.8/ref/class-based-views/mixins-simple/#django.views.generic.base.ContextMixin)
+esse mixin pega automaticamente os argumentos da URL que serviu a View.
+
+Considere por exemplo:
+
+```python
+
+from django.conf.urls import patterns, url
+from .views import HelloView
+
+urlpatterns = patterns(
+ '',
+ url(http://github.com/r'^say_hello/(?P%3Cname%3E[\w_-]+)/$', HelloView.as_view(), name='say_hello'),
+)
+```
+
+No caso do exemplo acima o template renderizado pela ``HelloView`` teria
+em seu contexto a variável ``name``.
+
+### Redirect View
+
+Redireciona o usuário para a url informada.
+
+A URL a ser redirecionada pode conter parâmetros no estilo dicionário-de-strings.
+Os parâmetros capturados na URL do ``RedirectView`` serão repassados para a
+URL que o usuário está sendo redirecionado.
+
+O fluxo básico de execução dessa classe quando recebe
+uma requisição é:
+
+1. ``dispatch()``
+2. ``http_method_not_allowed()``
+3. ``get_redirect_url()``
+
+Considere a seguinte configuração de URL's para o exemplo de implementação:
+
+```python
+
+from django.conf.urls import url
+from django.views.generic.base import RedirectView
+
+from article.views import ArticleCounterRedirectView, ArticleDetail
+
+urlpatterns = [
+ url(http://github.com/r'^counter/(?P%3Cpk%3E[0-9]+)/$', ArticleCounterRedirectView.as_view(), name='article-counter'),
+ url(http://github.com/r'^details/(?P%3Cpk%3E[0-9]+)/$', ArticleDetail.as_view(), name='article-detail'),
+]
+```
+
+Exemplo de implementação:
+
+```python
+
+from django.shortcuts import get_object_or_404
+from django.views.generic.base import RedirectView
+
+from articles.models import Article
+
+class ArticleCounterRedirectView(RedirectView):
+
+ permanent = False
+ query_string = True
+ pattern_name = 'article-detail'
+
+ def get_redirect_url(http://github.com/self,%20*args,%20**kwargs):
+ article = get_object_or_404(Article, pk=kwargs['pk'])
+ article.update_counter()
+ return super(ArticleCounterRedirectView, self).get_redirect_url(http://github.com/*args,%20**kwargs)
+
+```
+
+Principais atributos:
+
+- ``url``: A URL destino no formato de String
+- ``pattern_name``: O nome do padrão de URL. Um ``reverse`` será aplicado
+ usando os mesmos
+ ``args`` e ``kwargs`` passados para a ``RedirectView``
+- ``permanent``: Se for ``True`` retorna o status code como 301, caso contrário,
+ retorna 302.
+- ``query_string``: Se for ``True`` a query_string será enviada para a URL de
+ destino.
+
+## Display Views
+
+As duas views abaixo foram desenvolvidas para exibir informações. Tipicamente
+essas views são as mais usadas na maioria dos projetos.
+
+### DetailView
+
+Renderiza um template contendo em seu contexto **um objeto** obtido pelo
+parâmetro enviado na URL.
+
+No fluxo de execução dessa view o objeto que está sendo utilizado está em
+``self.object``
+
+O fluxo básico de execução dessa classe quando recebe
+uma requisição é:
+
+1. ``dispatch()``
+2. ``http_method_not_allowed()``
+3. ``get_template_names()``
+4. ``get_slug_field()``
+5. ``get_queryset()``
+6. ``get_object()``
+7. ``get_context_object_name()``
+8. ``get_context_data()``
+9. ``get()``
+10. ``render_to_response()``
+
+O fluxo parece grande e complexo mas na verdade é muito simples e facilmente
+customizável. Basicamente o que acontece é:
+
+``get_template_names()`` retorna uma lista de templates que devem ser usados para
+renderizar a resposta. Caso o primeiro template da lista não seja encontrado o
+Django tenta o segundo e assim por diante.
+
+Em seguida o ``get_slug_field()`` entra em ação, essa função deve retornar o
+nome do campo que será usado para fazer a busca pelo objeto. Por default o
+Django procura pelo campo ``slug``.
+
+Agora o ``get_queryset`` deve retornar um queryset que será usado para buscar
+um objeto. Aqui é um ótimo lugar para, por exemplo, aplicar um filtro para
+exibir somente o Artigo cujo autor é o usuário logado. Considere o exemplo
+abaixo:
+
+```python
+
+def ArtigoView(DetailView):
+ model = Artigo
+
+ get_queryset(self):
+ return self.model.filter(user=request.user)
+
+ # ... o restante do código foi suprimido
+```
+
+**IMPORTANTE:** O ``get_queryset()`` é chamado pela implementação default do
+método ``get_object()``, se o ``get_object()`` for sobrescrito a chamada ao
+``get_queryset()`` pode não ser realizada.
+
+O ``get_object()`` então é o responsável por retornar o objeto que será enviado
+para o template. Normalmente essa função não precisa ser sobrescrita.
+
+Depois de obter o objeto que será enviado para o template é necessário saber
+qual será o nome desse objeto no contexto do template, isso é feito pela função
+``get_context_object_name()``, por default o nome do objeto no template será o
+nome do ``Model``, no exemplo acima seria ``artigo``
+
+Depois disso temos o ``get_context_data()`` que já foi comentado acima e então
+o ``get()`` que obtém o objeto e coloca no contexto, e em seguida o
+``render_to_response`` que renderiza o template.
+
+**IMPORTANTE:** É importante notar que o Django oferece variáveis de
+instância para facilitar a customização do comportamento da classe.
+Por exemplo a troca do nome do objeto pode ser feita alterando a variável de
+instância ``context_object_name`` ao invés de sobrescrever a função
+``get_object_name()``.
+
+Abaixo segue um exemplo, onde exibir os detalhes de um *Artigo* somente se o
+usuário for o autor dele e vamos pegar esse *Artigo* pelo campo ``titulo`` e
+renderizar esse artigo no template ``detalhe_artigo.html`` com o nome
+``meu_artigo``.
+
+**views.py**
+```python
+
+from django.views.generic.detail import DetailView
+from django.utils import timezone
+
+from articles.models import Article
+
+class ArticleDetailView(DetailView):
+ slug_field = 'titulo'
+ model = Article
+ context_object_name = 'meu_artigo'
+ template_name = 'detalhe_artigo.html'
+
+ get_queryset(self):
+ return self.model.filter(user=self.request.user)
+
+```
+
+**urls.py**
+```python
+from django.conf.urls import url
+
+from article.views import ArticleDetailView
+
+urlpatterns = [
+ url(http://github.com/r'^(?P%3Ctitulo%3E[-\w]+)/$', ArticleDetailView.as_view(), name='article-detail'),
+]
+```
+
+**detalhe_artigo.html**
+```html
+
{{ meu_artigo.titulo }}
+
{{ meu_artigo.conteudo }}
+
Reporter: {{ meu_artigo.user.name }}
+
Published: {{ meu_artigo.data_publicacao|date }}
+```
+
+### ListView
+
+Uma página que representa uma lista de objetos.
+Enquanto essa view está executando a variável ``self.object_list`` vai conter
+a lista de objetos que a view está utilizando.
+
+O fluxo básico de execução dessa classe quando recebe
+uma requisição é:
+
+1. ``dispatch()``
+2. ``http_method_not_allowed()``
+3. ``get_template_names()``
+5. ``get_queryset()``
+6. ``get_object()``
+7. ``get_context_object_name()``
+8. ``get_context_data()``
+9. ``get()``
+10. ``render_to_response()``
+
+Nada de novo aqui certo? Podemos exibir apenas uma lista de Artigos que estão
+com ``status='publicado'``
+
+```python
+
+from django.views.generic.list import ListView
+from django.utils import timezone
+
+from articles.models import Artigo
+
+class ArticleListView(ListView):
+
+ model = Artigo
+
+ def get_queryset(self, **kwargs):
+ return Artigo.objects.filter(status='publicado')
+```
+
+Outra opção seria:
+
+```python
+
+from django.views.generic.list import ListView
+from django.utils import timezone
+
+from articles.models import Artigo
+
+class ArticleListView(ListView):
+
+ model = Artigo
+ queryset = Artigo.objects.filter(status='publicado')
+```
+
+**artigo_list.html**
+```html
+
+```
+
+**DICA:** Normalmente sobrescrevemos as funções quando o retorno depende dos
+parâmetros da requisição e utilizamos as variáveis de instância quando não há
+essa dependência.
+
+O nome do template que é usado em ambas as views ``DetailView`` e ``ListView``
+é determinado da seguinte forma:
+
+* O valor da variável ``template_name`` na View (se definido)
+* O valor do campo ``template_name_field`` na instância do objeto que a view
+ está usando.
+* ``/.html``
+
+
+## Editing Views
+
+As views descritas abaixo contém o comportamento básico para edição de conteúdo.
+
+### FormView
+
+Uma view que mostra um formulário. Se houver erro, mostra o formulário novamente
+contendo os erros de validação. Em caso de sucesso redireciona o usuário para
+uma nova URL.
+
+**forms.py**
+```python
+
+from django import forms
+
+class ContactForm(forms.Form):
+ name = forms.CharField()
+ message = forms.CharField(widget=forms.Textarea)
+
+ def send_email(self):
+ # send email using the self.cleaned_data dictionary
+ pass
+```
+
+**views.py**
+```python
+
+from myapp.forms import ContactForm
+from django.views.generic.edit import FormView
+
+class ContactView(FormView):
+ template_name = 'contact.html'
+ form_class = ContactForm
+ success_url = '/thanks/'
+
+ def form_valid(self, form):
+ # This method is called when valid form data has been POSTed.
+ # It should return an HttpResponse.
+ form.send_email()
+ return super(ContactView, self).form_valid(form)
+```
+
+**contact.html**
+```html
+
+```
+
+As funções mais importantes do ``FormView`` são:
+
+- ``form_valid()``: Chamada quando o formulário é validado com sucesso
+- ``form_invalid()``: Chamada quando o formuĺário contém erros
+- ``get_sucess_url()``: Chamada quando o formulário é validado com sucesso
+ e retorna a url para qual o usuário deve ser redirecionado.
+
+### Views para lidar com ``models`` (ModelForms)
+
+Grande parte do "poder" das CBV's vem quando precisamos trabalhar com models.
+
+As views listadas abaixo: ``CreateView``, ``UpdateView`` e ``DeleteView`` foram
+criadas para facilitar esse trabalho com os models, essas views podem gerar um
+``ModelForm`` de maneira automática, desde que seja possível determinar qual
+é o model que a view está utilizando.
+
+A view vai tentar determinar o model a ser usado das seguintes formas:
+
+- Se houver um atributo ``model`` na classe
+- Se o método ``get_object()`` retorna um objeto, a classe desse objeto será
+ usada
+- Se houver um atributo ``queryset`` o model do ``queryset`` será utilizado
+
+Você não precisa nem mesmo definir um ``success_url`` as views ``CreateView`` e
+``UpdateView`` utilizam automaticamente a função ``get_absolute_url()`` do model
+se essa função existir.
+
+Você também pode customizar o formulário usado na view se você precisar de algum
+tratamento adicional, para fazer isso basta definir a classe de formulários a ser
+usada no atributo ``form_class``:
+
+```python
+
+from django.views.generic.edit import CreateView
+from myapp.models import Author
+from myapp.forms import AuthorForm
+
+class AuthorCreate(CreateView):
+ model = Author
+ form_class = AuthorForm
+```
+
+### CreateView, UpdateView e DeleteView
+
+Uma view que exibe um form para criar, atualizar ou apagar um objeto.
+Caso existam erros no formulário, este é exibido novamente junto com as
+mensagens de erro.
+
+Em caso de sucesso o objeto é salvo.
+
+**models.py**
+
+```python
+
+from django.core.urlresolvers import reverse
+from django.db import models
+
+class Author(models.Model):
+ name = models.CharField(max_length=200)
+
+ def get_absolute_url(http://github.com/self):
+ return reverse('author-detail', kwargs={'pk': self.pk})
+```
+
+**views.py**
+```python
+
+from django.views.generic.edit import CreateView, UpdateView, DeleteView
+from django.core.urlresolvers import reverse_lazy
+from myapp.models import Author
+
+class AuthorCreate(CreateView):
+ model = Author
+ fields = ['name']
+
+class AuthorUpdate(UpdateView):
+ model = Author
+ fields = ['name']
+
+class AuthorDelete(DeleteView):
+ model = Author
+ success_url = reverse_lazy('author-list')
+
+```
+
+**urls.py**
+```python
+from django.conf.urls import url
+from myapp.views import AuthorCreate, AuthorUpdate, AuthorDelete
+
+urlpatterns = [
+ # ...
+ url(http://github.com/r'author/add/),%20AuthorCreate.as_view(), name='author_add'),
+ url(http://github.com/r'author/(?P%3Cpk%3E[0-9]+)/$', AuthorUpdate.as_view(), name='author_update'),
+ url(http://github.com/r'author/(?P%3Cpk%3E[0-9]+)/delete/$', AuthorDelete.as_view(), name='author_delete'),
+]
+```
+
+O atributo ``fields`` determina quais campos do model devem estar presentes no
+formulário. É obrigatório especificar o atributo ``fields`` ou então o atributo
+``form_class``, nunca os dois ao mesmo tempo, pois isso geraria uma exceção
+[``ImproperlyConfigured``](https://docs.djangoproject.com/en/1.8/ref/exceptions/#django.core.exceptions.ImproperlyConfigured).
+
+É importante notar também que a ``DeleteView`` exibe as informações do objeto que
+será deletado quando é acessada usando o verbo ``GET``, quando usado o verbo
+``POST`` o objeto é efetivamente apagado.
+
+**DICA**: O nome dos templates é determinado da seguinte forma:
+
+- ``CreateView`` e UpdateView usam myapp/author_form.html
+- ``DeleteView`` usa myapp/author_confirm_delete.html
+
+## Date Views
+
+Date-based generic views são views com a função de exibir páginas com dados
+filtrados por datas, por exemplo: posts em um blog, notícias, consultas ao médico, etc.
+
+### ArchiveIndexView
+
+Uma página que exibe os "últimas" objetos inseridos, desconsiderando aqueles com uma
+data futura a não ser que o atributo ``allow_future`` seja definido como ``True``.
+
+É importante notar que:
+
+- O nome default do ``context_object_name`` é ``latest``.
+- O sufixo ``_archive`` no nome do template.
+- Além da lista de objetos o contexto também contem a variável ``date_list``
+ contendo todos os anos que tem objetos em ordem decrescente.
+ Isso pode ser alterado para mês ou dia usando o atributo
+ ``date_list_period``. Isso se aplica a todas as *Data-based generic views*.
+
+Implementação simples:
+
+**urls.py**
+```python
+
+from django.conf.urls import url
+from django.views.generic.dates import ArchiveIndexView
+
+from myapp.models import Article
+
+urlpatterns = [
+ url(r'^archive/$',
+ ArchiveIndexView.as_view(model=Article, date_field="pub_date"),
+ name="article_archive"),
+]
+```
+
+### YearArchiveView
+
+Uma página para exibir um arquivo anual. Retorna todos os objetos de um
+determinado ano.
+
+No contexto além da lista de objetos temos ainda:
+
+- ``date_list``: Um objeto QuerySet contendo todos os meses que tenham objetos
+ naquele ano representados como objetos datetime.datetime em ordem crescente.
+- ``year``: Um objeto datetime.datetime representando o ano atual
+- ``next_year``: Um objeto datetime.datetime representando o próximo ano
+- ``previous_year``: Um objeto datetime.datetime representando o ano anterior
+
+Exemplo de implementação:
+
+**views.py**
+```python
+from django.views.generic.dates import YearArchiveView
+
+from myapp.models import Article
+
+class ArticleYearArchiveView(YearArchiveView):
+ queryset = Article.objects.all()
+ date_field = "pub_date"
+ make_object_list = True
+ allow_future = True
+```
+
+**urls.py**
+```python
+from django.conf.urls import url
+
+from myapp.views import ArticleYearArchiveView
+
+urlpatterns = [
+ url(http://github.com/r'^(?P%3Cyear%3E[0-9]{4})/$',
+ ArticleYearArchiveView.as_view(),
+ name="article_year_archive"),
+]
+```
+
+**article_archive_year.html**
+```html
+
+ {% for date in date_list %}
+
{{ date|date }}
+ {% endfor %}
+
+```
+
+### MonthArchiveView
+
+Uma página para exibir um arquivo mensal. Retorna todos os objetos de um
+determinado mês.
+
+No contexto além da lista de objetos temos ainda:
+
+- ``date_list``: Um objeto QuerySet contendo todos os dias que tenham objetos
+ naquele mês representados como objetos datetime.datetime em ordem crescente.
+- ``month``: Um objeto datetime.datetime representando o mês atual
+- ``next_month``: Um objeto datetime.datetime representando o próximo mês
+- ``previous_month``: Um objeto datetime.datetime representando o mês anterior
+
+Exemplo de implementação:
+
+**views.py**
+```python
+from django.views.generic.dates import MonthArchiveView
+
+from myapp.models import Article
+
+class ArticleMonthArchiveView(MonthArchiveView):
+ queryset = Article.objects.all()
+ date_field = "pub_date"
+ allow_future = True
+```
+
+**urls.py**
+```python
+from django.conf.urls import url
+
+from myapp.views import ArticleMonthArchiveView
+
+urlpatterns = [
+ # Example: /2012/aug/
+ url(http://github.com/r'^(?P%3Cyear%3E[0-9]{4})/(?P[-\w]+)/$',
+ ArticleMonthArchiveView.as_view(),
+ name="archive_month"),
+ # Example: /2012/08/
+ url(http://github.com/r'^(?P%3Cyear%3E[0-9]{4})/(?P[0-9]+)/$',
+ ArticleMonthArchiveView.as_view(month_format='%m'),
+ name="archive_month_numeric"),
+]
+```
+
+**article_archive_month.html**
+```html
+
+ {% if previous_week %}
+ Previous Week: {{ previous_week|date:"F Y" }}
+ {% endif %}
+ {% if previous_week and next_week %}--{% endif %}
+ {% if next_week %}
+ Next week: {{ next_week|date:"F Y" }}
+ {% endif %}
+
+```
+
+### DayArchiveView
+
+Uma página para exibir um arquivo diário. Retorna todos os objetos de um
+determinado dia.
+
+No contexto além da lista de objetos temos ainda:
+
+- ``day``: Um objeto datetime.datetime representando o dia atual
+- ``next_day``: Um objeto datetime.datetime representando o próximo dia
+- ``previous_day``: Um objeto datetime.datetime representando o dia anterior
+- ``next_month``: Um objeto datetime.datetime representando o primeiro dia do
+ próximo mês
+- ``previous_month``: Um objeto datetime.datetime representando o primeiro dia
+ do mês anterior
+
+Implementação simples:
+
+**views.py**
+```python
+from django.views.generic.dates import DayArchiveView
+
+from myapp.models import Article
+
+class ArticleDayArchiveView(DayArchiveView):
+ queryset = Article.objects.all()
+ date_field = "pub_date"
+ allow_future = True
+```
+
+**urls.py**
+```python
+from django.conf.urls import url
+
+from myapp.views import ArticleDayArchiveView
+
+urlpatterns = [
+ # Example: /2012/nov/10/
+ url(http://github.com/r'^(?P%3Cyear%3E[0-9]{4})/(?P[-\w]+)/(?P[0-9]+)/$',
+ ArticleDayArchiveView.as_view(),
+ name="archive_day"),
+]
+```
+
+**article_archive_day.html**
+```html
+
+ {% if previous_day %}
+ Previous Day: {{ previous_day }}
+ {% endif %}
+ {% if previous_day and next_day %}--{% endif %}
+ {% if next_day %}
+ Next Day: {{ next_day }}
+ {% endif %}
+
+```
+
+### TodayArchiveView
+
+É a mesma coisa do ``DayArchiveView`` mas não usa os parâmetros da URL para
+determinar o ano/mês/dia.
+
+O que muda é o urls.py, veja o exemplo abaixo:
+
+```python
+from django.conf.urls import url
+
+from myapp.views import ArticleTodayArchiveView
+
+urlpatterns = [
+ url(r'^today/$',
+ ArticleTodayArchiveView.as_view(),
+ name="archive_today"),
+]
+```
+
+### DateDetailView
+
+É a mesma coisa que a ``DetailView`` com a diferença que a data é utilizada
+junto com o pk/slug para determinar qual objeto deve ser obtido.
+
+O que muda é o urls.py, veja o exemplo abaixo:
+
+```python
+from django.conf.urls import url
+from django.views.generic.dates import DateDetailView
+
+urlpatterns = [
+ url(http://github.com/r'^(?P%3Cyear%3E[0-9]{4})/(?P[-\w]+)/(?P[0-9]+)/(?P[0-9]+)/$',
+ DateDetailView.as_view(model=Article, date_field="pub_date"),
+ name="archive_date_detail"),
+]
+```
+
+### Conclusão
+
+Longe de tentar exaurir um assunto de tamanha complexidade e abrangência minha
+intenção com esse artigo foi mostrar o funcionamento básico das Class Based Views
+e quem sabe incentivar você a utilizar CBV's no seu próximo projeto.
+
+Envie para mim qualquer dúvida, crítica ou sugestão que você tiver em qualquer
+uma das minhas redes sociais, posso demorar um pouco a responder mas eu respondo! :)
+
+Ah, se você se interessou pelo assunto e quer se aprofundar mais eu aconselho
+começar pela [Documentação oficial](https://docs.djangoproject.com/en/1.8/topics/class-based-views/intro/)
+
+### Referências
+
+-
+-
+-
+-
\ No newline at end of file
diff --git a/content/como-distribuir-seu-projeto-python-com-pypi.md b/content/como-distribuir-seu-projeto-python-com-pypi.md
new file mode 100644
index 000000000..d3e8ac050
--- /dev/null
+++ b/content/como-distribuir-seu-projeto-python-com-pypi.md
@@ -0,0 +1,222 @@
+Title: Como distribuir sua aplicação Python com PyPI
+Slug: como-distribuir-sua-aplicacao-python-com-pypi
+Date: 2016-06-17 13:47:24
+Category: Python
+Tags: python, pypi, tutorial, desenvolvimento, pypi, pip
+Author: Michell Stuttgart
+Email: michellstut@gmail.com
+Github: mstuttgart
+Linkedin: mstuttgart
+Site: https://mstuttgart.github.io
+
+Imagine a seguinte situação: você passou alguns dias (ou mesmo meses) desenvolvendo uma módulo python, escreveu testes, implementou funcionalidades e depois de alguns ajustes, chegou a hora de liberar seu software para que outros desenvolvedores possam utilizá-lo. Qual o melhor modo de distribuí-lo?
+
+Caro leitor, se você costuma programar em Python (seja profissionalmente ou não) provavelmente já instalou outros módulos usando o [PyPI](https://pypi.python.org/pypi), através do comando abaixo:
+
+```bash
+pip install nomedomodulo
+```
+
+Não seria interessante usar o mesmo método para distribuir a sua aplicação? Sim? Então mãos a obra.
+
+### Sobre o PyPI - Python Package Index
+
+O site [PyPI](https://pypi.python.org/pypi), é um repositório de *softwares* desenvolvidos na linguagem Python. Em outras palavras, ele garante que seu pacote Python sempre esteja disponível para a instalação. O seu funcionamente é simples, porém algumas configurações inicias devem ser feitas para que tudo funcione corretamente.
+
+### Crie uma conta
+
+Primeiramente, para distribuir seus pacotes usando o [PyPI](https://pypi.python.org/pypi), precisamos criar uma conta em ambos os sites:
+
+* [PyPI Live](https://pypi.python.org/pypi?%3Aaction=register_form)
+* [PyPI Test](https://testpypi.python.org/pypi?%3Aaction=register_form)
+
+Recomendo que você utilize o mesmo email e senha para ambos os sites. Posteriormente, isso tornará mais fácil o processo de configuração.
+
+### Configurando o ambiente
+
+O próximo passo é criar um arquivo `.pypirc` em sua `home`. Esse arquivo contem informações de auteticação, tanto para o [PyPI Live](https://pypi.python.org/pypi) quando para o [PyPI Test](https://testpypi.python.org/pypi).
+
+```bash
+touch ~/.pypirc
+```
+
+Apesar de não ser obrigatório a criação desse aquivo, ele facilita muito nosso trabalho, uma vez que você não precisaremos inserir nosso email e senha toda vez que formos enviar nosso código para o [PyPI Live](https://pypi.python.org/pypi).
+
+Abra o arquivo `.pypirc` em seu editor de texto favorito, e insira as informações abaixo.
+
+```bash
+[distutils]
+index-servers =
+ pypi
+ pypitest
+
+[pypi]
+repository=https://pypi.python.org/pypi
+username=seu_nomedeusuario
+password=sua_senha
+
+[pypitest]
+repository=https://testpypi.python.org/pypi
+username=seu_nomedeusuario
+password=sua_senha
+
+```
+Em *username* insira seu nome de usuário e *password*, insira sua senha. Faça isso tanto para o `pypi` quanto para o `pypitest`.
+
+Um observação importante é que, caso a sua senha possua espaço, não a coloque entre aspas. Por exemplo, se a sua senha for "batuque da viola doida", coloque exatamente o mesmo texto em *password*.
+
+
+```bash
+password=batuque da viola doida
+```
+
+### Preparando o seu módulo Python
+
+Todo pacote distribuído pelo [PyPI](https://pypi.python.org/pypi) precisa ter uma arquivo `setup.py` em seu diretório raiz. E se seu projeto também usa um arquivo *readme* em *markdown* (normalmente chamado `README.md`) você também precisará criar um arquivo chamado `setup.cfg`no diretório raiz do módulo.
+
+Como exemplo, iremos utilizar o módulo [codigo_avulso_test_tutorial](https://github.com/mstuttgart/codigo-avulso-test-tutorial) que criei para ser utilizado como exemplo em nossos tutoriais. Assim, temos a seguinte estrutura básica de diretórios:
+
+```bash
+.
+├── codigo_avulso_test_tutorial
+│ ├── circulo.py
+│ ├── figura_geometrica.py
+│ ├── __init__.py
+│ └── quadrado.py
+├── LICENSE
+├── README.md
+├── setup.cfg
+├── setup.py
+└── test
+ ├── circulo_test.py
+ ├── figura_geometrica_test.py
+ ├── __init__.py
+ └── quadrado_test.py
+
+```
+Aqui, o que nos interessa são os arquivos `setup.py` e `setup.cfg`. Dentro do arquivo `setup.py` temos várias informações sobre nossa aplicação que serão usadas pelo [PyPI](https://pypi.python.org/pypi).
+
+```python
+# -*- coding: utf-8 -*-
+from setuptools import setup
+
+setup(
+ name='codigo-avulso-test-tutorial',
+ version='0.1.1',
+ url='https://github.com/mstuttgart/codigo-avulso-test-tutorial',
+ license='MIT License',
+ author='Michell Stuttgart',
+ author_email='michellstut@gmail.com',
+ keywords='tutorial test unittest codigoavulso',
+ description=u'Tutorial de teste unitário em Python para o blog Código Avulso',
+ packages=['codigo_avulso_test_tutorial'],
+ install_requires=[],
+)
+```
+O nome de cada *tag* é autoexplicativo, então não vou entrar em detalhes. Basta você usar o código acima e substituir com os dados do seu pacote.
+
+O próximo passo é adicionar o seguinte conteúdo no arquivo `setup.cfg` (caso você o tenha criado).
+
+```bash
+[metadata]
+description-file = README.md
+```
+Esse arquivo irá dizer ao [PyPI](https://pypi.python.org/pypi) onde seu arquivo *readme* está.
+
+### Publicando sua aplicação Python
+
+Agora iremos estudar os passos para enviar nossa aplicação para [PyPI](https://pypi.python.org/pypi), para que ela fique disponível para ser instalada através do `pip`.
+
+#### Enviando para PyPI Test
+
+Primeiramente, vamos registrar nossa aplicação no [PyPI Test](https://testpypi.python.org/pypi). Esse passo serve para verificarmos se está tudo certo com nosso pacote e também validar se já não existe outro módulo com o mesmo nome.
+Registramos nossa aplicação com o seguinte comando:
+
+```bash
+python setup.py register -r pypitest
+```
+
+Se tudo ocorrer bem teremos a seguinte saída (Server responde 200):
+
+```bash
+running register
+running egg_info
+creating codigo_avulso_test_tutorial.egg-info
+writing codigo_avulso_test_tutorial.egg-info/PKG-INFO
+writing top-level names to codigo_avulso_test_tutorial.egg-info/top_level.txt
+writing dependency_links to codigo_avulso_test_tutorial.egg-info/dependency_links.txt
+writing manifest file 'codigo_avulso_test_tutorial.egg-info/SOURCES.txt'
+reading manifest file 'codigo_avulso_test_tutorial.egg-info/SOURCES.txt'
+writing manifest file 'codigo_avulso_test_tutorial.egg-info/SOURCES.txt'
+running check
+Registering codigo-avulso-test-tutorial to https://testpypi.python.org/pypi
+Server response (200): OK
+```
+Caso exista outro pacote com o mesmo nome, teríamos de escolher outro nome para o nosso pacote. Agora com nosso pacote devidamente registrado, executamos o comando abaixo para que o pacote seja enviado para o [PyPI Test](https://testpypi.python.org/pypi).
+
+```bash
+python setup.py sdist upload -r pypitest
+```
+
+Se tudo ocorrer bem (Server responde 200), você verá uma saída semelhante a esta e já poderá ver sua aplicação na lista do [PyPI Test](https://testpypi.python.org/pypi).
+
+```bash
+running sdist
+running egg_info
+writing codigo_avulso_test_tutorial.egg-info/PKG-INFO
+writing top-level names to codigo_avulso_test_tutorial.egg-info/top_level.txt
+writing dependency_links to codigo_avulso_test_tutorial.egg-info/dependency_links.txt
+reading manifest file 'codigo_avulso_test_tutorial.egg-info/SOURCES.txt'
+writing manifest file 'codigo_avulso_test_tutorial.egg-info/SOURCES.txt'
+warning: sdist: standard file not found: should have one of README, README.rst, README.txt
+
+.
+.
+.
+
+creating dist
+Creating tar archive
+removing 'codigo-avulso-test-tutorial-0.1.1' (and everything under it)
+running upload
+Submitting dist/codigo-avulso-test-tutorial-0.1.1.tar.gz to https://testpypi.python.org/pypi
+Server response (200): OK
+
+```
+
+#### Enviando para PyPI Live
+
+Agora é pra valer. Executamos o mesmo passos para o [PyPI Test](https://testpypi.python.org/pypi).
+
+```bash
+python setup.py register -r pypi
+```
+
+Tudo ocorrendo bem, enviamos nosso pacote:
+
+```bash
+python setup.py sdist upload -r pypi
+```
+
+Parabéns! Com esse ultimo passo, publicamos o nosso pacote Python com sucesso! Agora ele pode ser [visualizado na lista de aplicações](https://pypi.python.org/pypi/codigo-avulso-test-tutorial/0.1.1) do [PyPI](https://pypi.python.org/pypi) e ser instalado usando `pip`.
+
+```bash
+pip install nomedopacote
+```
+
+Ou, para o nosso exemplo:
+
+```bash
+pip install codigo_avulso_test_tutorial
+```
+
+### Conclusão
+
+É isso pessoal. Neste tutorial vimos como distribuir nossa aplicação Python, desde a crição na conta no [PyPI](https://pypi.python.org/pypi) até o registro e *upload* da nossa aplicação. Espero que tenham gostado e caso tenham alguma dúvida, deixem um comentário.
+
+Obrigado pela leitura e até o próximo tutorial.
+
+### Referências
+
+* [Documentação oficial](https://wiki.python.org/moin/CheeseShopTutorial#Submitting_Packages_to_the_Package_Index)
+* [How to Host your Python Package on PyPI with GitHub](https://www.codementor.io/python/tutorial/host-your-python-package-using-github-on-pypi)
+* [How to submit a package to PyPI](http://peterdowns.com/posts/first-time-with-pypi.html)
diff --git a/content/como-encontrar-solucoes-python.md b/content/como-encontrar-solucoes-python.md
new file mode 100644
index 000000000..0c3fbe70d
--- /dev/null
+++ b/content/como-encontrar-solucoes-python.md
@@ -0,0 +1,221 @@
+Title: Como encontrar soluções para seus problemas com Python
+Slug: como-encontrar-solucoes-python
+Date: 2016-01-09 10:30
+Tags: python,blog,tutorial,aulas
+Author: Eric Hideki
+Email: eric8197@gmail.com
+Github: erichideki
+Bitbucket: erichideki
+Site: http://ericstk.wordpress.com
+Twitter: erichideki
+Category: Python
+
+Como encontrar soluções para seus problemas com Python
+----------------------------------------------------
+
+Quando estamos aprendendo algo, o início geralmente é difícil. Conseguir absorver novos conceitos e entender como as coisas funcionam não é uma das tarefas mais simples, porém nessas horas precisamos lembrar do conceito de 'babysteps' (Um passo de cada vez), ter paciência e persistir.
+
+A diferença entre uma pessoa experiente comparado a um iniciante é que a pessoa experiente errou muito mais vezes do que um iniciante, e com o tempo aprendeu com os erros.
+
+E na área de programação temos inúmeros tutoriais e cursos espalhados pela internet, e geralmente as pessoas começam a aprender por conta própria. E quando acontece um problema (e isso irá acontecer inevitavelmente) e não sabemos como solucioná-lo, se torna um obstáculo chato.
+
+Neste artigo irei mostrar algumas formas de poder encontrar soluções para seus problemas enquanto aprende a programar.
+
+Os exemplos se aplicam a Python, mas podem ser exemplificados para qualquer outra linguagem ou tecnologia (se você trabalha com outras tecnologias como PHP ou Ruby, deixe nos comentários quais ferramentas utilizam para encontrar soluções dos seus problemas).
+
+**Dica:** Veja essa palestra fantástica do [Josh Kaufman sobre as primeiras 20 horas de aprendizado](http://tedxtalks.ted.com/video/The-First-20-Hours-How-to-Learn)
+
+## Python console
+
+Uma grande feature do Python é seu console interativo, por ele conseguimos testar nossos códigos e alguns scripts em tempo real. Vejamos um exemplo:
+
+```python
+Python 3.4.3 (default, Oct 14 2015, 20:28:29)
+[GCC 4.8.4] on linux
+Type "help", "copyright", "credits" or "license" for more information.
+>>> a = {'Eric', 'Python', 'JavaScript'}
+>>> b = ('Django', 'Flask')
+>>> a + b
+Traceback (most recent call last):
+ File "", line 1, in
+TypeError: unsupported operand type(s) for +: 'set' and 'tuple'
+>>> tuple(a) + b
+('JavaScript', 'Eric', 'Python', 'Django', 'Flask')
+```
+
+### O que aconteceu?
+
+Na variável **a** criamos um *set* com os nomes *Eric, Python e JavaScript*, e depois criamos uma *tupla* com os nomes *Django e Flask*.
+
+Ao tentarmos juntar **a + b**, o interpretador Python nos retorna um erro: *TypeError: unsupported operand type(s) for +: 'set' and 'tuple'*. Ou seja, o que ele diz é que não podemos somar um set a uma tupla.
+
+O interpretador Python realiza as operações em tempo real, e se caso o que você deseja fazer não estiver correto, o interpretador irá informar o erro. Se o erro não for explícito para você, basta copiar e colar o erro no Google e encontrará os motivos do erro.
+
+Para resolver esse problema, uma das soluções apresentada é transformar nosso set em uma tupla, onde fazemos a conversão em tempo de execução com o comando **tuple(a) + b**.
+
+## [Ipython](http://ipython.org/)
+
+Que tal termos um interpretador Python mais poderoso e com mais funcionalidades que o tradicional? O [Ipython](http://ipython.org/) foi criado especificamente esse objetivo.
+
+Vamos explorá-lo um pouco:
+
+
+```python
+$ipython
+
+Python 2.7.6 (default, Jun 22 2015, 17:58:13)
+Type "copyright", "credits" or "license" for more information.
+
+IPython 1.2.1 -- An enhanced Interactive Python.
+? -> Introduction and overview of IPython's features.
+%quickref -> Quick reference.
+help -> Python's own help system.
+object? -> Details about 'object', use 'object??' for extra details.
+
+In [1]: nome = "Eric"
+
+In [2]: nome.
+nome.capitalize nome.isalnum nome.lstrip nome.splitlines
+nome.center nome.isalpha nome.partition nome.startswith
+nome.count nome.isdigit nome.replace nome.strip
+nome.decode nome.islower nome.rfind nome.swapcase
+nome.encode nome.isspace nome.rindex nome.title
+nome.endswith nome.istitle nome.rjust nome.translate
+nome.expandtabs nome.isupper nome.rpartition nome.upper
+nome.find nome.join nome.rsplit nome.zfill
+nome.format nome.ljust nome.rstrip
+nome.index nome.lower nome.split
+
+In [2]: nome.startswith("O")
+Out[2]: False
+
+In [3]: nome.startswith("E")
+Out[3]: True
+
+In [4]: len(nome)
+Out[4]: 4
+
+In [5]: nome.lower()
+Out[5]: 'eric'
+
+```
+
+### O que aconteceu?
+
+Criamos uma variável **nome** onde é um simples String "Eric". E se digitarmos **nome.** e apertar TAB, o Ipython irá apresentar diversas operações que podemos fazer com essa variável.
+
+Por exemplo, **nome.startswith** verifica se a primeira letra ou número começa com o parâmetro passado a ele.
+
+Na primeira tentativa verificamos se a variável começa com a letra *O*, e ele me retornou **False**, ou seja, não é verdade.
+
+Na segunda tentativa tentamos com a letra **E**, e ele me retornou **True**, o que é verdade já que o nome **Eric** começa com a letra "E".
+
+O Ipython vai muito mais além do que isso, vá e dê uma olhada na página oficial e o que ele pode fazer.
+
+E temos também o [Ipython Notebook](http://ipython.org/notebook.html) que é FANTÁSTICO, amplamente utilizado para computação científica.
+
+## [Dreampie](http://www.dreampie.org/)
+
+Quando queremos testar algo mais elaborado, ter um console que permita criamos nossos códigos de forma mais organizada além do Interpretador padrão do Python e o IPython, podemos utilizar o [Dreampie](http://www.dreampie.org/) que é ideal para isso.
+
+Vamos a outro exemplo:
+
+```python
+Python 2.7.6 (default, Jun 22 2015, 17:58:13)
+[GCC 4.8.2] on linux2
+Type "copyright", "credits" or "license()" for more information.
+DreamPie 1.1.1
+>>> colunas = [
+... {'pnome': 'Eric', 'unome': 'Hideki', 'id': 4},
+... {'pnome': 'Luciano', 'unome': 'Ramalho', 'id': 2},
+... {'pnome': 'David', 'unome': 'Beazley', 'id': 8},
+... {'pnome': 'Tim', 'unome': 'Peters', 'id': 1},
+... ]
+>>> from operator import itemgetter
+>>> colunas_por_nome = sorted(colunas, key=itemgetter('pnome'))
+>>> colunas_por_id = sorted(colunas, key=itemgetter('id'))
+>>> from pprint import pprint
+>>> pprint(colunas_por_nome)
+[{'id': 8, 'pnome': 'David', 'unome': 'Beazley'},
+ {'id': 4, 'pnome': 'Eric', 'unome': 'Hideki'},
+ {'id': 2, 'pnome': 'Luciano', 'unome': 'Ramalho'},
+ {'id': 1, 'pnome': 'Tim', 'unome': 'Peters'}]
+>>> pprint(colunas_por_id)
+[{'id': 1, 'pnome': 'Tim', 'unome': 'Peters'},
+ {'id': 2, 'pnome': 'Luciano', 'unome': 'Ramalho'},
+ {'id': 4, 'pnome': 'Eric', 'unome': 'Hideki'},
+ {'id': 8, 'pnome': 'David', 'unome': 'Beazley'}]
+```
+
+
+
+### O que aconteceu?
+
+Temos uma lista de dicionários desordenado e queremos ordenar de acordo com os parâmetros que queremos. Com isso, utilizamos duas bibliotecas que já estão por padrão com o Python: *[operator](https://docs.python.org/2/library/operator.html?highlight=itemgetter#operator.itemgetter) e [pprint](https://docs.python.org/2/library/pprint.html)*.
+
+Dentro da biblioteca **operator** temos a funcionalidade **itemgetter**, onde através dos parâmetros que passamos, ele irá fazer a seleção. Já o **pprint** irá mostrar o resultado da nossa seleção de forma mais bonita. Vamos explicar detalhadamente o que cada coisa faz.
+
+Criamos nossa lista de dicionários:
+
+```python
+>>> colunas = [
+... {'pnome': 'Eric', 'unome': 'Hideki', 'id': 4},
+... {'pnome': 'Luciano', 'unome': 'Ramalho', 'id': 2},
+... {'pnome': 'David', 'unome': 'Beazley', 'id': 8},
+... {'pnome': 'Tim', 'unome': 'Peters', 'id': 1},
+... ]
+```
+
+Importamos a biblioteca operator e sua funcionalidade itemgetter:
+
+```python
+>>>from operator import itemgetter
+```
+
+E criamos nossa funcionalidade para ordenar a lista de dicionários de acordo com o parâmetro selecionado, que nesse caso será o primeiro nome, o **pname**:
+
+```python
+>>> colunas_por_nome = sorted(colunas, key=itemgetter('pnome'))
+```
+
+Finalizando, utilizamos o **pprint** para exibir o resultado:
+
+```python
+>>>pprint(colunas_por_nome)
+```
+
+Bacana, né? E também utilizamos a mesma lógica para ordenar de acordo com o **id**.
+
+Se quiser saber mais a respeito do Ipython, indico o artigo do [Python Help que fala a respeito](https://pythonhelp.wordpress.com/2011/02/22/dreampie-o-shell-python-que-voce-sempre-sonhou/).
+
+# [Python Tutor](http://www.pythontutor.com/)
+
+Ao observarmos uma funcionalidade queremos entender o que ele faz, e muitas vezes o que cada coisa no código faz não fica muito bem claro em nossas ideias. Por isso o Python Tutor existe! Ele exibe passo-a-passo o que está acontecendo no código.
+
+
+
+Clique em **Forward** e veja o que acontece.
+
+# Outras opções
+
+Também existem outras ferramentas que podem auxiliar e melhorar seu código:
+
+- **Anaconda para Sublime Text** - [http://damnwidget.github.io/anaconda/](http://damnwidget.github.io/anaconda/)
+- **Autopep8** - [https://pypi.python.org/pypi/autopep8](https://pypi.python.org/pypi/autopep8)
+- **Jedi** - [https://github.com/davidhalter/jedi](https://github.com/davidhalter/jedi)
+- **Pyflakes** - [https://pypi.python.org/pypi/pyflakes](https://pypi.python.org/pypi/pyflakes)
+- **PDB** - [https://docs.python.org/2/library/pdb.html](https://docs.python.org/2/library/pdb.html)
+
+## Locais onde podemos postar nossas dúvidas
+
+Vale sempre lembrar que é muito importante consultar a documentação oficial do Python, seja a [versão 2](https://docs.python.org/2/) ou a [versão 3](https://docs.python.org/3/).
+
+Também existem outro lugar muito legal, o **[Stackoverflow](http://pt.stackoverflow.com/)**. Se ainda o problema persistir, acesse as listas de discussões da comunidade Python no Brasil.
+
+- **Python Brasil** - [https://groups.google.com/forum/#!forum/python-brasil](https://groups.google.com/forum/#!forum/python-brasil)
+- **Django Brasil** - [https://groups.google.com/forum/#!forum/django-brasil](https://groups.google.com/forum/#!forum/django-brasil)
+- **Web2py Brasil** - [https://groups.google.com/forum/#!forum/web2py-users-brazil](https://groups.google.com/forum/#!forum/web2py-users-brazil)
+- **Flask Brasil** - [https://groups.google.com/forum/#!forum/flask-brasil](https://groups.google.com/forum/#!forum/flask-brasil)
+- **Comunidades locais da comunidade Python ao redor do Brasil** - [http://pythonbrasil.github.io/wiki/comunidades-locais](http://pythonbrasil.github.io/wiki/comunidades-locais)
+
+Deixe nos comentários seu feedback, e se tiver outra dica que não foi citado, não deixe de indicar.
diff --git a/content/configurando-ambiente-django-com-apache-e-mod-wsgi.md b/content/configurando-ambiente-django-com-apache-e-mod-wsgi.md
new file mode 100644
index 000000000..06ab38919
--- /dev/null
+++ b/content/configurando-ambiente-django-com-apache-e-mod-wsgi.md
@@ -0,0 +1,269 @@
+Title: Configurando ambiente Django com Apache e mod_wsgi
+Date: 2015-03-02 00:20
+Tags: python, django, apache, mod_wsgi, virtualenv, virtualenvwrapper
+Category: Django
+Slug: configurando-ambiente-django-com-apache-e-mod-wsgi
+Author: Guilherme Louro
+Email: guilherme-louro@hotmail.com
+Github: guilouro
+Twitter: guilhermelouro
+Facebook: guilherme.louro.3
+
+
+### Entendendo a necessidade
+
+Muitas vezes encontramos dificuldade em colocar nossas aplicações para funcionar em um servidor devido ao pouco conhecimento em infraestrutura, principalmente aqueles que vieram do php, onde, subir um site e já o ver funcionando no ambiente final se trata apenas de subir os arquivos para a pasta **www** e pronto, certo? Não, não é bem por aí ...
+
+Normalmente quando configuramos a hospedagem de um domínio através de um software de gestão de alojamento web *([cpanel](http://cpanel.net) é o mais conhecido)* automaticamente o sistema configura o VirtualHost específico para o seu domínio cadastrado, ja direcionando a path para a sua pasta *www* ou **public_html**. Mas como isso é feito? Não entrarei em detalhes de como o cpanel funciona, mas irei demonstrar aqui como configuramos um servidor com [apache](http://httpd.apache.org/docs/) para receber nossa aplicação.
+
+### Mas por que o Apache?
+
+A partir do momento que eu mudei meu foco, saindo do PHP para trabalhar com **Python**, eu acabei "abandonando" o Apache para trabalhar com Nginx. Porém, me deparei com um projeto onde tinha que funcionar em uma Hospedagem compartilhada na qual só funciona o apache. Como não vi nada relacionado a essa configuração aqui no **Pythonclub**, achei que seria útil para muitos que podem cair em uma situação parecida com a minha, ou simplesmente prefira usar o Apache do que o Nginx.
+
+Caso o seu interesse seja mesmo usar o Nginx (pode parar por aqui), acho ótimo!!! Te dou todo apoio e ainda te indico um ótimo post para isso, do nosso amigo [Igor Santos](https://github.com/igr-santos).
+
+- [Configurando um servidor de produção para aplicações Python](http://pythonclub.com.br/configurando-um-servidor-de-producao-para-aplicacoes-python.html) (Nginx)
+
+Agora, chega de conversa e vamos ao que interessa.
+
+### Como fazer?
+
+Existem várias maneiras de se fazer o Django trabalhar com apache, uma delas é a combinação Apache + [mod_wsgi](http://code.google.com/p/modwsgi/) e será dessa forma que faremos. Com mod_wsgi podemos implementar qualquer aplicação Python que suporte a interface **Python WSGI**.
+
+##### Instalando alguns pacotes necessários
+
+Antes de mais nada, atualize a lista de pacotes
+
+```bash
+$ sudo apt-get update
+```
+
+*Apache + mod_wsgi*
+
+```bash
+$ sudo apt-get install apache2 libapache2-mod-wsgi
+```
+
+*Python setup tools + pip*
+
+```bash
+$ sudo apt-get install python-setuptools
+$ sudo apt-get install python-pip
+```
+
+### Vamos testar o WSGI?
+
+Vamos fazer um teste com uma aplicação simples em python.
+
+Começe criando um diretório na raiz do apache *(DocumentRoot)*
+
+```bash
+$ sudo mkdir /var/www/wsgi_test
+```
+
+Em seguida vamos criar nossa app de teste ...
+
+```bash
+$ sudo vim /var/www/app.wsgi
+```
+
+... e escrever nossa app python compatível com WSGI
+
+```python
+def application(environ, start_response):
+ status = '200 OK'
+ output = 'Hello World!'
+ response_headers = [('Content-type', 'text/plain'),
+ ('Content-Length', str(len(output)))]
+ start_response(status, response_headers)
+ return [output]
+```
+
+Vamos criar agora um host para usar como nosso domínio da aplicação teste
+
+```bash
+$ sudo vim /etc/hosts
+```
+
+Adicione esta linha ao seu arquivo hosts
+
+```bash
+127.0.0.1 wsgi_test
+```
+
+E vamos configurar nosso VirtualHost no Apache.
+
+```bash
+$ sudo vim /etc/apache2/sites-available/wsgi_test
+```
+
+```apache
+
+ ServerName wsgi_test
+ DocumentRoot /var/www/wsgi_test
+
+ Order allow,deny
+ Allow from all
+
+ WSGIScriptAlias / /var/www/wsgi_test/app.wsgi
+
+```
+
+Ative-o
+
+```bash
+$ sudo a2ensite wsgi_test
+```
+
+*Obs: esse comando cria um link simbólico do wsgi_test para a pasta sites-enabled. Você pode fazer isso manualmente.*
+
+Reinicie o apache:
+
+```bash
+$ sudo service apache2 reload
+```
+
+Feito isso abra o internet explorer seu navegador preferido e acesse [http://wsgi_test](http://wsgi_test). Se você está vendo a mensagem *"Hello World"* pode comemorar, o wsgi está funcionando com o apache.
+
+### Configurando Django com WSGI
+
+Até o momento entendemos como funciona a configuração do apache para receber uma aplicação Python com WSGI. Podemos usar essa ideia para qualquer aplicação python, porém veremos como fica essa configuração para trabalhar com **Django**.
+
+##### Criando o ambiente
+
+É sempre bom trabalharmos com ambientes virtuais em nossas aplicações python, para isso temos o [virtualenv](https://virtualenv.pypa.io/en/latest/). Eu, particularmente, prefiro usar o [VirtualenvWrapper](https://virtualenvwrapper.readthedocs.org/en/latest/), que separa os ambientes virtuais das aplicações. Caso você não conheça, indico o post do [Arruda](https://github.com/arruda/) que foi o que me guiou quando comecei a usar. [Usando VirtualEnvWrapper](http://www.arruda.blog.br/programacao/python/usando-virtualenvwrapper/)
+
+No meu caso usei o virtualenvwrapper e meu filesystem é o seguinte:
+
+```bash
++-- /home/guilouro
+| +-- .virtualenvs #[Ambientes]
+| +-- www #[Projetos]
+```
+
+O Virtualenvwrapper criará meus projetos dentro de **www** e os ambientes em **.virtualenvs**. Mas para que isso aconteça temos que adicionar algumas linhas em nosso `~/.bashrc`
+
+```bash
+# adicione no final do arquivo ~/.bashrc
+# ...
+export PROJECT_HOME=~/www
+export WORKON_HOME=~/.virtualenvs
+source /usr/local/bin/virtualenvwrapper.sh
+```
+
+Atualize com o comando:
+
+```bash
+source ~/.bashrc
+```
+
+##### Criando nosso projeto
+
+```bash
+$ mkproject wsgi
+```
+
+Com as configurações anteriores o virtualenvwrapper já irá ativar o ambiente e levar você para a pasta do projeto. Mas para ativar é muito simples, basta usar:
+
+```bash
+$ workon wsgi
+```
+
+Com o nosso ambiente virtual criado partiremos então para a criação do nosso projeto django. Utilizarei a versão mais atual até o momento, nesse caso 1.7
+
+```bash
+$ pip install django
+```
+
+Não entrarei em detalhes para a configuração inicial do django, portanto irei usar um template que eu criei para a inicialização dos meus projeto. Criamos então o projeto dessa forma:
+
+```bash
+# startproject pelo template
+$ django-admin.py startproject --template https://github.com/guilouro/django-boilerplate/archive/master.zip wsgitest .
+# instala os pacotes
+$ pip install -r requirements.txt
+# faz a migração
+$ python manage.py migrate
+```
+
+Você encontra esse template aqui -> [django-boilerplate](https://github.com/guilouro/django-boilerplate)
+
+##### Criando um site no apache para o projeto
+
+Primeiramente, vamos criar um domínio fictício para responder com o nosso projeto ao ser acessado.
+
+```bash
+$ sudo vim /etc/hosts
+
+127.0.0.1 djangowsgi.com
+```
+
+Agora vamos configurar o apache:
+
+```bash
+$ sudo vim /etc/apache2/sites-available/djangowsgi
+```
+
+```apache
+WSGIDaemonProcess djangowsgi.com python-path=/home/guilouro/www/wsgi:/home/guilouro/.virtualenvs/wsgi/lib/python2.7/site-packages
+WSGIProcessGroup djangowsgi.com
+
+
+ ServerName djangowsgi.com
+ WSGIScriptAlias / /home/guilouro/www/wsgi/wsgitest/wsgi.py
+
+
+
+ Order allow,deny
+ Allow from all
+
+
+
+ Alias /media/ /home/guilouro/www/wsgi/media/
+ Alias /static/ /home/guilouro/www/wsgi/static/
+
+
+ Order allow,deny
+ Allow from all
+
+
+
+ Order allow,deny
+ Allow from all
+
+
+
+```
+
+Reinicie novamente o apache:
+
+```bash
+$ sudo service apache2 reload
+```
+
+Explicarei agora um pouco do que foi usado nessa configuração
+
+**WSGIScriptAlias:** é a url na qual você estará servindo sua aplicação (/ indica a url raiz), e a segunda parte é a localização de um "arquivo WSGI".
+
+### Modo daemon
+
+ O modo *daemon* é o modo recomendado para a execução do mod_wsgi(em plataformas não-windows). Ele gera um processo independente que lida com as solicitações e pode ser executado como um usuário diferente do servidor web. Um dos pontos positivos dele é que a cada alteração em seu projeto você não precisa restartar o apache, basta executar um `touch` no seu arquivo `wsgi.py`
+
+##### Directivas para o daemon
+
+**WSGIDaemonProcess:** Foi atribuido a ele o nosso servername e utilizamos `python-path` por se tratar de um projeto que esta em ambiente virtual. Passamos então nossas paths nele.
+
+**WSGIProcessGroup:** Atribuímos o servername a ele
+
+#### Testando a aplicação
+
+Agora acesse [http://djangowsgi.com](http://djangowsgi.com) e corre para o abraço.
+
+Espero que tenha ficado claro. Qualquer dúvida ou problema deixe nos comentários e vamos juntos tentar resolver.
+
+---
+##### Referências:
+
+- modwsgiwiki - [https://code.google.com/p/modwsgi/wiki/](https://code.google.com/p/modwsgi/wiki/)
+- Blogalizado - [http://www.blogalizado.com.br/deploy-de-aplicacao-django-no-apache-com-mod_wsgi/](http://www.blogalizado.com.br/deploy-de-aplicacao-django-no-apache-com-mod_wsgi/)
+- Documentação oficial do django - [https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/modwsgi/](https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/modwsgi/)
\ No newline at end of file
diff --git a/content/configurando-python-3.5-openshift.md b/content/configurando-python-3.5-openshift.md
new file mode 100644
index 000000000..12e9b17d9
--- /dev/null
+++ b/content/configurando-python-3.5-openshift.md
@@ -0,0 +1,171 @@
+Title: Configurando OpenShift com Python 3.5 + Flask + Gunicorn
+Slug: configurando-python-3.5-openshift-flask-gunicorn
+Date: 2017-04-23 20:37:39
+Category: Python
+Tags: python,tutorial,install,cloud
+Author: Horácio Dias
+Email: horacio.dias92@gmail.com
+Gravatarid:57db1afcce141efc81193425d4a5bbf0
+Github: nenodias
+Linkedin: nenodias92
+Facebook: nenodias
+Summary: Tutorial básico de como configurar o python 3.5 com o openshift + flask + gunicorn, utilizando o diy (Do It Yourself), carregando um cartridge customizado ...
+
+Configurando OpenShift com Python 3.5
+
+### Introdução
+
+O [OpenShift](https://www.openshift.com/) é uma plataforma de PasS que possibilita aos desenvolvedores "subir" aplicações na nuvem de uma maneira simples e rápida. Ele funciona a partir de gears(engrenagens) que representam máquinas que irão rodar as aplicações. Dentro de cada gear é possível instalar serviços, os são chamados de "cartridges".
+
+Existem 3 planos:
+
++ Online (gratuito, com três gears)
++ Enterprise (pago com suporte)
++ Origin (versão da comunidade e pode ser utilizado livremente)
+
+Um problema que me deparei ao utilizar o Openshift é que ele não possui um cartridge com Python3.5. Porém existe uma forma um pouco mais complicada de resolver esse problema.
+
+Após fazer seu cadastro no OpenShift e instalar o [client tools](https://developers.openshift.com/managing-your-applications/client-tools.html) que contém as ferramentas necessárias para configurar nossa aplicação.
+
+Após tudo isso vamos colocar a mão na massa, abra seu terminal e vamos lá.
+
+### Criando a aplicação
+
+``` shell
+rhc app create https://raw.githubusercontent.com/Grief/openshift-cartridge-python-3.5/master/metadata/manifest.yml diy-0.1
+```
+Substituindo "``" pelo nome de sua aplicação.
+O arquivo manifest.yml criado por Changaco(github) e "forkeado" por Grief(github) contém as configurações de um cartridge customizado que contém o python 3.5.
+
+Para os curiosos o conteúdo do arquivo
+```
+---
+Name: python
+Cartridge-Short-Name: PYTHON
+Display-Name: Only Python
+Description: 'An embedded cartridge that provides only python, nothing else.'
+Version: '3.5.0'
+Versions: ['3.5.0', '2.7.11']
+License: The Python License
+License-Url: http://docs.python.org/3/license.html
+Vendor: python.org
+Cartridge-Version: 0.0.2
+Cartridge-Vendor: praisebetoscience
+Categories:
+- service
+- python
+- embedded
+Website: https://github.com/praisebetoscience/openshift-cartridge-python-3.5
+Help-Topics:
+ Developer Center: https://www.openshift.com/developers
+Provides:
+- python
+Publishes:
+Subscribes:
+ set-env:
+ Type: ENV:*
+ Required: false
+ set-doc-url:
+ Type: STRING:urlpath
+ Required: false
+Scaling:
+ Min: 1
+ Max: -1
+Version-Overrides:
+ '2.7.11':
+ Display-Name: Python 2.7
+ License: The Python License, version 2.7
+ Provides:
+ - python-2.7
+ - python
+ - python(version) = 2.7
+ '3.5.0':
+ Display-Name: Python 3.5
+ License: The Python License, version 3.5
+ Provides:
+ - python-3.5
+ - python
+ - python(version) = 3.5
+```
+
+Após isso sua aplicação já estárá executando, caso deseje acessar o endereço da mesma deverá ser http://``-.rhcloud.com.
+Você verá que a página do seu projeto não é nada mais do que o diy (Dot It Yourself), que é uma aplicação Ruby de exemplo que você pode alterar, e é o que vamos fazer.
+
+Se você acessar o diretório do seu projeto verá que existe um diretório ".openshift", dentro desse diretório existe um outro diretório chamado "action_hooks", e dentro desse diretório existem dois arquivos "start" e "stop".
+
++ "``/.openshift/action_hooks/start"
++ "``/.openshift/action_hooks/stop"
+
+Os dois arquivos são respectivamente os comandos para "subir" e "pausar" sua aplicação.
+
+### Flask
+Vamos criar um projeto de exemplo, bem simples, que apenas nos retorne a versão do python utilizada.
+Primeiramente vamos criar nosso requirements.txt, com gunicorn e o flask.
+
+"requirements.txt"
+```
+gunicorn
+flask
+```
+
+Depois disso vamos criar o arquivo app.py que conterá nossa aplicação.
+
+"app.py"
+```
+import sys
+from flask import Flask
+
+app = Flask(__name__)
+
+@app.route('/')
+def index():
+ return sys.version
+
+```
+ Após isso basta fazer o commit de suas alterações.
+
+ ``` shell
+ git add .
+ git commit -am 'Minhas alterações'
+ ```
+
+Após isso você verá que sua aplicação não está rodando, pois ainda não alteramos os arquivos "start" e "stop".
+
+### Configurando o Gunicorn no Start e Stop
+O projeto diy do openshift nos deixa uma variável de ambiente $OPENSHIFT_DIY_IP com o IP da máquina, dessa forma podemos passar a variável e porta ao gunicorn.
+
+"start"
+``` shell
+#!/bin/bash
+nohup $HOME/python/usr/bin/pip3 install -r $OPENSHIFT_REPO_DIR/requirements.txt
+cd $OPENSHIFT_REPO_DIR
+nohup $HOME/python/usr/bin/gunicorn app:app --bind=$OPENSHIFT_DIY_IP:8080 |& /usr/bin/logshifter -tag diy &
+```
+
+A primeira linha é o [Shebang](https://pt.wikipedia.org/wiki/Shebang), o que significa que esse arquivo será executado pelo bash.
+Na segunda linha vemos [nohup](https://pt.wikipedia.org/wiki/Nohup), que executa os comandos em uma sessão separada, vemos logo apóes vemos o uma chamada ao pip para instalar nossas dependências.
+Na terceira linha vemos o nohup, e depois o gunicorn inicializa nossa aplicação flask.
+
+Isso só funciona pois o cartridge customizado instala o python3.5 dentro da pasta home do servidor do openshift.
+
+"stop"
+``` shell
+#!/bin/bash
+source $OPENSHIFT_CARTRIDGE_SDK_BASH
+
+# The logic to stop your application should be put in this script.
+if [ -z "$(ps -ef | grep gunicorn | grep -v grep)" ]
+then
+ client_result "Application is already stopped"
+else
+ kill `ps -ef | grep gunicorn | grep -v grep | awk '{ print $2 }'` > /dev/null 2>&1
+fi
+```
+Podemos ver que o comando ps procura algum processo do gunicorn, caso ele exista o kill será chamado.
+
+Após isso, é só fazer o commit das alterações e você verá sua aplicação rodando.
+
+Espero que o post ajude a quem quer subir alguma aplicação com python3.5 e só tem o heroku como opção.
+
+### Referências
+- [Imaster](https://www.profissionaisti.com.br/2015/04/openshift-paas-de-verdade/)
diff --git a/content/conteinerizando-suas-aplicacoes-django-com-docker-e-fig.md b/content/conteinerizando-suas-aplicacoes-django-com-docker-e-fig.md
new file mode 100755
index 000000000..0b09f92d4
--- /dev/null
+++ b/content/conteinerizando-suas-aplicacoes-django-com-docker-e-fig.md
@@ -0,0 +1,116 @@
+Title: Conteinerizando suas aplicações django com docker e fig
+Slug: conteinerizando-suas-aplicacoes-django-com-docker-e-fig
+Date: 2015-01-25 13:00
+Tags: django, docker, fig
+Author: Hudson Brendon
+Email: contato.hudsonbrendon@gmail.com
+Github: hudsonbrendon
+Twitter: hudsonbrendon
+Facebook: hudson.brendon
+Category: Django
+
+
+
+
+Se você como eu é um desenvolvedor incansável quando o assunto é automatizar ao máximo seu workflow de trabalho,este post foi feito para você. O [fig](http://www.fig.sh/) utilizando-se do docker, torna a criação de ambientes de desenvolvimento algo muito simples.
+
+
+#Instalação
+
+A instalação do fig é bem simples, primeiro você terá que ter o docker instalado em sua máquina, caso não tenha, siga esse [tutorial](http://hudsonbrendon.com/docker-introducao-e-aplicacao.html) onde exemplifico a instalação do mesmo de forma bem simples. Com o docker pronto, é hora de instalar o fig, essa ferramenta é um pacote python, e a forma mais simples de instalá-la é através do pip, que é o gerenciador de pacotes do python, caso não o tenha instalado em sua máquina, acesse o [site oficial](https://pip.pypa.io/en/latest/installing.html) e veja a forma mais simples para você obtê-lo. Com tudo pronto, execute no terminal.
+
+```bash
+$ pip install -U fig
+```
+
+#Utilizando o fig com django
+
+Com o docker e o fig devidamente instalados em sua máquina, é hora de integrar o django com essa maravilhosa ferramenta, para tanto, criaremos um diretório com um nome qualquer, aqui chamado de "app", e dentro do mesmo criaremos um arquivo chamado "Dockerfile", com o seguinte conteúdo.
+
+```bash
+FROM python:2.7
+ENV PYTHONUNBUFFERED 1
+RUN mkdir /code
+WORKDIR /code
+ADD requirements.txt /code/
+RUN pip install -r requirements.txt
+ADD . /code/
+```
+Em seguinda criaremos um arquivo chamado "requirements.txt", com os seguintes pacotes.
+
+```bash
+Django
+psycopg2
+```
+E por fim um arquivo, "fig.yml", com a configuração abaixo.
+
+```bash
+db:
+ image: postgres
+web:
+ build: .
+ command: python manage.py runserver 0.0.0.0:8000
+ volumes:
+ - .:/code
+ ports:
+ - "8000:8000"
+ links:
+ - db
+```
+#Quem é quem no jogo do bicho
+
+Com os arquivos criados é hora de entender qual a função de cada um no workflow.
+
+* **Dockerfile** - É o arquivo que especifica como uma imagem no docker será criada, os pacotes que serão instalados, usuários que serão criados, portas que serão expostas, diretórios que serão compartilhados entre o host e um container, etc. Para mais informações [acesse](http://hudsonbrendon.com/docker-introducao-e-aplicacao.html).
+
+* **requirements.txt** - É um arquivo que guarda todas as dependências de um projeto python.
+
+* **fig.yml** - É o arquivo de configuração do fig, é composto por blocos e cada bloco corresponde a um container, podendo eles serem "linkados", o fig utilizará esse arquivo como base para criar os conteineres necessários, e executar tudo que foi especificado no mesmo.
+
+Com os arquivos finalizados, é hora de criar uma aplicação em django, para isso basta.
+
+```bash
+$ fig run web django-admin.py startproject figexample .
+```
+E o resultado será esse:
+
+```bash
+$ ls
+Dockerfile fig.yml figexample manage.py requirements.txt
+```
+Com a aplicação em mãos, a primeira coisa que você deve fazer é abrir o arquivo settings.py de sua aplicação, e configurar o banco de dados da mesma. Para isso no arquivo figexample/settings.py basta especificar as configurações abaixo no banco de dados.
+
+```python
+DATABASES = {
+'default': {
+'ENGINE': 'django.db.backends.postgresql_psycopg2',
+'NAME': 'postgres',
+'USER': 'postgres',
+'HOST': 'db',
+'PORT': 5432,
+ }
+}
+```
+Com o banco configurado é hora de subir sua aplicação, na pasta raiz do projeto use.
+
+```bash
+$ fig up
+```
+O fig se encarregará de criar todos os conteineres, linkalos, e startar sua aplicação na porta 8000, acesse seu [localhost:8000](http://localhost:8000/) e você verá sua aplicação em execução.
+
+
+Para rodar os comandos do django, você pode fazer da seguinte forma.
+
+```bash
+$ fig run
+```
+Por exemplo.
+
+```bash
+$ fig run web ./manage.py syncdb
+```
+Lembrando que esse comando sempre será o padrão.
+
+#Conclusão
+
+Como você pode ver, o fig em conjunto com o docker torna seu workflow algo extremamente simples e eficaz. O melhor disso tudo, é que, para trabalhar com esse mesmo ambiente em uma nova máquina, você apenas precisará do fig e docker instalados, acessar a rais do projeto e executar um fig up, gerando com isso,uma comodidade jamais vista.
diff --git a/content/criando-dict-a-partir-de-dois-ou-mais-dicts.md b/content/criando-dict-a-partir-de-dois-ou-mais-dicts.md
new file mode 100644
index 000000000..b07c0ef53
--- /dev/null
+++ b/content/criando-dict-a-partir-de-dois-ou-mais-dicts.md
@@ -0,0 +1,161 @@
+Title: Criando dicts a partir de outros dicts
+Slug: crie_dict-a-partir-de-outros-dicts
+Date: 2019-10-01 20:20:29
+Category: Python
+Tags: Python, Dict
+Author: Michell Stuttgart
+Email: michellstut@gmail.com
+Github: mstuttgart
+Linkedin: mstuttgart
+Site: https://mstuttgart.github.io
+Summary: Crie dicts a partir de outros dicts
+
+Neste tutorial, será abordado o processo de criação de um *dict* ou dicionário, a partir de um ou mais *dicts* em Python.
+
+Como já é de costume da linguagem, isso pode ser feito de várias maneiras diferentes.
+
+## Abordagem inicial
+
+Pra começar, vamos supor que temos os seguintes dicionários:
+
+```python
+dict_1 = {
+ 'a': 1,
+ 'b': 2,
+}
+
+dict_2 = {
+ 'b': 3,
+ 'c': 4,
+}
+```
+
+Como exemplo, vamos criar um novo dicionário chamado **new_dict** com os valores de **dict_1** e **dict_2** logo acima. Uma abordagem bem conhecida é utilizar o método *update*.
+
+```python
+new_dict = {}
+
+new_dcit.update(dict_1)
+new_dcit.update(dict_2)
+```
+
+Assim, temos que **new_dict** será:
+
+```python
+>> print(new_dict)
+{
+ 'a': 1,
+ 'b': 3,
+ 'c': 4,
+}
+```
+
+Este método funciona bem, porém temos de chamar o método *update* para cada *dict* que desejamos mesclar em **new_dict**. Não seria interessante se fosse possível passar todos os *dicts* necessários já na inicialização de **new_dict**?
+
+### Novidades do Python 3
+
+O Python 3 introduziu uma maneira bem interessante de se fazer isso, utilizando os operadores `**`.
+
+```python
+new_dict = {
+ **dict_1,
+ **dict_2,
+}
+
+```
+
+Assim, de maneira semelhante ao exemplo anterior, temos que **new_dict** será :
+
+```python
+>> print(new_dict['a'])
+1
+>> print(new_dict['b'])
+3
+>> print(new_dict['c'])
+4
+```
+
+## Cópia real de *dicts*
+
+Ao utilizamos o procedimento de inicialização acima, devemos tomar conseiderar alguns fatores. Apenas os valores do primeiro nível serão realmente duplicados no novo dicionário. Como exemplo, vamos alterar uma chave presente em ambos os *dicts* e verificar se as mesmas possuem o mesmo valor:
+
+```python
+>> dict_1['a'] = 10
+>> new_dict['a'] = 11
+>> print(dict_1['a'])
+10
+>> print(new_dict['a'])
+11
+```
+
+Porém isso muda quando um dos valores de **dict_1** for uma *list*, outro *dict* ou algum objeto complexo. Por exemplo:
+
+```python
+dict_3 = {
+ 'a': 1,
+ 'b': 2,
+ 'c': {
+ 'd': 5,
+ }
+}
+```
+
+e agora, vamos criar um novo *dict* a partir desse:
+
+```python
+new_dict = {
+ **dict_3,
+}
+
+```
+
+Como no exemplo anterior, podemos imaginar que foi realizado uma cópia de todos os elementos de **dict_3**, porém isso não é totalmente verdade. O que realmente aconteceu é que foi feita uma cópia *superficial* dos valores de **dict_3**, ou seja, apenas os valores de *primeiro nível* foram duplicados. Observe o que acontece quando alteramos o valor do *dict* presente na chave **c**.
+
+```python
+>> new_dict['c']['d'] = 11
+>> print(new_dict['c']['d'])
+11
+>> print(dict_3['c']['d'])
+11
+# valor anterior era 5
+
+```
+
+No caso da chave **c**, ela contem uma referência para outra estrutura de dados (um *dict*, no caso). Quando alteramos algum valor de **dict_3['c']**, isso reflete em todos os *dict* que foram inicializados com **dict_3**. Em outras palavras, deve-se ter cuidado ao inicializar um *dict* a partir de outros **dicts** quando os mesmos possuírem valores complexos, como *list*, *dict* ou outros objetos (os atributos deste objeto não serão duplicados).
+
+De modo a contornar este inconveniente, podemos utilizar o método *deepcopy* da *lib* nativa [copy](https://docs.python.org/2/library/copy.html). Agora, ao inicializarmos **new_dict**:
+
+```python
+import copy
+
+dict_3 = {
+ 'a': 1,
+ 'b': 2,
+ 'c': {
+ 'd': 5,
+ }
+}
+
+new_dict = copy.deepcopy(dict_3)
+```
+
+O método *deepcopy* realiza uma cópia recursiva de cada elemento de **dict_3**, resolvendo nosso problema. Veja mais um exemplo:
+
+```python
+>> new_dict['c']['d'] = 11
+>> print(new_dict['c']['d'])
+11
+>> print(dict_3['c']['d'])
+5
+# valor não foi alterado
+```
+
+## Conclusão
+
+Este artigo tenta demonstrar de maneira simples a criação de *dicts*, utilizando os diversos recursos que a linguagem oferece bem como os prós e contras de cada abordagem.
+
+## Referências
+
+Para mais detalhes e outros exemplos, deem uma olhada neste *post* do forum da Python Brasil [aqui](https://groups.google.com/forum/#!topic/python-brasil/OhUqYQ32M7E).
+
+É isso pessoal. Obrigado por ler!
diff --git a/content/criando-novos-comandos-no-django-admin.md b/content/criando-novos-comandos-no-django-admin.md
new file mode 100644
index 000000000..1d491774d
--- /dev/null
+++ b/content/criando-novos-comandos-no-django-admin.md
@@ -0,0 +1,440 @@
+title: Criando novos comandos no django-admin
+Slug: criando-novos-comandos-no-django-admin
+Date: 2015-12-03 22:00
+Tags: Python, Django
+Author: Regis da Silva
+Email: regis.santos.100@gmail.com
+Github: rg3915
+Twitter: rg3915
+Category: Django
+
+Veja aqui como criar o seu próprio comando para ser usado com o django-admin ou manage.py do Django.
+
+O [django-admin ou manage.py][1] já tem um bocado de comandos interessantes, os mais utilizados são:
+
+* [startproject][4] - cria novos projetos.
+* [startapp][5] - cria novas apps.
+* [makemigrations][6] - cria novas migrações baseadas nas mudanças detectadas nos modelos Django.
+* [migrate][7] - sincroniza o banco de dados com as novas migrações.
+* [createsuperuser][8] - cria novos usuários.
+* [test][9] - roda os testes da aplicação.
+* [loaddata][14] - carrega dados iniciais a partir de um json, por exemplo, `python manage.py loaddata fixtures.json`
+* [shell][15] - inicializa um interpretador Python interativo.
+* [dbshell][18] - acessa o banco de dados através da linha de comando, ou seja, você pode executar comandos sql do banco, por exemplo, diretamente no terminal.
+* [inspectdb][16] - retorna todos os modelos Django que geraram as tabelas do banco de dados.
+* [runserver][17] - roda o servidor local do projeto Django.
+
+Mas de repente você precisa criar um comando personalizado conforme a sua necessidade. A palavra chave é `BaseCommand` ou [Writing custom django-admin commands][2].
+
+## Começando do começo
+
+> Importante: estamos usando Django 1.8.12 e Python 3.
+
+### Criando o projeto
+
+Eu usei esta sequência de comandos para criar o projeto.
+
+```bash
+# Criando djangoproject.
+mkdir djangoproject
+cd djangoproject
+
+# Criando virtualenv
+virtualenv -p python3 .venv
+
+# Ativando o .venv.
+source .venv/bin/activate
+# Diminuindo o caminho do prompt (opcional)
+PS1="(`basename \"$VIRTUAL_ENV\"`)\e[1;34m:/\W\033[00m$ "
+
+# Instalando o Django
+pip install django==1.8.12
+pip freeze > requirements.txt
+
+# Criando o projeto myproject ...
+django-admin.py startproject myproject .
+cd myproject
+
+# Criando a app 'core' ...
+python ../manage.py startapp core
+cd ..
+
+# Editando settings.py"
+sed -i "/django.contrib.staticfiles/a\ 'myproject.core'," myproject/settings.py
+```
+
+Pronto! Agora nós já temos um projetinho Django funcionando. Note que o nome da app é **core**.
+
+### Criando as pastas
+
+Para criarmos um novo comando precisamos das seguintes pastas:
+
+ core
+ ├── management
+ │ ├── __init__.py
+ │ ├── commands
+ │ │ ├── __init__.py
+ │ │ ├── novocomando.py
+
+No nosso caso, teremos 3 novos comandos, então digite, estando na pasta `djangoproject`
+
+```bash
+mkdir -p core/management/commands
+touch core/management/__init__.py
+touch core/management/commands/{__init__.py,hello.py,initdata.py,search.py}
+```
+
+
+## Sintaxe do novo comando
+
+> Importante: estamos usando Django 1.8.12 e Python 3.
+
+O Django 1.8 usa o `argparse` como parser de argumentos do `command`, mais informações em [module-argparse][19].
+
+```python
+from django.core.management.base import BaseCommand, CommandError
+from optparse import make_option
+
+class Command(BaseCommand):
+ help = 'Texto de ajuda aqui.'
+ option_list = BaseCommand.option_list + (
+ make_option('--awards', '-a',
+ action="http://github.com/store_true",
+ help='Ajuda da opção aqui.'),
+ )
+
+ def handle(self, **options):
+ self.stdout.write('Hello world.')
+ if options['awards']:
+ self.stdout.write('Awards')
+```
+
+Entendeu? Basicamente o `handle` é a função que executa o comando principal, no caso o `self.stdout.write('Hello world.')`, ou seja, se você digitar o comando a seguir ele imprime a mensagem na tela.
+
+```bash
+$ python manage.py hello
+Hello World
+```
+
+`--awards` é um argumento opcional, você também pode digitar `-a`.
+
+```bash
+$ python manage.py hello -a
+Hello World
+Awards
+```
+
+`action="http://github.com/store_true"` significa que ele armazena um valor verdadeiro.
+
+**Obs**: A partir do Django 1.8 os comandos de argumentos opcionais são baseados em `**options`.
+
+
+Veja uma outra forma de escrever
+
+```python
+from django.core.management.base import BaseCommand, CommandError
+
+class Command(BaseCommand):
+
+ def add_arguments(self, parser):
+ # Argumento nomeado (opcional)
+ parser.add_argument('--awards', '-a',
+ action='http://github.com/store_true',
+ help='Ajuda da opção aqui.')
+
+ def handle(self, *args, **options):
+ self.stdout.write('Hello world.')
+ if options['awards']:
+ self.stdout.write('Awards')
+```
+
+A diferença é que aqui usamos `parser.add_argument` ao invés de `make_option`.
+
+### hello.py
+
+```python
+from django.core.management.base import BaseCommand, CommandError
+# minimalista
+class Command(BaseCommand):
+ help = 'Print hello world'
+
+ def handle(self, **options):
+ self.stdout.write('Hello World')
+```
+
+**Uso**
+
+```bash
+$ python manage.py hello
+```
+
+### initdata.py
+
+**Objetivo**: Obter alguns filmes de uma api e salvar os dados no banco.
+
+**api**: [omdbapi.com][3]
+
+**models.py**
+
+```python
+from django.db import models
+
+class Movie(models.Model):
+ title = models.CharField(u'título', max_length=100)
+ year = models.PositiveIntegerField('ano', null=True, blank=True)
+ released = models.CharField(u'lançamento', max_length=100, default='', blank=True)
+ director = models.CharField('diretor', max_length=100, default='', blank=True)
+ actors = models.CharField('atores', max_length=100, default='', blank=True)
+ poster = models.URLField('poster', null=True, blank=True)
+ imdbRating = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
+ imdbID = models.CharField(max_length=50, default='', blank=True)
+
+ class Meta:
+ ordering = ['title']
+ verbose_name = 'filme'
+ verbose_name_plural = 'filmes'
+
+ def __str__(self):
+ return self.title
+```
+
+Não se esqueça de fazer
+
+```bash
+python manage.py makemigrations
+python manage.py migrate
+```
+
+**admin.py**
+
+Vamos visualizar pelo admin.
+
+```python
+from django.contrib import admin
+from core.models import Movie
+
+admin.site.register(Movie)
+```
+
+Instale o `requests`
+
+```bash
+pip install requests
+```
+
+**initdata.py**
+
+O código a seguir é longo, mas basicamente temos
+
+* `print_red(name)` função que imprime um texto em vermelho (opcional)
+* `get_html(year)` função que lê os dados da api usando [requests][20], e depois escolhe um filme randomicamente a partir de 2 letras
+* `get_movie(year)` se o dicionário conter `{'Response': 'True', ...}` então retorna um dicionário do filme localizado
+* `save()` salva os dados no banco
+* `handle(movies, year)` este é o comando principal. Busca os filmes várias vezes, conforme definido pela variável `movies`, e salva os n filmes.
+
+
+```python
+# -*- coding: utf-8 -*- #
+
+import random
+import string
+import requests
+from django.core.management.base import BaseCommand, CommandError
+from django.core.exceptions import ValidationError
+from optparse import make_option
+from core.models import Movie
+
+
+class Command(BaseCommand):
+ help = """Faz o crawler numa api de filmes e retorna os dados.
+ Uso: python manage.py initdata
+ ou: python manage.py initdata -m 20
+ ou: python manage.py initdata -m 20 -y 2015"""
+ option_list = BaseCommand.option_list + (
+ make_option('--movies', '-m',
+ dest='movies',
+ default=10,
+ help='Define a quantidade de filmes a ser inserido.'),
+ make_option('--year', '-y',
+ dest='year',
+ action='http://github.com/store',
+ default=None,
+ help='Define o ano de lançamento do filme.'),
+ )
+
+ def print_red(self, name):
+ """imprime em vermelho"""
+ print("\033[91m {}\033[00m".format(name))
+
+ def get_html(self, year):
+ """
+ Le os dados na api http://www.omdbapi.com/ de forma aleatoria
+ e escolhe um filme buscando por 2 letras
+ """
+
+ # Escolhe duas letras aleatoriamente
+ letters = ''.join(random.choice(string.ascii_lowercase) for _ in range(2))
+
+ # Se não for definido o ano, então escolhe um randomicamente
+ if year is None:
+ year = str(random.randint(1950, 2015))
+ url = 'http://www.omdbapi.com/?t={letters}*&y={year}&plot=short&r=json'.format(letters=letters, year=str(year))
+ return requests.get(url).json()
+
+ def get_movie(self, year, **kwargs):
+ """ Retorna um dicionário do filme """
+
+ movie = self.get_html(year)
+ j = 1 # contador
+
+ # Faz a validação de Response. Se a resposta for falsa, então busca outro filme.
+ while movie['Response'] == 'False' and j < 100:
+ movie = self.get_html(year)
+ self.print_red('Tentanto %d vezes\n' % j)
+ j += 1
+ return movie
+
+ def save(self, **kwargs):
+ """SALVA os dados"""
+ try:
+ Movie.objects.create(**kwargs)
+ except ValidationError as e:
+ self.print_red(e.messages)
+ self.print_red('O objeto não foi salvo.\n')
+
+ def handle(self, movies, year, **options):
+ """ se "movies" não for nulo, transforma em inteiro """
+
+ self.verbosity = int(options.get('verbosity'))
+
+ if movies is not None:
+ movies = int(movies)
+
+ # busca os filmes n vezes, a partir da variavel "movies"
+ for i in range(movies):
+ # verifica as validações
+ m = self.get_movie(year)
+ if m['imdbRating'] == "N/A":
+ m['imdbRating'] = 0.0
+
+ # Transforma "year" em inteiro
+ if "–" in m['Year']:
+ m['Year'] = year
+
+ data = {
+ "title": m['Title'],
+ "year": m['Year'],
+ "released": m['Released'],
+ "director": m['Director'],
+ "actors": m['Actors'],
+ "poster": m['Poster'],
+ "imdbRating": m['imdbRating'],
+ "imdbID": m['imdbID'],
+ }
+
+ self.save(**data)
+
+ if self.verbosity > 0:
+ self.stdout.write('\n {0} {1} {2}'.format(i + 1, data['year'], data['title']))
+ if self.verbosity > 0:
+ self.stdout.write('\nForam salvos %d filmes' % movies)
+```
+
+**Uso**
+
+```bash
+Usage: python manage.py initdata [options]
+
+Faz o crawler numa api de filmes e retorna os dados.
+ Uso: python manage.py initdata
+ ou: python manage.py initdata -m 20
+ ou: python manage.py initdata -m 20 -y 2015
+```
+
+
+### search.py
+
+**Objetivo**: Localizar o filme pelo título ou ano de lançamento.
+
+```python
+from django.core.management.base import BaseCommand, CommandError
+from optparse import make_option
+from core.models import Movie
+
+
+class Command(BaseCommand):
+ help = """Localiza um filme pelo título ou ano de lançamento.
+ Uso: python manage.py search -t 'Ted 2'
+ ou: python manage.py search -y 2015
+ ou: python manage.py search -t 'a' -y 2015"""
+
+ option_list = BaseCommand.option_list + (
+ make_option('--title', '-t',
+ dest='title',
+ default=None,
+ help='Localiza um filme pelo título.'),
+ make_option('--year', '-y',
+ dest='year',
+ default=None,
+ help='Localiza um filme pelo ano de lançamento.'),
+ )
+
+ def handle(self, title=None, year=None, **options):
+ """ dicionário de filtros """
+ self.verbosity = int(options.get('verbosity'))
+
+ filters = {
+ 'title__istartswith': title,
+ 'year': year
+ }
+
+ filter_by = {key: value for key, value in filters.items() if value is not None}
+ queryset = Movie.objects.filter(**filter_by)
+
+ if self.verbosity > 0:
+ for movie in queryset:
+ self.stdout.write("{0} {1}".format(movie.year, movie.title))
+ self.stdout.write('\n{0} filmes localizados.'.format(queryset.count()))
+```
+
+**Uso**
+
+```bash
+Usage: python manage.py search [options]
+
+Localiza um filme pelo título ou ano de lançamento.
+ Uso: python manage.py search -t 'Ted 2'
+ ou: python manage.py search -y 2015
+ ou: python manage.py search -t 'a' -y 2015
+```
+
+
+[Aqui][10] tem um exemplo legal que eu usei como ideia pra fazer este post.
+
+Mais algumas referências:
+
+[Writing custom django-admin commands][2]
+
+[Zachary Voase: Fixing Django Management Commands][11]
+
+[Adding Custom Commands to manage.py and django-admin.py by dave][12]
+
+[1]: https://docs.djangoproject.com/en/1.8/ref/django-admin/
+[2]: https://docs.djangoproject.com/en/1.8/howto/custom-management-commands/
+[3]: http://www.omdbapi.com/
+[4]: https://docs.djangoproject.com/en/1.8/ref/django-admin/#startproject-projectname-destination
+[5]: https://docs.djangoproject.com/en/1.8/ref/django-admin/#startapp-app-label-destination
+[6]: https://docs.djangoproject.com/en/1.8/ref/django-admin/#makemigrations-app-label
+[7]: https://docs.djangoproject.com/en/1.8/ref/django-admin/#migrate-app-label-migrationname
+[8]: https://docs.djangoproject.com/en/1.8/ref/django-admin/#createsuperuser
+[9]: https://docs.djangoproject.com/en/1.8/ref/django-admin/#test-app-or-test-identifier
+[10]: https://github.com/rhblind/django-gcharts/blob/master/demosite/management/commands/initdata.py
+[11]: http://zacharyvoase.com/2009/12/09/django-boss/
+[12]: http://thingsilearned.com/2009/03/13/adding-custom-commands-to-managepy-and-django-adminpy/
+[14]: https://docs.djangoproject.com/en/1.8/ref/django-admin/#loaddata-fixture-fixture
+[15]: https://docs.djangoproject.com/en/1.8/ref/django-admin/#shell
+[16]: https://docs.djangoproject.com/en/1.8/ref/django-admin/#inspectdb
+[17]: https://docs.djangoproject.com/en/1.8/ref/django-admin/#runserver-port-or-address-port
+[18]: https://docs.djangoproject.com/en/1.8/ref/django-admin/#dbshell
+[19]: https://docs.python.org/2/library/argparse.html#module-argparse
+[20]: http://docs.python-requests.org/en/latest/
\ No newline at end of file
diff --git a/content/curso-asyncio-aula00.rst b/content/curso-asyncio-aula00.rst
new file mode 100644
index 000000000..5c10a5be4
--- /dev/null
+++ b/content/curso-asyncio-aula00.rst
@@ -0,0 +1,28 @@
+Curso Python asyncio: Aula 00 - Introdução ao módulo asyncio
+############################################################
+
+:date: 2016-06-03 17:30
+:tags: python, asyncio
+:category: Tutoriais
+:slug: curso-asyncio-aula00
+:author: Carlos Maniero
+:email: carlosmaniero@gmail.com
+:github: carlosmaniero
+:twitter: carlosmaniero
+:linkedin: carlosmaniero
+:facebook: carlosmaniero
+
+
+Primeira aula do curso de asyncio.
+Nessa vídeo aula são abordadas as principais diferenças entre Concorrência e Paralelismo.
+
+Slides:
+http://carlosmaniero.github.io/curso-asyncio/aula00/
+
+GitHub:
+http://github.com/carlosmaniero/
+http://github.com/carlosmaniero/curso-asyncio
+
+http://carlosmaniero.github.io/
+
+.. youtube:: xGoEpCaachs
diff --git a/content/curso-asyncio-aula1.rst b/content/curso-asyncio-aula1.rst
new file mode 100644
index 000000000..962a2ba4d
--- /dev/null
+++ b/content/curso-asyncio-aula1.rst
@@ -0,0 +1,30 @@
+Curso Python asyncio: Aula 01 - Iterators e Generators
+######################################################
+
+:date: 2016-06-11 12:30
+:tags: python, asyncio
+:category: Tutoriais
+:slug: curso-asyncio-aula1
+:author: Carlos Maniero
+:email: carlosmaniero@gmail.com
+:github: carlosmaniero
+:twitter: carlosmaniero
+:linkedin: carlosmaniero
+:facebook: carlosmaniero
+
+
+Entendendo o conceito de Iterator e Generator.
+
+Primeira Aula:
+https://www.youtube.com/watch?v=xGoEpCaachs
+
+Slides:
+http://carlosmaniero.github.io/curso-asyncio/aula01/
+
+GitHub:
+http://github.com/carlosmaniero/
+http://github.com/carlosmaniero/curso-asyncio
+
+http://carlosmaniero.github.io/
+
+.. youtube:: jksHpfsarfc
diff --git a/content/debugging-em-python-sem-ide.md b/content/debugging-em-python-sem-ide.md
new file mode 100644
index 000000000..0495cc291
--- /dev/null
+++ b/content/debugging-em-python-sem-ide.md
@@ -0,0 +1,242 @@
+title: Debugging em python (sem IDE)
+Slug: debugging-em-python-sem-ide
+Date: 2015-02-15 22:30
+Tags: python,pdb,ipython,ipdb,debugging
+Author: Diego Garcia
+Email: drgarcia1986@gmail.com
+Github: drgarcia1986
+Site: http://www.codeforcloud.info
+Twitter: drgarcia1986
+Linkedin: drgarcia1986
+Category: debugging
+
+
+
+
+
+
+Um dos principais motivos que ainda levam desenvolvedores Python a recorrerem a IDEs pesadas e que requerem instalação é o **debugging**.
+Devs que vieram de linguagens como _DotNet_, _Java_ e _Delphi_ por exemplo, estão acostumados a IDEs super pesadas e inchadas que no final das contas, além do debugging, só servem para drenar memória RAM.
+Brincadeiras a parte, não a motivos para você não dar uma chance ao **VIM** ou ao **Sublime**, pois para fazer debugging em scripts python, tudo que você precisa é o **PDB**.
+
+
+
+# PDB
+O `pdb` é um módulo _buit-in_ que funciona como um console interativo, onde é posssível realizar debug de códigos python.
+Nele é possível fazer um _step-by-step_ do código, verificando o valor de variaveis, definindo breakpoints, manipulando valores, etc.
+É possível inclusive realizer _step-into_ em métodos. Ou seja, tudo que uma boa ferramenta de debug precisa ter.
+
+## Comandos
+Antes de partirmos para prática, é importante conhecer alguns comandos básicos para já começar o uso do pdb de forma efetiva.
+
+Durante o debugging, eventualmente seu script irá _estacionar_ em pontos de paradas, possívelmente definidos por você, neste momento, os comandos a seguir poderão ser utilizados.
+
+### q (quit)
+Sai da execução do script.
+
+### n (next)
+Avança para a próxima linha do script.
+
+### p (print)
+Executa o comando `print` do python, por exemplo:
+```python
+> /script.py(1)()
+-> foo = "foo var"
+(Pdb) p foo
+'foo var'
+```
+> Vale ressaltar que no exemplo acima, não é necessário utilizar o comando `p`, basta digitar o nome da variável e pressionar `enter`, o efeito seria o mesmo.
+
+### c (continue)
+Avança o debug até o próximo **breakpoint** ou até ocorrer uma **exception**.
+
+### l (list)
+Lista algumas linhas do código que estão em volta da linha atual.
+Por padrão serão apresentadas 11 linhas (5 acima e 5 abaixo).
+
+### s (step into)
+Ao realizar a navegação através do comando `n` o debug **não** irá _entrar_ em métodos que possívelmente forem invocados.
+Para que o debug entre no método que está sendo invocado na linha corrente, basta trocar o comando `n`, pelo comando `s`.
+```python
+> /home/user/foo.py(20)()
+-> foo.bar('barz')
+(Pdb) s
+--Call--
+> /home/user/foo.py(3)bar()
+-> def bar(self, the_bar):
+(Pdb)
+```
+
+### r (return)
+Já o comando `r` libera a execução do script até sair da função atual.
+
+### b (breakpoint)
+Cria um breakpoint em uma determinada linha ou método, por exemplo.
+```python
+> /script.py(1)()
+(Pdb) b 21
+Breakpoint 1 at /script.py:21
+```
+No comando acima, setamos um breakpoint na linha 21 de nosso script.
+```python
+> /script.py(1)()
+(Pdb) b foo
+Breakpoint 1 at /script.py:30
+```
+Já no exemplo acima, setamos o breakpoint para o método `foo`.
+O pdb informa qual linha ele setou o breakpoint, em nosso exemplo o método `foo` está na linha 30 do script.
+
+### a (arguments)
+O comando `a` mostra os argumentos que foram passados para a função atual.
+
+```python
+> /home/user/foo.py(20)()
+-> foo.bar('barz')
+(Pdb) s
+--Call--
+> /home/user/foo.py(3)bar()
+-> def bar(self, the_bar):
+(Pdb) a
+the_bar = "barz"
+```
+
+### ENTER
+Se você pressionar o `ENTER` sem nenhum comando no pdb, ele irá repetir o último comando executado.
+
+## Debug na prática
+Vamos utilizar um script python simples e didático como exemplo.
+
+```python
+class NumberList(object):
+ def __init__(self):
+ self.numbers = list()
+
+ def add(self, number):
+ if not isinstance(number, (int, float)):
+ raise TypeError
+ self.numbers.append(number)
+
+ def sum(self):
+ result = 0
+ for i in self.numbers:
+ result += i
+ return result
+
+
+if "__main__" == __name__:
+ numbers = NumberList()
+
+ numbers.add(5)
+ assert numbers.sum() == 5
+
+ numbers.add(10)
+ assert numbers.sum() == 15
+
+ print "The End"
+```
+Esse script possui uma classe chamada `NumberList` que armazena uma lista de numeros e retorna a soma deles.
+Além destas classe, esse script também realiza algumas operações como instanciar essa classe e realizar alguns testes de asserção.
+Salve esse script em um arquivo chamado `numbers.py` para ser utilizado em nossos exemplos.
+
+## Modos de uso do pdb
+
+Na prática o pdb se assemelha bastante ao prompt interativo do python, com a diferença dos caracteres identificadores.
+Enquanto que no prompt interativo do python o identificador é o `>>>`, no pdb o identificador é `(Pdb)`.
+Existem algumas maneiras de usar o pdb, depende da forma como você pretende realizer o debug.
+
+### pdb.py
+Uma delas é através da chamada do script `pdb.py` passando como paramêtro o script para ser feito do debug, por exemplo:
+
+```bash
+python -m pdb numbers.py
+```
+Isso fará com que o pdb seja iniciado na primeira linha do script `numbers.py`, no caso, a declaração da classe `NumberList()`.
+Caso você execute o comando `n`, a próxima linha será o `if "__main__" == __name__:` e assim por diante.
+Utilizando desta maneira, você pode verificar linha a linha do script ou _setar_ um breakpoint assim que entrar no debug, por exemplo, se você quer criar um breakpoint na execução do método `sum()` de uma instância da classe `NumberList()`, basta executar o comando `b numbers.sum`.
+
+```python
+(venv)user@machine:~/$ python -m pdb numbers.py
+> /home/user/numbers.py(4)()
+-> class NumberList(object):
+(Pdb) n
+> /home/user/numbers.py(20)()
+-> if __name__ == "__main__":
+(Pdb) n
+> /home/user/numbers.py(21)()
+-> numbers = NumberList()
+(Pdb) n
+> /home/user/numbers.py(23)()
+-> numbers.add(5)
+(Pdb) b numbers.sum
+Breakpoint 1 at /home/user/numbers.py:13
+(Pdb)
+```
+
+Ou para simplificar, também poderiamos setar o breakpoint pelo número da linha.
+
+```python
+(venv)user@machine:~/$ python -m pdb numbers.py
+> /home/user/numbers.py(4)()
+-> class NumberList(object):
+(Pdb) b 13
+Breakpoint 1 at /home/user/numbers.py:13
+(Pdb)
+```
+### pdb.set_trace()
+Outra forma é utilizando o método `set_trace()` do pacote `pdb`.
+Com o `pdb.set_trace()` você pode definir onde será o seu breakpoint via código, por exemplo, faremos uma alteração em nosso script para setar um breakpoint no método `NumberList().sum()`.
+```python
+class NumberList(object):
+ def __init__(self):
+ self.numbers = list()
+
+ def add(self, number):
+ if not isinstance(number, (int, float)):
+ raise TypeError
+ self.numbers.append(number)
+
+ def sum(self):
+ import pdb
+ pdb.set_trace()
+
+ result = 0
+ for i in self.numbers:
+ result += i
+ return result
+
+"""
+Resto do script omitido
+"""
+```
+Dessa forma, ao executar o script (sem a necessidade de ser via pdb) e passar pelo método `pdb.set_trace()` será iniciado um prompt interativo do pdb.
+
+```python
+(venv)user@machine:~/$ python numbers.py
+> /home/user/numbers.py(16)sum()
+-> result = 0
+(Pdb)
+```
+
+## ipdb
+Uma das desvantagens do prompt interativo do python é a falta de _syntax highlighting_ e _code completion_, com o pdb não é diferente, porém, assim como podemos recorrer ao [ipython](http://ipython.org/) para isso, também podemos utilizar o [ipdb](https://github.com/gotcha/ipdb).
+O `ipdb` é uma espécie de wrapper para o pdb que faz uso das rotinas de debug do `IPython`.
+A maneira de uso se assemelha bastante ao pdb, bastando trocar o pacote `pdb` pelo pacote `ipdb`.
+
+```python
+import ipdb
+
+foo = "foo"
+ipdb.set_trace()
+bar = "bar"
+```
+
+Para instalar o ipdb basta utilizar o `pip`
+
+```
+pip install ipdb
+```
+Com certeza recomendo o uso do `ipdb` principalmente por ser mais intuitivo.
+
+**Referências**
+[Documentação Oficial](https://docs.python.org/2/library/pdb.html)
+[ipdb](https://github.com/gotcha/ipdb)
diff --git a/content/debugging-logging.md b/content/debugging-logging.md
new file mode 100644
index 000000000..48c16c2b4
--- /dev/null
+++ b/content/debugging-logging.md
@@ -0,0 +1,66 @@
+Title: Debugging - logging
+Date: 2016-11-27 17:48
+Tags: python, debugging, logging
+Category: Python
+Slug: debugging-logging
+Author: Bruno Santana
+Email: santanasta@gmail.com
+Github: BrunoLSA
+
+
+Achei algo interessante no livro que estou lendo (Automatize tarefas maçantes com Python) e resolvi compartilhar.
+
+Trata-se do Logging, que ajuda no debug do programa.
+
+Vejam o exemplo nesse programa, com falha:
+
+ :::python
+ import logging
+ logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %(message)s')
+
+
+ logging.debug('Start of program')
+
+ def factorial(n):
+ logging.debug('Start of factorial(%s%%)' % (n))
+ total = 1
+ for i in range(n+1):
+ total *= i
+ logging.debug('i is ' + str(i) + ', total is ' + str(total))
+ logging.debug('End of factorial(%s%%)' % (n))
+ return total
+
+ print(factorial(5))
+ logging.debug('End of program')
+
+
+O programa retorna:
+
+ :::python
+ 2016-11-15 16:17:30,339 - DEBUG - Start of program
+ 2016-11-15 16:17:30,340 - DEBUG - Start of factorial(5%)
+ 2016-11-15 16:17:30,340 - DEBUG - i is 0, total is 0
+ 2016-11-15 16:17:30,340 - DEBUG - i is 1, total is 0
+ 2016-11-15 16:17:30,340 - DEBUG - i is 2, total is 0
+ 2016-11-15 16:17:30,340 - DEBUG - i is 3, total is 0
+ 2016-11-15 16:17:30,340 - DEBUG - i is 4, total is 0
+ 2016-11-15 16:17:30,340 - DEBUG - i is 5, total is 0
+ 2016-11-15 16:17:30,340 - DEBUG - End of factorial(5%)
+ 2016-11-15 16:17:30,340 - DEBUG - End of program
+ 0
+
+Dessa forma, podemos ver o passo a passo que o programa está realizando e identificar onde está o erro. No caso, vemos que para corrigir o problema, devemos alterar o **for i in range(n+1):** para **for i in range(1, n+1):**.
+Quando o desenvolvedor não quiser mais visualizar as mensagens de logging, basta chamar **logging.disable(logging.CRITICAL)** logo embaixo do **import logging**. Essa função faz com que não seja necessário alterar o programa removendo todas as chamadas de logging manualmente.
+
+Também é possível gravar as mensagens de log num arquivo, ao invés de mostrá-las na tela. A função aceita o argumento **filename**.
+
+ :::python
+ import logging
+ logging.basicConfig(filename='myProgramLog.txt', level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %(message)s')
+
+
+Lado negativo do uso dessa função: a leitura do código fica difícil, por causa desse monte de logging.debug no meio do código. Para evitar isso, pode-se usar um decorator.
+
+
+
+
diff --git a/content/deploy-com-dokku.md b/content/deploy-com-dokku.md
new file mode 100644
index 000000000..2fcfb097a
--- /dev/null
+++ b/content/deploy-com-dokku.md
@@ -0,0 +1,144 @@
+Title: Deploy rápido e simples com Dokku
+Slug: deploy-rapido-simples-com-dokku
+Date: 2016-11-23 17:30
+Tags: python,deploy,django,dokku
+Author: Júnior Carvalho
+Email: joseadolfojr@gmail.com
+Github: juniorcarvalho
+twitter: @joseadolfojr
+Linkedin: juniorcarvalhorj
+Category: Python, Deploy
+
+Sempre busquei alternativas para deploy simples como o heroku. Vou mostrar neste passo-a-passo uma forma simples e rápida utilizando o [Dokku].
+
+[Dokku] é a menor implementação PaaS que você já viu. De uma forma simples e rápida consegue-se configurar um servidor para deploy. Se existe alguma dúvida sobre PaaS, SaaS, etc., uma pesquisa rápida no google vai retornar várias referências.
+
+Nesse exemplo vou utilizar uma vps básica na [DigitalOcean]. Uma máquina 512 MB / 20 Gb/ 1 CPU, rodando Ubuntu Server 16.04.1. É possível criar uma máquina já com [Dokku] instalado e pré-configurado mas vou fazer com uma máquina 'limpa' para servir de base para outras vps.
+### Instalando
+Com o servidor em execução vamos acessar via ssh pelo terminal. No caso de utilizar OS Windows, utilize o [putty] para acessar o servidor.
+
+```
+ssh [ip-do-servidor] -l root
+```
+No primeiro acesso confirme com yes o questionamento sobre a autenticidade. No caso da [DigitalOcean] vai ser solicitado a mudança de senha nesse primeiro acesso.
+
+Seguindo a documentação do [Dokku] vamos realizar a instação. Este processo demora +- uns 10 minutos.
+```
+wget https://raw.githubusercontent.com/dokku/dokku/v0.7.2/bootstrap.sh
+sudo DOKKU_TAG=v0.7.2 bash bootstrap.sh
+```
+Finalizado o script de instação vamos adicionar a chave publica de nosso usuário de desenvolvimento para conseguir fazer o deploy no servidor recem criado.
+### Chaves
+Na nossa máquina de desenvolvimento, vamos checar se nosso usuário tem uma chave pública:
+```
+ls -al ~/.ssh
+```
+Por padrão os arquivos das chaves públicas podem ser:
+
+ - id_dsa.pub
+ - id_ecdsa.pub
+ - id_ed25519.pub
+ - id_rsa.pub
+
+
+No meu caso eu tenho o id_rsa.pub, então vou ler o conteúdo, seleciona-lo e copiar:
+```
+cat ~/.ssh/id_rsa.pub
+```
+
+Para gerar a chave: (caso não exista nenhum arquivo .pub na pasta ~/.ssh)
+```
+ssh-keygen -t rsa
+```
+Aceite as três opções pedidas por default. (não inserir password)
+Para OS Windows achei este artigo. [ssh Windows] (não testei)
+### Inserindo a chave pública no servidor
+Com nossa chave pública copiada (ctrl+c) vamos abrir o browser e digitar o ip do nosso servidor. Vai aparecer uma tela como a da imagem a seguir:
+
+
+
+
+No campo Public Key, colamos nossa chave (Ctrl+V) e depois é so clicar em Finish Setup. Feito isto você vai ser redirecionado para página da documentação do [Dokku].
+### Criando nossa APP
+No terminal, conectado no nosso servidor:
+```
+dokku apps:create [nome-app]
+```
+No meu caso:
+```
+dokku apps:create fjfundo
+```
+Para listar as apps existentes:
+```
+dokku apps
+```
+Quando você cria um novo aplicativo, por padrão o [dokku] nao fornece nenhum banco de dados como MySql ou PostgreSQL. É preciso instalar plugins. [Dokku] tem plugins oficiais para banco de dados. Neste passo-a-passo vou utilizar o PostgreSQL.
+
+### Instalando o plugin postgres e configurando o serviço de banco de dados
+No terminal, conectado no nosso servidor:
+```
+sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git
+```
+Criando o serviço postgres:
+
+```
+dokku postgres:create [nome-servico]
+```
+No meu caso:
+```
+dokku postgres:create fjfundo-database
+```
+Vamos criar o link entre os serviços de banco de dados e nossa app.
+```
+dokku postgres:link [nome-servico] [nome-app]
+```
+No meu caso:
+```
+dokku postgres:link fjfundo-database fjfundo
+```
+
+### Configurando e executando nosso primeiro deploy.
+Na nossa máquina cliente vamos configurar o git para fazer o primeiro deploy.
+Vamos adicionar nosso repositorio remoto dokku da seguinte forma:
+```
+git remote add dokku dokku@[ip-do-servidor]:[nome-app]
+```
+No meu caso:
+```
+git remote add dokku dokku@xxx.xxx.xxx.xxx:fjfundo
+```
+No meu caso antes de fazer o deploy eu tenho que configurar algumas variáveis de ambiente como DEBUG e SECRET_KEY. Para esta configuração executo os comandos no servidor. Vou seguir a documentação existente em [Environment Variables]
+```
+dokku config:set fjfundo DEBUG='False'
+dokku config:set fjfundo SECRET_KEY='sua secret_key'
+```
+Para listar as variáveis de ambiente de nossa app:
+```
+dokku config [nome da app]
+```
+Feito isto vamos ao nosso primeiro deploy: ( na nossa máquina cliente/dev)
+```
+git push dokku master --force
+```
+Pronto! Agora é so acessar nossa aplicação. No final do deploy o [dokku] mostra a url de conexão. Caso precise obter a url use o comando no servidor:
+```
+dokku url [nome-app]
+```
+### Criando as tabelas do banco de dados
+
+Nossa app está no ar mais ainda não tem as tabelas de nossa base de dados.
+```
+dokku run fjfundo python manage.py migrate
+```
+Troque fjfundo pelo nome da sua app.
+
+### Considerações finais
+Ainda estou estudando e aprendendo a utilizar o [dokku]. Utilizo apenas em ambiente de desenvolvimento mas pretendo utilizar em produção.
+Ainda tenho muito que aprender e fica aqui meu email, joseadolfojr@gmail.com, para quem tiver alguma dúvida e quizer também contribuir para meus estudos.
+
+
+[Dokku]: http://dokku.viewdocs.io/dokku/
+[DigitalOcean]: https://m.do.co/c/2f45101e7ccf
+[putty]:http://www.putty.org/
+[ssh Windows]: http://adrianorosa.com/blog/seguranca/como-criar-ssh-key-pair-windows.html
+[Environment Variables]: http://dokku.viewdocs.io/dokku/configuration/environment-variables/
\ No newline at end of file
diff --git a/content/desenvolvendo-com-bottle-parte-1.md b/content/desenvolvendo-com-bottle-parte-1.md
new file mode 100644
index 000000000..10948dfba
--- /dev/null
+++ b/content/desenvolvendo-com-bottle-parte-1.md
@@ -0,0 +1,177 @@
+Title: Desenvolvendo com Bottle - Parte 1
+Slug: desenvolvendo-com-bottle-parte-1
+Date: 2014-12-03 15:05
+Tags: bottle,python
+Author: Eric Hideki
+Email: eric8197@gmail.com
+Github: erichideki
+Site: http://ericstk.wordpress.com
+Twitter: erichideki
+Category: begginers, bottle, tutorial
+
+# Texto originalmente escrito em:
+
+[https://realpython.com/blog/python/developing-with-bottle-part-1/]
+
+Eu amo [bottle]. Ele é simples, rápido e poderoso micro-framework Python, perfeito para pequenas aplicações web e rápida prototipação. É também perfeita ferramenta de ensino para aqueles que estão começando agora com desenvolvimento web.
+
+Vamos dar um rápido exemplo.
+
+> Este tutorial espera que você esteja em um ambiente baseado em Unix - e.g, Mac OSX, sistemas Linux, ou no Linux rodando uma Virtual Machine no Windows. Eu irei estar usando Sublime Text 2 como meu editor de texto.
+
+#Começando
+
+Primeiramente, vamos criar um diretório para trabalhar:
+
+```
+$ mkdir bottle
+$ cd bottle
+```
+
+Depois, você precisa ter pip, virtualenv, e git instalados.
+
+[virtualenv] é uma ferramenta Python que torna fácil gerenciar módulos Python necessários para um particular projeto. Ele também mantém os módulos isolados para que não entre em conflito com outros projetos.[pip], entretanto, é um gerenciador de pacotes usado para gerenciar a instalaçao de bibliotecas e módulos.
+
+Para ajudar com a instalação do pip(e todas as dependências) em um ambiente Unix, siga as instruções nesse [Gist]. Se você está no Windows, por favor veja esse [vídeo] para sua ajuda.
+
+Uma vez com o pip instalado, execute os seguintes comandos para instalar o virtualenv:
+
+```
+$ pip install virtualenv
+```
+
+Agora nós podemos facilmente configurar nosso ambiente local executando:
+
+```
+$ virtualenv --no-site-packages testenv
+$ source testenv/bin/activate
+```
+
+Instalar o Bottle:
+
+```
+$ pip install bottle
+```
+
+Agora criamos o arquivo *requirements.txt*, que permitirá você instalar os exatos módulos e dependências iguais no caso de você querer usar esse aplicativo em qualquer outro local. Clique [aqui] para aprender mais.
+
+```
+pip freeze > requirements.txt
+```
+
+Finalmente, vamos colocar nossa aplicação em um controle de versão usando Git. Para mais informações sobre o Git, por favor veja esse [site], que inclui instruções de instalação.
+
+```
+$ git init
+$ git add .
+$ git commit -m "initial commit"
+```
+
+##Escrevendo sua aplicação
+
+Agora estamos preparados para escrever nossa aplicação com Bottle. Crie seu arquivo de aplicação, *app.py*, que irá carregar todo o nosso primeiro aplicativo:
+
+```
+import os
+from bottle import route, run, template
+
+index_html = '''Minha primeira aplicação! Por {{ autor }}'''
+
+@route('/:qualquer')
+def alguma_coisa(qualquer=''):
+ return template(index_html, autor=qualquer)
+
+@route('/')
+def index():
+ return template(index_html, autor='Seu nome aqui:')
+
+if __name__ == '__main__':
+ port = int(os.environ.get('PORT', 8080))
+ run(host='0.0.0.0', port=port, debug=True)
+```
+
+Salve o arquivo.
+
+Agora você pode executar sua aplicação localmente:
+
+```
+$ python app.py
+```
+
+Você poderá ser capaz de conectar a [http://localhost:8080/abc] e ver sua aplicação rodando!
+Mude ```abc``` para seu nome. Dê refresh no navegador.
+
+#O que tá acontecendo?
+
+1. O decorator ```@route``` diz que a aplicação deve interpretar o caminho depois do ```/``` como variável ```qualquer```.
+2. Isto é passado para a função sendo como um argumento```(def alguma_coisa(qualquer='')```.
+3. Nós então passamos isto para a função do template sendo um argumento(```autor=qualquer```)
+4. O template então renderiza a variável autor com ```{{ autor }}```
+
+#Shell script
+
+Quer começar de forma rápida? Crie um inicializador de aplicação em poucos segundos usando o Shell script.
+
+```
+mkdir bottle
+cd bottle
+pip install virtualenv
+virtualenv --no-site-packages testenv
+source testenv/bin/activate
+pip install bottle
+pip freeze > requirements.txt
+git init
+git add .
+git commit -m "initial commit"
+
+cat >app.py <=}}{{ autor }}<%={{ }}=%>'''
+
+@route('/:qualquer')
+def alguma_coisa(qualquer=''):
+ return template(index_html, autor=qualquer)
+
+@route('/')
+def index():
+ return template(index_html, autor='Seu nome aqui:')
+
+if __name__ == '__main__':
+ port = int(os.environ.get('PORT', 8080))
+ run(host='0.0.0.0', port=port, debug=True)
+EOF
+
+chmod a+x app.py
+
+git init
+git add .
+git commit -m "Updated"
+```
+
+Baixe esse script através dessa [Gist-list], e então execute o seguinte comando:
+
+```
+$ bash bottle.sh
+```
+
+#Próximos passos
+
+A partir desse ponto, é tão fácil adicionando uma nova ```@route```-decorated functions() para criar novas páginas, assim como fizemos com essas duas páginas.
+
+Criar o HTML é simples: Nessa aplicação, nós apenas adicionamos HTML na mesma linha e arquivo.Isto é fácil de modificar(usando, por exemplo, ```open('index.html').read())``` para ler o template de um arquivo.
+
+Referências para a [documentação] do Bottle para mais informações.
+
+[https://realpython.com/blog/python/developing-with-bottle-part-1/]:https://realpython.com/blog/python/developing-with-bottle-part-1/
+[bottle]:http://bottlepy.org/docs/stable/
+[virtualenv]:https://pypi.python.org/pypi/virtualenv
+[pip]:https://pypi.python.org/pypi/pip
+[Gist]:https://gist.github.com/mjhea0/5692708
+[vídeo]:https://www.youtube.com/watch?v=MIHYflJwyLk
+[aqui]:https://pip.pypa.io/en/latest/user_guide.html#requirements-files
+[site]:http://git-scm.com/book/pt-br/v1/Primeiros-passos-No%C3%A7%C3%B5es-B%C3%A1sicas-de-Git
+[http://localhost:8080/abc]:http://localhost:8080/abc
+[Gist-list]:https://gist.github.com/mjhea0/5784132
+[documentação]:http://bottlepy.org/docs/dev/
diff --git a/content/desenvolvendo-para-google-app-engine-com-tekton.md b/content/desenvolvendo-para-google-app-engine-com-tekton.md
new file mode 100644
index 000000000..154cf7085
--- /dev/null
+++ b/content/desenvolvendo-para-google-app-engine-com-tekton.md
@@ -0,0 +1,65 @@
+Title: Criação de aplicações no Google App Engine com o Tekton
+Slug: desenvolvendo-para-google-app-engine-com-tekton
+Date: 24-05-2015
+Tags: tekton, python, google app engine, tutorial
+Author: Guido Luz Percú
+Email: guidopercu@gmail.com
+Github: GuidoBR
+Site: http://www.guidopercu.com.br/
+Twitter: oumguido
+Category: google app engine
+
+## Google App Engine (GAE)
+É a plataforma de Cloud Computing do Google, com ela você pode desenvolver e hospedar aplicações usando Python (2.7) que escalam facilmente, [pagando muito pouco por isso].
+
+As desvantagens (em relação a outras plataformas de nuvem, como o Heroku por exemplo) são:
+- Você terá que desenvolver pensando na plataforma (banco de dados NoSQL, por isso o [Django não é recomendável].).
+- Versão do Python é antiga e não há planos para mudar isso no momento.
+
+## Tekton
+É um framework para desenvolvimento Web especialmente pensado para uso no Google App Engine. Nele podemos aproveitar o melhor do Django (scaffold, código HTML e validação de formulários a partir de modelos, apps isoladas) sem perder as vantagens que o GAE nos oferece.
+
+## Como iniciar
+O primeiro passo é baixar o [SDK do Google App Engine], com isso pronto podemos começar a conhecer o Tekton.
+
+Em seguida, vamos baixar a aplicação template.
+```
+$ wget https://github.com/renzon/tekton/archive/master.zip
+$ unzip master && rm master.zip
+$ mv tekton-master projeto_appengine && cd projeto_appengine
+```
+Nesse ponto podemos explorar e conhecer a estrutura de diretórios.
+```
+└── backend
+ ├── appengine
+ ├── apps
+ ├── build_scripts
+ ├── test
+ └── venv
+```
+
+```
+$ cd backend/venv/ && ./venv.sh
+$ source ./bin/activate
+```
+Com o ambiente virtual pronto, tudo deve estar funcionando. Para testar,
+vamos utilizar o próprio servidor que vem com o pacote antes de subir parao GAE.
+
+```
+cd ../appengine && dev_appserver.py .
+```
+
+Tudo certo! Você deve estar vendo o projeto template no seu localhost:8080
+
+Para realizar o deploy no App Engine:
+
+```
+appcfg.py update . --oauth2
+```
+Você pode conhecer mais sobre o projeto no [Github], no [grupo de discussões] ou nas vídeo aulas gratuitas no [Youtube].
+[SDK do Google App Engine]:https://cloud.google.com/appengine/downloads
+[pagando muito pouco por isso]:https://cloud.google.com/appengine/pricing
+[Django não é recomendável]:http://imasters.com.br/desenvolvimento/app-engine-e-django-hell/
+[Youtube]:https://www.youtube.com/playlist?list=PLA05yVJtRWYRGIeBxag8uT-3ftcMVT5oF
+[Github]:https://github.com/renzon/tekton
+[grupo de discussões]:https://groups.google.com/forum/#!forum/tekton-web
diff --git a/content/diferenca-operadores.md b/content/diferenca-operadores.md
new file mode 100644
index 000000000..bac3444b3
--- /dev/null
+++ b/content/diferenca-operadores.md
@@ -0,0 +1,109 @@
+Title: Diferença entre == e is
+Slug: diferenca-operadores
+Date: 2015-11-27 14:00
+Tags: python,dica,operadores
+Author: Gildásio Júnior
+Email: gjuniioor@protonmail.ch
+Github: gjuniioor
+Site: https://gjuniioor.github.io
+Category: Python
+Status: draft
+
+Diferença entre == e is
+-----------
+
+Olá, galera, tudo tranquilo?
+
+Estava conversando com um amigo que está estudando python e tudo mais, e então ele veio com a seguinte dúvida:
+
+> Qual a diferença entre == e is no python?
+
+Para quem não sabe, no python tem o operador *is* que "tem a mesma função do *==*. Veja:
+
+```python
+>>> x = 10
+>>> y = 10
+>>> x == y
+True
+>>> x is y
+True
+```
+
+Mas...
+
+```python
+>>> x = 1000
+>>> y = 1000
+>>> x == y
+True
+>>> x is y
+False
+```
+
+Viu só? Pois bem, o que que acontece então??
+
+O python tem um mecanismo interessante nesse ponto... Quando se tratam de *coisas pequenas* ele utiliza de ponteiros para apontar outros rótulos para um mesmo endereço de memória. Quando o que é armazenado na variável já começa a crescer, fica maior e tal, ele já não usa disso, para não pesar, mas sim de outro endereço ...
+
+Seria um cache que ele faz de alguns tipos de objetos, entre eles estão int e string, por exemplo. Float e dicionário já não são assim.
+
+Para ter uma ideia melhor disso, vamos ver os endereços que as variáveis ocupam e o resultado da comparação:
+
+```python
+>>> x = 10
+>>> y = 10
+>>> hex(id(x))
+'0x98a1844'
+>>> hex(id(y))
+'0x98a1844'
+>>> x == y
+True
+>>> x is y
+True
+```
+
+Perceba que ele pega o mesmo endereço ... Agora, se colocarmos valores maiores:
+
+```python
+>>> x = 1000
+>>> y = 1000
+>>> hex(id(x))
+'0x98e5520'
+>>> hex(id(y))
+'0x98e5508'
+>>> x == y
+True
+>>> x is y
+False
+```
+
+Ou seja, o *is* (como a tradução mostra) vai verificar se algo é aquilo a que a comparação está se referindo, ou seja, se são a mesma coisa. Já o *==* vai analisar se são iguais, assim como o esperado.
+
+Mais algumas demonstrações:
+
+* Float:
+
+```python
+>>> x = 1.0
+>>> y = 1.0
+>>> x == y
+True
+>>> x is y
+False
+```
+
+* Dict:
+
+```python
+>>> x = [1]
+>>> y = [1]
+>>> x == y
+True
+>>> x is y
+False
+```
+
+> Não há motivo para me aprofundar tanto aqui, é apenas uma dica rápida. Espero que resolva os problemas de dúvidas de quem necessitar ... Tem mais algum caso como esse? Quer tirar alguma dúvida do tipo? Comenta ai ou entra em contato (olha no topo da página).
+
+Vlw pessoal, até mais ver!!
+
+> Texto originalmente postado aqui: [https://gjuniioor.github.io/blog/python-diferenca-operadores/](https://gjuniioor.github.io/blog/python-diferenca-operadores/).
diff --git a/content/django-ci-github-actions.rst b/content/django-ci-github-actions.rst
new file mode 100644
index 000000000..fb4bbdbab
--- /dev/null
+++ b/content/django-ci-github-actions.rst
@@ -0,0 +1,21 @@
+Criando um CI de uma aplicação Django usando Github Actions
+###########################################################
+
+:date: 2020-01-24 12:10
+:tags: python, django, github actions
+:category: Python
+:slug: django-ci-github-actions
+:author: Lucas Magnum
+:email: lucasmagnumlopes@gmail.com
+:github: lucasmagnum
+:linkedin: lucasmagnum
+
+Fala pessoal, tudo bom?
+
+Nos vídeo abaixo vou mostrar como podemos configurar um CI de uma aplicação Django usando Github Actions.
+
+`https://www.youtube.com/watch?v=KpSlY8leYFY `_.
+
+
+.. youtube:: KpSlY8leYFY
+
diff --git a/content/django-rest-framework-class-based-views.md b/content/django-rest-framework-class-based-views.md
new file mode 100644
index 000000000..c5dac7958
--- /dev/null
+++ b/content/django-rest-framework-class-based-views.md
@@ -0,0 +1,155 @@
+title: Django Rest Framework - #3 Class Based Views
+Slug: django-rest-framework-class-based-views
+Date: 2018-02-15 23:00
+Tags: Python, Django, REST
+Author: Regis da Silva
+Email: regis.santos.100@gmail.com
+Github: rg3915
+Twitter: rg3915
+Category: Python, Django, REST
+
+* 0 - [Quickstart][10]
+* 1 - [Serialization][11]
+* 2 - [Requests & Responses][12]
+* 3 - **Class based views**
+
+Este post é continuação do post [Django Rest Framework Requests & Responses][12].
+
+Finalmente chegamos as views baseadas em classes. A grande vantagem é que com poucas linhas de código já temos nossa API pronta.
+
+Veja como fica a [views.py](https://github.com/rg3915/drf/blob/b0aa989ffc756e6dc5f65e172dfb43d47127d743/core/views.py):
+
+```python
+from django.http import Http404
+from rest_framework.views import APIView
+from rest_framework.response import Response
+from rest_framework import status
+from core.models import Person
+from core.serializers import PersonSerializer
+
+
+class PersonList(APIView):
+ """
+ List all persons, or create a new person.
+ """
+
+ def get(self, request, format=None):
+ persons = Person.objects.all()
+ serializer = PersonSerializer(persons, many=True)
+ return Response(serializer.data)
+
+ def post(self, request, format=None):
+ serializer = PersonSerializer(data=request.data)
+ if serializer.is_valid():
+ serializer.save()
+ return Response(serializer.data, status=status.HTTP_201_CREATED)
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+
+
+class PersonDetail(APIView):
+ """
+ Retrieve, update or delete a person instance.
+ """
+
+ def get_object(self, pk):
+ try:
+ return Person.objects.get(pk=pk)
+ except Person.DoesNotExist:
+ raise Http404
+
+ def get(self, request, pk, format=None):
+ person = self.get_object(pk)
+ serializer = PersonSerializer(person)
+ return Response(serializer.data)
+
+ def put(self, request, pk, format=None):
+ person = self.get_object(pk)
+ serializer = PersonSerializer(person, data=request.data)
+ if serializer.is_valid():
+ serializer.save()
+ return Response(serializer.data)
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+
+ def delete(self, request, pk, format=None):
+ person = self.get_object(pk)
+ person.delete()
+ return Response(status=status.HTTP_204_NO_CONTENT)
+```
+
+E `urls.py`:
+
+```python
+urlpatterns = [
+ path('persons/', views.PersonList.as_view()),
+ path('persons//', views.PersonDetail.as_view()),
+]
+```
+
+## Usando Mixins
+
+Repare que no exemplo anterior tivemos que definir os métodos `get()`, `post()`, `put()` e `delete()`. Podemos reduzir ainda mais esse código com o uso de mixins.
+
+```python
+from rest_framework import mixins
+from rest_framework import generics
+from core.models import Person
+from core.serializers import PersonSerializer
+
+
+class PersonList(mixins.ListModelMixin,
+ mixins.CreateModelMixin,
+ generics.GenericAPIView):
+ queryset = Person.objects.all()
+ serializer_class = PersonSerializer
+
+ def get(self, request, *args, **kwargs):
+ return self.list(request, *args, **kwargs)
+
+ def post(self, request, *args, **kwargs):
+ return self.create(request, *args, **kwargs)
+
+
+class PersonDetail(mixins.RetrieveModelMixin,
+ mixins.UpdateModelMixin,
+ mixins.DestroyModelMixin,
+ generics.GenericAPIView):
+ queryset = Person.objects.all()
+ serializer_class = PersonSerializer
+
+ def get(self, request, *args, **kwargs):
+ return self.retrieve(request, *args, **kwargs)
+
+ def put(self, request, *args, **kwargs):
+ return self.update(request, *args, **kwargs)
+
+ def delete(self, request, *args, **kwargs):
+ return self.destroy(request, *args, **kwargs)
+```
+
+## Usando generic class-based views
+
+E para finalizar usamos `ListCreateAPIView` e `RetrieveUpdateDestroyAPIView` que já tem todos os métodos embutidos.
+
+```python
+from rest_framework import generics
+from core.models import Person
+from core.serializers import PersonSerializer
+
+
+class PersonList(generics.ListCreateAPIView):
+ queryset = Person.objects.all()
+ serializer_class = PersonSerializer
+
+
+class PersonDetail(generics.RetrieveUpdateDestroyAPIView):
+ queryset = Person.objects.all()
+ serializer_class = PersonSerializer
+```
+
+Versão final de [views.py](https://github.com/rg3915/drf/blob/263ca63e5c8e2dd2e0f5d6c88c5733fcfdab4f74/core/views.py).
+
+Abraços.
+
+[10]: http://pythonclub.com.br/django-rest-framework-quickstart.html
+[11]: http://pythonclub.com.br/django-rest-framework-serialization.html
+[12]: http://pythonclub.com.br/django-rest-framework-requests-responses.html
\ No newline at end of file
diff --git a/content/django-rest-framework-quickstart.md b/content/django-rest-framework-quickstart.md
new file mode 100644
index 000000000..f1218a4e0
--- /dev/null
+++ b/content/django-rest-framework-quickstart.md
@@ -0,0 +1,213 @@
+title: Django Rest Framework Quickstart
+Slug: django-rest-framework-quickstart
+Date: 2015-11-17 14:00
+Tags: Python, Django, REST
+Author: Regis da Silva
+Email: regis.santos.100@gmail.com
+Github: rg3915
+Twitter: rg3915
+Category: Python, Django, REST
+
+Veremos aqui uma forma rápida de criar uma API REST com [Django Rest Framework][0].
+
+> Este artigo foi atualizado em 14 de Fevereiro de 2018.
+
+Este artigo está usando:
+
+* Python 3.5.2
+* Django 2.0.2
+* djangorestframework 3.7.7
+
+Favor clonar o projeto do [GitHub](https://github.com/rg3915/drf#clonando-o-projeto), favor ler o README para instalação.
+
+Repare nas alterações das urls na nova versão do Django.
+
+```python
+urls.py
+from django.urls import include, path
+from django.contrib import admin
+
+urlpatterns = [
+ path('', include('core.urls')),
+ path('admin/', admin.site.urls),
+]
+```
+
+```python
+# core/urls.py
+from django.urls import path
+from core import views
+
+urlpatterns = [
+ path('persons/', views.person_list),
+ path('persons//', views.person_detail),
+]
+```
+
+Além disso, tivemos alterações significativas em [settings.py](https://github.com/rg3915/drf/blob/master/myproject/settings.py).
+
+**Obs**: *Tem coisas que é melhor nem traduzir. ;)*
+
+* 0 - **Quickstart**
+* 1 - [Serialization][11]
+* 2 - [Requests & Responses][12]
+* 3 - [Class based views][13]
+
+> **Obs**: se você não sabe [Django][3] sugiro que leia este [tutorial][4] antes.
+
+## Começando
+
+```bash
+$ python3 -m venv .venv
+$ source env/bin/activate
+$ mkdir drf-quickstart
+$ cd drf-quickstart
+$ pip install django djangorestframework
+$ pip freeze > requirements.txt
+$ django-admin.py startproject myproject . # tem um ponto '.' aqui
+$ python manage.py startapp core
+$ python manage.py migrate
+$ python manage.py createsuperuser --username='admin' --email=''
+```
+
+Veja o meu requirements.txt
+
+```bash
+dj-database-url==0.4.2
+Django==2.0.2
+django-extensions==1.9.9
+django-filter==1.1.0
+djangorestframework==3.7.7
+drf-nested-routers==0.90.0
+python-decouple==3.1
+```
+
+## Editando `settings.py`
+
+Abra o arquivo `settings.py` e em `INSTALLED_APPS` acrescente
+
+```python
+INSTALLED_APPS = (
+ ...
+ 'rest_framework',
+)
+
+REST_FRAMEWORK = {
+ 'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',),
+ 'PAGE_SIZE': 10
+}
+```
+
+## Editando `serializers.py`
+
+Crie o arquivo
+
+```bash
+$ cd core/
+$ touch serializers.py
+```
+
+Edite
+
+```python
+from django.contrib.auth.models import User, Group
+from rest_framework import serializers
+
+
+class UserSerializer(serializers.HyperlinkedModelSerializer):
+
+ class Meta:
+ model = User
+ fields = ('url', 'username', 'email', 'groups')
+
+
+class GroupSerializer(serializers.HyperlinkedModelSerializer):
+
+ class Meta:
+ model = Group
+ fields = ('url', 'name')
+```
+
+## Editando `views.py`
+
+```python
+from django.contrib.auth.models import User, Group
+from rest_framework import viewsets
+from core.serializers import UserSerializer, GroupSerializer
+
+
+class UserViewSet(viewsets.ModelViewSet):
+ """
+ API endpoint that allows users to be viewed or edited.
+ """
+ queryset = User.objects.all().order_by('-date_joined')
+ serializer_class = UserSerializer
+
+
+class GroupViewSet(viewsets.ModelViewSet):
+ """
+ API endpoint that allows groups to be viewed or edited.
+ """
+ queryset = Group.objects.all()
+ serializer_class = GroupSerializer
+```
+
+
+## Editando `urls.py`
+
+
+```python
+from django.conf.urls import url, include
+from rest_framework import routers
+from core import views
+
+router = routers.DefaultRouter()
+router.register(r'users', views.UserViewSet)
+router.register(r'groups', views.GroupViewSet)
+
+urlpatterns = [
+ url(http://github.com/r'%5E',%20include(router.urls)),
+ url(http://github.com/r'%5Eapi-auth/',%20include('rest_framework.urls',%20namespace='rest_framework'))
+]
+```
+
+## Rodando a API
+
+Abra duas abas no terminal, numa rode a aplicação.
+
+```bash
+$ python manage.py runserver
+```
+
+
+
+Na outra teste a API.
+
+```bash
+curl -H 'Accept: application/json; indent=4' -u admin:admin http://127.0.0.1:8000/users/
+```
+
+onde `admin:admin` equivale a `username:password`.
+
+Experimente com [httpie][7]
+
+```bash
+http -a admin:admin http://127.0.0.1:8000/users/
+```
+
+> **Atenção**: se você receber erro 301, muito provavelmente é porque você esqueceu da barra `/` no final da url.
+
+
+
+Veja o código no [GitHub][8].
+
+> Haverá continuação ...
+
+[0]: http://www.django-rest-framework.org/
+[3]: https://www.djangoproject.com/
+[4]: http://pythonclub.com.br/tutorial-django-17.html
+[7]: https://github.com/jakubroztocil/httpie#installation
+[8]: https://github.com/rg3915/drf-quickstart.git
+[11]: http://pythonclub.com.br/django-rest-framework-serialization.html
+[12]: http://pythonclub.com.br/django-rest-framework-requests-responses.html
+[13]: http://pythonclub.com.br/django-rest-framework-class-based-views.html
\ No newline at end of file
diff --git a/content/django-rest-framework-requests-responses.md b/content/django-rest-framework-requests-responses.md
new file mode 100644
index 000000000..0f8172c4c
--- /dev/null
+++ b/content/django-rest-framework-requests-responses.md
@@ -0,0 +1,121 @@
+title: Django Rest Framework - #2 Requests and Responses
+Slug: django-rest-framework-requests-responses
+Date: 2018-02-14 23:00
+Tags: Python, Django, REST
+Author: Regis da Silva
+Email: regis.santos.100@gmail.com
+Github: rg3915
+Twitter: rg3915
+Category: Python, Django, REST
+
+* 0 - [Quickstart][10]
+* 1 - [Serialization][11]
+* 2 - **Requests & Responses**
+* 3 - [Class based views][12]
+
+Este post é continuação do post [Django Rest Framework Serialization][11].
+
+O uso de *requests* e *responses* torna nossa api mais flexível. A funcionalidade principal do objeto **Request** é o atributo `request.data`, que é semelhante ao `request.POST`, mas é mais útil para trabalhar com APIs.
+
+## Objeto Response
+
+Introduzimos aqui um objeto `Response`, que é um tipo de `TemplateResponse` que leva conteúdo não renderizado e usa a negociação de conteúdo para determinar o tipo de conteúdo correto para retornar ao cliente.
+
+```python
+return Response(data) # Renderiza para o tipo de conteúdo conforme solicitado pelo cliente.
+```
+
+Repare também no uso de *status code* pré definidos, exemplo: `status.HTTP_400_BAD_REQUEST`.
+
+E usamos o decorador `@api_view` para trabalhar com funções. Ou `APIView` para classes.
+
+Nosso código ficou assim:
+
+```python
+# views.py
+from rest_framework import status
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from core.models import Person
+from core.serializers import PersonSerializer
+
+
+@api_view(['GET', 'POST'])
+def person_list(request):
+ """
+ List all persons, or create a new person.
+ """
+ if request.method == 'GET':
+ persons = Person.objects.all()
+ serializer = PersonSerializer(persons, many=True)
+ return Response(serializer.data)
+
+ elif request.method == 'POST':
+ serializer = PersonSerializer(data=request.data)
+ if serializer.is_valid():
+ serializer.save()
+ return Response(serializer.data, status=status.HTTP_201_CREATED)
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+
+
+@api_view(['GET', 'PUT', 'DELETE'])
+def person_detail(request, pk):
+ """
+ Retrieve, update or delete a person instance.
+ """
+ try:
+ person = Person.objects.get(pk=pk)
+ except Person.DoesNotExist:
+ return Response(status=status.HTTP_404_NOT_FOUND)
+
+ if request.method == 'GET':
+ serializer = PersonSerializer(person)
+ return Response(serializer.data)
+
+ elif request.method == 'PUT':
+ serializer = PersonSerializer(person, data=request.data)
+ if serializer.is_valid():
+ serializer.save()
+ return Response(serializer.data)
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+
+ elif request.method == 'DELETE':
+ person.delete()
+ return Response(status=status.HTTP_204_NO_CONTENT)
+```
+
+Veja no [GitHub](https://github.com/rg3915/drf/commit/69205da9262415eaf83ff04f22a635e912880a60).
+
+
+## Usando sufixo opcional
+
+Em `core/urls.py` acrescente
+
+```python
+from rest_framework.urlpatterns import format_suffix_patterns
+
+...
+
+urlpatterns = format_suffix_patterns(urlpatterns)
+```
+
+E em `views.py` acrescente `format=None` como parâmetro das funções a seguir:
+
+```python
+def person_list(request, format=None):
+
+def person_detail(request, pk, format=None):
+```
+
+Com isso você pode chamar a api da seguinte forma:
+
+```bash
+http http://127.0.0.1:8000/persons.json # ou
+http http://127.0.0.1:8000/persons.api
+```
+
+Até a próxima.
+
+[10]: http://pythonclub.com.br/django-rest-framework-quickstart.html
+[11]: http://pythonclub.com.br/django-rest-framework-serialization.html
+[12]: http://pythonclub.com.br/django-rest-framework-class-based-views.html
\ No newline at end of file
diff --git a/content/django-rest-framework-serialization.md b/content/django-rest-framework-serialization.md
new file mode 100644
index 000000000..9b313d0cc
--- /dev/null
+++ b/content/django-rest-framework-serialization.md
@@ -0,0 +1,450 @@
+title: Django Rest Framework Serialization
+Slug: django-rest-framework-serialization
+Date: 2016-04-24 18:00
+Tags: Python, Django, REST
+Author: Regis da Silva
+Email: regis.santos.100@gmail.com
+Github: rg3915
+Twitter: rg3915
+Category: Python, Django, REST
+
+Eu resolvi estudar um pouco mais de [DRF][0] depois do tutorial do [Hugo Brilhante][1] na [Python Brasil 11][2].
+
+> Este artigo foi atualizado em 14 de Fevereiro de 2018.
+
+Este artigo está usando:
+
+* Python 3.5.2
+* Django 2.0.2
+* djangorestframework 3.7.7
+
+Favor clonar o projeto do [GitHub](https://github.com/rg3915/drf#clonando-o-projeto), favor ler o README para instalação.
+
+> **Obs**: se você não sabe Django sugiro que leia este [tutorial][4] antes.
+
+**Obs**: *Tem coisas que é melhor nem traduzir. ;)*
+
+* 0 - [Quickstart][10]
+* 1 - **Serialization**
+* 2 - [Requests & Responses][11]
+* 3 - [Class based views][12]
+
+Pra quem não sabe, para usar API Web usamos REST, no caso, [Django Rest Framework][0], framework web do [Django][3].
+
+> **Nota:** este tutorial não é exatamente igual ao do Hugo, é baseado nele. E baseado também em [Tutorial 1: Serialization][9].
+
+Então para criar a API, no meu caso, eu usei:
+
+* Ambiente: .venv
+* Projeto: myproject
+* App: core
+* Model: Person
+* Fields: first_name, last_name, email, active (boolean), created
+
+
+
+
+## Configurando um novo ambiente
+
+```bash
+$ python3 -m venv .venv
+$ source .venv/bin/activate
+$ mkdir drf; cd drf
+$ pip install django==2.0.2 djangorestframework==3.7.7
+$ pip install django-filter drf-nested-routers
+$ pip freeze > requirements.txt
+$ django-admin.py startproject myproject .
+$ python manage.py startapp core
+```
+
+Veja o meu requirements.txt
+
+```bash
+dj-database-url==0.4.2
+Django==2.0.2
+django-extensions==1.9.9
+django-filter==1.1.0
+djangorestframework==3.7.7
+drf-nested-routers==0.90.0
+python-decouple==3.1
+```
+
+## Step-0 Projeto inicial
+
+Abra o arquivo `settings.py` e em `INSTALLED_APPS` acrescente
+
+```python
+INSTALLED_APPS = (
+ ...
+ 'rest_framework',
+ 'core',
+)
+```
+
+## Step-1 Serializer
+
+### `models.py`: Criando o modelo `Person`
+
+```python
+from django.db import models
+
+class Person(models.Model):
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
+ email = models.EmailField(null=True, blank=True)
+ active = models.BooleanField(default=True)
+ created = models.DateTimeField(auto_now_add=True, auto_now=False)
+
+ class Meta:
+ ordering = ['first_name']
+ verbose_name = u'pessoa'
+ verbose_name_plural = u'pessoas'
+
+ def __str__(self):
+ return self.first_name + " " + self.last_name
+
+ full_name = property(__str__)
+```
+
+
+
+### `serializers.py`: Criando `PersonSerializer`
+
+Precisamos proporcionar uma forma de [serialização][5] e desserialização das instâncias de `person` em uma representação JSON.
+
+```bash
+$ cd core/
+$ touch serializers.py
+```
+
+Edite
+
+```python
+from rest_framework import serializers
+from core.models import Person
+
+class PersonSerializer(serializers.Serializer):
+ pk = serializers.IntegerField(read_only=True)
+ first_name = serializers.CharField(max_length=30)
+ last_name = serializers.CharField(max_length=30)
+ email = serializers.EmailField()
+ active = serializers.BooleanField(default=True)
+ created = serializers.DateTimeField()
+
+ def create(self, validated_data):
+ """
+ Create and return a new `Person` instance, given the validated data.
+ :param validated_data:
+ """
+ return Person.objects.create(**validated_data)
+
+ def update(self, instance, validated_data):
+ """
+ Update and return an existing `Person` instance, given the validated data.
+ """
+
+ instance.first_name = validated_data.get(
+ 'first_name', instance.first_name)
+ instance.last_name = validated_data.get(
+ 'last_name', instance.last_name)
+ instance.email = validated_data.get('email', instance.email)
+ instance.save()
+ return instance
+```
+
+A primeira parte da classe define os campos que serão serializados. Os métodos `create()` e `update()` criam e atualizam as instâncias, respectivamente, quando chamados.
+
+Uma classe de serialização é similar a uma classe `Form` do Django, e inclui validações similares para os campos, tais como `required`, `max_length` e `default`.
+
+
+### Fazendo a migração
+
+```bash
+$ ./manage.py makemigrations core
+$ ./manage.py migrate
+```
+
+### Trabalhando com a serialização
+
+Abra o `shell` do Django.
+
+```bash
+$ ./manage.py shell
+```
+
+Primeiro vamos criar uma pessoa.
+
+```python
+>>> from core.models import Person
+>>> from core.serializers import PersonSerializer
+>>> from rest_framework.renderers import JSONRenderer
+>>> from rest_framework.parsers import JSONParser
+
+>>> person = Person(first_name='Paul', last_name='Van Dyke', email='paul@email.com')
+>>> person.save()
+
+>>> person = Person(first_name='Regis', last_name='Santos', email='regis@email.com')
+>>> person.save()
+```
+
+Agora que já temos alguns dados podemos ver a serialização da última instância.
+
+```python
+>>> serializer = PersonSerializer(person)
+>>> serializer.data
+# {'pk': 2, 'first_name': 'Regis', 'created': '2015-11-15T03:20:25.084990Z', 'last_name': 'Santos', 'email': 'regis@email.com', 'active': True}
+```
+
+Neste ponto nós traduzimos a instância do modelo em tipos de dados nativos do Python. Para finalizar o processo de serialização nós vamos renderizar os dados em `json`.
+
+```python
+>>> content = JSONRenderer().render(serializer.data)
+>>> content
+# b'{"pk":2,"first_name":"Regis","last_name":"Santos","email":"regis@email.com","active":true,"created":"2015-11-15T03:20:25.084990Z"}'
+```
+
+
+A desserialização é similar.
+
+```python
+>>> from core.models import Person
+>>> from core.serializers import PersonSerializer
+>>> from rest_framework.renderers import JSONRenderer
+>>> from rest_framework.parsers import JSONParser
+>>> from django.utils.six import BytesIO
+
+>>> person = Person.objects.get(pk=1)
+>>> serializer = PersonSerializer(person)
+>>> content = JSONRenderer().render(serializer.data)
+>>> stream = BytesIO(content)
+>>> data = JSONParser().parse(stream)
+>>> serializer = PersonSerializer(data=data)
+>>> serializer.is_valid()
+# True
+>>> serializer.validated_data
+# OrderedDict([('first_name', 'Paul'), ('last_name', 'Van Dyke'), ('email', 'paul@email.com'), ('active', True)])
+```
+
+## Step-2 ModelSerializer
+
+Nossa classe `PersonSerializer` está replicando um monte de informações que está contido no modelo `Person`.
+
+Da mesma forma que o Django fornece `Form` e `ModelForm`, REST framework inclui as classes `Serializer` e `ModelSerializer`.
+
+Vamos refatorar nosso arquivo `serializers.py`, que agora ficará assim:
+
+```python
+from rest_framework import serializers
+from core.models import Person
+
+class PersonSerializer(serializers.ModelSerializer):
+
+ class Meta:
+ model = Person
+ fields = ('pk', 'first_name', 'last_name','email', 'active', 'created')
+```
+
+Uma propriedade legal que a serialização tem é que você pode inspecionar todos os campos em uma instância serializer, imprimindo sua representação. Abra o `shell` do Django.
+
+```bash
+$ ./manage.py shell
+```
+
+```python
+>>> from core.serializers import PersonSerializer
+>>> serializer = PersonSerializer()
+>>> print(repr(serializer))
+# PersonSerializer():
+# pk = IntegerField(label='ID', read_only=True)
+# first_name = CharField(max_length=30)
+# last_name = CharField(max_length=30)
+# email = EmailField(allow_blank=True, allow_null=True, max_length=254, required=False)
+# active = BooleanField(required=False)
+# created = DateTimeField(read_only=True)
+```
+
+É importante lembrar que as classes `ModelSerializer` não faz nenhuma mágica, são simplesmente um atalho para a criação das classes de serialização:
+
+* Os campos são definidos automaticamente.
+* Os métodos `create()` e `update()` são implementados por padrão de uma forma simplificada.
+
+
+
+### `views.py`: Criando *views* regulares usando nosso *Serializer*
+
+Vamos criar uma *view* simples para visualizar os dados em `json`.
+
+Edite o arquivo `views.py`
+
+```python
+from django.http import HttpResponse
+from django.views.decorators.csrf import csrf_exempt
+from rest_framework.renderers import JSONRenderer
+from rest_framework.parsers import JSONParser
+from core.models import Person
+from core.serializers import PersonSerializer
+
+class JSONResponse(HttpResponse):
+ """
+ An HttpResponse that renders its content into JSON.
+ """
+
+ def __init__(self, data, **kwargs):
+ content = JSONRenderer().render(data)
+ kwargs['content_type'] = 'application/json'
+ super(JSONResponse, self).__init__(content, **kwargs)
+```
+
+A raiz da nossa API será uma lista de todas as pessoas, ou podemos criar uma pessoa nova.
+
+
+```python
+@csrf_exempt
+def person_list(request):
+ """
+ List all persons, or create a new person.
+ """
+ if request.method == 'GET':
+ persons = Person.objects.all()
+ serializer = PersonSerializer(persons, many=True)
+ return JSONResponse(serializer.data)
+
+ elif request.method == 'POST':
+ data = JSONParser().parse(request)
+ serializer = PersonSerializer(data=data)
+ if serializer.is_valid():
+ serializer.save()
+ return JSONResponse(serializer.data, status=201)
+ return JSONResponse(serializer.errors, status=400)
+```
+
+Note que nós queremos usar o POST mas não temos o CSRF Token, por isso usamos o `@csrf_exempt`.
+
+Também vamos precisar visualizar os detalhes de cada pessoa. Assim podemos recuperar, atualizar ou deletar cada registro.
+
+```python
+@csrf_exempt
+def person_detail(request, pk):
+ """
+ Retrieve, update or delete a person.
+ """
+ try:
+ person = Person.objects.get(pk=pk)
+ except Person.DoesNotExist:
+ return HttpResponse(status=404)
+
+ if request.method == 'GET':
+ serializer = PersonSerializer(person)
+ return JSONResponse(serializer.data)
+
+ elif request.method == 'PUT':
+ data = JSONParser().parse(request)
+ serializer = PersonSerializer(person, data=data)
+ if serializer.is_valid():
+ serializer.save()
+ return JSONResponse(serializer.data)
+ return JSONResponse(serializer.errors, status=400)
+
+ elif request.method == 'DELETE':
+ person.delete()
+ return HttpResponse(status=204)
+```
+
+Agora, vamos criar as urls. Crie um novo arquivo `core/urls.py`.
+
+```python
+from django.urls import path
+from core import views
+
+urlpatterns = [
+ path('persons/', views.PersonList.as_view()),
+ path('persons//', views.PersonDetail.as_view()),
+]
+```
+
+E acrescente a seguinte linha em `myproject/urls.py`.
+
+```python
+from django.urls import include, path
+from django.contrib import admin
+
+urlpatterns = [
+ path('', include('core.urls')),
+ path('admin/', admin.site.urls),
+]
+```
+
+## Instalando `httpie`
+
+Podemos usar o [curl][6], mas o [httpie][7] é mais amigável, e escrito em Python.
+
+```bash
+$ sudo pip install httpie
+```
+
+Vamos usar o `httpie` digitando
+
+```bash
+$ http http://127.0.0.1:8000/persons/
+
+HTTP/1.0 200 OK
+Content-Type: application/json
+Date: Sun, 15 Nov 2015 03:24:44 GMT
+Server: WSGIServer/0.2 CPython/3.4.3
+X-Frame-Options: SAMEORIGIN
+
+[
+ {
+ "active": true,
+ "created": "2015-11-15T03:20:24.938378Z",
+ "email": "paul@email.com",
+ "first_name": "Paul",
+ "last_name": "Van Dyke",
+ "pk": 1
+ },
+ {
+ "active": true,
+ "created": "2015-11-15T03:20:25.084990Z",
+ "email": "regis@email.com",
+ "first_name": "Regis",
+ "last_name": "Santos",
+ "pk": 2
+ }
+]
+```
+
+Veja os detalhes
+
+```bash
+$ http http://127.0.0.1:8000/persons/1/
+```
+
+> **Atenção**: se você receber erro 301, muito provavelmente é porque você esqueceu da barra `/` no final da url.
+
+
+### Como seria em curl?
+
+Assim
+
+```bash
+$ curl http://127.0.0.1:8000/persons/
+
+[{"pk":1,"first_name":"Paul","last_name":"Van Dyke","email":"paul@email.com","active":true,"created":"2015-11-15T03:20:24.938378Z"},{"pk":2,"first_name":"Regis","last_name":"Santos","email":"regis@email.com","active":true,"created":"2015-11-15T03:20:25.084990Z"}]
+```
+
+
+GitHub: Se você quiser pode olhar meu [GitHub][8], mas terá que ver os *commits* para ver os passos.
+
+[0]: http://www.django-rest-framework.org/
+[1]: https://github.com/hugobrilhante/drf-tutorial-pybr11
+[2]: http://pythonbrasil.github.io/pythonbrasil11-site/
+[3]: https://www.djangoproject.com/
+[4]: http://pythonclub.com.br/tutorial-django-17.html
+[5]: https://pt.wikipedia.org/wiki/Serializa%C3%A7%C3%A3o
+[6]: http://curl.haxx.se/
+[7]: https://github.com/jakubroztocil/httpie#installation
+[8]: https://github.com/rg3915/drf.git
+[9]: http://www.django-rest-framework.org/tutorial/1-serialization/
+[10]: http://pythonclub.com.br/django-rest-framework-quickstart.html
+[11]: http://pythonclub.com.br/django-rest-framework-requests-responses.html
+[12]: http://pythonclub.com.br/django-rest-framework-class-based-views.html
\ No newline at end of file
diff --git a/content/django_na_pratica_parte_1.rst b/content/django_na_pratica_parte_1.rst
new file mode 100644
index 000000000..d09caf94d
--- /dev/null
+++ b/content/django_na_pratica_parte_1.rst
@@ -0,0 +1,276 @@
+Django na prática - Hello World
+#############################################
+
+:date: 2015-10-26 09:25
+:tags: python, django, django-na-pratica
+:category: Python
+:slug: django-na-pratica-aula-01
+:author: Lucas Magnum
+:email: lucasmagnumlopes@gmail.com
+:github: lucasmagnum
+:linkedin: lucasmagnum
+
+
+Esse é o primeiro post do Curso **django na prática** onde você aprenderá tudo que precisa para criar um sistema web :D
+
+Utilizaremos a versão 1.8 do Django com Python 3!
+
+
+==========
+Requisitos
+==========
+
+* Linux (Ubuntu)
+* Python 3.X
+* Virtualenv
+* pip
+
+
+Se precisar de ajuda para instalar o pip, você pode utilizar esse `tutorial `_.
+
+
+----------------
+Convenções
+----------------
+
+.. code-block:: bash
+
+ $ indica que comando deve ser executado no terminal do Linux
+ >>> indica que comando deve ser executado pelo interpretador Python em modo interativo
+
+
+===========
+Instalação
+===========
+
+Ative seu virtualenv e instale o Django na versão 1.8:
+
+.. code-block:: bash
+
+ $ pip install "django>=1.8,<1.9"
+
+
+Se tiver alguma duvida, você pode olhar na `documentação `_ como instalar o framework.
+
+
+Para verificar se está tudo certo, abra o interpretador python e verifique a versão do Django:
+
+.. code-block:: python
+
+ >>> import django
+ >>> print(django.get_version())
+ 1.8.5
+
+
+Isso é tudo que precisamos para começar =)
+
+
+===============
+django-admin.py
+===============
+
+o ``django-admin.py`` é um script de linha de comando do Django que nos oferece vários comandos administrativos.
+
+Existem várias opções, para visualizar todas basta executar:
+
+.. code-block:: bash
+
+ $ django-admin.py help
+
+
+Alguns parâmetros importantes são ``--pythonpath`` e ``--settings``.
+
+ * Como vamos criar nosso projeto do zero, precisamos informar onde nossos módulos estarão localizados e para isso utilizaremos o parâmetro ``--pythonpath``.
+ * Precisamos informar ao Django onde encontrar nossas configurações e para isso utilizaremos o parâmetro ``--settings``.
+
+
+=======================
+Configurando o ambiente
+=======================
+
+Crie um arquivo chamado ``helloworld.py``:
+
+.. code-block:: bash
+
+ $ touch helloworld.py
+
+Criamos nosso arquivo e agora vamos rodar o `ambiente de desenvolvimento `_ do Django :D
+
+.. code-block:: bash
+
+ $ django-admin.py runserver --pythonpath=. --settings=helloworld
+
+
+Dessa forma, estamos dizendo ao Django que nossos arquivos estão no diretório atual e que nossas configurações estão no arquivo ``heloworld`` (não devemos informar a extensão do arquivo no parâmetro).
+
+**Puts**, ocorreu um erro!
+
+.. code-block:: bash
+
+ django.core.exceptions.ImproperlyConfigured: The SECRET_KEY setting must not be empty.
+
+Atualmente o Django não inicia sem a `SECRET_KEY `_ configurada. Precisamos adiciona-la ao nosso arquivo.
+
+Abra o arquivo ``helloworld.py`` e insira uma SECRET_KEY qualquer:
+
+.. code-block:: python
+
+ SECRET_KEY='helloworld'
+
+
+Para ambiente de teste não existe nenhum problema em deixar nossa SECRET_KEY com esse valor, porém para o ambiente de produção é necessário que seja um valor randômico. A SECRET_KEY é utilizada em diversas partes do Django para criar hashes e encriptar chaves. Por esse fato, você NUNCA DEVE deixar pública o valor de SECRET_KEY utilizado em ambientes de produção.
+Mais informações `aqui `_.
+
+Executando novamente nosso ambiente, teremos o seguinte erro:
+
+.. code-block:: bash
+
+ $ django-admin.py runserver --pythonpath=. --settings=helloworld
+
+ CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False.
+
+Como estamos em ambiente de desenvolvimento, precisamos utilizar o ``DEBUG`` como ``True``, somente para produção que ele será desativado.
+
+Nosso arquivo ``helloworld.py`` agora está assim:
+
+.. code-block:: python
+
+ SECRET_KEY='helloworld'
+ DEBUG = True
+
+Com isso já é possível subir o ambiente de desenvolvimento.
+
+.. code-block:: bash
+
+ $ django-admin.py runserver --pythonpath=. --settings=helloworld
+
+ Performing system checks...
+
+ System check identified some issues:
+
+ WARNINGS:
+ ?: (1_7.W001) MIDDLEWARE_CLASSES is not set.
+ HINT: Django 1.7 changed the global defaults for the MIDDLEWARE_CLASSES. django.contrib.sessions.middleware.SessionMiddleware, django.contrib.auth.middleware.AuthenticationMiddleware, and django.contrib.messages.middleware.MessageMiddleware were removed from the defaults. If your project needs these middleware then you should configure this setting.
+
+ System check identified 1 issue (0 silenced).
+ September 26, 2015 - 08:50:09
+ Django version 1.8.5, using settings 'helloworld'
+ Starting development server at http://127.0.0.1:8000/
+ Quit the server with CONTROL-C.
+
+
+Pronto! Nosso ambiente já está rodando na porta 8000, abra seu navegador e digite ``http://127.0.0.1:8000/``.
+
+============
+Hello World
+============
+
+
+Ok, nosso ambiente está rodando, porém ainda temos erros. O que aconteceu?
+
+Se você visualizar no terminal onde o ambiente está sendo executado, verá a seguinte mensagem:
+
+.. code-block:: bash
+
+ AttributeError: 'Settings' object has no attribute 'ROOT_URLCONF'
+
+
+Para encontrar as *views* que serão renderizadas no projeto, o Django procura primeiro as configurações no
+arquivo apontado pelo ``ROOT_URLCONF``.
+
+* uma view é uma função responsável por retornar algo para ser renderizado no browser, pode ser um html, um arquivo, um json e etc.
+
+
+Como toda nossa aplicação ficará por enquanto no arquivo ``helloworld.py``, vamos apontar nosso ``ROOT_URLCONF`` para ele.
+
+Abra o arquivo ``helloworld.py`` e insira o seguinte código:
+
+.. code-block:: python
+
+ SECRET_KEY = 'helloworld'
+ DEBUG = True
+ ROOT_URLCONF = __name__
+
+Estamos dizendo ao Django que nossas `urls` estão nesse arquivo, para reconhecer as urls, o django procura
+pela variável ``urlpatterns``.
+
+Logo, nosso arquivo deve ficar assim:
+
+.. code-block:: python
+
+
+ SECRET_KEY = 'helloworld'
+ DEBUG = True
+ ROOT_URLCONF = __name__
+
+ urlpatterns = []
+
+
+Agora, se abrirmos nosso navegador no endereço ``http://127.0.0.1:8000/`` já recebemos a página de bem vindo do Django =DDD
+
+
+.. image:: images/lucasmagnum/itworked.png
+ :alt: itworked
+
+
+---------------------
+Nossa primeira view
+---------------------
+
+Agora sim, tudo está pronto para criarmos nossa primeira view!
+
+Vamos criar nossa view chamada ``hello_world``, toda view recebe como primeiro o ``request``,
+e precisa retornar alguma resposta para o navegador, vamos retornar um ``HttpResponse`` com o texto
+*Django na prática - Hello World!*
+
+Modifique seu ``helloworld.py`` para que fique assim:
+
+.. code-block:: python
+
+ from django.http import HttpResponse
+
+
+ SECRET_KEY = 'helloworld'
+ DEBUG = True
+ ROOT_URLCONF = __name__
+
+ def hello_world(request):
+ return HttpResponse('Django na prática - Hello World!')
+
+ urlpatterns = []
+
+
+Pronto! Temos nossa view criada, porém ainda não conseguimos acessá-la.
+Precisamos dizer ao framework como essa view pode ser encontrada e para qual ``url`` ela deve responder.
+
+Façamos dessa forma:
+
+.. code-block:: python
+
+ from django.conf.urls import url
+ from django.http import HttpResponse
+
+
+ SECRET_KEY = 'helloworld'
+ DEBUG = True
+ ROOT_URLCONF = __name__
+
+ def hello_world(request):
+ return HttpResponse('Django na prática - Hello World!')
+
+ urlpatterns = [
+ url(http://github.com/r'%5E),%20hello_world)
+ ]
+
+Dentro do ``urlpatterns`` nós informamos quais são as urls disponíveis no nosso projeto.
+Fazemos isso usando utilizado uma expressão regular associada à uma função, que no nosso caso é o ``hello_world``.
+
+Agora, se abrirmos o navegador, iremos nos deparar com o seguinte resultado:
+
+.. image:: images/lucasmagnum/helloworld.png
+ :alt: hello world
+
+
+Por hoje é isso!!! Guarde o arquivo criado hoje, pois ele será utilizado nas próximas aulas!
+
+Até a próxima =)
diff --git a/content/django_releases_em_10_minutos.rst b/content/django_releases_em_10_minutos.rst
new file mode 100644
index 000000000..7650ca630
--- /dev/null
+++ b/content/django_releases_em_10_minutos.rst
@@ -0,0 +1,118 @@
+Django - 3 anos em 10 minutos
+#############################################
+
+:date: 2015-04-06 11:55
+:tags: python, django
+:category: Python
+:slug: django-overview-10-minutos
+:author: Lucas Magnum
+:email: lucasmagnumlopes@gmail.com
+:github: lucasmagnum
+:linkedin: lucasmagnum
+
+
+Em março de 2012 foi lançada a versão 1.4 e por aqui que nossa jornada começa, o objetivo não é entrar em detalhes em todas as features que foram implementadas e sim um `overview` sobre as que considero mais importantes para nosso dia a dia.
+
+
+Versão `1.4 `_ (Há 3 anos)
+--------------------------------------------------------------------------------
+
+Lançada em Março de 2012, suporta Python 2.5 >= 2.7.
+
+Foi a primeira versão LTS (long-term support), ou seja, ela recebeu correções e atualizações de segurança por pelo menos 3 anos após à data de lançamento.
+
+É a primeira versão a permitir o uso de `custom project template `_ e tornar o arquivo `manage.py` *o* que conhecemos hoje.
+
+Foi a primeira versão a suportar o `testes `_ feitos com frameworks que utilizam o browser como o `Selenium `_.
+
+
+Algumas outras coisas legais foram:
+
+ * `bulk_create `_ para criar vários objetos de uma só vez.
+ * `prefetch_related `_ para realizar `joins` em tabelas que possuem relações `many-to-many`.
+ * `reverse_lazy `_ que permite fazer `reverse` antes das configurações de URL serem carregadas.
+
+
+Versão `1.5 `_ (Há 2 anos)
+--------------------------------------------------------------------------------
+
+Lançada em Fevereiro de 2013, suporta Python 2.6.5 >= 2.7 (Python 3 - Experimental).
+
+A versão 1.5 ficou bem conhecida pela reformulação na aplicação de autenticação, é possível `customizar um usuário `_ conforme nossas necessidades.
+
+É possivel também passar os campos que você `gostaria de atualizar `_ quando for salvar um objeto.
+
+Algumas outras coisas legais foram:
+
+ * `verbatim `_ template tag para casos onde você deseja ignorar a sintaxe do template.
+ * `novos tutoriais `_ foram inseridos para ajudar iniciantes e existe também uma seção `Tutoriais Avançados`
+
+
+Versão `1.6 `_ (Há 1 ano e meio)
+--------------------------------------------------------------------------------------
+
+Lançada em Novembro de 2013, suporta Python 2.6.5 >= 3.3. (Python 3!!!!)
+
+
+A versão 1.6 alterou um pouco do `layout` padrão dos projetos que estávamos acostumados, para nossa alegria o `admin.py` é adicionado por padrão na aplicação :)
+
+Transações possuem `autocommit` habilitado por padrão!
+
+Os testes são localizados em qualquer arquivo que possua o nome começando com **test_** (Antigamente os testes só eram encontrados nos arquivos models.py e tests.py)
+
+Algumas outras coisas legais foram:
+
+ * `check `_ comando para validar se suas configurações estão compatíveis com a versão do django.
+
+
+Versão `1.7 `_ (Há 8 meses)
+---------------------------------------------------------------------------------
+
+Lançada em Setembro de 2014, suporta Python 2.7 >= 3.4.
+
+Um novo conceito de `migrações `_ foi implementado, até o momento a maioria utilizava o `South `_, nessa versão tudo é `built-in`.
+
+Para criar uma aplicação não é necessário mais conter o arquivo `models.py`.
+
+O `conceito de aplicação `_ foi atualizado e existem vários novas maneiras de se customizar seus dados :)
+
+O comando `check` foi evoluído e agora realiza uma varredura quase completa no seu código para identificar possíveis problemas.
+
+
+Versão `1.8 `_
+--------------------------------------------------------------------
+
+Lançada em Abril de 2015, a versão 1.8 introduz várias features legais!
+
+É a segunda versão LTS (long-term support), ou seja, vai receber correções e atualizações de segurança por pelo menos 3 anos após à data de lançamento.
+
+Agora há a possibilidade de utilizar `várias linguagens(engines) `_ de templates, ou seja, você pode usar simuntaneamente (não no mesmo arquivo) a `Django Template Language `_ ou `Jinja2 `_. Há uma API padronizada para quem quiser adicionar suporte para outras linguagens de template no futuro. Há um `guia de migração `_ .
+
+Novos campos foram introduzidos como `UUIDField `_ e `DurationField `_ e ainda tem mais!
+
+O `Model._meta API `_ foi totalmente refatorado e padronizado. O `Model._meta` API foi incluida no Django 0.96 e serve para obter informações sobre campos do Model, contudo, essa api era considerada de uso privado, e não tinha qualquer documentação ou comentários. Agora com tudo padronizado, abre uma gama de novas opções para criar apps plugaveis que fazem coisas com Models arbitrarios.
+
+Algumas outras coisas legais foram:
+
+ * O comando de gerenciamento `inspectdb `_ agora suporta fazer engenharia reversa tambem de Database Views (nas versões anteriores, o inspectdb inspecionava somente tabelas, mas não conseguia "ver" as views).
+ * Adição/Melhoria de `Query Expressions, Conditional Expressions, and Database Functions `_ . Isso adiciona muito mais flexibilidade para fazer pesquisas mais complexas no banco de dados.
+
+
+
+Há várias outras ótimas melhorias que omiti, veja o `release note `_ completo.
+
+O Futuro
+----------
+
+Versão 1.9 (Com lançamento previsto para Outubro de 2016)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+O Django 1.8 mal foi lançado, e já há algumas novidades que talvez venham no Django 1.9.
+
+Muito provavelmete, o Django 1.9 vai adicionar os tão esperados `Campos Compostos `_ . Isso vai permitir fazer coisas mais complexas, como ter um campo Dinheiro, que "sabe" como fazer conversões de moeda (ex. Real para Dolar).
+
+Tambem existe uma expectativa que o tema `django-flat-admin `_ para o `Admin `_ seja integrado no Django 1.9, virando o tema padrão. O `django-flat-admin `_ somente modifica o CSS, nenhuma tag HTML é alterada em relação ao HTML original do Admin, então ele é relativemente compativel (desde que você não tenha incluido customizações no CSS do Admin). Os core developers do Django estão tratando desse assunto `neste tópico `_
+
+
+Veja o `Roadmap `_ do que vem por ai no Django 1.9
+
diff --git a/content/encapsulamento-da-logica-do-algoritmo.md b/content/encapsulamento-da-logica-do-algoritmo.md
new file mode 100644
index 000000000..34a5104c0
--- /dev/null
+++ b/content/encapsulamento-da-logica-do-algoritmo.md
@@ -0,0 +1,178 @@
+Title: Encapsulamento da lógica do algoritmo
+Slug: encapsulamento-da-logica-do-algoritmo
+Date: 2021-03-02 15:00
+Category: Python
+Tags: python, poo
+Author: Eduardo Klosowski
+Email: eduardo_klosowski@yahoo.com
+Github: eduardoklosowski
+Twitter: eduklosowski
+Site: https://dev.to/eduardoklosowski
+About_author: Programador, formado em redes de computadores, estuda DevOps
+
+Muitas listas de exercícios de lógica de programação pedem em algum momento que um valor seja lido do teclado, e caso esse valor seja inválido, deve-se avisar, e repetir a leitura até que um valor válido seja informado. Utilizando a ideia de [otimização do algoritmo passo a passo](https://dev.to/acaverna/otimizando-o-algoritmo-passo-a-passo-4co0), começando com uma solução simples, pretendo estudar como reduzir a duplicação de código alterando o algoritmo, encapsulando a lógica em funções, e encapsulando em classes.
+
+## Exercício
+
+Um exemplo de exercício que pede esse tipo de validação é a leitura de notas, que devem estar entre 0 e 10. A solução mais simples, consiste em ler um valor, e enquanto esse valor for inválido, dar o aviso e ler outro valor. Exemplo:
+
+```python
+nota = float(input('Digite a nota: '))
+while nota < 0 or nota > 10:
+ print('Nota inválida')
+ nota = float(input('Digite a nota: '))
+```
+
+Esse algoritmo funciona, porém existe uma duplicação no código que faz a leitura da nota (uma antes do *loop* e outra dentro). Caso seja necessário uma alteração, como a mudança da nota para um valor inteiro entre 0 e 100, deve-se alterar os dois lugares, e se feito em apenas um lugar, o algoritmo poderia processar valores inválidos.
+
+### Alterando o algoritmo
+
+Visando remover a repetição de código, é possível unificar a leitura do valor dentro do *loop*, uma vez que é necessário repetir essa instrução até que o valor válido seja obtido. Exemplo:
+
+```python
+while True:
+ nota = float(input('Digite a nota: '))
+ if 0 <= nota <= 10:
+ break
+ print('Nota inválida!')
+```
+
+Dessa forma, não existe mais a repetição de código. A condição de parada, que antes verificava se o valor era inválido (o que pode ter uma leitura não tão intuitiva), agora verifica se é um valor válido (que é geralmente é mais fácil de ler e escrever a condição). E a ordem dos comandos dentro do *loop*, que agora estão em uma ordem que facilita a leitura, visto que no algoritmo anterior era necessário tem em mente o que era executado antes do *loop*.
+
+Porém esses algoritmos validam apenas o valor lido, apresentando erro caso seja informado um valor com formato inválido, como letras em vez de números. Isso pode ser resolvido tratando as exceções lançadas. Exemplo:
+
+```python
+while True:
+ try:
+ nota = float(input('Digite a nota: '))
+ if 0 <= nota <= 10:
+ break
+ except ValueError:
+ ...
+ print('Nota inválida!')
+```
+
+### Encapsulamento da lógica em função
+
+Caso fosse necessário ler várias notas, com os algoritmos apresentados até então, seria necessário repetir todo esse trecho de código, ou utilizá-lo dentro de uma estrutura de repetição. Para facilitar sua reutilização, evitando a duplicação de código, é possível encapsular esse algoritmo dentro de uma função. Exemplo:
+
+```python
+def nota_input(prompt):
+ while True:
+ try:
+ nota = float(input(prompt))
+ if 0 <= nota <= 10:
+ break
+ except ValueError:
+ ...
+ print('Nota inválida!')
+ return nota
+
+
+nota1 = nota_input('Digite a primeira nota: ')
+nota2 = nota_input('Digite a segunda nota: ')
+```
+
+### Encapsulamento da lógica em classes
+
+Em vez de encapsular essa lógica em uma função, é possível encapsulá-la em uma classe, o que permitiria separar cada etapa do algoritmo em métodos, assim como ter um método responsável por controlar qual etapa deveria ser chamada em qual momento. Exemplo:
+
+```python
+class ValidaNotaInput:
+ mensagem_valor_invalido = 'Nota inválida!'
+
+ def ler_entrada(self, prompt):
+ return input(prompt)
+
+ def transformar_entrada(self, entrada):
+ return float(entrada)
+
+ def validar_nota(self, nota):
+ return 0 <= nota <= 10
+
+ def __call__(self, prompt):
+ while True:
+ try:
+ nota = self.transformar_entrada(self.ler_entrada(prompt))
+ if self.validar_nota(nota):
+ break
+ except ValueError:
+ ...
+ print(self.mensagem_valor_invalido)
+ return nota
+
+
+nota_input = ValidaNotaInput()
+
+
+nota = nota_input('Digite a nota: ')
+```
+
+Vale observar que o método `__call__` permite que o objeto criado a partir dessa classe seja chamado como se fosse uma função. Nesse caso ele é o responsável por chamar cada etapa do algoritmo, como: `ler_entrada` que é responsável por ler o que foi digitado no teclado, `transformar_entrada` que é responsável por converter o texto lido para o tipo desejado (converter de `str` para `float`), e `validar_nota` que é responsável por dizer se o valor é válido ou não. Vale observar que ao dividir o algoritmo em métodos diferentes, seu código principal virou uma espécie de código comentado, descrevendo o que está sendo feito e onde está sendo feito.
+
+Outra vantagem de encapsular a lógica em classe, em vez de uma função, é a possibilidade de generalizá-la. Se fosse necessário validar outro tipo de entrada, encapsulando em uma função, seria necessário criar outra função repetindo todo o algoritmo, alterando apenas a parte referente a transformação do valor lido, e validação, o que gera uma espécie de repetição de código. Ao encapsular em classes, é possível se aproveitar dos mecanismos de herança para evitar essa repetição. Exemplo:
+
+```python
+class ValidaInput:
+ mensagem_valor_invalido = 'Valor inválido!'
+
+ def ler_entrada(self, prompt):
+ return input(prompt)
+
+ def transformar_entrada(self, entrada):
+ raise NotImplementedError
+
+ def validar_valor(self, valor):
+ raise NotImplementedError
+
+ def __call__(self, prompt):
+ while True:
+ try:
+ valor = self.transformar_entrada(self.ler_entrada(prompt))
+ if self.validar_valor(valor):
+ break
+ except ValueError:
+ ...
+ print(self.mensagem_valor_invalido)
+ return valor
+
+
+class ValidaNomeInput(ValidaInput):
+ mensagem_valor_invalido = 'Nome inválido!'
+
+ def transformar_entrada(self, entrada):
+ return entrada.strip().title()
+
+ def validar_valor(self, valor):
+ return valor != ''
+
+
+class ValidaNotaInput(ValidaInput):
+ mensagem_valor_invalido = 'Nota inválida!'
+
+ def transformar_entrada(self, entrada):
+ return float(entrada)
+
+ def validar_valor(self, valor):
+ return 0 <= valor <= 10
+
+
+nome_input = ValidaNomeInput()
+nota_input = ValidaNotaInput()
+
+
+nome = nome_input('Digite o nome: ')
+nota = nota_input('Digite a nota: ')
+```
+
+Dessa forma, é possível reutilizar o código já existente para criar outras validações, sendo necessário implementar apenas como converter a `str` lida do teclado para o tipo desejado, e como esse valor deve ser validado. Não é necessário entender e repetir a lógica de ler o valor, validá-lo, imprimir a mensagem de erro, e repetir até que seja informado um valor válido.
+
+## Considerações
+
+É possível encapsular a lógica de um algoritmo em funções ou em classes. Embora para fazê-lo em uma classe exija conhecimentos de programação orientada a objetos, o seu reaproveitamento é facilitado, abstraindo toda a complexidade do algoritmo, que pode ser disponibilizado através de uma biblioteca, exigindo apenas a implementações de métodos simples por quem for a utilizar.
+
+Ainda poderia ser discutido outras formas de fazer essa implementação, como passar funções como parâmetro e a utilização de [corrotinas](https://docs.python.org/pt-br/3/library/asyncio-task.html) no encapsulamento do algoritmo em função, assim como a utilização de [classmethod](https://docs.python.org/pt-br/3/library/functions.html#classmethod), [staticmethod](https://docs.python.org/pt-br/3/library/functions.html#staticmethod) e [ABC](https://docs.python.org/pt-br/3/library/abc.html) no encapsulamento do algoritmo em classes.
+
+---
+
+Esse artigo foi publicado originalmente no [meu blog](https://eduardoklosowski.github.io/blog/), passe por lá, ou siga-me no [DEV](https://dev.to/eduardoklosowski) para ver mais artigos que eu escrevi.
diff --git a/content/entrevista_henrique_bastos.md b/content/entrevista_henrique_bastos.md
new file mode 100644
index 000000000..001a244d9
--- /dev/null
+++ b/content/entrevista_henrique_bastos.md
@@ -0,0 +1,54 @@
+Title: Entrevista com Henrique Bastos
+Slug: entrevista-henrique-bastos
+Date: 2015-01-23 23:45
+Tags: python,entrevistas
+Author: Matheus Ap Godoy Ribeiro
+Email: matheusgodoy@me.com
+Github: matheus
+Bitbucket:
+Site: http://matheus.me
+Twitter: matheusdotme
+Category: Python,Entrevistas
+
+
+Entrevista com Henrique Bastos
+------------------------------
+
+A primeira vez que ouvi o [HenriqueBastos](https://twitter.com/henriquebastos) foi no [GrokPodcast](http://www.grokpodcast.com/), foi o que me motivou a pesquisar sobre Python
+
+
+
+[Episodio 6 - A linguagem Python - Parte 1](http://www.grokpodcast.com/2010/10/20/episodio-6-a-linguagem-python-parte-1/)
+
+[Episodio 7 - A linguagem Python - Parte 2](http://www.grokpodcast.com/2010/10/28/episodio-7-a-linguagem-python-parte-2/)
+
+[Episodio 12 - Django - O framework web para perfeccionistas](http://www.grokpodcast.com/2010/12/01/episodio-12-django-o-framework-web-para-perfeccionistas/)
+
+Li todos os posts no blog [henriquebastos.net](http://henriquebastos.net/). Mas precisava de algo mais,conhecer mais de perto o Henrique e seus conhecimentos foi entao que ingressei
+no curso [Welcome To The Django](http://welcometothedjango.com.br/), o qual RECOMENDO FORTEMENTE, mesmo para quem usa outro framework ou linguagem, as boas práticas de desenvolvimento ensinadas no curso são para a vida toda.
+
+Atualmente acompanho o Henrique Bastos pelo youtube e no podcast [Curto Circuito](http://www.curtocircuito.cc/).
+
+Seguem as respostas do Henrique.
+
+> Muito obrigado Henrique!
+
+_1. Em que momentos na vida agradeceu por ter escolhido Python e quais os motivos?_
+
+Essa pergunta e cabulosa e tem muitas sutilezas nas entrelinhas.
+
+Minha resposta curta é: NENHUM.
+
+Não me entenda mal. Eu gosto muitíssimo da linguagem Python e de todo o seu ecossistema. Mas observe que eu só "escolhi Python" porquê gosto, porquê percebo afinidades que me atraem. Esses valores subjetivos são difíceis de demonstrar, mas por sorte temos uma pequena referência no [Zen do Python](http://ideiaemconflito.blogspot.com.br/2012/05/o-zen-de-python.html), por exemplo. De todo modo, eu programo mais em Python do que em outras linguagens, mas estou sempre aprendendo algo novo.
+
+Se há algo pelo que agradecer, fica meu agradecimento para todas as pessoas que colaboraram direta ou indiretamente para que Python exista. E do meu lado, fica a satisfação de "me ouvir" continuamente e insistir na busca pelo que gosto.
+
+_2. Qual conselho compartilha com jovens desenvolvedores?_
+
+Seja cético, principalmente com as suas certezas. Mas seja passional com as suas preferências, sem esquecer que são apenas _suas_ _preferências_.
+
+_3. Qual pergunta que não fiz você gostaria de responder e qual e a resposta?_
+
+Que dia e hoje?
+
+Dia 2 de Janeiro de 2014.
diff --git a/content/explicit_is_better_than_implicit.md b/content/explicit_is_better_than_implicit.md
new file mode 100644
index 000000000..0b45b9587
--- /dev/null
+++ b/content/explicit_is_better_than_implicit.md
@@ -0,0 +1,108 @@
+Title: Explicit is better than implicit
+Date: 2016-04-22 23:00
+Tags: python, zen of python, experências, decisões de desenvolvimento
+Category: Experiências
+Slug: explicit-is-better-than-implicit
+Author: Ivan Neto
+About_author: Desenvolvedor Python, esposo, pai, escritor nas horas vagas
+Email: ivan.cr.neto@gmail.com
+Github: ivancrneto
+Twitter: ivancrneto
+Linkedin: ivanrocha
+
+Esse post não é diretamente relacionado a desenvolvimento com Python, mas conta a história de uma das muitas experiências que passamos desenvolvendo e mostra como a filosofia e o _mindset_ __Python__ podem nos influenciar a tomar decisões melhores.
+
+## Contexto geral
+
+Atualmente trabalho remotamente pela Toptal, uma empresa de consultoria em _software_ com foco em trabalho remoto e que tem um processo seletivo bastante rígido para garantir uma qualidade acima da média para seus clientes ([saiba mais sobre a Toptal aqui](https://www.toptal.com/#book-tested-programmers)).
+
+No time em que faço parte os papéis são bem definidos entre desenvolvedores _front-end_ e _back-end_ e faço parte da equipe de _back-end_, que usa principalmente __Django__ nas aplicações. À medida que evoluímos e nos tornamos mais maduros como time, buscamos soluções que pudessem otimizar nosso processo de desenvolvimento.
+
+Atualmente utilizamos _CircleCI_ -- uma plataforma para integração e entrega contínuas -- para tarefas como rodar nossa suíte de testes, fazer a integração de nosso código, instanciar uma nova versão de nossos sistemas em um ambiente de _staging_ e criar imagens __Docker__ posteriormente colocadas em produção.
+
+## Melhorias
+
+Nosso time constantemente reavalia processos, ferramentas e o resultado são discussões interessantes sobre como tornar nosso trabalho mais rápido e produtivo.
+
+Recentemente começamos a utilizar um servidor __NPM__ -- um dos mais usados gerenciadores de pacotes para __Javascript__ -- privado para uma melhor separação de pacotes _front-end_, otimizando o tempo de _build_ de _assets_ de 47 para 25 segundos.
+
+Na raiz do nosso projeto temos um _package.json_ com o seguinte conteúdo:
+
+``` json
+{
+ // [ ... ]
+ "dependencies": {
+ "cat": "^1.0.0",
+ "front": "^1.0.0",
+ "core": "^1.0.0",
+ },
+ // [ ... ]
+}
+```
+
+Sendo que __cat__, __front__ e __core__ (renomeados para exemplificar) são pacotes mantidos por nós mesmos no __NPM__ privado. Por padrão, se você lista o pacote com `“^”` (como por exemplo acima `“^1.0.0”`), o _npm_ considera apenas o número que representa a _major version_, no caso o número 1, e fará o _download_ da última versão que começa com 1.
+
+Essa abordagem tem quatro pontos fracos:
+
+ 1. Ela pode quebrar seu código. Se pacote de terceiro atualizar, seu código pode não estar preparado para lidar com as novas funcionalidades adicionadas, principalmente porque _libs_ evoluem tão rapidamente que se torna fácil acontecer uma atualização sem _backwards compatibility_.
+ 2. Você não sabe exatamente qual versão do pacote seu sistema está usando em produção. Para saber, você teria que acessar os servidores remotamente e executar o comando `npm list`, por exemplo (poderia fazer localmente também mas existe a possibilidade de que no momento em que ocorreu o _deploy_, aquele pacote estava em uma versão anterior à sua versão local).
+ 3. Você perde o controle de quando quer que seu sistema utilize a nova versão do pacote.
+ 4. Se você precisar fazer um _rollback_ ou usar uma imagem antiga de seu sistema em produção, ainda assim ela vai utilizar a última versão do pacote, o que pode levar a mais dores de cabeça.
+
+# Problema
+
+Recentemente tivemos um _bug_ em produção, e uma mudança no pacote _core_ resolveria. __O que fazer com o sistema principal?__ Nada, não era necessária nenhuma alteração. Só precisaríamos gerar uma nova imagem _Docker_ que ela seria montada do zero e no momento de instalar os pacotes _npm_, baixaria a última versão.
+
+Bastava realizar _rebuild_ na branch _master_ no __CircleCI__, que assim que terminado ele trataria de enviar um _webhook_ para o nossa ferramenta que cria imagens _Docker_. Nós utilizamos o seguinte padrão de nomenclatura dessas imagens:
+```
+myapp-production--
+```
+Como não fizemos nenhuma alteração no sistema principal, o _branch_ e o _sha_ continuaram os mesmos.
+
+Resumindo, nosso _Docker_ recebeu um pedido de _build_ para aquela _branch_ e _sha_ e, por padrão, primeiro procurou em seu _cache_ de imagens se já existia alguma imagem pronta com aquele nome. O resultado foi que a mesma imagem, sem o _hotfix_, foi para produção (pois ela havia sido criada antes e no momento em que baixou os pacotes _npm_ ainda não havia alterações no _core_).
+
+Demoramos um pouco para perceber o problema, mas o suficiente para resolvê-lo sem que _stakeholders_ percebessem.
+
+## Solução
+
+Algum tempo depois discutimos e nós desenvolvedores _back-end_ sugerimos a seguinte solução:
+
+``` json
+{
+ // [ ... ]
+ "dependencies": {
+ "cat": "1.0.5",
+ "front": "1.0.7",
+ "core": "1.0.10",
+ },
+ // [ ... ]
+}
+```
+
+Com essa abordagem:
+
+1. Você pode fazer _rollback_ do seu código sem problemas pois o código antigo vai usar a versão antiga do pacote.
+2. Você tem controle sobre quando quer que seu sistema utilize a nova versão do pacote.
+3. Você sabe exatamente quais versões de pacotes seu sistema está utilizando, bastando abrir o _packages.json_.
+4. Caso uma nova versão quebre seu código, você pode voltar uma versão rapidamente até que o problema seja resolvido.
+
+O problema que tivemos em produção não aconteceria caso tivéssemos utilizado a abordagem acima. Assim que os pacotes fossem atualizados, criaríamos uma _pull request_ no repositório do sistema principal com as seguintes alterações:
+
+``` diff
+diff --git i/package.json w/package.json
+index eaae10d..5aa773b 100644
+--- i/package.json
++++ w/package.json
+@@ -9,7 +9,7 @@
+ "dependencies": {
+ "cat": "1.0.5",
+ "front": "1.0.7",
+- "core": "1.0.10",
++ "core": "1.0.11",
+ },
+```
+Após o _merge_, um novo _build_ aconteceria no __CircleCI__, e um novo _sha_ seria enviado via _webhook_. O _Docker_ não encontraria nenhuma imagem com essa combinação de _branch_ e _sha_ e criaria uma nova do zero. Produção teria o _hotfix_ e não haveria constrangimento.
+
+Os desenvolvedores _front-end_ não gostaram da ideia de ter que atualizar o arquivo toda vez que alguma dependência subisse de versão. Discutimos bastante e a última coisa que eu disse foi: __“from the Zen of Python: explicit is better than implicit”__.
+
+Lição aprendida.
diff --git a/content/extraindo_texto_de_imagens_com_python.md b/content/extraindo_texto_de_imagens_com_python.md
new file mode 100644
index 000000000..30fe41f79
--- /dev/null
+++ b/content/extraindo_texto_de_imagens_com_python.md
@@ -0,0 +1,73 @@
+Title: Extraindo Texto de Imagens com Python
+Date: 2015-11-22 17:00
+Tags: imagens,ocr,pytesseract,extrair texto
+Category: Manipulação de imagens
+Slug: extraindo-texto-de-imagens-com-python
+Author: André Ramos
+Email: andrel.ramos97@gmail.com
+Github: andrelramos
+About_author: Programador web/desktop/mobile. Apaixonado por tecnologia, programação e python.
+
+Introdução
+-----------
+
+Já precisou extrair texto de imagens mas não sabia como? aprenda como fazer isso com apenas 3 linhas de código (Por isso amo python!). Antes de começarmos, vamos ver um pouco de teoria.
+
+### O que é OCR?
+
+Segundo o Wikipedia, OCR é um acrónimo para o inglês Optical Character Recognition, é uma tecnologia para reconhecer caracteres a partir de um arquivo de imagem ou mapa de bits sejam eles escaneados, escritos a mão, datilografados ou impressos. Dessa forma, através do OCR é possível obter um arquivo de texto editável por um computador. A engine OCR que vamos utilizar é a **Tesseract**, a mesma foi inicialmente desenvolvida nos laboratórios da HP e tem seu projeto hospedado em: [https://github.com/tesseract-ocr/tesseract](https://github.com/tesseract-ocr/tesseract). Texto adaptado de: [https://pt.wikipedia.org/wiki/Reconhecimento_ótico_de_caracteres](https://pt.wikipedia.org/wiki/Reconhecimento_%C3%B3tico_de_caracteres)
+
+Como descrito acima, já existe uma tecnologia para realizar essa função, então apenas precisamos utilizá-la em nosso script python e assim desenvolvermos o que a imaginação permitir.
+
+### Instalando Dependências (Ubuntu)
+
+Primeiro vamos começar pela instalação do Tesseract OCR. Abra o terminal e digite o seguinte comando:
+
+ :::bash
+ $ sudo apt-get install tesseract-ocr tesseract-ocr-por
+
+Também precisamos instalar a biblioteca Pillow e suas dependências. Ela será necessária para carregar a imagem para nosso script:
+
+Ubuntu 12.04/14.04:
+
+ :::bash
+ $ sudo apt-get install python-dev python3-dev build-essential liblcms1-dev zlib1g-dev libtiff4-dev libjpeg8-dev libfreetype6-dev libwebp-dev
+ $ sudo -H pip install Pillow
+
+Ubuntu 15.04/15.10/16.04:
+
+ :::bash
+ $ sudo apt-get install python-dev python3-dev build-essential liblcms2-dev zlib1g-dev libtiff5-dev libjpeg8-dev libfreetype6-dev libwebp-dev
+ $ sudo -H pip install Pillow
+
+
+Agora partiremos para a instalação do wrapper que irá permitir a utilização do Tesseract através do python:
+
+ :::bash
+ $ sudo -H pip install pytesseract
+
+
+### Mão Na Massa!
+
+Finalmente chegamos a parte prática desse artigo. Como dito anteriormente, são apenas 3 linhas de código, mas antes de começar baixe a seguinte imagem para realizar seus testes:
+
+
+
+Agora vamos ao código:
+
+ :::python
+
+ from PIL import Image # Importando o módulo Pillow para abrir a imagem no script
+
+ import pytesseract # Módulo para a utilização da tecnologia OCR
+
+ print( pytesseract.image_to_string( Image.open('nome_da_imagem.jpg') ) ) # Extraindo o texto da imagem
+
+Simples né? Mas nem sempre o texto sai 100% correto, depende muito da qualidade da imagem e da quantidade de detalhes que a mesma possui, porem existe algumas técnicas usadas para fazer melhorias na imagem diminuindo a chance de erros na hora da extração.
+
+**Alguns links que podem te ajudar a aproveitar ao maximo da tecnologia OCR:**
+
+* [http://pt.scribd.com/doc/88203318/Como-escanear-livros-com-qualidade-e-produzir-textos-por-OCR#scribd](http://pt.scribd.com/doc/88203318/Como-escanear-livros-com-qualidade-e-produzir-textos-por-OCR#scribd)
+
+* [http://profs.if.uff.br/tjpp/blog/entradas/ocr-de-qualidade-no-linux](http://profs.if.uff.br/tjpp/blog/entradas/ocr-de-qualidade-no-linux)
+
diff --git a/content/fazendo-backup-do-banco-de-dados-no-django.md b/content/fazendo-backup-do-banco-de-dados-no-django.md
new file mode 100644
index 000000000..c8de95af4
--- /dev/null
+++ b/content/fazendo-backup-do-banco-de-dados-no-django.md
@@ -0,0 +1,122 @@
+Title: Fazendo backup do banco de dados no Django
+Date: 2020-10-30 10:40
+Tags: Python,Django,backup
+Category: Python
+Slug: fazendo-backup-do-banco-de-dados-no-django
+Author: Jackson Osvaldo
+Email: jacksonosvaldo@live.com
+Github: JacksonOsvaldo
+About_author: Um curioso apaixonado por livros, tecnologia e programação.
+
+## Apresentação
+
+Em algum momento, durante o seu processo de desenvolvimento com Django, pode ser que surja a necessidade de criar e restaurar o banco de dados da aplicação. Pensando nisso, resolvi fazer um pequeno tutorial, básico, de como realizar essa operação.
+
+Nesse tutorial, usaremos o [django-dbbackup](https://github.com/django-dbbackup/django-dbbackup), um pacote desenvolvido especificamente para isso.
+
+## Configurando nosso ambiente
+
+Primeiro, partindo do início, vamos criar uma pasta para o nosso projeto e, nela, isolar o nosso ambiente de desenvolvimento usando uma [virtualenv](https://virtualenv.pypa.io/en/latest/index.html):
+
+```shell
+mkdir projeto_db && cd projeto_db #criando a pasta do nosso projeto
+
+virtualenv -p python3.8 env && source env/bin/activate #criando e ativando a nossa virtualenv
+```
+
+Depois disso e com o nosso ambiente já ativo, vamos realizar os seguintes procedimentos:
+
+```shell
+pip install -U pip #com isso, atualizamos a verão do pip instalado
+```
+
+## Instalando as dependências
+
+Agora, vamos instalar o [Django](https://www.djangoproject.com/) e o pacote que usaremos para fazer nossos backups.
+
+```shell
+pip install Django==3.1.2 #instalando o Django
+
+pip install django-dbbackup #instalando o django-dbbackup
+```
+
+## Criando e configurando projeto
+
+Depois de instaladas nossas dependências, vamos criar o nosso projeto e configurar o nosso pacote nas configurações do Django.
+
+```shell
+django-admin startproject django_db . #dentro da nossa pasta projeto_db, criamos um projeto Django com o nome de django_db.
+```
+
+Depois de criado nosso projeto, vamos criar e popular o nosso banco de dados.
+
+```shell
+python manage.py migrate #com isso, sincronizamos o estado do banco de dados com o conjunto atual de modelos e migrações.
+```
+
+Criado nosso banco de dados, vamos criar um superusuário para podemos o painel admin do nosso projeto.
+
+```shell
+python manage.py createsuperuser
+```
+
+Perfeito. Já temos tudo que precisamos para executar nosso projeto. Para execução dele, é só fazermos:
+
+```shell
+python manage.py runserver
+```
+
+Você terá uma imagem assim do seu projeto:
+
+
+
+## Configurando o django-dbbackup
+
+Dentro do seu projeto, vamos acessar o arquivo settings.py, como expresso abaixo:
+
+```shell
+django_db/
+├── settings.py
+```
+
+Dentro desse arquivos iremos, primeiro, adiconar o django-dbbackup às apps do projeto:
+
+```python
+INSTALLED_APPS = (
+ ...
+ 'dbbackup', # adicionando django-dbbackup
+)
+```
+
+Depois de adicionado às apps, vamos dizer para o Django o que vamos salvar no backup e, depois, indicar a pasta para onde será encaminhado esse arquivo. Essa inserção deve ou pode ser feita no final do arquivo _settings.py_:
+
+```python
+DBBACKUP_STORAGE = 'django.core.files.storage.FileSystemStorage' #o que salvar
+DBBACKUP_STORAGE_OPTIONS = {'location': 'backups/'} # onde salvar
+```
+
+Percebam que dissemos para o Django salvar o backup na pasta _backups_, mas essa pasta ainda não existe no nosso projeto. Por isso, precisamos criá-la [fora da pasta do projeto]:
+
+```shell
+mkdir backups
+```
+
+## Criando e restaurando nosso backup
+
+Já temos tudo pronto. Agora, vamos criar o nosso primeiro backup:
+
+```shell
+python manage.py dbbackup
+```
+
+Depois de exetudado, será criado um arquivo -- no nosso exemplo, esse arquivo terá uma extensão .dump --, salvo na pasta _backups_. Esse arquivo contem todo backup do nosso banco de dados.
+
+Para recuperarmos nosso banco, vamos supor que migramos nosso sistema de um servidor antigo para um novo e, por algum motivo, nossa base de dados foi corrompida, inviabilizando seu uso. Ou seja, estamos com o sistema/projeto sem banco de dados -- ou seja, exlua ou mova a a sua base dados .sqlite3 para que esse exemplo seja útil --, mas temos os backups. Com isso, vamos restaurar o banco:
+
+```shell
+python manage.py dbrestore
+```
+
+Prontinho, restauramos nosso banco de dados. O interessante do django-dbbackup, dentre outras coisas, é que ele gera os backups com datas e horários específicos, facilitando o processo de recuperação das informações mais recentes.
+
+Por hoje é isso, pessoal. Até a próxima. ;)
diff --git a/content/fork_clone_push_pull-request.rst b/content/fork_clone_push_pull-request.rst
index 1e6566a24..b2e34cb0d 100644
--- a/content/fork_clone_push_pull-request.rst
+++ b/content/fork_clone_push_pull-request.rst
@@ -54,7 +54,7 @@ Arquivo .gitignore:
O que é o arquivo .gitignore: http://pt-br.gitready.com/iniciante/2009/01/19/ignoring-files.html
-Gerador de arquivo .gitignore: http://www.gitignore.io/
+Gerador de arquivo .gitignore: https://www.toptal.com/developers/gitignore
Exemplos de arquivo .gitignore para diversas linguagens: https://github.com/github/gitignore
diff --git a/content/funcao-inplace-ou-copia-de-valor.md b/content/funcao-inplace-ou-copia-de-valor.md
new file mode 100644
index 000000000..fda5844c7
--- /dev/null
+++ b/content/funcao-inplace-ou-copia-de-valor.md
@@ -0,0 +1,177 @@
+Title: Funções in place ou cópia de valor
+Slug: funcao-inplace-ou-copia-de-valor
+Date: 2021-03-29 12:00
+Category: Python
+Tags: python, funções
+Author: Eduardo Klosowski
+Email: eduardo_klosowski@yahoo.com
+Github: eduardoklosowski
+Twitter: eduklosowski
+Site: https://dev.to/eduardoklosowski
+About_author: Programador, formado em redes de computadores e estuda DevOps
+
+Eventualmente observo dificuldades de algumas pessoas em usar corretamente alguma função, seja porque a função deveria ser executada isoladamente, e utilizado a própria variável que foi passada como argumento posteriormente, seja porque deveria se atribuir o retorno da função a alguma variável, e utilizar essa nova variável. No Python, essa diferença pode ser observada nos métodos das listas `sort` e `reverse` para as funções `sorted` e `reversed`, que são implementadas com padrões diferentes, *in place* e cópia de valor respectivamente. Assim pretendo discutir esses dois padrões de funções, comentando qual a diferença e o melhor caso de aplicação de cada padrão.
+
+## Função de exemplo
+
+Para demonstrar como esses padrões funcionam, será implementado uma função que recebe uma lista e calcula o dobro dos valores dessa lista. Exemplo:
+
+```python
+entrada = [5, 2, 8, 6, 4]
+
+# Execução da função
+
+resultado = [10, 4, 16, 12, 8]
+```
+
+### Função com in place
+
+A ideia do padrão *in place* é alterar a própria variável recebida pela função (ou o próprio objeto, caso esteja lidando com orientação a objetos). Neste caso, bastaria calcular o dobro do valor de cada posição da lista, e sobrescrever a posição com seu resultado. Exemplo:
+
+```python
+from typing import List
+
+
+def dobro_inplace(lista: List[int]) -> None:
+ for i in range(len(lista)):
+ lista[i] = 2 * lista[i]
+
+
+valores = [5, 2, 8, 6, 4]
+retorno = dobro_inplace(valores)
+
+print(f'Variável: valores | Tipo: {type(valores)} | Valor: {valores}')
+print(f'Variável: retorno | Tipo: {type(retorno)} | Valor: {retorno}')
+```
+
+Resultado da execução:
+
+```
+Variável: valores | Tipo: | Valor: [10, 4, 16, 12, 8]
+Variável: retorno | Tipo: | Valor: None
+```
+
+Com essa execução é possível observar que os valores da lista foram alterados, e que o retorno da função é nulo (`None`), ou seja, a função alterou a própria lista passada como argumento. Outro ponto importante a ser observado é a assinatura da função (tipo dos argumentos e do retorno da função), que recebe uma lista de inteiros e não tem retorno ou é nulo (`None`). Dessa forma embora seja possível chamar essa função diretamente quando está se informando os argumentos de outra função, como `print(dobro_inplace(valores))`, a função `print` receberia `None` e não a lista como argumento.
+
+### Função com cópia de valor
+
+A ideia do padrão cópia de valor é criar uma cópia do valor passado como argumento e retornar essa cópia, sem alterar a variável recebida (ou criando um novo objeto, no caso de orientação a objetos). Neste caso, é necessário criar uma nova lista e adicionar nela os valores calculados. Exemplo:
+
+```python
+from typing import List
+
+
+def dobro_copia(lista: List[int]) -> List[int]:
+ nova_lista = []
+
+ for i in range(len(lista)):
+ nova_lista.append(2 * lista[i])
+
+ return nova_lista
+
+
+valores = [5, 2, 8, 6, 4]
+retorno = dobro_copia(valores)
+
+print(f'Variável: valores | Tipo: {type(valores)} | Valor: {valores}')
+print(f'Variável: retorno | Tipo: {type(retorno)} | Valor: {retorno}')
+```
+
+Resultado da execução:
+
+```
+Variável: valores | Tipo: | Valor: [5, 2, 8, 6, 4]
+Variável: retorno | Tipo: | Valor: [10, 4, 16, 12, 8]
+```
+
+Com essa execução é possível observar que a variável `valores` continua com os valores que tinha antes da execução da função, e a variável retorno apresenta uma lista com os dobros, ou seja, a função não altera a lista passada como argumento e retorna uma nova lista com os valores calculados. Observado a assinatura da função, ela recebe uma lista de inteiros e retorna uma lista de inteiros. Isso permite chamar essa função diretamente nos argumentos para outra função, como `print(dobro_copia(valores))`, nesse caso a função `print` receberia a lista de dobros como argumento. Porém caso o retorno da função não seja armazenado, parecerá que a função não fez nada, ou não funcionou. Então em alguns casos, quando o valor anterior não é mais necessário, pode-se reatribuir o retorno da função a própria variável passada como argumento:
+
+```python
+valores = dobro_copia(valores)
+```
+
+### Função híbrida
+
+Ainda é possível mesclar os dois padrões de função, alterando o valor passado e retornando-o. Exemplo:
+
+```python
+from typing import List
+
+
+def dobro_hibrido(lista: List[int]) -> List[int]:
+ for i in range(len(lista)):
+ lista[i] = 2 * lista[i]
+
+ return lista
+
+
+valores = [5, 2, 8, 6, 4]
+retorno = dobro_hibrido(valores)
+
+print(f'Variável: valores | Tipo: {type(valores)} | Valor: {valores}')
+print(f'Variável: retorno | Tipo: {type(retorno)} | Valor: {retorno}')
+```
+
+Resultado da execução:
+
+```
+Variável: valores | Tipo: | Valor: [10, 4, 16, 12, 8]
+Variável: retorno | Tipo: | Valor: [10, 4, 16, 12, 8]
+```
+
+Nesse caso, pode-se apenas chamar a função, como também utilizá-la nos argumentos de outras funções. Porém para se ter os valores originais, deve-se fazer uma cópia manualmente antes de executar a função.
+
+## Exemplo na biblioteca padrão
+
+Na biblioteca padrão do Python, existem os métodos `sort` e `reverse` que seguem o padrão *in place*, e as funções `sorted` e `reversed` que seguem o padrão cópia de valor, podendo ser utilizados para ordenar e inverter os valores de uma lista, por exemplo. Quando não é mais necessário uma cópia da lista com a ordem original, é preferível utilizar funções *in place*, que alteram a própria lista, e como não criam uma cópia da lista, utilizam menos memória. Exemplo:
+
+```python
+valores = [5, 2, 8, 6, 4]
+valores.sort()
+valores.reverse()
+print(valores)
+```
+
+Se for necessário manter uma cópia da lista inalterada, deve-se optar pelas funções de cópia de valor. Exemplo:
+
+```python
+valores = [5, 2, 8, 6, 4]
+novos_valores = reversed(sorted(valores))
+print(novos_valores)
+```
+
+Porém esse exemplo cria duas cópias da lista, uma em cada função. Para criar apenas uma cópia, pode-se misturar funções *in place* com cópia de valor. Exemplo:
+
+```python
+valores = [5, 2, 8, 6, 4]
+novos_valores = sorted(valores)
+novos_valores.reverse()
+print(novos_valores)
+```
+
+Também vale observar que algumas utilizações dessas funções podem dar a impressão de que elas não funcionaram, como:
+
+```python
+valores = [5, 2, 8, 6, 4]
+
+sorted(valores)
+print(valores) # Imprime a lista original, e não a ordenada
+
+print(valores.sort()) # Imprime None e não a lista
+```
+
+## Considerações
+
+Nem sempre é possível utilizar o padrão desejado, *strings* no Python (`str`) são imutáveis, logo todas as funções que manipulam elas seguiram o padrão cópia de valor, e para outros tipos, pode ocorrer de só existir funções *in place*, sendo necessário fazer uma cópia manualmente antes de chamar a função, caso necessário. Para saber qual padrão a função implementa, é necessário consultar sua documentação, ou verificando sua assinatura, embora ainda possa existir uma dúvida entre cópia de valor e híbrida, visto que a assinatura dos dois padrões são iguais.
+
+Os exemplos aqui dados são didáticos. Caso deseja-se ordenar de forma reversa, tanto o método `sort`, quanto a função `sorted` podem receber como argumento `reverse=True`, e assim já fazer a ordenação reversa. Assim como é possível criar uma nova lista já com os valores, sem precisar adicionar manualmente item por item, como os exemplos:
+
+```python
+valores = [5, 2, 8, 6, 4]
+partes_dos_valores = valores[2:]
+novos_valores = [2 * valor for valor in valores]
+```
+
+---
+
+Esse artigo foi publicado originalmente no [meu blog](https://eduardoklosowski.github.io/blog/), passe por lá, ou siga-me no [DEV](https://dev.to/eduardoklosowski) para ver mais artigos que eu escrevi.
diff --git a/content/gerando-relatorios-de-teste-com-coveralls.md b/content/gerando-relatorios-de-teste-com-coveralls.md
new file mode 100644
index 000000000..569baf09a
--- /dev/null
+++ b/content/gerando-relatorios-de-teste-com-coveralls.md
@@ -0,0 +1,146 @@
+Title: Relatórios de testes com Coveralls
+Slug: gerando-relatorios-de-testes-com-coveralls
+Date: 2016-06-03 11:28:55
+Category: Python
+Tags: python, coveralls, coverage, relatório, test
+Author: Michell Stuttgart
+Email: michellstut@gmail.com
+Github: mstuttgart
+Linkedin: mstuttgart
+Site: https://mstuttgart.github.io/
+
+Na [terceira parte](https://mstuttgart.github.io/2016/04/29/2016-04-29-python-com-unittest-travis-ci-coveralls-e-landscape-parte-3-de-4/) do tutorial sobre *unittest*, vimos como utilizar o serviço [Coveralls](https://coveralls.io/) para gerar relatórios sobre o testes do nosso projeto. Entretanto, uma "desvantagem" do serviço é que o processo de análise é iniciado apenas quando executarmos um *push* ou um *pull request*. Sendo assim, não seria interessante termos a liberdade de executar esses testes localmente?
+
+Felizmente, os desenvolvedores do [Coveralls](https://coveralls.io/) pensaram nisso e criaram um conjunto de comandos que nos permite executá-lo pelo terminal.
+
+### Instalação
+
+Então, antes de iniciarmos, vamos a instalação do módulo, que pode ser feito pelo comando a seguir:
+
+```bash
+pip install coveralls
+```
+
+Quando você instala o módulo, um *script* de linha de comando chamado `coverage` é adicionado ao diretório de *scripts* python no seu sistema. Para suportar diferentes versões do Python, o módulo vem com um conjunto de *scripts*. Então, para a versão 2.7 do Python, você pode utilizar o comando `coverage` ou `coverage2`. Para a versão 3, utilize `coverage3`.
+
+### Gerando relatórios
+
+O comando usado para obtermos um relatório sobre os testes do nosso projeto é simples. No diretório do projeto, basta executar:
+
+```bash
+coverage run --source=nomedopacote setup.py test
+```
+o comando `run` irá coletar dados sobre nosso código fonte. No nosso caso, usaremos o repositorio que criamos para o tutorial anterior: [codigo-avulso-test-tutorial](https://github.com/mstuttgart/codigo-avulso-test-tutorial). Assim, o comando seria:
+
+```bash
+coverage run --source=codigo_avulso_test_tutorial setup.py test
+```
+
+Se você executar o comando `ls -la` no terminal, verá que um arquivo chamando `.coverage` foi criado. Esse arquivo contém algumas informações sobre o seu código. Vale alertar que para gerar os relatórios precisamos, obrigatoriamente, executar o comando acima, quando formos gerar o relatórios pela primeira vez ou quando o código sofrer alguma modificação.
+
+Uma vez que o arquivo `.coverage` foi gerado, execute o seguinte comando:
+
+```bash
+coverage report
+```
+
+Um relatório com a porcentagem de cobertura de testes (entre outras informações) de cada arquivo de código fonte será exibido no terminal.
+
+```bash
+Name Stmts Miss Cover
+----------------------------------------------------------------------
+codigo_avulso_test_tutorial/__init__.py 0 0 100%
+codigo_avulso_test_tutorial/circulo.py 9 0 100%
+codigo_avulso_test_tutorial/figura_geometrica.py 5 0 100%
+codigo_avulso_test_tutorial/quadrado.py 8 0 100%
+----------------------------------------------------------------------
+TOTAL 22 0 100%
+```
+
+As colunas exibidas no relatório possuem informações interessantes. São elas:
+
+* Stmts: indica o total de trechos do código que, segundo o Coveralls, devem ser testados.
+* Miss: coluna que indica quantos trechos do código ainda não estão sob testes.
+* Cover: talvez a coluna mais importante, ela indica a porcentagem de cobertura de testes do arquivo fonte.
+
+Em `TOTAL` temos a cobertura total de testes do nosso projeto. Nesse projeto em especial, temos 100% porque o mesmo possui pouco código e os códigos existentes são simples de testar. Entretanto, em projeto mais complexos, nem sempre é possível chegar nessa porcentagem, então vale a pena se focar em escrever testes para as partes mais críticas do seu código e conseguir uma porcentagem perto dos 80%, considerado pelo `Coveralls` como satisfatório.
+
+#### Gerando relatório em HTML
+
+Uma opção interessante é gerar o relatório em formato `html` com o comando:
+
+```bash
+coverage html
+```
+
+Um diretório chamado `htmlcov` será criado no diretório do projeto. Dentro desse diretório existe um arquivo de nome `index.html`, que pode ser aberto no navegador.
+
+Para o Google Chrome, usamos:
+
+```bash
+google-chrome htmlcov/index.html
+```
+ou com o Firefox
+
+```bash
+firefox htmlcov/index.html
+```
+
+Abaixo temos o `index.html` aberto. Nele podemos ver a lista dos arquivos que estão cobertos pelo `Coveralls`.
+
+
+
+
+
+Vamos analisar os dados do arquivo `circulo.py`. Assim, temos as seguintes colunas:
+
+* `statements`: indica o total de trechos do código que, segundo o Coveralls, devem ser testadas. No caso do arquivo `circulo.py`, o valor da coluna é 9, indicando que existem 9 trechos do código quem devem estar sob teste.
+* `missing`: coluna que indica quantos trechos do código ainda não estão sob testes.
+* `excluded`: coluna que indica quantos trechos do código foram ignorados pelo Coveralls. Algumas vezes pode ser necessário excluir alguns trechos de código do relatório devido ao tipo de código nele contido ou porque você simplesmente não deseja que aquele trecho seja incluido no relatorio. Isso é feito através de um arquivo de configuração, visto mais adiante.
+* `coverage`: indica a porcentagem de cobertura de testes do nosso código.
+
+Na imagem abaixo, logo após clicarmos em `codigo_avulso_test_tutorial/circulo.py`, podemos ver os pontos do código que devem ser testados.
+
+
+
+
+
+Ao clicarmos nos três botões no cabeçalho da página:
+
+
+
+
+
+A página irá destacar, respectivamente, os trechos cobertos por testes, trechos sem testes ou que foram excluídos do `Coveralls`.
+
+#### Gerando relatório em XML
+Os relatórios em XML podem ser facilmente gerados por:
+
+```bash
+coverage xml
+```
+Um arquivo chamado `coverage.xml` será criado.
+
+#### Criando o arquivo coveragerc
+
+O arquivo `.coveragesrc` é usado para determinar parâmetros de funcionamento do comando `coverage`. Nele podemos incluir e excluir aquivos da analise do `Coveralls` entre outras configurações. Abaixo temos um exemplo de arquivo de configuração.
+
+```
+[run]
+source = codigo_avulso_test_tutorial
+omit =
+ codigo_avulso_test_tutorial/__init__.py
+ codigo_avulso_test_tutorial/main.py
+```
+
+Na configuração acima, vamos omitir da análise o arquivo `__init__.py` e um arquivo `main.py`. Indicamos o *source* que é onde o `Coveralls` deve executar a análise.
+
+O arquivo de configuração ainda pode receber várias informações. Você pode ver mais delas [aqui](http://coverage.readthedocs.io/en/latest/source.html#source).
+
+### Conclusão
+
+Neste tutorial vimos um pouco mais sobre o `Coveralls`. Evitei colocar as informações deste tutorial nos tutoriais anteriores a fim de simplificá-los. Você pode aprender mais sobre o módulo consultando sua documentação [aqui](http://coverage.readthedocs.io/en/latest/index.html).
+
+É isso pessoal, obrigado pela leitura e até o próximo tutorial.
+
+**Publicado originalmente:** [gerando-relatorios-de-testes-com-coveralls](https://mstuttgart.github.io/2016/05/18/2016-05-18-gerando-relatorios-de-teste-com-coveralls/)
diff --git a/content/gerenciando-banco-dados-sqlite3-python-parte1.rst b/content/gerenciando-banco-dados-sqlite3-python-parte1.rst
index 1fe72bb1c..0909a4e26 100644
--- a/content/gerenciando-banco-dados-sqlite3-python-parte1.rst
+++ b/content/gerenciando-banco-dados-sqlite3-python-parte1.rst
@@ -12,7 +12,7 @@ Gerenciando banco de dados SQLite3 com Python - Parte 1
Eu separei este post em duas partes: a **Parte 1** é bem elementar e objetiva, visando apresentar o básico sobre a realização do CRUD num banco de dados SQLite3 em Python usando o terminal.
-A **Parte 2**, num nível intermediário, usa classes e métodos mais elaborados para gerenciar o CRUD, e algumas coisinhas a mais.
+A `parte 2 `_ , num nível intermediário, usa classes e métodos mais elaborados para gerenciar o CRUD, e algumas coisinhas a mais.
Nota: Para entender o uso de classes e métodos leia o post `Introdução a Classes e Métodos em Python `_. E para entender os comandos SQL e a manipulação de registros no SQLite3 leia `Guia rápido de comandos SQLite3 `_.
@@ -44,7 +44,7 @@ Para os exemplos considere a tabela ``clientes`` e seus campos:
Obs: O campo ``bloqueado`` nós vamos inserir depois com o comando ``ALTER TABLE``.
-Veja os exemplos em `github `_.
+Veja os exemplos em `github `_.
Como mencionado antes, esta parte será **básica e objetiva**. A intenção é realizar o CRUD da forma mais simples e objetiva possível.
@@ -635,12 +635,12 @@ Para executar digite no terminal:
Com o último comando você verá que os dados estão lá. São e salvo!!!
-Leia a continuação deste artigo em *Gerenciando banco de dados SQLite3 com Python - Parte 2*.
+Leia a continuação deste artigo em `Gerenciando banco de dados SQLite3 com Python - Parte 2 `_.
Exemplos
--------
-Veja os exemplos em `github `_.
+Veja os exemplos em `github `_.
Referências
-----------
diff --git a/content/gerenciando-banco-dados-sqlite3-python-parte2.rst b/content/gerenciando-banco-dados-sqlite3-python-parte2.rst
new file mode 100644
index 000000000..8ec7d9eb7
--- /dev/null
+++ b/content/gerenciando-banco-dados-sqlite3-python-parte2.rst
@@ -0,0 +1,1235 @@
+Gerenciando banco de dados SQLite3 com Python - Parte 2
+=======================================================
+
+:date: 2014-11-23 23:59
+:tags: Python, Banco de dados
+:category: Python, Banco de dados
+:slug: gerenciando-banco-dados-sqlite3-python-parte2
+:author: Regis da Silva
+:email: regis.santos.100@gmail.com
+:github: rg3915
+:summary: Esta é a continuação do artigo Gerenciando banco de dados SQLite3 com Python - Parte 1.
+
+Esta é a continuação do artigo `Gerenciando banco de dados SQLite3 com Python - Parte 1 `_. Na 1ª parte nós vimos como realizar o CRUD num banco de dados SQLite3 usando o Python, mas cada tarefa foi feita num arquivo ``.py`` separado. A intenção agora é utilizar um **único arquivo** e, usando classes e métodos realizar as mesmas tarefas, só que de uma forma mais sofisticada.
+
+ Também fiz uma série de videos, assista a primeira aula abaixo ou acesse todas as aulas no `YouTube `_.
+
+.. image:: images/regisdasilva/youtube_logo.png
+ :alt: youtube_logo.png
+ :target: https://www.youtube.com/watch?v=Qe3N7jiGZAc&list=PLsGCdfxkV9upVUtH0zsJ2f4WhQvJrZsVb
+
+
+.. youtube:: Qe3N7jiGZAc
+
+Vou repetir a tabela ``clientes`` apenas por comodidade:
+
++-----------+-----------------+-----------+
+| Campo | Tipo | Requerido |
++===========+=================+===========+
+| id | inteiro | sim |
++-----------+-----------------+-----------+
+| nome | texto | sim |
++-----------+-----------------+-----------+
+| idade | inteiro | não |
++-----------+-----------------+-----------+
+| cpf | texto (11) | sim |
++-----------+-----------------+-----------+
+| email | texto | sim |
++-----------+-----------------+-----------+
+| fone | texto | não |
++-----------+-----------------+-----------+
+| cidade | texto | não |
++-----------+-----------------+-----------+
+| uf | texto (2) | sim |
++-----------+-----------------+-----------+
+| criado_em | data | sim |
++-----------+-----------------+-----------+
+| bloqueado | boleano | não |
++-----------+-----------------+-----------+
+
+Obs: O campo ``bloqueado`` nós vamos inserir depois com o comando ``ALTER TABLE``.
+
+ PS: *Considere a sintaxe para Python 3*. Mas o programa roda em python2 também.
+
+Veja os exemplos em `github `_.
+
+`Preparando o terreno`_
+
+`Configurando um VirtualEnv para Python 3`_
+
+`Criando valores randômicos`_
+
+`Conectando e desconectando do banco`_
+
+`Modo interativo`_
+
+`Criando um banco de dados`_
+
+`Criando uma tabela`_
+
+`Create - Inserindo um registro com comando SQL`_
+
+`Inserindo n registros com uma lista de dados`_
+
+`Inserindo registros de um arquivo externo`_
+
+`Importando dados de um arquivo csv`_
+
+`Inserindo um registro com parâmetros de entrada definido pelo usuário`_
+
+`Inserindo valores randômicos`_
+
+`Read - Lendo os dados`_
+
+`Mais SELECT`_
+
+`SELECT personalizado`_
+
+`Update - Alterando os dados`_
+
+`Delete - Deletando os dados`_
+
+`Adicionando uma nova coluna`_
+
+`Lendo as informações do banco de dados`_
+
+`Fazendo backup do banco de dados (exportando dados)`_
+
+`Recuperando o banco de dados (importando dados)`_
+
+`Conectando-se a outro banco`_
+
+`Exemplos`_
+
+`Referências`_
+
+Preparando o terreno
+--------------------
+
+Neste artigo eu usei os pacotes `names `_ e `rstr `_ , o primeiro gera nomes randômicos e o segundo gera string e números randômicos. No meu SO estou usando o Python 3.4, mas para não ter problemas com os pacotes eu criei um ambiente virtual.
+
+**Obs**: *Se você estiver usando Python 3 ou Python 2x não é obrigado a usar virtualenv mas mesmo assim precisará instalar os pacotes names e rstr.*
+
+Configurando um VirtualEnv para Python 3
+----------------------------------------
+
+Não é obrigatório, mas como eu tenho no meu SO o Python 3.4, tive que criar um virtualenv, que se configura da seguinte forma:
+
+Faça um clone deste repositório
+
+.. code-block:: bash
+
+ $ git clone https://github.com/rg3915/python-sqlite.git
+
+Crie o virtualenv com o nome **python-sqlite**
+
+.. code-block:: bash
+
+ $ virtualenv python-sqlite
+
+Habilite o python3
+
+.. code-block:: bash
+
+ $ virtualenv -p /usr/bin/python3 python-sqlite
+
+Vá para a pasta
+
+.. code-block:: bash
+
+ $ cd python-sqlite
+
+Ative o ambiente
+
+.. code-block:: bash
+
+ $ source bin/activate
+
+Seu prompt ficará assim (ou parecido)
+
+.. code-block:: bash
+
+ (python-sqlite)~/git/python-sqlite$
+
+Instale as dependências
+
+.. code-block:: bash
+
+ $ pip install -r requirements.txt
+
+Entre na pasta
+
+.. code-block:: bash
+
+ $ cd intermediario
+
+Agora vamos diminuir o caminho do prompt
+
+.. code-block:: bash
+
+ PS1="(`basename \"$VIRTUAL_ENV\"`):/\W$ "
+
+O prompt vai ficar assim:
+
+.. code-block:: bash
+
+ (python-sqlite):/intermediario$
+
+Pronto! Agora vai começar a brincadeira.
+
+Criando valores randômicos
+--------------------------
+
+Antes de mexer no banco de fato vamos criar uns valores randômicos para popular o banco futuramente.
+
+O arquivo `gen_random_values.py `_ gera idade, cpf, telefone, data e cidade aleatoriamente. Para isso vamos importar algumas bibliotecas.
+
+.. code-block:: python
+
+ # gen_random_values.py
+ import random
+ import rstr
+ import datetime
+
+Vamos criar uma função ``gen_age()`` para gerar um número inteiro entre 15 e 99 usando o comando `random.randint(a,b) `_ .
+
+.. code-block:: python
+
+ def gen_age():
+ return random.randint(15, 99)
+
+A função ``gen_cpf()`` gera uma string com 11 caracteres numéricos. No caso, o primeiro parâmetro são os caracteres que serão sorteados e o segundo é o tamanho da string.
+
+.. code-block:: python
+
+ def gen_cpf():
+ return rstr.rstr('1234567890', 11)
+
+Agora vamos gerar um telefone com a função ``gen_phone()`` no formato (xx) xxxx-xxxx
+
+.. code-block:: python
+
+ def gen_phone():
+ return '({0}) {1}-{2}'.format(
+ rstr.rstr('1234567890', 2),
+ rstr.rstr('1234567890', 4),
+ rstr.rstr('1234567890', 4))
+
+A função ``gen_timestamp()`` gera um *datetime* no formato ``yyyy-mm-dd hh:mm:ss.000000``. Repare no uso do ``random.randint(a,b)`` com um intervalo definido para cada parâmetro.
+
+Quando usamos o comando `datetime.datetime.now().isoformat() `_ ele retorna a data e hora atual no formato ``yyyy-mm-ddThh:mm:ss.000000``. Para suprimir a letra T usamos o comando ``.isoformat(" ")`` que insere um espaço no lugar da letra T.
+
+.. code-block:: python
+
+ def gen_timestamp():
+ year = random.randint(1980, 2015)
+ month = random.randint(1, 12)
+ day = random.randint(1, 28)
+ hour = random.randint(1, 23)
+ minute = random.randint(1, 59)
+ second = random.randint(1, 59)
+ microsecond = random.randint(1, 999999)
+ date = datetime.datetime(
+ year, month, day, hour, minute, second, microsecond).isoformat(" ")
+ return date
+
+A função ``gen_city()`` escolhe uma cidade numa lista com o comando `random.choice(seq) `_ (suprimi alguns valores).
+
+.. code-block:: python
+
+ def gen_city():
+ list_city = [
+ [u'São Paulo', 'SP'],
+ [u'Rio de Janeiro', 'RJ'],
+ [u'Porto Alegre', 'RS'],
+ [u'Campo Grande', 'MS']]
+ return random.choice(list_city)
+
+
+Conectando e desconectando do banco
+-----------------------------------
+
+Como mencionado antes, a intenção é criar um único arquivo. Mas, inicialmente, vamos usar um arquivo exclusivo para conexão o qual chamaremos de `connect_db.py `_ , assim teremos um arquivo que pode ser usado para vários testes de conexão com o banco de dados.
+
+.. code-block:: python
+
+ # connect_db.py
+ import sqlite3
+
+ class Connect(object):
+
+ def __init__(self, db_name):
+ try:
+ # conectando...
+ self.conn = sqlite3.connect(db_name)
+ self.cursor = self.conn.cursor()
+ # imprimindo nome do banco
+ print("Banco:", db_name)
+ # lendo a versão do SQLite
+ self.cursor.execute('SELECT SQLITE_VERSION()')
+ self.data = self.cursor.fetchone()
+ # imprimindo a versão do SQLite
+ print("SQLite version: %s" % self.data)
+ except sqlite3.Error:
+ print("Erro ao abrir banco.")
+ return False
+
+Aqui usamos o básico já visto na `parte 1 `_ que são os comandos ``sqlite3.connect()`` e ``cursor()``. Criamos uma classe "genérica" chamada ``Connect()`` que representa o banco de dados. E no inicializador da classe ``__init__`` fazemos a conexão com o banco e imprimimos a versão do SQLite, definido em ``self.cursor.execute('SELECT SQLITE_VERSION()')``.
+
+O próximo passo é fechar a conexão com o banco:
+
+.. code-block:: python
+
+ def close_db(self):
+ if self.conn:
+ self.conn.close()
+ print("Conexão fechada.")
+
+Este método está dentro da classe ``Connect()``, portanto atente-se a **identação**.
+
+Agora, criamos uma instância da classe acima e chamamos de ``ClientesDb()``, representando um banco chamado *clientes.db*.
+
+.. code-block:: python
+
+ class ClientesDb(object):
+
+ def __init__(self):
+ self.db = Connect('clientes.db')
+
+ def close_connection(self):
+ self.db.close_db()
+
+Fazendo desta forma é possível instanciar outras classes, uma para cada banco, como ``PessoasDb()`` que veremos mais pra frente.
+
+Finalmente, para rodar o programa podemos escrever o código abaixo...
+
+.. code-block:: python
+
+ if __name__ == '__main__':
+ cliente = ClientesDb()
+ cliente.close_connection()
+
+salvar... e no terminal digitar:
+
+.. code-block:: bash
+
+ $ python3 connect_db.py
+ $ ls *.db
+
+Pronto, o banco *clientes.db* está criado.
+
+Modo interativo
+---------------
+
+Legal mesmo é quando usamos o modo interativo para rodar os comandos do python, para isso podemos usar o python3 ou `ipython3 `_. No terminal basta digitar python3 ``ENTER`` que vai aparecer o prompt abaixo (*na mesma pasta do projeto, tá?*)
+
+.. code-block:: bash
+
+ $ python3
+ Python 3.4.0 (default, Apr 11 2014, 13:05:18)
+ [GCC 4.8.2] on linux
+ Type "help", "copyright", "credits" or "license" for more information.
+ >>>
+
+Agora vamos digitar os seguintes comandos, e depois eu explico tudo.
+
+.. code-block:: python
+
+ >>> from connect_db import Connect
+ >>> dir(Connect)
+ >>> db = Connect('clientes.db')
+ >>> dir(db)
+ >>> db.close_db()
+ >>> exit()
+
+A primeira linha importa a classe ``Connect`` do arquivo *connect_db.py*.
+
+O comando ``dir(Connect)`` lista todos os métodos da classe ``Connect()``, inclusive ``__init__`` e ``close_db()``.
+
+``db = Connect('clientes.db')`` cria uma instância da classe ``Connect()`` e usa o argumento ``'clientes.db'`` para criar o banco com o nome especificado.
+
+o comando ``dir(db)`` lista os métodos da instância.
+
+E ``db.close_db()`` fecha a conexão com o banco.
+
+
+Criando um banco de dados
+-------------------------
+
+Nosso arquivo principal se chamará `manager_db.py `_ e iremos incrementá-lo aos poucos. Na verdade quando usamos o comando ``c = ClientesDb()`` já criamos o banco de dados com o nome especificado, e instanciamos uma classe chamada ``ClientesDb``. Portanto esta fase já está concluida.
+
+Mas vou repetir o código inicial para criar e conectar o banco de dados:
+
+.. code-block:: python
+
+ # manager_db.py
+ import os
+ import sqlite3
+ import io
+ import datetime
+ import names
+ import csv
+ from gen_random_values import *
+
+
+ class Connect(object):
+
+ def __init__(self, db_name):
+ try:
+ # conectando...
+ self.conn = sqlite3.connect(db_name)
+ self.cursor = self.conn.cursor()
+ print("Banco:", db_name)
+ self.cursor.execute('SELECT SQLITE_VERSION()')
+ self.data = self.cursor.fetchone()
+ print("SQLite version: %s" % self.data)
+ except sqlite3.Error:
+ print("Erro ao abrir banco.")
+ return False
+
+ def commit_db(self):
+ if self.conn:
+ self.conn.commit()
+
+ def close_db(self):
+ if self.conn:
+ self.conn.close()
+ print("Conexão fechada.")
+
+
+ class ClientesDb(object):
+
+ tb_name = 'clientes'
+
+ def __init__(self):
+ self.db = Connect('clientes.db')
+ self.tb_name
+
+ def fechar_conexao(self):
+ self.db.close_db()
+
+ if __name__ == '__main__':
+ c = ClientesDb()
+
+Rodando no **terminal**...
+
+.. code-block:: bash
+
+ $ python3 manager_db.py
+ $ ls *.db
+
+O banco ``clientes.db`` está criado.
+
+Ou no **modo interativo**...
+
+.. code-block:: python
+
+ $ python3
+ >>> from manager_db import *
+ >>> c = ClientesDb()
+ Banco: clientes.db
+ SQLite version: 3.8.2
+ >>> exit()
+
+
+Criando uma tabela
+------------------
+
+Agora é tudo continuação do arquivo `manager_db.py `_ ...
+
+.. code-block:: python
+
+ def criar_schema(self, schema_name='sql/clientes_schema.sql'):
+ print("Criando tabela %s ..." % self.tb_name)
+
+ try:
+ with open(schema_name, 'rt') as f:
+ schema = f.read()
+ self.db.cursor.executescript(schema)
+ except sqlite3.Error:
+ print("Aviso: A tabela %s já existe." % self.tb_name)
+ return False
+
+ print("Tabela %s criada com sucesso." % self.tb_name)
+
+ ...
+
+ if __name__ == '__main__':
+ c = ClientesDb()
+ c.criar_schema()
+
+Aqui nós criamos a função ``criar_schema(self, schema_name)`` dentro da classe ``ClientesDb()``.
+
+Com ``with open(name)`` abrimos o arquivo `clientes_schema.sql `_ .
+
+Com ``f.read()`` lemos as linhas do arquivo.
+
+E com `cursor.executescript() `_ executamos a instrução sql que está dentro do arquivo.
+
+**Modo interativo**...
+
+.. code-block:: python
+
+ $ python3
+ >>> from manager_db import *
+ >>> c = ClientesDb()
+ >>> c.criar_schema()
+ Criando tabela clientes ...
+ Tabela clientes criada com sucesso.
+
+Se você digitar no terminal...
+
+.. code-block:: bash
+
+ $ sqlite3 clientes.db .tables
+
+Você verá que a tabela foi criada com sucesso.
+
+
+
+Create - Inserindo um registro com comando SQL
+----------------------------------------------
+
+A função a seguir insere um registro na tabela. Repare no uso do comando ``self.db.commit_db()`` que grava de fato os dados.
+
+.. code-block:: python
+
+ def inserir_um_registro(self):
+ try:
+ self.db.cursor.execute("""
+ INSERT INTO clientes (nome, idade, cpf, email, fone, cidade, uf, criado_em)
+ VALUES ('Regis da Silva', 35, '12345678901', 'regis@email.com', '(11) 9876-5342',
+ 'São Paulo', 'SP', '2014-07-30 11:23:00.199000')
+ """)
+ # gravando no bd
+ self.db.commit_db()
+ print("Um registro inserido com sucesso.")
+ except sqlite3.IntegrityError:
+ print("Aviso: O email deve ser único.")
+ return False
+
+ ...
+
+ if __name__ == '__main__':
+ c = ClientesDb()
+ c.criar_schema()
+ c.inserir_um_registro()
+
+
+Inserindo n registros com uma lista de dados
+--------------------------------------------
+
+A função a seguir insere vários registros a partir de uma lista. Repare no uso do comando `executemany(sql, [parâmetros]) `_
+
+.. code-block:: python
+
+ self.db.cursor.executemany("""INSERT INTO tabela (campos) VALUES (?)""", lista)
+
+que executa a instrução sql várias vezes. Note também, pela sintaxe, que a quantidade de ``?`` deve ser igual a quantidade de campos, e o parâmetro, no caso está sendo a lista criada.
+
+.. code-block:: python
+
+ def inserir_com_lista(self):
+ # criando uma lista de dados
+ lista = [('Agenor de Sousa', 23, '12345678901', 'agenor@email.com',
+ '(10) 8300-0000', 'Salvador', 'BA', '2014-07-29 11:23:01.199001'),
+ ('Bianca Antunes', 21, '12345678902', 'bianca@email.com',
+ '(10) 8350-0001', 'Fortaleza', 'CE', '2014-07-28 11:23:02.199002'),
+ ('Carla Ribeiro', 30, '12345678903', 'carla@email.com',
+ '(10) 8377-0002', 'Campinas', 'SP', '2014-07-28 11:23:03.199003'),
+ ('Fabiana de Almeida', 25, '12345678904', 'fabiana@email.com',
+ '(10) 8388-0003', 'São Paulo', 'SP', '2014-07-29 11:23:04.199004'),
+ ]
+ try:
+ self.db.cursor.executemany("""
+ INSERT INTO clientes (nome, idade, cpf, email, fone, cidade, uf, criado_em)
+ VALUES (?,?,?,?,?,?,?,?)
+ """, lista)
+ # gravando no bd
+ self.db.commit_db()
+ print("Dados inseridos da lista com sucesso: %s registros." %
+ len(lista))
+ except sqlite3.IntegrityError:
+ print("Aviso: O email deve ser único.")
+ return False
+
+
+
+Inserindo registros de um arquivo externo
+-----------------------------------------
+
+Também podemos escrever as instruções sql num arquivo externo (`clientes_dados.sql `_) e executá-lo com o comando ``executescript(sql_script)``. Note que as instruções a seguir já foram vistas anteriormente.
+
+.. code-block:: python
+
+ def inserir_de_arquivo(self):
+ try:
+ with open('sql/clientes_dados.sql', 'rt') as f:
+ dados = f.read()
+ self.db.cursor.executescript(dados)
+ # gravando no bd
+ self.db.commit_db()
+ print("Dados inseridos do arquivo com sucesso.")
+ except sqlite3.IntegrityError:
+ print("Aviso: O email deve ser único.")
+ return False
+
+Importando dados de um arquivo csv
+----------------------------------
+
+Agora vamos importar os dados de `clientes.csv `_ . A única novidade é o comando `csv.reader() `_ .
+
+.. code-block:: python
+
+ import csv
+ ...
+
+ def inserir_de_csv(self, file_name='csv/clientes.csv'):
+ try:
+ reader = csv.reader(
+ open(file_name, 'rt'), delimiter=',')
+ linha = (reader,)
+ for linha in reader:
+ self.db.cursor.execute("""
+ INSERT INTO clientes (nome, idade, cpf, email, fone, cidade, uf, criado_em)
+ VALUES (?,?,?,?,?,?,?,?)
+ """, linha)
+ # gravando no bd
+ self.db.commit_db()
+ print("Dados importados do csv com sucesso.")
+ except sqlite3.IntegrityError:
+ print("Aviso: O email deve ser único.")
+ return False
+
+**Obs**: Veja em `gen_csv.py `_ como podemos gerar dados randômicos para criar um novo `clientes.csv `_.
+
+Inserindo um registro com parâmetros de entrada definido pelo usuário
+---------------------------------------------------------------------
+
+Agora está começando a ficar mais interessante. Quando falamos *parâmetros de entrada* significa interação direta do usuário na aplicação. Ou seja, vamos inserir os dados diretamente pelo terminal em tempo de execução. Para isso nós usamos o comando ``input()`` para Python 3 ou ``raw_input()`` para Python 2.
+
+.. code-block:: python
+
+ def inserir_com_parametros(self):
+ # solicitando os dados ao usuário
+ self.nome = input('Nome: ')
+ self.idade = input('Idade: ')
+ self.cpf = input('CPF: ')
+ self.email = input('Email: ')
+ self.fone = input('Fone: ')
+ self.cidade = input('Cidade: ')
+ self.uf = input('UF: ') or 'SP'
+ date = datetime.datetime.now().isoformat(" ")
+ self.criado_em = input('Criado em (%s): ' % date) or date
+
+ try:
+ self.db.cursor.execute("""
+ INSERT INTO clientes (nome, idade, cpf, email, fone, cidade, uf, criado_em)
+ VALUES (?,?,?,?,?,?,?,?)
+ """, (self.nome, self.idade, self.cpf, self.email, self.fone,
+ self.cidade, self.uf, self.criado_em))
+ # gravando no bd
+ self.db.commit_db()
+ print("Dados inseridos com sucesso.")
+ except sqlite3.IntegrityError:
+ print("Aviso: O email deve ser único.")
+ return False
+
+Note que, em ``criado_em`` se você não informar uma data ele insere a data atual. E os parâmetros informados são passados no final do comando ``execute()``.
+
+Veja a interação:
+
+.. code-block:: python
+
+ $ python3
+ >>> from manager_db import *
+ >>> c = ClientesDb()
+ >>> c.criar_schema()
+ >>> c.inserir_com_parametros()
+ Nome: Regis
+ Idade: 35
+ CPF: 11100011100
+ Email: regis@email.com
+ Fone: (11) 1111-1111
+ Cidade: São Paulo
+ UF: SP
+ Criado em (2014-10-07 01:40:48.836683):
+ Dados inseridos com sucesso.
+
+
+Inserindo valores randômicos
+----------------------------
+
+Se lembra de `gen_random_values.py `_? Agora vamos usar ele.
+
+Para preencher *criado_em* usamos a data atual ``.now()``.
+
+Para gerar o *nome* usamos a função ``names.get_first_name()`` e ``names.get_last_name()``.
+
+Para o *email* pegamos a primeira letra do nome e o sobrenome + ``@email.com``, ou seja, o formato r.silva@email.com, por exemplo.
+
+Para a *cidade* e *uf* usamos a função ``gen_city()`` retornando os dois elementos de ``list_city``.
+
+O ``repeat`` é 10 por padrão, mas você pode mudar, exemplo ``inserir_randomico(15)`` na chamada da função.
+
+.. code-block:: python
+
+ def inserir_randomico(self, repeat=10):
+ ''' Inserir registros com valores randomicos names '''
+ lista = []
+ for _ in range(repeat):
+ date = datetime.datetime.now().isoformat(" ")
+ fname = names.get_first_name()
+ lname = names.get_last_name()
+ name = fname + ' ' + lname
+ email = fname[0].lower() + '.' + lname.lower() + '@email.com'
+ c = gen_city()
+ city = c[0]
+ uf = c[1]
+ lista.append((name, gen_age(), gen_cpf(),
+ email, gen_phone(),
+ city, uf, date))
+ try:
+ self.db.cursor.executemany("""
+ INSERT INTO clientes (nome, idade, cpf, email, fone, cidade, uf, criado_em)
+ VALUES (?,?,?,?,?,?,?,?)
+ """, lista)
+ self.db.commit_db()
+ print("Inserindo %s registros na tabela..." % repeat)
+ print("Registros criados com sucesso.")
+ except sqlite3.IntegrityError:
+ print("Aviso: O email deve ser único.")
+ return False
+
+
+
+Read - Lendo os dados
+---------------------
+
+Eu preferi fazer duas funções ``ler_todos_clientes()`` e ``imprimir_todos_clientes()``. A primeira apenas retorna os valores com o comando ``fetchall()``, pois eu irei usá-lo mais vezes. E a segunda imprime os valores na tela. No caso, eu usei uma tabulação mais bonitinha...
+
+.. code-block:: python
+
+ def ler_todos_clientes(self):
+ sql = 'SELECT * FROM clientes ORDER BY nome'
+ r = self.db.cursor.execute(sql)
+ return r.fetchall()
+
+ def imprimir_todos_clientes(self):
+ lista = self.ler_todos_clientes()
+ print('{:>3s} {:20s} {:<5s} {:15s} {:21s} {:14s} {:15s} {:s} {:s}'.format(
+ 'id', 'nome', 'idade', 'cpf', 'email', 'fone', 'cidade', 'uf', 'criado_em'))
+ for c in lista:
+ print('{:3d} {:23s} {:2d} {:s} {:>25s} {:s} {:15s} {:s} {:s}'.format(
+ c[0], c[1], c[2],
+ c[3], c[4], c[5],
+ c[6], c[7], c[8]))
+
+mas se quiser você pode usar simplesmente
+
+.. code-block:: python
+
+ def imprimir_todos_clientes(self):
+ lista = self.ler_todos_clientes()
+ for c in lista:
+ print(c)
+
+Mais SELECT
+-----------
+
+**Exemplo**: Vamos explorar um pouco mais o ``SELECT``. Veja a seguir como localizar um cliente pelo ``id``. Uma *sutileza* é a vírgula logo depois do ``id``, isto é necessário porque quando usamos a ``?`` é esperado que os parâmetros sejam uma tupla.
+
+.. code-block:: python
+
+ def localizar_cliente(self, id):
+ r = self.db.cursor.execute(
+ 'SELECT * FROM clientes WHERE id = ?', (id,))
+ return r.fetchone()
+
+ def imprimir_cliente(self, id):
+ if self.localizar_cliente(id) == None:
+ print('Não existe cliente com o id informado.')
+ else:
+ print(self.localizar_cliente(id))
+
+O ``fetchone()`` retorna apenas uma linha de registro.
+
+
+**Exemplo**: Veja um exemplo de como contar os registros.
+
+.. code-block:: python
+
+ def contar_cliente(self):
+ r = self.db.cursor.execute(
+ 'SELECT COUNT(*) FROM clientes')
+ print("Total de clientes:", r.fetchone()[0])
+
+
+**Exemplo**: Contar os clientes maiores que 50 anos de idade. Veja novamente a necessidade da vírgula em ``(t,)``.
+
+.. code-block:: python
+
+ def contar_cliente_por_idade(self, t=50):
+ r = self.db.cursor.execute(
+ 'SELECT COUNT(*) FROM clientes WHERE idade > ?', (t,))
+ print("Clientes maiores que", t, "anos:", r.fetchone()[0])
+
+Caso queira outra idade mude o valor ao chamar a função:
+
+.. code-block:: python
+
+ c.contar_cliente_por_idade(18)
+
+
+**Exemplo**: Localizar clientes por idade.
+
+.. code-block:: python
+
+ def localizar_cliente_por_idade(self, t=50):
+ resultado = self.db.cursor.execute(
+ 'SELECT * FROM clientes WHERE idade > ?', (t,))
+ print("Clientes maiores que", t, "anos:")
+ for cliente in resultado.fetchall():
+ print(cliente)
+
+
+**Exemplo**: Localizar clientes por uf.
+
+.. code-block:: python
+
+ def localizar_cliente_por_uf(self, t='SP'):
+ resultado = self.db.cursor.execute(
+ 'SELECT * FROM clientes WHERE uf = ?', (t,))
+ print("Clientes do estado de", t, ":")
+ for cliente in resultado.fetchall():
+ print(cliente)
+
+
+SELECT personalizado
+--------------------
+
+**Exemplo**: Vejamos agora como fazer nosso próprio ``SELECT``.
+
+.. code-block:: python
+
+ def meu_select(self, sql="SELECT * FROM clientes WHERE uf='RJ';"):
+ r = self.db.cursor.execute(sql)
+ # gravando no bd
+ self.db.commit_db()
+ for cliente in r.fetchall():
+ print(cliente)
+
+Assim, podemos escrever qualquer ``SELECT`` direto na chamada da função:
+
+.. code-block:: python
+
+ c.meu_select("SELECT * FROM clientes WHERE uf='MG' ORDER BY nome;")
+
+Acabamos de mudar a função original. Eu coloquei o ``commit_db()`` porque se quiser você pode escrever uma instrução SQL com ``INSERT`` ou ``UPDATE``, por exemplo.
+
+
+**Exemplo**: Lendo instruções de arquivos externos
+
+No arquivo `clientes_sp.sql `_ eu escrevi várias instruções SQL.
+
+.. code-block:: sql
+
+ SELECT * FROM clientes WHERE uf='SP';
+ SELECT COUNT(*) FROM clientes WHERE uf='SP';
+ SELECT * FROM clientes WHERE uf='RJ';
+ SELECT COUNT(*) FROM clientes WHERE uf='RJ';
+
+Para que todas as instruções sejam lidas e retorne valores é necessário que usemos os comandos ``split(';')`` para informar ao interpretador qual é o final de cada linha. E o comando ``execute()`` dentro de um ``for``, assim ele lê e executa todas as instruções SQL do arquivo.
+
+.. code-block:: python
+
+ def ler_arquivo(self, file_name='sql/clientes_sp.sql'):
+ with open(file_name, 'rt') as f:
+ dados = f.read()
+ sqlcomandos = dados.split(';')
+ print("Consulta feita a partir de arquivo externo.")
+ for comando in sqlcomandos:
+ r = self.db.cursor.execute(comando)
+ for c in r.fetchall():
+ print(c)
+ # gravando no bd
+ self.db.commit_db()
+
+Novamente você pode usar qualquer instrução SQL porque o ``commit_db()`` já está ai.
+
+.. code-block:: python
+
+ c.ler_arquivo('sql/clientes_maior60.sql')
+
+
+Update - Alterando os dados
+---------------------------
+
+Nenhuma novidade, todos os comandos já foram vistos antes. No caso, informamos o ``id`` do cliente. Veja que aqui usamos novamente a função ``localizar_cliente(id)`` para localizar o cliente.
+
+.. code-block:: python
+
+ def atualizar(self, id):
+ try:
+ c = self.localizar_cliente(id)
+ if c:
+ # solicitando os dados ao usuário
+ # se for no python2.x digite entre aspas simples
+ self.novo_fone = input('Fone: ')
+ self.db.cursor.execute("""
+ UPDATE clientes
+ SET fone = ?
+ WHERE id = ?
+ """, (self.novo_fone, id,))
+ # gravando no bd
+ self.db.commit_db()
+ print("Dados atualizados com sucesso.")
+ else:
+ print('Não existe cliente com o id informado.')
+ except e:
+ raise e
+
+Chamando a função:
+
+.. code-block:: python
+
+ c.atualizar(10)
+
+
+Delete - Deletando os dados
+---------------------------
+
+Novamente vamos localizar o cliente para depois deletá-lo.
+
+.. code-block:: python
+
+ def deletar(self, id):
+ try:
+ c = self.localizar_cliente(id)
+ # verificando se existe cliente com o ID passado, caso exista
+ if c:
+ self.db.cursor.execute("""
+ DELETE FROM clientes WHERE id = ?
+ """, (id,))
+ # gravando no bd
+ self.db.commit_db()
+ print("Registro %d excluído com sucesso." % id)
+ else:
+ print('Não existe cliente com o código informado.')
+ except e:
+ raise e
+
+Chamando a função:
+
+.. code-block:: python
+
+ c.deletar(10)
+
+
+Adicionando uma nova coluna
+---------------------------
+
+Para adicionar uma nova coluna é bem simples.
+
+.. code-block:: python
+
+ def alterar_tabela(self):
+ try:
+ self.db.cursor.execute("""
+ ALTER TABLE clientes
+ ADD COLUMN bloqueado BOOLEAN;
+ """)
+ # gravando no bd
+ self.db.commit_db()
+ print("Novo campo adicionado com sucesso.")
+ except sqlite3.OperationalError:
+ print("Aviso: O campo 'bloqueado' já existe.")
+ return False
+
+
+
+Lendo as informações do banco de dados
+--------------------------------------
+
+Obtendo informações da tabela
+
+.. code-block:: python
+
+ def table_info(self):
+ t = self.db.cursor.execute(
+ 'PRAGMA table_info({})'.format(self.tb_name))
+ colunas = [tupla[1] for tupla in t.fetchall()]
+ print('Colunas:', colunas)
+
+Chamando e vendo o resultado:
+
+.. code-block:: bash
+
+ >>> c.table_info()
+ Colunas: ['id', 'nome', 'idade', 'cpf', 'email', 'fone', 'cidade', 'uf', 'criado_em']
+
+
+Listando as tabelas do bd
+
+.. code-block:: python
+
+ def table_list(self):
+ l = self.db.cursor.execute("""
+ SELECT name FROM sqlite_master WHERE type='table' ORDER BY name
+ """)
+ print('Tabelas:')
+ for tabela in l.fetchall():
+ print("%s" % (tabela))
+
+Chamando e vendo o resultado:
+
+.. code-block:: bash
+
+ >>> c.table_list()
+ Tabelas:
+ clientes
+ sqlite_sequence
+
+
+Obtendo o schema da tabela
+
+.. code-block:: python
+
+ def table_schema(self):
+ s = self.db.cursor.execute("""
+ SELECT sql FROM sqlite_master WHERE type='table' AND name=?
+ """, (self.tb_name,))
+
+ print('Schema:')
+ for schema in s.fetchall():
+ print("%s" % (schema))
+
+Chamando e vendo o resultado:
+
+.. code-block:: sql
+
+ >>> c.table_schema()
+ Schema:
+ CREATE TABLE clientes (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ nome TEXT NOT NULL,
+ idade INTEGER,
+ cpf VARCHAR(11) NOT NULL,
+ email TEXT NOT NULL UNIQUE,
+ fone TEXT,
+ cidade TEXT,
+ uf VARCHAR(2) NOT NULL,
+ criado_em DATETIME NOT NULL
+ )
+
+
+Fazendo backup do banco de dados (exportando dados)
+---------------------------------------------------
+
+.. code-block:: python
+
+ import io
+ ...
+ def backup(self, file_name='sql/clientes_bkp.sql'):
+ with io.open(file_name, 'w') as f:
+ for linha in self.db.conn.iterdump():
+ f.write('%s\n' % linha)
+
+ print('Backup realizado com sucesso.')
+ print('Salvo como %s' % file_name)
+
+Se quiser pode salvar com outro nome.
+
+.. code-block:: python
+
+ c.backup('sql/clientes_backup.sql')
+
+
+Recuperando o banco de dados (importando dados)
+-----------------------------------------------
+
+Aqui nós usamos dois parâmetros: ``db_name`` para o banco de dados recuperado (no caso, um banco novo) e ``file_name`` para o nome do arquivo de backup com as instruções SQL salvas.
+
+.. code-block:: python
+
+ def importar_dados(self, db_name='clientes_recovery.db', file_name='sql/clientes_bkp.sql'):
+ try:
+ self.db = Connect(db_name)
+ f = io.open(file_name, 'r')
+ sql = f.read()
+ self.db.cursor.executescript(sql)
+ print('Banco de dados recuperado com sucesso.')
+ print('Salvo como %s' % db_name)
+ except sqlite3.OperationalError:
+ print(
+ "Aviso: O banco de dados %s já existe. Exclua-o e faça novamente." %
+ db_name)
+ return False
+
+Fechando conexão:
+
+.. code-block:: python
+
+ def fechar_conexao(self):
+ self.db.close_db()
+
+
+Conectando-se a outro banco
+---------------------------
+
+Agora, no mesmo arquivo `manager_db.py `_ vamos criar uma outra instância chamada ``PessoasDb()``. Neste exemplo vamos relacionar duas tabelas: ``pessoas`` e ``cidades``.
+
+Veja na figura a seguir como as tabelas se relacionam.
+
+.. image:: images/regisdasilva/tabelas.png
+ :alt: tabelas.png
+
+Agora os códigos:
+
+.. code-block:: python
+
+ class PessoasDb(object):
+
+ tb_name = 'pessoas'
+
+ def __init__(self):
+ self.db = Connect('pessoas.db')
+ self.tb_name
+
+Criando o *schema* a partir de `pessoas_schema.sql `_.
+
+.. code-block:: python
+
+ def criar_schema(self, schema_name='sql/pessoas_schema.sql'):
+ print("Criando tabela %s ..." % self.tb_name)
+
+ try:
+ with open(schema_name, 'rt') as f:
+ schema = f.read()
+ self.db.cursor.executescript(schema)
+ except sqlite3.Error:
+ print("Aviso: A tabela %s já existe." % self.tb_name)
+ return False
+
+ print("Tabela %s criada com sucesso." % self.tb_name)
+
+Inserindo as cidades a partir de `cidades.csv `_.
+
+.. code-block:: python
+
+ def inserir_de_csv(self, file_name='csv/cidades.csv'):
+ try:
+ c = csv.reader(
+ open(file_name, 'rt'), delimiter=',')
+ t = (c,)
+ for t in c:
+ self.db.cursor.execute("""
+ INSERT INTO cidades (cidade, uf)
+ VALUES (?,?)
+ """, t)
+ # gravando no bd
+ self.db.commit_db()
+ print("Dados importados do csv com sucesso.")
+ except sqlite3.IntegrityError:
+ print("Aviso: A cidade deve ser única.")
+ return False
+
+Agora vamos contar quantas cidades temos na tabela...
+
+.. code-block:: python
+
+ def gen_cidade(self):
+ ''' conta quantas cidades estão cadastradas e escolhe uma delas pelo id. '''
+ sql = 'SELECT COUNT(*) FROM cidades'
+ q = self.db.cursor.execute(sql)
+ return q.fetchone()[0]
+
+para a partir dai gerar valores randômicos apenas com as cidades existentes.
+
+.. code-block:: python
+
+ def inserir_randomico(self, repeat=10):
+ lista = []
+ for _ in range(repeat):
+ fname = names.get_first_name()
+ lname = names.get_last_name()
+ email = fname[0].lower() + '.' + lname.lower() + '@email.com'
+ cidade_id = random.randint(1, self.gen_cidade())
+ lista.append((fname, lname, email, cidade_id))
+ try:
+ self.db.cursor.executemany("""
+ INSERT INTO pessoas (nome, sobrenome, email, cidade_id)
+ VALUES (?,?,?,?)
+ """, lista)
+ self.db.commit_db()
+ print("Inserindo %s registros na tabela..." % repeat)
+ print("Registros criados com sucesso.")
+ except sqlite3.IntegrityError:
+ print("Aviso: O email deve ser único.")
+ return False
+
+Agora é só alegria!
+
+.. code-block:: python
+
+ def ler_todas_pessoas(self):
+ sql = 'SELECT * FROM pessoas INNER JOIN cidades ON pessoas.cidade_id = cidades.id'
+ r = self.db.cursor.execute(sql)
+ return r.fetchall()
+
+ def imprimir_todas_pessoas(self):
+ lista = self.ler_todas_pessoas()
+ for c in lista:
+ print(c)
+
+ # myselect, imprime todos os nomes que começam com R
+ def meu_select(self, sql="SELECT * FROM pessoas WHERE nome LIKE 'R%' ORDER BY nome;"):
+ r = self.db.cursor.execute(sql)
+ self.db.commit_db()
+ print('Nomes que começam com R:')
+ for c in r.fetchall():
+ print(c)
+
+ def table_list(self):
+ # listando as tabelas do bd
+ l = self.db.cursor.execute("""
+ SELECT name FROM sqlite_master WHERE type='table' ORDER BY name
+ """)
+ print('Tabelas:')
+ for tabela in l.fetchall():
+ print("%s" % (tabela))
+
+ def fechar_conexao(self):
+ self.db.close_db()
+
+Chamando tudo no **modo interativo**
+
+.. code-block:: python
+
+ >>> from manager_db import *
+ >>> p = PessoasDb()
+ >>> p.criar_schema()
+ >>> p.inserir_de_csv()
+ >>> p.gen_cidade()
+ >>> p.inserir_randomico(100)
+ >>> p.imprimir_todas_pessoas()
+ >>> p.meu_select()
+ >>> p.table_list()
+ >>> p.fechar_conexao()
+
+
+Exemplos
+--------
+
+Assista os videos no `youtube `_.
+
+Veja os exemplos no `github `_.
+
+Referências
+-----------
+
+`sqlite3 — DB-API 2.0 interface for SQLite databases `_
+`sqlite3 Embedded Relational Database `_
+`Lets Talk to a SQLite Database with Python `_
+`Advanced SQLite Usage in Python `_
+`Python A Simple Step by Step SQLite Tutorial `_
\ No newline at end of file
diff --git a/content/github-pages-com-pelican-e-travis-ci.rst b/content/github-pages-com-pelican-e-travis-ci.rst
new file mode 100644
index 000000000..22fe6c91a
--- /dev/null
+++ b/content/github-pages-com-pelican-e-travis-ci.rst
@@ -0,0 +1,261 @@
+GitHub Pages com Pelican e Travis-CI
+====================================
+
+:slug: github-pages-com-pelican-e-travis-ci
+:date: 2016-05-04 21:46
+:tags: tutorial,gh-pages,pelican,travis-ci
+:category: Pelican
+:author: Humberto Rocha
+:email: humrochagf@gmail.com
+:github: humrochagf
+:twitter: humrochagf
+:facebook: humrochagf
+:linkedin: humrochagf
+
+**Publicado originalmente em:** `df.python.org.br/blog/github-pages-com-pelican-e-travis-ci`_
+
+Olá pessoal!
+
+Estou fazendo esta postagem para ajudar quem quer criar seu site no `GitHub Pages`_ usando `Pelican`_ para a criação das páginas e o `Travis-CI`_ para automatizar a tarefa de geração e publicação.
+
+Este guia assume que o leitor possua conta no `GitHub`_ e no `Travis-CI`_ e tenha familiaridade com o ambiente python. A versão do pelican utilizada ao elaborar esta publicação foi a 3.6.
+
+O GitHub Pages
+--------------
+
+O GitHub Pages é uma funcionalidade gratuita do GitHub para hospedar conteúdo estático (html, css, js e imagens) e publicar através de um sub-domínio de **github.io** ou até mesmo de um domínio customizado. É baseado em seu funcionamento que iremos guiar nossos próximos passos.
+
+Resumidamente existem duas formas de se criar uma página pelo gh pages:
+
+**1 - Página de usuário/organização**
+
+Para este tipo de página crie um repositório com o nome ``usuario.github.io``, onde ``usuario`` é o nome de usuário ou organização da conta em que o repositório será criado:
+
+.. figure:: /images/humrochagf/gh-pelican-travis/pagina-usuario.png
+ :alt: Repositório da página de usuário
+ :align: center
+
+O conteúdo a ser publicado deve ser colocado na **branch master** e os arquivos do pelican na **branch pelican**.
+
+**2 - Página de projeto**
+
+Para este tipo de página crie um repositório com o nome ``meuprojeto``, onde ``meuprojeto`` é o nome desejado para o projeto que será publicado em ``usuario.github.io``:
+
+.. figure:: /images/humrochagf/gh-pelican-travis/pagina-projeto.png
+ :alt: Repositório da página de projeto
+ :align: center
+
+O conteúdo a ser publicado deve ser colocado na **branch gh-pages** e os arquivos do pelican na **branch pelican**.
+
+Para mais informações acesse o site oficial do `GitHub Pages`_.
+
+Pelican
+-------
+
+O pelican é um gerador de site estático otimizado por padrão para criação de blogs. Utilizaremos aqui, para fins de demonstração, o modelo padrão do de blog seguindo o caminho de criação de página de usuário/organização, qualquer diferença do caminho de página de projeto será descrita quando necessário.
+
+Para instalar o pelican basta rodar o comando:
+
+.. code-block:: bash
+
+ $ pip install pelican==3.6
+
+Para criar um projeto faça:
+
+.. code-block:: bash
+
+ $ mkdir humrochagf.github.io
+ $ cd humrochagf.github.io
+ $ pelican-quickstart
+ Welcome to pelican-quickstart v3.6.3.
+
+ This script will help you create a new Pelican-based website.
+
+ Please answer the following questions so this script can generate the files
+ needed by Pelican.
+
+
+ > Where do you want to create your new web site? [.]
+ > What will be the title of this web site? Meu Blog
+ > Who will be the author of this web site? Humberto Rocha
+ > What will be the default language of this web site? [en] pt
+ > Do you want to specify a URL prefix? e.g., http://example.com (Y/n) n
+ > Do you want to enable article pagination? (Y/n) y
+ > How many articles per page do you want? [10]
+ > What is your time zone? [Europe/Paris] America/Sao_Paulo
+ > Do you want to generate a Fabfile/Makefile to automate generation and publishing? (Y/n) y
+ > Do you want an auto-reload & simpleHTTP script to assist with theme and site development? (Y/n) y
+ > Do you want to upload your website using FTP? (y/N) n
+ > Do you want to upload your website using SSH? (y/N) n
+ > Do you want to upload your website using Dropbox? (y/N) n
+ > Do you want to upload your website using S3? (y/N) n
+ > Do you want to upload your website using Rackspace Cloud Files? (y/N) n
+ > Do you want to upload your website using GitHub Pages? (y/N) y
+ > Is this your personal page (username.github.io)? (y/N) y
+ Done. Your new project is available at /caminho/para/humrochagf.github.io
+
+Inicialize um repositório neste diretório e suba os dados para a **branch pelican**:
+
+.. code-block:: bash
+
+ $ git init
+ $ git remote add origin git@github.com:humrochagf/humrochagf.github.io.git
+ $ git checkout -b pelican
+ $ git add .
+ $ git commit -m 'iniciando branch pelican'
+ $ git push origin pelican
+
+Para publicar o conteúdo na **branch master** é necessário o módulo ghp-import:
+
+.. code-block:: bash
+
+ $ pip install ghp-import
+ $ echo 'pelican==3.6\nghp-import' > requirements.txt
+ $ git add requirements.txt
+ $ git commit -m 'adicionando requirements'
+ $ git push origin pelican
+
+
+Publicando o blog:
+
+.. code-block:: bash
+
+ $ make github
+
+.. figure:: /images/humrochagf/gh-pelican-travis/blog.png
+ :alt: Primeira publicação do blog
+ :align: center
+
+Para publicar no caso da página de projeto altere o conteúdo da variável ``GITHUB_PAGES_BRANCH`` do makefile de ``master`` para ``gh-pages``.
+
+Agora que o nosso blog está rodando no gh pages vamos automatizar a tarefa de geração das páginas para poder alterar o conteúdo do blog e fazer novas postagens sem precisar estar um uma máquina com o ambiente do pelican configurado.
+
+Travis-CI
+---------
+
+O Travis-CI é uma plataforma de Integração Contínua que monta e testa projetos hospedados no github e será nossa ferramenta para automatizar a montagem das páginas do blog.
+
+A Primeira coisa a ser feita é ir ao `Travis-CI`_ e habilitar seu repositório.
+
+.. figure:: /images/humrochagf/gh-pelican-travis/travis-repo1.png
+ :alt: Habilitando repositório no travis
+ :align: center
+
+Em seguida vá nas configurações do repositório no travis e desabilite a opção **Build pull requests** para seu blog não ser atualizado quando alguém abrir um pull request e habilite o **Build only if .travis.yml is present** para que somente a branch que possuir o arquivo .travis.yml gerar atualização no blog.
+
+.. figure:: /images/humrochagf/gh-pelican-travis/travis-repo2.png
+ :alt: Configurando remositório no travis
+ :align: center
+
+O próximo passo é criar uma **Deploy Key** para que o travis possa publicar conteúdo no github. Para isso gere uma chave ssh na raiz do repositório local:
+
+.. code-block:: bash
+
+ $ ssh-keygen -f publish-key
+ Generating public/private rsa key pair.
+ Enter passphrase (empty for no passphrase):
+ Enter same passphrase again:
+ Your identification has been saved in publish-key.
+ Your public key has been saved in publish-key.pub.
+
+Criada a chave vamos cifrar usando a ferramenta `Travis-CLI`_ (certifique-se de que esteja instalada em sua máquina) para poder publicar em nosso repositório sem expor o conteúdo da chave privada:
+
+.. code-block:: bash
+
+ $ travis encrypt-file publish-key
+ Detected repository as humrochagf/humrochagf.github.io, is this correct? |yes| yes
+ encrypting publish-key for humrochagf/humrochagf.github.io
+ storing result as publish-key.enc
+ storing secure env variables for decryption
+
+ Please add the following to your build script (before_install stage in your .travis.yml, for instance):
+
+ openssl aes-256-cbc -K $encrypted_591fe46d4973_key -iv $encrypted_591fe46d4973_iv -in publish-key.enc -out publish-key -d
+
+ Pro Tip: You can add it automatically by running with --add.
+
+ Make sure to add publish-key.enc to the git repository.
+ Make sure not to add publish-key to the git repository.
+ Commit all changes to your .travis.yml.
+
+Como dito no resultado do comando podemos adicionar a opção `--add` para já adicionar as informações no `.travis.yml`, porém, para evitar de sobrescrever algum comando que venha existir no seu arquivo é recomendado editar manualmente.
+
+Em nosso caso iremos criar o arquivo:
+
+.. code-block:: bash
+
+ $ touch .travis.yml
+
+E adicionar o seguinte conteúdo:
+
+.. code-block:: yaml
+
+ sudo: false
+ branches:
+ only:
+ - pelican
+ language: python
+ before_install:
+ # troque a linha abaixo pelo resultado do comando:
+ # travis encrypt-file publish-key
+ # porém mantenha o final:
+ # -out ~/.ssh/publish-key -d
+ - openssl aes-256-cbc -K $encrypted_591fe46d4973_key -iv $encrypted_591fe46d4973_iv -in publish-key.enc -out ~/.ssh/publish-key -d
+ - chmod u=rw,og= ~/.ssh/publish-key
+ - echo "Host github.com" >> ~/.ssh/config
+ - echo " IdentityFile ~/.ssh/publish-key" >> ~/.ssh/config
+ # substitua git@github.com:humrochagf/humrochagf.github.io.git
+ # pelo endereço de acesso ssh do seu repositório
+ - git remote set-url origin git@github.com:humrochagf/humrochagf.github.io.git
+ # Caso esteja montando a página de projeto troque master:master
+ # por gh-pages:gh-pages
+ - git fetch origin -f master:master
+ install:
+ - pip install --upgrade pip
+ - pip install -r requirements.txt
+ script:
+ - make github
+
+Removemos em seguida a chave privada não cifrada para não correr o risco de publicar no repositório:
+
+.. code-block:: bash
+
+ $ rm publish-key
+
+**ATENÇÃO**: Em hipótese alguma adicione o arquivo **publish-key** em seu repositório, pois ele contém a chave privada não cifrada que tem poder de commit em seu repositório, e não deve ser publicada. Adicione somente o arquivo **publish-key.enc**. Se você adicionou por engano refaça os passos de geração da chave e cifração para gerar uma chave nova.
+
+Agora adicionaremos os arquivos no repositório:
+
+.. code-block:: bash
+
+ $ git add .travis.yml publish-key.enc
+ $ git commit -m 'adicionando arquivos do travis'
+ $ git push origin pelican
+
+Para liberar o acesso do travis adicionaremos a deploy key no github com o conteúdo da chave pública **publish-key.pub**:
+
+.. figure:: /images/humrochagf/gh-pelican-travis/deploy-key.png
+ :alt: Adicionando a deploy key no github
+ :align: center
+
+Pronto, agora podemos publicar conteúdo em nosso blog sem a necessidade de ter o pelican instalado na máquina:
+
+.. figure:: /images/humrochagf/gh-pelican-travis/primeira-postagem1.png
+ :alt: Fazendo a primeira postagem
+ :align: center
+
+Que o travis irá publicar para você:
+
+.. figure:: /images/humrochagf/gh-pelican-travis/primeira-postagem2.png
+ :alt: Blog com a primeira postagem
+ :align: center
+
+Caso você tenha animado de criar seu blog pessoal e quer saber mais sobre pelican você pode acompanhar a série do `Mind Bending`_ sobre o assunto.
+
+.. _df.python.org.br/blog/github-pages-com-pelican-e-travis-ci: http://df.python.org.br/blog/github-pages-com-pelican-e-travis-ci
+.. _GitHub Pages: http://pages.github.com
+.. _Pelican: http://blog.getpelican.com
+.. _Travis-CI: https://travis-ci.org
+.. _GitHub: http://github.com
+.. _Travis-CLI: https://github.com/travis-ci/travis.rb
+.. _Mind Bending: http://mindbending.org/pt/series/migrando-para-o-pelican
diff --git a/content/images/andrealmar/Generator_FooFighters.jpg b/content/images/andrealmar/Generator_FooFighters.jpg
new file mode 100644
index 000000000..139ecea50
Binary files /dev/null and b/content/images/andrealmar/Generator_FooFighters.jpg differ
diff --git a/content/images/andrelramos/ocr2.png b/content/images/andrelramos/ocr2.png
new file mode 100644
index 000000000..112b855fe
Binary files /dev/null and b/content/images/andrelramos/ocr2.png differ
diff --git a/content/images/arthur-alves/captaoboing.png b/content/images/arthur-alves/captaoboing.png
index d3abb7f72..35ccff5d4 100644
Binary files a/content/images/arthur-alves/captaoboing.png and b/content/images/arthur-alves/captaoboing.png differ
diff --git a/content/images/drgarcia1986/debugging.png b/content/images/drgarcia1986/debugging.png
new file mode 100644
index 000000000..b7dde7909
Binary files /dev/null and b/content/images/drgarcia1986/debugging.png differ
diff --git a/content/images/drgarcia1986/heroku.png b/content/images/drgarcia1986/heroku.png
new file mode 100644
index 000000000..a0eec946d
Binary files /dev/null and b/content/images/drgarcia1986/heroku.png differ
diff --git a/content/images/drgarcia1986/is_a_trap.png b/content/images/drgarcia1986/is_a_trap.png
new file mode 100644
index 000000000..1540dd275
Binary files /dev/null and b/content/images/drgarcia1986/is_a_trap.png differ
diff --git a/content/images/drgarcia1986/locust.png b/content/images/drgarcia1986/locust.png
new file mode 100644
index 000000000..2fe6c626c
Binary files /dev/null and b/content/images/drgarcia1986/locust.png differ
diff --git a/content/images/drgarcia1986/locust_distributed.png b/content/images/drgarcia1986/locust_distributed.png
new file mode 100644
index 000000000..30ef5f5c1
Binary files /dev/null and b/content/images/drgarcia1986/locust_distributed.png differ
diff --git a/content/images/drgarcia1986/locust_inicial.png b/content/images/drgarcia1986/locust_inicial.png
new file mode 100644
index 000000000..2dd3da3fe
Binary files /dev/null and b/content/images/drgarcia1986/locust_inicial.png differ
diff --git a/content/images/drgarcia1986/locust_name.png b/content/images/drgarcia1986/locust_name.png
new file mode 100644
index 000000000..8eaac1451
Binary files /dev/null and b/content/images/drgarcia1986/locust_name.png differ
diff --git a/content/images/drgarcia1986/locust_random.png b/content/images/drgarcia1986/locust_random.png
new file mode 100644
index 000000000..5abcc1fd1
Binary files /dev/null and b/content/images/drgarcia1986/locust_random.png differ
diff --git a/content/images/drgarcia1986/locust_session_fail.png b/content/images/drgarcia1986/locust_session_fail.png
new file mode 100644
index 000000000..fdae2e289
Binary files /dev/null and b/content/images/drgarcia1986/locust_session_fail.png differ
diff --git a/content/images/drgarcia1986/locust_session_success.png b/content/images/drgarcia1986/locust_session_success.png
new file mode 100644
index 000000000..cfcfba347
Binary files /dev/null and b/content/images/drgarcia1986/locust_session_success.png differ
diff --git a/content/images/drgarcia1986/the_locust.jpg b/content/images/drgarcia1986/the_locust.jpg
new file mode 100644
index 000000000..71778eaa8
Binary files /dev/null and b/content/images/drgarcia1986/the_locust.jpg differ
diff --git a/content/images/dyesten/cadastro_cloudinary.png b/content/images/dyesten/cadastro_cloudinary.png
new file mode 100644
index 000000000..c7d39a795
Binary files /dev/null and b/content/images/dyesten/cadastro_cloudinary.png differ
diff --git a/content/images/dyesten/template_inicial.png b/content/images/dyesten/template_inicial.png
new file mode 100644
index 000000000..6d4a6369d
Binary files /dev/null and b/content/images/dyesten/template_inicial.png differ
diff --git a/content/images/eduardo-matos/criando-permissao.png b/content/images/eduardo-matos/criando-permissao.png
new file mode 100644
index 000000000..fb5bd2124
Binary files /dev/null and b/content/images/eduardo-matos/criando-permissao.png differ
diff --git a/content/images/eduardo-matos/permissao-de-usuario.png b/content/images/eduardo-matos/permissao-de-usuario.png
new file mode 100644
index 000000000..5a8279aa3
Binary files /dev/null and b/content/images/eduardo-matos/permissao-de-usuario.png differ
diff --git a/content/images/eduardoklosowski/oo-de-outra-forma-4/mro.png b/content/images/eduardoklosowski/oo-de-outra-forma-4/mro.png
new file mode 100644
index 000000000..9a126c665
Binary files /dev/null and b/content/images/eduardoklosowski/oo-de-outra-forma-4/mro.png differ
diff --git a/content/images/eduardoklosowski/questoes-para-estudo-de-algoritmos/fatorial.png b/content/images/eduardoklosowski/questoes-para-estudo-de-algoritmos/fatorial.png
new file mode 100644
index 000000000..b999023c7
Binary files /dev/null and b/content/images/eduardoklosowski/questoes-para-estudo-de-algoritmos/fatorial.png differ
diff --git a/content/images/eduardoklosowski/questoes-para-estudo-de-algoritmos/fibonacci.png b/content/images/eduardoklosowski/questoes-para-estudo-de-algoritmos/fibonacci.png
new file mode 100644
index 000000000..4fa54d20f
Binary files /dev/null and b/content/images/eduardoklosowski/questoes-para-estudo-de-algoritmos/fibonacci.png differ
diff --git a/content/images/ehriq/ConfigPyCharm.png b/content/images/ehriq/ConfigPyCharm.png
new file mode 100644
index 000000000..601b78636
Binary files /dev/null and b/content/images/ehriq/ConfigPyCharm.png differ
diff --git a/content/images/erichideki/bottle.png b/content/images/erichideki/bottle.png
new file mode 100644
index 000000000..1f3edb30b
Binary files /dev/null and b/content/images/erichideki/bottle.png differ
diff --git a/content/images/erichideki/dreampie.jpg b/content/images/erichideki/dreampie.jpg
new file mode 100644
index 000000000..9073bd5b5
Binary files /dev/null and b/content/images/erichideki/dreampie.jpg differ
diff --git a/content/images/hudsonbrendon/django-fig.png b/content/images/hudsonbrendon/django-fig.png
new file mode 100644
index 000000000..6e594a7c6
Binary files /dev/null and b/content/images/hudsonbrendon/django-fig.png differ
diff --git a/content/images/humrochagf/gh-pelican-travis/banner.png b/content/images/humrochagf/gh-pelican-travis/banner.png
new file mode 100644
index 000000000..559f0bbbf
Binary files /dev/null and b/content/images/humrochagf/gh-pelican-travis/banner.png differ
diff --git a/content/images/humrochagf/gh-pelican-travis/blog.png b/content/images/humrochagf/gh-pelican-travis/blog.png
new file mode 100644
index 000000000..1935a7aac
Binary files /dev/null and b/content/images/humrochagf/gh-pelican-travis/blog.png differ
diff --git a/content/images/humrochagf/gh-pelican-travis/deploy-key.png b/content/images/humrochagf/gh-pelican-travis/deploy-key.png
new file mode 100644
index 000000000..55a55f222
Binary files /dev/null and b/content/images/humrochagf/gh-pelican-travis/deploy-key.png differ
diff --git a/content/images/humrochagf/gh-pelican-travis/pagina-projeto.png b/content/images/humrochagf/gh-pelican-travis/pagina-projeto.png
new file mode 100644
index 000000000..d7ea9061d
Binary files /dev/null and b/content/images/humrochagf/gh-pelican-travis/pagina-projeto.png differ
diff --git a/content/images/humrochagf/gh-pelican-travis/pagina-usuario.png b/content/images/humrochagf/gh-pelican-travis/pagina-usuario.png
new file mode 100644
index 000000000..0dfd1dc13
Binary files /dev/null and b/content/images/humrochagf/gh-pelican-travis/pagina-usuario.png differ
diff --git a/content/images/humrochagf/gh-pelican-travis/primeira-postagem1.png b/content/images/humrochagf/gh-pelican-travis/primeira-postagem1.png
new file mode 100644
index 000000000..37621788f
Binary files /dev/null and b/content/images/humrochagf/gh-pelican-travis/primeira-postagem1.png differ
diff --git a/content/images/humrochagf/gh-pelican-travis/primeira-postagem2.png b/content/images/humrochagf/gh-pelican-travis/primeira-postagem2.png
new file mode 100644
index 000000000..01097f831
Binary files /dev/null and b/content/images/humrochagf/gh-pelican-travis/primeira-postagem2.png differ
diff --git a/content/images/humrochagf/gh-pelican-travis/travis-repo1.png b/content/images/humrochagf/gh-pelican-travis/travis-repo1.png
new file mode 100644
index 000000000..86cb10d92
Binary files /dev/null and b/content/images/humrochagf/gh-pelican-travis/travis-repo1.png differ
diff --git a/content/images/humrochagf/gh-pelican-travis/travis-repo2.png b/content/images/humrochagf/gh-pelican-travis/travis-repo2.png
new file mode 100644
index 000000000..aedb5a429
Binary files /dev/null and b/content/images/humrochagf/gh-pelican-travis/travis-repo2.png differ
diff --git a/content/images/humrochagf/meusite-admin.png b/content/images/humrochagf/meusite-admin.png
new file mode 100644
index 000000000..6c16b22b5
Binary files /dev/null and b/content/images/humrochagf/meusite-admin.png differ
diff --git a/content/images/humrochagf/meusite.png b/content/images/humrochagf/meusite.png
new file mode 100644
index 000000000..98cfc3eb4
Binary files /dev/null and b/content/images/humrochagf/meusite.png differ
diff --git a/content/images/juniorcarvalho/dokku-setup.jpg b/content/images/juniorcarvalho/dokku-setup.jpg
new file mode 100644
index 000000000..bfa4ad726
Binary files /dev/null and b/content/images/juniorcarvalho/dokku-setup.jpg differ
diff --git a/content/images/lucasmagnum/helloworld.png b/content/images/lucasmagnum/helloworld.png
new file mode 100644
index 000000000..363e2eecd
Binary files /dev/null and b/content/images/lucasmagnum/helloworld.png differ
diff --git a/content/images/lucasmagnum/itworked.png b/content/images/lucasmagnum/itworked.png
new file mode 100644
index 000000000..4692f857d
Binary files /dev/null and b/content/images/lucasmagnum/itworked.png differ
diff --git a/content/images/mstuttgart/snapshot_17.png b/content/images/mstuttgart/snapshot_17.png
new file mode 100644
index 000000000..34199b251
Binary files /dev/null and b/content/images/mstuttgart/snapshot_17.png differ
diff --git a/content/images/mstuttgart/snapshot_18.png b/content/images/mstuttgart/snapshot_18.png
new file mode 100644
index 000000000..eaaf3e097
Binary files /dev/null and b/content/images/mstuttgart/snapshot_18.png differ
diff --git a/content/images/mstuttgart/snapshot_19.png b/content/images/mstuttgart/snapshot_19.png
new file mode 100644
index 000000000..6e0a3e827
Binary files /dev/null and b/content/images/mstuttgart/snapshot_19.png differ
diff --git a/content/images/mstuttgart/snapshot_20.png b/content/images/mstuttgart/snapshot_20.png
new file mode 100644
index 000000000..4df153ce0
Binary files /dev/null and b/content/images/mstuttgart/snapshot_20.png differ
diff --git a/content/images/mstuttgart/snapshot_21.png b/content/images/mstuttgart/snapshot_21.png
new file mode 100644
index 000000000..fc87e91e8
Binary files /dev/null and b/content/images/mstuttgart/snapshot_21.png differ
diff --git a/content/images/mstuttgart/snapshot_22.png b/content/images/mstuttgart/snapshot_22.png
new file mode 100644
index 000000000..dae0f5472
Binary files /dev/null and b/content/images/mstuttgart/snapshot_22.png differ
diff --git a/content/images/mstuttgart/snapshot_23.png b/content/images/mstuttgart/snapshot_23.png
new file mode 100644
index 000000000..8c1820168
Binary files /dev/null and b/content/images/mstuttgart/snapshot_23.png differ
diff --git a/content/images/mstuttgart/snapshot_24.png b/content/images/mstuttgart/snapshot_24.png
new file mode 100644
index 000000000..4de56ec0c
Binary files /dev/null and b/content/images/mstuttgart/snapshot_24.png differ
diff --git a/content/images/mstuttgart/snapshot_25.png b/content/images/mstuttgart/snapshot_25.png
new file mode 100644
index 000000000..b8c1257ea
Binary files /dev/null and b/content/images/mstuttgart/snapshot_25.png differ
diff --git a/content/images/mstuttgart/snapshot_26.png b/content/images/mstuttgart/snapshot_26.png
new file mode 100644
index 000000000..79aa775f8
Binary files /dev/null and b/content/images/mstuttgart/snapshot_26.png differ
diff --git a/content/images/mstuttgart/snapshot_27.png b/content/images/mstuttgart/snapshot_27.png
new file mode 100644
index 000000000..0fd89c3c3
Binary files /dev/null and b/content/images/mstuttgart/snapshot_27.png differ
diff --git a/content/images/mstuttgart/snapshot_28.png b/content/images/mstuttgart/snapshot_28.png
new file mode 100644
index 000000000..6273a117e
Binary files /dev/null and b/content/images/mstuttgart/snapshot_28.png differ
diff --git a/content/images/mstuttgart/snapshot_29.png b/content/images/mstuttgart/snapshot_29.png
new file mode 100644
index 000000000..6273a117e
Binary files /dev/null and b/content/images/mstuttgart/snapshot_29.png differ
diff --git a/content/images/mstuttgart/snapshot_30.png b/content/images/mstuttgart/snapshot_30.png
new file mode 100644
index 000000000..4cc306018
Binary files /dev/null and b/content/images/mstuttgart/snapshot_30.png differ
diff --git a/content/images/mstuttgart/snapshot_31.png b/content/images/mstuttgart/snapshot_31.png
new file mode 100644
index 000000000..0546a5b27
Binary files /dev/null and b/content/images/mstuttgart/snapshot_31.png differ
diff --git a/content/images/mstuttgart/snapshot_32.png b/content/images/mstuttgart/snapshot_32.png
new file mode 100644
index 000000000..2676a2360
Binary files /dev/null and b/content/images/mstuttgart/snapshot_32.png differ
diff --git a/content/images/mstuttgart/snapshot_33.png b/content/images/mstuttgart/snapshot_33.png
new file mode 100644
index 000000000..5aef9e56e
Binary files /dev/null and b/content/images/mstuttgart/snapshot_33.png differ
diff --git a/content/images/mstuttgart/snapshot_34.png b/content/images/mstuttgart/snapshot_34.png
new file mode 100644
index 000000000..c21cfea8e
Binary files /dev/null and b/content/images/mstuttgart/snapshot_34.png differ
diff --git a/content/images/mstuttgart/snapshot_35.png b/content/images/mstuttgart/snapshot_35.png
new file mode 100644
index 000000000..eae50680f
Binary files /dev/null and b/content/images/mstuttgart/snapshot_35.png differ
diff --git a/content/images/mstuttgart/snapshot_36.png b/content/images/mstuttgart/snapshot_36.png
new file mode 100644
index 000000000..fa6d59b81
Binary files /dev/null and b/content/images/mstuttgart/snapshot_36.png differ
diff --git a/content/images/mstuttgart/snapshot_37.png b/content/images/mstuttgart/snapshot_37.png
new file mode 100644
index 000000000..539859f30
Binary files /dev/null and b/content/images/mstuttgart/snapshot_37.png differ
diff --git a/content/images/mstuttgart/snapshot_38.png b/content/images/mstuttgart/snapshot_38.png
new file mode 100644
index 000000000..b5f6372b9
Binary files /dev/null and b/content/images/mstuttgart/snapshot_38.png differ
diff --git a/content/images/mstuttgart/snapshot_39.png b/content/images/mstuttgart/snapshot_39.png
new file mode 100644
index 000000000..4d7488eaa
Binary files /dev/null and b/content/images/mstuttgart/snapshot_39.png differ
diff --git a/content/images/mstuttgart/snapshot_40.png b/content/images/mstuttgart/snapshot_40.png
new file mode 100644
index 000000000..6279fed38
Binary files /dev/null and b/content/images/mstuttgart/snapshot_40.png differ
diff --git a/content/images/mstuttgart/snapshot_41.png b/content/images/mstuttgart/snapshot_41.png
new file mode 100644
index 000000000..a32a23568
Binary files /dev/null and b/content/images/mstuttgart/snapshot_41.png differ
diff --git a/content/images/mstuttgart/snapshot_42.png b/content/images/mstuttgart/snapshot_42.png
new file mode 100644
index 000000000..02d9789f4
Binary files /dev/null and b/content/images/mstuttgart/snapshot_42.png differ
diff --git a/content/images/mstuttgart/snapshot_43.png b/content/images/mstuttgart/snapshot_43.png
new file mode 100644
index 000000000..e70cf29ad
Binary files /dev/null and b/content/images/mstuttgart/snapshot_43.png differ
diff --git a/content/images/mstuttgart/snapshot_44.png b/content/images/mstuttgart/snapshot_44.png
new file mode 100644
index 000000000..e9df7ac60
Binary files /dev/null and b/content/images/mstuttgart/snapshot_44.png differ
diff --git a/content/images/othonalberto/2016-01-24_gitshot_othonalberto.png b/content/images/othonalberto/2016-01-24_gitshot_othonalberto.png
new file mode 100644
index 000000000..ea3f26071
Binary files /dev/null and b/content/images/othonalberto/2016-01-24_gitshot_othonalberto.png differ
diff --git a/content/images/rafpyprog/pybr-banner.jpeg b/content/images/rafpyprog/pybr-banner.jpeg
new file mode 100644
index 000000000..1008c6d7c
Binary files /dev/null and b/content/images/rafpyprog/pybr-banner.jpeg differ
diff --git a/content/images/ramalho/Tweedledum-Tweedledee_500x390.png b/content/images/ramalho/Tweedledum-Tweedledee_500x390.png
new file mode 100644
index 000000000..6f7194aff
Binary files /dev/null and b/content/images/ramalho/Tweedledum-Tweedledee_500x390.png differ
diff --git a/content/images/ramalho/diagrams/dum-dee.png b/content/images/ramalho/diagrams/dum-dee.png
new file mode 100644
index 000000000..6e0220213
Binary files /dev/null and b/content/images/ramalho/diagrams/dum-dee.png differ
diff --git a/content/images/ramalho/diagrams/dum-skills-references.png b/content/images/ramalho/diagrams/dum-skills-references.png
new file mode 100644
index 000000000..af09581b2
Binary files /dev/null and b/content/images/ramalho/diagrams/dum-skills-references.png differ
diff --git a/content/images/ramalho/diagrams/dum-t_doom-dee.png b/content/images/ramalho/diagrams/dum-t_doom-dee.png
new file mode 100644
index 000000000..812501001
Binary files /dev/null and b/content/images/ramalho/diagrams/dum-t_doom-dee.png differ
diff --git a/content/images/regisdasilva/admin.png b/content/images/regisdasilva/admin.png
new file mode 100644
index 000000000..454ff65a7
Binary files /dev/null and b/content/images/regisdasilva/admin.png differ
diff --git a/content/images/regisdasilva/band_detail.png b/content/images/regisdasilva/band_detail.png
new file mode 100644
index 000000000..bcdbbcf16
Binary files /dev/null and b/content/images/regisdasilva/band_detail.png differ
diff --git a/content/images/regisdasilva/band_form.png b/content/images/regisdasilva/band_form.png
new file mode 100644
index 000000000..25d4d7694
Binary files /dev/null and b/content/images/regisdasilva/band_form.png differ
diff --git a/content/images/regisdasilva/band_listing.png b/content/images/regisdasilva/band_listing.png
new file mode 100644
index 000000000..fd98466be
Binary files /dev/null and b/content/images/regisdasilva/band_listing.png differ
diff --git a/content/images/regisdasilva/drf01.png b/content/images/regisdasilva/drf01.png
new file mode 100644
index 000000000..120526207
Binary files /dev/null and b/content/images/regisdasilva/drf01.png differ
diff --git a/content/images/regisdasilva/drf02.png b/content/images/regisdasilva/drf02.png
new file mode 100644
index 000000000..07958dbb8
Binary files /dev/null and b/content/images/regisdasilva/drf02.png differ
diff --git a/content/images/regisdasilva/erd.png b/content/images/regisdasilva/erd.png
new file mode 100644
index 000000000..3c8e81dc0
Binary files /dev/null and b/content/images/regisdasilva/erd.png differ
diff --git a/content/images/regisdasilva/erd_vendas.png b/content/images/regisdasilva/erd_vendas.png
new file mode 100644
index 000000000..6b087ffb5
Binary files /dev/null and b/content/images/regisdasilva/erd_vendas.png differ
diff --git a/content/images/regisdasilva/form.png b/content/images/regisdasilva/form.png
new file mode 100644
index 000000000..c7c2a57af
Binary files /dev/null and b/content/images/regisdasilva/form.png differ
diff --git a/content/images/regisdasilva/home.png b/content/images/regisdasilva/home.png
new file mode 100644
index 000000000..18a0e6ecc
Binary files /dev/null and b/content/images/regisdasilva/home.png differ
diff --git a/content/images/regisdasilva/member_form.png b/content/images/regisdasilva/member_form.png
new file mode 100644
index 000000000..40d8aa02c
Binary files /dev/null and b/content/images/regisdasilva/member_form.png differ
diff --git a/content/images/regisdasilva/person.jpg b/content/images/regisdasilva/person.jpg
new file mode 100644
index 000000000..12617f010
Binary files /dev/null and b/content/images/regisdasilva/person.jpg differ
diff --git a/content/images/regisdasilva/postgresql.png b/content/images/regisdasilva/postgresql.png
new file mode 100644
index 000000000..c25aab2bd
Binary files /dev/null and b/content/images/regisdasilva/postgresql.png differ
diff --git a/content/images/regisdasilva/postgresql_django.png b/content/images/regisdasilva/postgresql_django.png
new file mode 100644
index 000000000..4000aac68
Binary files /dev/null and b/content/images/regisdasilva/postgresql_django.png differ
diff --git a/content/images/regisdasilva/postgresql_python.png b/content/images/regisdasilva/postgresql_python.png
new file mode 100644
index 000000000..fb738665a
Binary files /dev/null and b/content/images/regisdasilva/postgresql_python.png differ
diff --git a/content/images/regisdasilva/tabelas.png b/content/images/regisdasilva/tabelas.png
new file mode 100644
index 000000000..0006e97d6
Binary files /dev/null and b/content/images/regisdasilva/tabelas.png differ
diff --git a/content/images/regisdasilva/youtube_logo.png b/content/images/regisdasilva/youtube_logo.png
new file mode 100644
index 000000000..f8c2f9532
Binary files /dev/null and b/content/images/regisdasilva/youtube_logo.png differ
diff --git a/content/images/rochacbruno/access.png b/content/images/rochacbruno/access.png
new file mode 100644
index 000000000..a7c86f5c6
Binary files /dev/null and b/content/images/rochacbruno/access.png differ
diff --git a/content/images/rochacbruno/admin.jpg b/content/images/rochacbruno/admin.jpg
new file mode 100644
index 000000000..b508f0b11
Binary files /dev/null and b/content/images/rochacbruno/admin.jpg differ
diff --git a/content/images/rochacbruno/admin_index.png b/content/images/rochacbruno/admin_index.png
new file mode 100644
index 000000000..6785e1b67
Binary files /dev/null and b/content/images/rochacbruno/admin_index.png differ
diff --git a/content/images/rochacbruno/admin_index_login.png b/content/images/rochacbruno/admin_index_login.png
new file mode 100644
index 000000000..99ddd0b45
Binary files /dev/null and b/content/images/rochacbruno/admin_index_login.png differ
diff --git a/content/images/rochacbruno/admin_noticia.png b/content/images/rochacbruno/admin_noticia.png
new file mode 100644
index 000000000..7d69c6614
Binary files /dev/null and b/content/images/rochacbruno/admin_noticia.png differ
diff --git a/content/images/rochacbruno/admin_user_columns.png b/content/images/rochacbruno/admin_user_columns.png
new file mode 100644
index 000000000..c33cc1ec9
Binary files /dev/null and b/content/images/rochacbruno/admin_user_columns.png differ
diff --git a/content/images/rochacbruno/admin_user_full.png b/content/images/rochacbruno/admin_user_full.png
new file mode 100644
index 000000000..eeb5b91be
Binary files /dev/null and b/content/images/rochacbruno/admin_user_full.png differ
diff --git a/content/images/rochacbruno/bootstrap.png b/content/images/rochacbruno/bootstrap.png
new file mode 100644
index 000000000..34aceded9
Binary files /dev/null and b/content/images/rochacbruno/bootstrap.png differ
diff --git a/content/images/rochacbruno/cached.png b/content/images/rochacbruno/cached.png
new file mode 100644
index 000000000..226fddd63
Binary files /dev/null and b/content/images/rochacbruno/cached.png differ
diff --git a/content/images/rochacbruno/cms_index.png b/content/images/rochacbruno/cms_index.png
new file mode 100644
index 000000000..73567ac48
Binary files /dev/null and b/content/images/rochacbruno/cms_index.png differ
diff --git a/content/images/rochacbruno/code.png b/content/images/rochacbruno/code.png
new file mode 100644
index 000000000..1fe7d8532
Binary files /dev/null and b/content/images/rochacbruno/code.png differ
diff --git a/content/images/rochacbruno/deboas.jpg b/content/images/rochacbruno/deboas.jpg
new file mode 100644
index 000000000..ce7719f0a
Binary files /dev/null and b/content/images/rochacbruno/deboas.jpg differ
diff --git a/content/images/rochacbruno/debug_toolbar.png b/content/images/rochacbruno/debug_toolbar.png
new file mode 100644
index 000000000..d00eb3a25
Binary files /dev/null and b/content/images/rochacbruno/debug_toolbar.png differ
diff --git a/content/images/rochacbruno/lego_snake.jpg b/content/images/rochacbruno/lego_snake.jpg
new file mode 100644
index 000000000..5916aa579
Binary files /dev/null and b/content/images/rochacbruno/lego_snake.jpg differ
diff --git a/content/images/rochacbruno/login.png b/content/images/rochacbruno/login.png
new file mode 100644
index 000000000..1a6de79f4
Binary files /dev/null and b/content/images/rochacbruno/login.png differ
diff --git a/content/images/rochacbruno/mongo.jpg b/content/images/rochacbruno/mongo.jpg
new file mode 100644
index 000000000..48480caa4
Binary files /dev/null and b/content/images/rochacbruno/mongo.jpg differ
diff --git a/content/images/rochacbruno/profiler.png b/content/images/rochacbruno/profiler.png
new file mode 100644
index 000000000..c11f7cc79
Binary files /dev/null and b/content/images/rochacbruno/profiler.png differ
diff --git a/content/images/rochacbruno/register.png b/content/images/rochacbruno/register.png
new file mode 100644
index 000000000..8502d3c1f
Binary files /dev/null and b/content/images/rochacbruno/register.png differ
diff --git a/content/images/rochacbruno/robomongo.png b/content/images/rochacbruno/robomongo.png
new file mode 100644
index 000000000..2b3e06010
Binary files /dev/null and b/content/images/rochacbruno/robomongo.png differ
diff --git a/content/images/rochacbruno/security.jpg b/content/images/rochacbruno/security.jpg
new file mode 100644
index 000000000..717f2ba0a
Binary files /dev/null and b/content/images/rochacbruno/security.jpg differ
diff --git a/content/images/rochacbruno/wtf_index.png b/content/images/rochacbruno/wtf_index.png
new file mode 100644
index 000000000..f0ec82fe9
Binary files /dev/null and b/content/images/rochacbruno/wtf_index.png differ
diff --git a/content/images/z4r4tu5tr4/lambda.jpg b/content/images/z4r4tu5tr4/lambda.jpg
new file mode 100644
index 000000000..1b818652f
Binary files /dev/null and b/content/images/z4r4tu5tr4/lambda.jpg differ
diff --git a/content/images/z4r4tu5tr4/lambda.png b/content/images/z4r4tu5tr4/lambda.png
new file mode 100644
index 000000000..426069c2d
Binary files /dev/null and b/content/images/z4r4tu5tr4/lambda.png differ
diff --git "a/content/instalando-o-python-vers\303\243o-3.7.0-alpha-1-no-ubuntu-16.04.md" "b/content/instalando-o-python-vers\303\243o-3.7.0-alpha-1-no-ubuntu-16.04.md"
new file mode 100644
index 000000000..02be88830
--- /dev/null
+++ "b/content/instalando-o-python-vers\303\243o-3.7.0-alpha-1-no-ubuntu-16.04.md"
@@ -0,0 +1,64 @@
+Title: Instalando o Python versão 3.7.0 alpha 1 no Ubuntu 16.04
+Slug: instalando-o-python-versão-3.7.0-alpha-1-no-ubuntu-16.04.md
+Date: 2017-01-16 20:37:39
+Category: Python
+Tags: python,tutorial,install
+Author: Welton Vaz
+Email: weltonvaz@gmail.com
+Github: weltonvaz
+Linkedin: welton-vaz-de-souza
+Facebook: weltonv
+Site: http://www.weltonvaz.com/
+
+
+Instalando o Python versão 3.7.0 alpha 1 no Ubuntu 16.04
+
+A versão mais recente do Python, a 3.7.0 alfa 1, pode agora ser baixada ou clonada do GitHub facilmente. Uma das linguagens mais fáceis de usar e aprender, o Python foi criado nos anos 90 e é elogiado por sua fácil leitura de código e necessidade de poucas linhas de código, comparada a outras linguagens. Agora mais próxima da comunidade no Github!
+
+Depois disso os caminhos mudaram e conheci a profissão de Analista de Suporte e me ocupo disso desde então. Atualmente voltei a aprender uma linguagem, antes de mais nada, dei uma atualizada em lógica de programação, por sinal existem muitas boas apostilas e cursos gratuitos na Internet, dois caminhos muito bons.
+
+Sobre linguagem de programação, existem várias. Neste quesito comecei a conhecer a linguagem Python e logo me apaixonei pela simplicidade, beleza e eficiência.
+
+Depois disso tudo, você tem que instalar a linguagem em sua máquina. Por padrão, o Ubuntu 16.04 instala a versão 3.4, mas se você quiser, pode usar a versão 3.7.0a0
+
+Obs.: Execute os comandos como root, ou usando o comando sudo no terminal.
+
+```shell
+git clone https://github.com/python/cpython
+cd cpython
+apt-get install build-essential libssl-dev libffi-dev python3-dev
+./configure
+make
+make test
+make install
+
+# Se você quiser usar várias versões do Python 2.7, 3.6 e 3.7 use o comando abaixo
+make altinstall
+```
+
+Observação: via apt instalei as dependências do python, no caso o openssl, porque o pip apresenta vários problemas com certificados na instalação dos módulos, mas, isso é para outro artigo
+
+Depois disso é só entrar no interpretador:
+
+```shell
+python3.7
+```
+
+Tela do interpretador Python
+
+```shell
+Python 3.7.0a0 (default, Feb 16 2017, 18:59:44)
+[GCC 5.4.0 20160609] on linux
+Type "help", "copyright", "credits" or "license" for more information.
+>>>
+
+```
+### Referências
+Para ler mais sobre a linguagem:
+* [Python] - Site oficial da Linguagem Python!
+* [This is Python version 3.7.0 alpha 1] - Git da próxima versão do Python, hospedado no Github!
+* [Python-Brasil] - A comunidade Python Brasil reune grupos de usuários em todo o Brasil interessados em difundir e divulgar a linguagem de programação.
+
+[Python]:
+[Python-Brasil]:
+[This is Python version 3.7.0 alpha 1]:
\ No newline at end of file
diff --git a/content/instalando-pycharm.md b/content/instalando-pycharm.md
new file mode 100644
index 000000000..19938702e
--- /dev/null
+++ b/content/instalando-pycharm.md
@@ -0,0 +1,88 @@
+Title: Instalando o PyCharm no Ubuntu (e irmãos)
+Slug: instalando-pycharm-ubuntu
+Date: 2015-06-14 12:58
+Tags: python,blog,tutorial,pycharm
+Author: Erick Müller
+Email: erick.muller@gmail.com
+Github: ehriq
+Bitbucket: ehriq
+Twitter: ehriq
+Category: Python
+
+
+O objetivo aqui é instalar o PyCharm no Ubuntu e distribuições "irmãs" (como o Mint); estou instalando a versão **Community Edition**, que acredito que é a que muita gente que começa com essa poderosa IDE vai instalar pra dar os primeiros passos, experimentar.
+
+(aliás, bom avisar antes de começar: fiz o guia baseado no Ubuntu 14.04 e no Linux Mint 17.1; mas já fiz o mesmo procedimento em versões anteriores tanto do PyCharm quanto do Ubuntu, e com a versão "Professional" do PyCharm, e funcionou bem.)
+
+
+## Parte 1 - instalar o Java
+
+As aplicações da JetBrains não são exatamente compatíveis com a versão do Java que vem por padrão no Ubuntu. Por isso, precisamos atualizar.
+
+Abra o terminal e execute os comandos abaixo:
+
+```
+sudo add-apt-repository ppa:webupd8team/java
+sudo apt-get update
+echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | sudo /usr/bin/debconf-set-selections
+sudo apt-get install oracle-java8-installer -y
+sudo apt-get install oracle-java8-set-default -y
+```
+
+Após os comandos acima, veja se a instalação está correta, executando no console:
+
+```bash
+java -version
+```
+
+a saída esperada é algo como:
+
+```
+java version "1.8.0_45"
+Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
+Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)
+```
+
+## Parte 2 - pip e virtualenv
+
+O PyCharm usa o **pip** para baixar módulos/bibliotecas/extensões (como quiser chamar) do python, e o **virtualenv** para criar os queridos ambientes virtuais que mantém a sanidade dos programadores python. Então, para tirar proveito dessas funcionalidades, é bom garantir que estejam instalados também.
+
+Para isto, abra o console e:
+
+```
+cd ~/Downloads
+wget -c https://bootstrap.pypa.io/get-pip.py
+sudo -H python2 get-pip.py
+sudo -H python3 get-pip.py
+sudo -H pip2 install virtualenv
+```
+
+
+## Parte 3 - copiar o PyCharm
+
+- clique no link ao lado para ir à página de [Download do PyCharm](https://www.jetbrains.com/pycharm/download/)
+- clique em "Download Community"
+- grave o arquivo no diretório que quiser
+
+
+
+
+## Parte 4 - instalar o PyCharm
+
+Com os pré-requisitos prontos e instalados, vamos ao prato principal:
+
+```
+sudo tar -C /opt/ -xzf /pycharm-community-4.5.1.tar.gz
+```
+
+- Abra o navegador de arquivos e vá ao diretório */opt/pycharm-community-4.5.1*
+- Entre no diretório 'bin' e, com dois cliques sobre, execute o script *'pycharm.sh'*
+- Se aparecer uma janela perguntando como rodar o programa, clique no último botão (*'Executar'* ou *'Run'*)
+- Dê "OK" na janela que abrir
+- E na próxima janela, deixe todas as últimas opções selecionadas. Ao clicar em *'OK'* o PyCharm vai pedir a senha de 'root' para criar as entradas no menu.
+
+
+
+
+
+Pronto, é isso. O software está instalado, e pronto para uso.
diff --git a/content/integrando-django-com-cloudinary.md b/content/integrando-django-com-cloudinary.md
new file mode 100644
index 000000000..edf095b6e
--- /dev/null
+++ b/content/integrando-django-com-cloudinary.md
@@ -0,0 +1,219 @@
+Title: Integrando o Django com Cloudinary
+Date: 2014-12-09 23:08
+Tags: Django, Cloudinary, Heroku
+Category: deploy-infraestrutura
+Slug: integrando-django-com-cloudinary
+Author: Dyesten Paulon
+Email: dyesten.pt@gmail.com
+Github: dyesten
+Facebook: dyesten.paulon
+
+
+
+### O que é?
+
+O Cloudinary é um serviço de gerenciamento de imagens e arquivos na nuvem, muito útil por exemplo para utilização junto ao Heroku, que não oferece o serviço de hospedagem de arquivos. Além de nos oferecer o serviço de hospedagem de imagens, o Cloudinary disponibiliza diversas manipulações, uso de efeitos, detecção facial e muitos outros recursos para as imagens enviadas.
+
+### O que é preciso?
+
+Para iniciarmos é preciso se __cadastrar__ no site. O cadastro pode ser feito com uma conta gratuita limitada.
+
+[Cadastro Gratuito](https://cloudinary.com/users/register/free)
+
+
+Ao finalizar o cadastro, uma tela como a exibida abaixo estará disponível. Atenção nos itens Cloud name, API Key e API Secret, eles serão úteis mais adiante.
+
+
+
+
+
+### Configurando o ambiente
+
+A instalação do pacote pode ser feita via __pip__:
+
+ pip install cloudinary
+
+Ou baixando o pacote pelo [link](https://pypi.python.org/pypi/cloudinary/1.0.18)
+
+
+### Configurando o settings
+_Obs.: focaremos apenas nas configurações do cloudinary._
+
+Primeiramente no INSTALLED_APPS incluiremos a linha __'cloudinary'__ e a linha com nossa app:
+
+ INSTALLED_APPS = (
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'cloudinary',
+ 'cloudinary_example.core',
+ )
+
+Ainda no settings adicione ao seu arquivo os parâmetros de configuração do Cloudinary:
+
+_Obs.: estes parâmetros são os mesmo da imagem inicial. E os abaixo apresentados são apenas ficticios._
+
+ CLOUDINARY = {
+ 'cloud_name' : seu_app_cloud,
+ 'api_key' : '00998877665544',
+ 'api_secret': 'DBseuAPPAKI-mtb7ZklCCBuJNoNetp'
+ }
+
+### Models
+
+Faremos a importação do Cloudinary e em seguida definiremos nossa classe __'Imagens'__:
+
+ from django.db import models
+ from cloudinary.models import CloudinaryField
+
+ class Imagens(models.Model):
+ imagem = CloudinaryField('imagem')
+
+_Obs.: execute o syncdb. No caso de utilização do South, acrescente o seguinte código:_
+
+ from south.modelsinspector import add_introspection_rules
+ add_introspection_rules([], ["^cloudinary\.models\.CloudinaryField"])
+
+### Forms
+Agora vamos importar o modelo em nosso __forms__, e definiremos nossa Classe em seguida:
+
+ from django import forms
+ from cloudinary_example.core.models import Imagens
+
+ class ImagensForm(forms.ModelForm):
+ class Meta:
+ model = Imagens
+
+
+Agora vamos criar o formulário para fazermos o upload das imagens.
+Antes vamos definir uma rota para nossa __views__, chamaremos ela de _'galeria'_:
+
+ urlpatterns = patterns('cloudinary_example.core.views',
+ url(http://github.com/r'%5Egaleria/'),%20'galeria',%20name='galeria'),
+ )
+
+Criaremos agora uma __views__ mais simples possível para chegar até nosso __template__:
+
+ from django.shortcuts import render_to_response
+ from cloudinary_example.core.forms import ImagensForm
+
+ def galeria(request):
+ return render_to_response('galeria.html', {'form':ImagensForm})
+
+
+Agora vamos criar nosso template _'galeria.html'_ com o seguinte código:
+
+ {% extends 'base.html' %}
+ {% load cloudinary %}
+
+ {% block content %}
+