Вы находитесь на странице: 1из 41

Class Based Views no Django

Esse tutorial tem como objetivo explicar o bsico sobre Class Based Views no Django. Por motivos
de agilidade vou usar CBV para me referir as Class Based Views.
Segundo a documentao do Django sobre CBV:
CBV's permitem voc estruturar as suas views e reutilizar cdigo aproveitando heranas e mixins
O Django j vem CBV's genricas que atendem as necessidades da maioria das aplicaes. Essas
views genricas so 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
View
TemplateView
RedirectView
Display views
DetailView
ListView
Editing views
Model based Views
CreateView, UpdateView, DeleteView
Date views
ArchiveView
YearView
MonthView
WeekView
DayView
TodayView
DateDetailView
Concluso
Referncias
Antes de comearmos a falar sobre as CBV's vamos ver como apontar uma rota do Django para
uma CBV:
from django.conf.urls import url
from django.views.generic import TemplateView
from meuapp.views import AboutView
urlpatterns = [
url(r'^about/', AboutView.as_view()),
]

Base Views
As classes listadas abaixo contm muito da funcionalidade necessria para criar views no Django.
Essas classes so a base sob a qual as outras CBV's so construdas.

View
A classe genrica master. Todas as outras classes herdam dessa classe. O fluxo bsico de execuo
dessa classe quando recebe uma requisio :
1. dispatch()
2. http_method_not_allowed()
3. options()
A funo dispatch() verifica se a classe tem um mtodo com o nome do verbo HTTP usado na
requisio. Caso no haja, um http.HttpResponseNotAllowed retornado.
Essa classe sempre responde a requisies com o verbo OPTIONS retornando nesse caso uma lista
com os verbos suportados. A no ser que o mtodo options() seja sobrescrito.
Um exemplo de implementao:
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 requisies do tipo GET e OPTIONS, todas as outras
requisies retornam http.HttpResponseNotAllowed.

Template View
Renderiza um template. O fluxo bsico de execuo dessa classe quando recebe uma requisio :
1. dispatch()
2. http_method_not_allowed()
3. get_context_data()
Quando voc precisa apenas renderizar uma pgina para o usurio essa com certeza a melhor
CBV para o caso. Voc pode editar o contexto que o template recebe sobrescrevendo a funo
get_context_data()
Um exemplo de implementao:
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
varivel chamada lastest_articles.
Uma coisa interessante que o contexto da TemplateView populado pelo ContextMixin esse
mixin pega automaticamente os argumentos da URL que serviu a View.
Considere por exemplo:
from django.conf.urls import patterns, url
from .views import HelloView
urlpatterns = patterns(
'',
url(r'^say_hello/(?P<name>[\w_-]+)/$', HelloView.as_view(),
name='say_hello'),
)

No caso do exemplo acima o template renderizado pela HelloView teria em seu contexto a
varivel name.

Redirect View
Redireciona o usurio para a url informada.
A URL a ser redirecionada pode conter parmetros no estilo dicionrio-de-strings. Os parmetros
capturados na URL do RedirectView sero repassados para a URL que o usurio est sendo
redirecionado.
O fluxo bsico de execuo dessa classe quando recebe uma requisio :
1. dispatch()
2. http_method_not_allowed()
3. get_redirect_url()
Considere a seguinte configurao de URL's para o exemplo de implementao:
from django.conf.urls import url
from django.views.generic.base import RedirectView
from article.views import ArticleCounterRedirectView, ArticleDetail
urlpatterns = [
url(r'^counter/(?P<pk>[0-9]+)/$', ArticleCounterRedirectView.as_view(),
name='article-counter'),
url(r'^details/(?P<pk>[0-9]+)/$', ArticleDetail.as_view(), name='articledetail'),
]

Exemplo de implementao:
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(self, *args, **kwargs):
article = get_object_or_404(Article, pk=kwargs['pk'])
article.update_counter()
return super(ArticleCounterRedirectView, self).get_redirect_url(*args,
**kwargs)

Principais atributos:
url: A URL destino no formato de String
pattern_name: O nome do padro 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 contrrio, 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 informaes. Tipicamente essas views so as
mais usadas na maioria dos projetos.

DetailView
Renderiza um template contendo em seu contexto um objeto obtido pelo parmetro enviado na
URL.
No fluxo de execuo dessa view o objeto que est sendo utilizado est em self.object
O fluxo bsico de execuo dessa classe quando recebe uma requisio :
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 customizvel.
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 no seja encontrado o Django tenta o segundo e assim
por diante.
Em seguida o get_slug_field() entra em ao, essa funo 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
usurio logado. Considere o exemplo abaixo:
def ArtigoView(DetailView):
model = Artigo
get_queryset(self):
return self.model.filter(user=request.user)
# ... o restante do cdigo foi suprimido

IMPORTANTE: O get_queryset() chamado pela implementao default do mtodo


get_object(), se o get_object() for sobrescrito a chamada ao get_queryset() pode
no ser realizada.
O get_object() ento o responsvel por retornar o objeto que ser enviado para o template.
Normalmente essa funo no precisa ser sobrescrita.
Depois de obter o objeto que ser enviado para o template necessrio saber qual ser o nome desse
objeto no contexto do template, isso feito pela funo 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 ento o get() que
obtm o objeto e coloca no contexto, e em seguida o render_to_response que renderiza o
template.
IMPORTANTE: importante notar que o Django oferece variveis de instncia para facilitar a
customizao do comportamento da classe. Por exemplo a troca do nome do objeto pode ser feita
alterando a varivel de instncia context_object_name ao invs de sobrescrever a funo
get_object_name().
Abaixo segue um exemplo, onde exibir os detalhes de um Artigo somente se o usurio 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
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
from django.conf.urls import url
from article.views import ArticleDetailView
urlpatterns = [
url(r'^(?P<titulo>[-\w]+)/$', ArticleDetailView.as_view(), name='articledetail'),
]

detalhe_artigo.html
<h1>{{ meu_artigo.titulo }}</h1>
<p>{{ meu_artigo.conteudo }}</p>
<p>Reporter: {{ meu_artigo.user.name }}</p>
<p>Published: {{ meu_artigo.data_publicacao|date }}</p>

ListView
Uma pgina que representa uma lista de objetos. Enquanto essa view est executando a varivel
self.object_list vai conter a lista de objetos que a view est utilizando.
O fluxo bsico de execuo dessa classe quando recebe uma requisio :
1.
2.
3.
4.
5.
6.
7.
8.
9.

dispatch()
http_method_not_allowed()
get_template_names()
get_queryset()
get_object()
get_context_object_name()
get_context_data()
get()
render_to_response()

Nada de novo aqui certo? Podemos exibir apenas uma lista de Artigos que esto com
status='publicado'
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 opo seria:

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
<h1>Articles</h1>
<ul>
{% for article in object_list %}
<li>{{ article.pub_date|date }} - {{ article.headline }}</li>
{% empty %}
<li>No articles yet.</li>
{% endfor %}
</ul>

DICA: Normalmente sobrescrevemos as funes quando o retorno depende dos parmetros da


requisio e utilizamos as variveis de instncia quando no h essa dependncia.
O nome do template que usado em ambas as views DetailView e ListView determinado
da seguinte forma:
O valor da varivel template_name na View (se definido)
O valor do campo template_name_field na instncia do objeto que a view est
usando.
<app_label>/<model_name><template_name_suffix>.html

Editing Views
As views descritas abaixo contm o comportamento bsico para edio de contedo.

FormView
Uma view que mostra um formulrio. Se houver erro, mostra o formulrio novamente contendo os
erros de validao. Em caso de sucesso redireciona o usurio para uma nova URL.
forms.py
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
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
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Send message" />
</form>

As funes mais importantes do FormView so:


form_valid(): Chamada quando o formulrio validado com sucesso
form_invalid(): Chamada quando o formurio contm erros
get_sucess_url(): Chamada quando o formulrio validado com sucesso e retorna a
url para qual o usurio 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
automtica, desde que seja possvel 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 mtodo get_object() retorna um objeto, a classe desse objeto ser usada
Se houver um atributo queryset o model do queryset ser utilizado
Voc no precisa nem mesmo definir um success_url as views CreateView e UpdateView
utilizam automaticamente a funo get_absolute_url() do model se essa funo existir.
Voc tambm pode customizar o formulrio usado na view se voc precisar de algum tratamento
adicional, para fazer isso basta definir a classe de formulrios a ser usada no atributo
form_class:
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
formulrio, este exibido novamente junto com as mensagens de erro.
Em caso de sucesso o objeto salvo.
models.py
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(self):
return reverse('author-detail', kwargs={'pk': self.pk})

views.py
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
from django.conf.urls import url
from myapp.views import AuthorCreate, AuthorUpdate, AuthorDelete
urlpatterns = [
# ...
url(r'author/add/$', AuthorCreate.as_view(), name='author_add'),
url(r'author/(?P<pk>[0-9]+)/$', AuthorUpdate.as_view(),
name='author_update'),
url(r'author/(?P<pk>[0-9]+)/delete/$', AuthorDelete.as_view(),
name='author_delete'),
]

O atributo fields determina quais campos do model devem estar presentes no formulrio.
obrigatrio especificar o atributo fields ou ento o atributo form_class, nunca os dois ao
mesmo tempo, pois isso geraria uma exceo ImproperlyConfigured.
importante notar tambm que a DeleteView exibe as informaes 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 so views com a funo de exibir pginas com dados filtrados por datas,
por exemplo: posts em um blog, notcias, consultas ao mdico, etc.

ArchiveIndexView
Uma pgina que exibe os "ltimas" objetos inseridos, desconsiderando aqueles com uma data futura
a no 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.
Alm da lista de objetos o contexto tambm contem a varivel date_list contendo todos
os anos que tem objetos em ordem decrescente. Isso pode ser alterado para ms ou dia
usando o atributo date_list_period. Isso se aplica a todas as Data-based generic
views.
Implementao simples:
urls.py
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 pgina para exibir um arquivo anual. Retorna todos os objetos de um determinado ano.
No contexto alm 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 prximo ano
previous_year: Um objeto datetime.datetime representando o ano anterior
Exemplo de implementao:
views.py
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
from django.conf.urls import url
from myapp.views import ArticleYearArchiveView
urlpatterns = [
url(r'^(?P<year>[0-9]{4})/$',
ArticleYearArchiveView.as_view(),
name="article_year_archive"),
]

article_archive_year.html
<ul>
{% for date in date_list %}
<li>{{ date|date }}</li>
{% endfor %}
</ul>

MonthArchiveView
Uma pgina para exibir um arquivo mensal. Retorna todos os objetos de um determinado ms.
No contexto alm da lista de objetos temos ainda:
date_list: Um objeto QuerySet contendo todos os dias que tenham objetos naquele ms
representados como objetos datetime.datetime em ordem crescente.
month: Um objeto datetime.datetime representando o ms atual
next_month: Um objeto datetime.datetime representando o prximo ms
previous_month: Um objeto datetime.datetime representando o ms anterior
Exemplo de implementao:
views.py
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
from django.conf.urls import url
from myapp.views import ArticleMonthArchiveView

urlpatterns = [
# Example: /2012/aug/
url(r'^(?P<year>[0-9]{4})/(?P<month>[-\w]+)/$',
ArticleMonthArchiveView.as_view(),
name="archive_month"),
# Example: /2012/08/
url(r'^(?P<year>[0-9]{4})/(?P<month>[0-9]+)/$',
ArticleMonthArchiveView.as_view(month_format='%m'),
name="archive_month_numeric"),
]

article_archive_month.html
<ul>
{% for article in object_list %}
<li>{{ article.pub_date|date:"F j, Y" }}: {{ article.title }}</li>
{% endfor %}
</ul>
<p>
{% if previous_month %}
Previous Month: {{ previous_month|date:"F Y" }}
{% endif %}
{% if next_month %}
Next Month: {{ next_month|date:"F Y" }}
{% endif %}
</p>

WeekArchiveView
Uma pgina para exibir um arquivo semanal. Retorna todos os objetos de uma determinada semana.
No contexto alm da lista de objetos temos ainda:
week: Um objeto datetime.datetime representando a semana atual
next_week: Um objeto datetime.datetime representando a prxima semana
previous_week: Um objeto datetime.datetime representando a semana anterior
Implementao simples:
views.py
from django.views.generic.dates import WeekArchiveView
from myapp.models import Article
class ArticleWeekArchiveView(WeekArchiveView):
queryset = Article.objects.all()
date_field = "pub_date"
week_format = "%W"
allow_future = True

urls.py
from django.conf.urls import url
from myapp.views import ArticleWeekArchiveView
urlpatterns = [
# Example: /2012/week/23/

url(r'^(?P<year>[0-9]{4})/week/(?P<week>[0-9]+)/$',
ArticleWeekArchiveView.as_view(),
name="archive_week"),

article_archive_week.html
<h1>Week {{ week|date:'W' }}</h1>
<ul>

{% for article in object_list %}


<li>{{ article.pub_date|date:"F j, Y" }}: {{ article.title }}</li>
{% endfor %}
</ul>
<p>

{% 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 %}

</p>

DayArchiveView
Uma pgina para exibir um arquivo dirio. Retorna todos os objetos de um determinado dia.
No contexto alm da lista de objetos temos ainda:

day: Um objeto datetime.datetime representando o dia atual


next_day: Um objeto datetime.datetime representando o prximo dia
previous_day: Um objeto datetime.datetime representando o dia anterior
next_month: Um objeto datetime.datetime representando o primeiro dia do prximo ms
previous_month: Um objeto datetime.datetime representando o primeiro dia do ms
anterior

Implementao simples:
views.py
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
from django.conf.urls import url
from myapp.views import ArticleDayArchiveView
urlpatterns = [
# Example: /2012/nov/10/

url(r'^(?P<year>[0-9]{4})/(?P<month>[-\w]+)/(?P<day>[0-9]+)/$',
ArticleDayArchiveView.as_view(),
name="archive_day"),

article_archive_day.html
<h1>{{ day }}</h1>
<ul>

{% for article in object_list %}


<li>{{ article.pub_date|date:"F j, Y" }}: {{ article.title }}</li>
{% endfor %}
</ul>
<p>

{% if previous_day %}
Previous Day: {{ previous_day }}
{% endif %}
{% if previous_day and next_day %}--{% endif %}
{% if next_day %}
Next Day: {{ next_day }}
{% endif %}

</p>

TodayArchiveView
a mesma coisa do DayArchiveView mas no usa os parmetros da URL para determinar o
ano/ms/dia.
O que muda o urls.py, veja o exemplo abaixo:
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 diferena 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:
from django.conf.urls import url
from django.views.generic.dates import DateDetailView
urlpatterns = [
url(r'^(?P<year>[0-9]{4})/(?P<month>[-\w]+)/(?P<day>[0-9]+)/(?P<pk>[0-9]+)/
$',
DateDetailView.as_view(model=Article, date_field="pub_date"),
name="archive_date_detail"),
]

Concluso
Longe de tentar exaurir um assunto de tamanha complexidade e abrangncia minha inteno com
esse artigo foi mostrar o funcionamento bsico das Class Based Views e quem sabe incentivar voc
a utilizar CBV's no seu prximo projeto.
Envie para mim qualquer dvida, crtica ou sugesto que voc tiver em qualquer uma das minhas
redes sociais, posso demorar um pouco a responder mas eu respondo! :)

Criando novos comandos no django-admin


Veja aqui como criar o seu prprio comando para ser usado com o django-admin ou manage.py do
Django.
O django-admin ou manage.py j tem um bocado de comandos interessantes, os mais utilizados so:
startproject - cria novos projetos.
startapp - cria novas apps.
makemigrations - cria novas migraes baseadas nas mudanas detectadas nos modelos
Django.
migrate - sincroniza o banco de dados com as novas migraes.
createsuperuser - cria novos usurios.
test - roda os testes da aplicao.
loaddata - carrega dados iniciais a partir de um json, por exemplo, python manage.py
loaddata fixtures.json
shell - inicializa um interpretador Python interativo.
dbshell - acessa o banco de dados atravs da linha de comando, ou seja, voc pode executar
comandos sql do banco, por exemplo, diretamente no terminal.
inspectdb - retorna todos os modelos Django que geraram as tabelas do banco de dados.
runserver - 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.

Comeando do comeo
Importante: estamos usando Django 1.8 e Python 3.

Criando o projeto
Eu usei este Makefile para criar o projeto.
wget --output-document=Makefile https://goo.gl/UMTpZ1
make setup

Ele vai criar um virtualenv e pedir pra voc executar os seguintes comandos:
source venv/bin/activate
cd djangoproject
make install

Pronto! Agora ns 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, ento digite, estando na pasta djangoproject
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 e Python 3.
O Django 1.8 usa o argparse como parser de argumentos do command, mais informaes em
module-argparse.
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="store_true",
help='Ajuda da opo aqui.'),
)
def handle(self, **options):
self.stdout.write('Hello world.')
if options['awards']:
self.stdout.write('Awards')

Entendeu? Basicamente o handle a funo 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.
$ python manage.py hello
Hello World

--awards um argumento opcional, voc tambm pode digitar -a.


$ python manage.py hello -a
Hello World
Awards

action="store_true" significa que ele armazena um valor verdadeiro.


Obs: A partir do Django 1.8 os comandos de argumentos opcionais so baseados em **options.

Veja uma outra forma de escrever


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='store_true',
help='Ajuda da opo aqui.')
def handle(self, *args, **options):
self.stdout.write('Hello world.')
if options['awards']:
self.stdout.write('Awards')

A diferena que aqui usamos parser.add_argument ao invs de make_option.

hello.py
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
$ python manage.py hello

initdata.py
Objetivo: Obter alguns filmes de uma api e salvar os dados no banco.
api: omdbapi.com
models.py
from django.db import models
class Movie(models.Model):
title = models.CharField(u'ttulo', max_length=100)
year = models.PositiveIntegerField('ano', null=True, blank=True)
released = models.CharField(u'lanamento', 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

No se esquea de fazer
python manage.py makemigrations
python manage.py migrate

admin.py
Vamos visualizar pelo admin.
from django.contrib import admin
from core.models import Movie
admin.site.register(Movie)

Instale o requests
pip install requests

initdata.py
O cdigo a seguir longo, mas basicamente temos
print_red(name) funo que imprime um texto em vermelho (opcional)
get_html(year) funo que l os dados da api usando requests, e depois escolhe um
filme randomicamente a partir de 2 letras
get_movie(year) se o dicionrio conter {'Response': 'True', ...} ento
retorna um dicionrio do filme localizado
save() salva os dados no banco
handle(movies, year) este o comando principal. Busca os filmes vrias vezes,
conforme definido pela varivel movies, e salva os n filmes.
# -*- 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='store',

default=None,
help='Define o ano de lanamento 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 no for definido o ano, ento 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 dicionrio do filme """
movie = self.get_html(year)
j = 1 # contador
# Faz a validao de Response. Se a resposta for falsa, ento 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 no foi salvo.\n')
def handle(self, movies, year, **options):
""" se "movies" no 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 validaes
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
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 ttulo ou ano de lanamento.
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 ttulo ou ano de lanamento.
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 ttulo.'),
make_option('--year', '-y',
dest='year',
default=None,
help='Localiza um filme pelo ano de lanamento.'),
)
def handle(self, title=None, year=None, **options):
""" dicionrio 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
Usage: python manage.py search [options]
Localiza um filme pelo ttulo ou ano de lanamento.
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 tem um exemplo legal que eu usei como ideia pra fazer este post.

Introduo a Classes e Mtodos em Python (bsico)


Eu no sou a melhor pessoa do mundo para explicar este assunto, mas eu escrevi este post para
introduzir um tpico sobre manipulao de banco de dados em SQLite3 com Python, porm mais
informaes sobre classes e mtodos podem ser encontradas nos links abaixo. Veja os exemplos em
https://github.com/rg3915/pythonDesktopApp.
PS: Considere a sintaxe para Python 3.
Segundo a documentao do Python e o video Python para Zumbis, uma classe associa dados
(atributos) e operaes (mtodos) numa s estrutura. Um objeto uma varivel cujo tipo uma
classe, ou seja, um objeto uma instncia de uma classe.
Na sua sintaxe mais elementar definimos uma classe conforme abaixo:
class NomeDaClasse(object):
pass

E um mtodo (funo) como:


def metodo(args):
pass

onde args so argumentos opcionais (parmetros de entrada). A funo metodo pode retornar um
valor de sada:
def metodo(args):
return args

Juntando os dois temos:


class NomeDaClasse(object):
atributo1 = None
def metodo(self, args):
pass

A primeira pergunta que voc vai ter o porque do self em metodo. A resposta curta , todo
metodo criado dentro de uma classe deve definir como primeiro parametro o self. Para a resposta
longa, por favor, leia a excelente explicao que o Pedro Werneck fez:
http://www.pedrowerneck.com/o-porque-do-self-explicito-em-python-pt-br.html
A segunda pergunta : para que serve o pass?
A resposta que, em Python, ao contrario de vrias outras liguagens de programao, os blocos de
cdigo NO so definidos com os caracteres { e }, mas sim com indentao e o caractere :.
Devido a esse fato, python necessitava de algo para explicitar quando se quer definir um bloco
vazio. O pass foi criado exatamente para explicitar essa situao.
Um exemplo de uma funo vazia feita em linguagem C e a mesma funo vazia feita em Python:
void metodo(int num){
}

def metodo(num):
pass

Voltando a explicao sobre a classe: O atributo1 um atributo com valor inicial None (nada).
Poderia ser atributo1 = 0, por exemplo.
Importante: Note que para nome de classes usamos PalavrasComeandoPorMaiscula (isso
tambem conhecido como "CamelCase") e para nome de mtodos (funes) usamos
minsculas_separadas_por_underscore. Esta uma conveno adotada pelos Pythonistas segundo
o Guia de Estilo PEP 8 - Style Guide for Python Code escrito por Guido Van Rossum.

Exemplo 1 - Calculadora simples


Existem pelo menos duas formas diferentes de trabalhar com os parmetros de entrada. Neste
exemplo, definiremos o parmetro apenas uma vez com um mtodo especial do Python chamado
__init__. Segundo Joo Reis, este mtodo chamado quando um objeto de uma classe
instnciado. Este mtodo til para fazer qualquer inicializao que voc queira com seu objeto, ou
seja, ele o mtodo "Inicializador" da instancia.
#calculadora.py
class Calculadora(object):
def __init__(self, a, b):
self.a = a
self.b = b
def soma(self):
return self.a + self.b
def subtrai(self):
return self.a - self.b
def multiplica(self):
return self.a * self.b
def divide(self):
return self.a / self.b

Note que definimos dois parmetros a e b (dentro do parnteses). E o self.a um novo campo.
Poderamos definir
def __init__(self, param1, param2):
self.a = param1
self.b = param2

para no confundir, mas usualmente usamos o mesmo nome tanto no parmetro quanto no novo
campo.
Como dito antes, definimos os valores iniciais apenas uma vez e depois apenas usamos os mtodos
para calcular os valores.
Podemos rodar o Python no modo modo interativo pelo terminal e importar a classe (veja este
video).
$ python3

>>>
>>>
>>>
>>>
>>>
>>>

from calculadora import Calculadora


c = Calculadora(128,2)
print('Soma:', c.soma())
print('Subtrao:', c.subtrai())
print('Multiplicao:', c.multiplica())
print('Diviso:', c.divide())

c = Calculadora(128,2) uma instncia da classe com dois valores iniciais.


O resultado :
>>>
>>>
>>>
>>>

Soma: 130
Subtrao: 126
Multiplicao: 256
Diviso: 64.0

Podemos redefinir os valores iniciais da seguinte forma:


>>> c.a = 12
>>> c.b = 42
>>> print c.soma()

Resultado:
>>> 54

Importante: apesar de no fazer parte do escopo deste artigo, mas vejam este video Operadores
aritmticos e diviso no Python 2 e Python 3, explicando sobre a diferena no resultado da diviso
nas duas verses do Python.
Vejam tambm este artigo sobre ponto flutuante: Floating Point Arithmetic Issues and Limitations.

Exemplo 2 - Calculadora
Agora faremos uma classe sem valor inicial e com dois parmetros para todos os mtodos.
#calculadora2.py
class Calculadora(object):
def soma(self, a, b):
return a + b
def subtrai(self, a, b):
return a - b
def multiplica(self, a, b):
return a * b
def divide(self, a, b):
return a / b

Usando o terminal no modo interativo faamos:


$ python3
>>> from calculadora2 import Calculadora
>>> c = Calculadora()
>>> print('Soma:', c.soma(2,3))
>>> print('Subtrao:', c.subtrai(2,10))
>>> print('Multiplicao:', c.multiplica(3,3))
>>> print('Diviso:', c.divide(128,2))

A vantagem de colocar os parmetros em cada mtodo, que podemos calcular qualquer valor sem
ter que instanciar uma nova classe para cada valor diferente.

Exemplo 3 - Classe Pedido


Agora veremos um exemplo que mais se aproxima do que iremos fazer em banco de dados, mas
aqui iremos apenas instanciar os objetos e armazen-los em memria numa lista.
Veremos o cdigo na ntegra e depois os comentrios.
#user.py
class User(object):
seq = 0
objects = []
def __init__(self, nome, idade):
self.id = None
self.nome = nome
self.idade = idade
def save(self):
self.__class__.seq += 1
self.id = self.__class__.seq
self.__class__.objects.append(self)
def __str__(self):
return self.nome
def __repr__(self):
return '<{}: {} - {} - {}>\n'.format(self.__class__.__name__, self.id,
self.nome, self.idade)
@classmethod
def all(cls):
return cls.objects
if __name__ == '__main__':
u1 = User('Regis', 35)
u1.save()
u2 = User('Fabio', 20)
u2.save()
print(User.all())

Podemos rodar o Python no modo modo interativo pelo terminal e importar a classe (veja este
video).
$ python3
>>> from user import User
>>> u1 = User('Regis', 35)
>>> u1.save()
>>> u2 = User('Fabio',20)
>>> u2.save()
>>> print(User.all())

Agora os comentrios:
Definindo a classe
class User(object):

Define um atributo que servir como contador inicial e um atributo objects (tupla vazia) que
uma lista de instncias de User que foram salvos (que chamaram o mtodo save).
seq = 0
objects = []

Atribui um valor inicial aos atributos no momento da chamada do construtor.


def __init__(self, nome, idade):

Inicializando os atributos, id comea com None, pois a instncia foi criada mas ainda no foi
salva.
self.id = None
self.nome = nome
self.idade = idade

Mtodo para salvar os dados ele incrementa o atributo de classe que conta quantas instncias foram
salvas e adiciona a instncia na lista de objects.
def save(self):

self.__class__ acessa a classe que criou a instncia, assim possvel acessar o atributo de
seq. Aqui poderia ser usado User.seq, porm caso User fosse herdado, o seq seria o de User
e no da classe filha.
self.__class__.seq += 1
self.id = self.__class__.seq

Da mesma forma que acessamos seq, acessamos objects e feito um append com a instncia.
self.__class__.objects.append(self)

Retorna uma representao do objeto como str, usado em converses para string. Exemplo:
str(my_user), print my_user.
def __str__(self):
return self.nome

Retorna uma representao do objeto usada para outros objetos. Exemplo: quando convertida uma
lista de user para string.
def __repr__(self):

self.__class__.__name__ a forma de acessar o nome da classe que gerou a instncia.


return '<{}: {} - {} - {}>\n'.format(self.__class__.__name__, self.id,
self.nome, self.idade)

Class method usado para acessar todas as instncias salvas (que chamaram o mtodo save). Aqui
usamos um @classmethod, pois faz mais sentido ser um mtodo de classe do que de instncia,
pois estamos retornando informaes da classe e no de uma instncia isolada.
@classmethod
def all(cls):
return cls.objects

Demonstrao do uso da classe.


if __name__ == '__main__':
u1 = User('Regis', 35)
u2 = User('Fabio',20)
print(User.all())

Note que nesse print a lista est vazia.


u1.save()
u2.save()
print(User.all())

Aps chamar o save para as duas instncias elas so guardadas e o mtodo User.all() retorna
essa lista.

Guia rpido de comandos SQLite3


sempre bom ter tudo que voc precisa de forma rpida e simples.
Criando uma tabela
CRUD
Backup
Relacionando tabelas
Escrevi este post para um mini tutorial de SQLite3. Atravs do terminal:

Criando uma tabela


1. Criando um banco de dados.
$ sqlite3 Clientes.db

2. A Ajuda.
sqlite> .help

3. Criando a tabela clientes.


sqlite>
...>
...>
...>
...>
...>
...>
...>

CREATE TABLE clientes(


id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
Nome VARCHAR(100) NOT NULL,
CPF VARCHAR(11) NOT NULL,
Email VARCHAR(20) NOT NULL,
Fone VARCHAR(20),
UF VARCHAR(2) NOT NULL
);

Nota: Se usamos AUTOINCREMENT no precisamos do NOT NULL.


sqlite> CREATE TABLE clientes(
...> id INTEGER PRIMARY KEY AUTOINCREMENT,
...> ...

4. Visualizando o cdigo SQL que criou a tabela.


sqlite> .schema clientes

5. Visualizando todas as tabelas existentes.


sqlite> .table

6. Saindo do SQLite3.
sqlite> .exit

CRUD
Abra um editor de texto e salve um arquivo com o nome inserirdados.sql.
$ gedit inserirdados.sql

E digite a insero de alguns dados.


INSERT INTO clientes
'1100000000', 'SP');
INSERT INTO clientes
'1112345678', 'RJ');
INSERT INTO clientes
'1187654321', 'SP');
INSERT INTO clientes
'1199999999', 'RJ');

VALUES(1, 'Regis', '00000000000', 'rg@email.com',


VALUES(2, 'Abigail', '11111111111', 'abigail@email.com',
VALUES(3, 'Benedito', '22222222222', 'benedito@email.com',
VALUES(4, 'Zacarias', '33333333333', 'zacarias@email.com',

Nota: No caso do INSERT INTO no precisamos numerar, basta trocar o nmero do id por
NULL, exemplo:
INSERT INTO clientes VALUES(NULL, 'Carlos', '99999999999', 'carlos@email.com',
'118888-8888', 'SP');

7. Importe estes comandos no sqlite.


$ sqlite3 Clientes.db < inserirdados.sql

8. Abra o SQLite3 novamente, e visualize os dados.


$ sqlite3 Clientes.db
sqlite> SELECT * FROM clientes;

9. Voc pode exibir o nome das colunas digitando


sqlite> .header on

10.Para escrever o resultado num arquivo externo digite


sqlite> .output resultado.txt
sqlite> SELECT * FROM clientes;
sqlite> .exit
$ cat resultado.txt

11.Adicionando uma nova coluna na tabela clientes.


sqlite> ALTER TABLE clientes ADD COLUMN bloqueado BOOLEAN;

No SQLite3 os valores para boolean so 0 (falso) e 1 (verdadeiro).


12.Visualizando as colunas da tabela clientes.
sqlite> PRAGMA table_info(clientes);

13.Alterando os valores do campo bloqueado.


sqlite> UPDATE clientes SET bloqueado=0; -- comentario: Atualiza todos os
registros para Falso.
sqlite> UPDATE clientes SET bloqueado=1 WHERE id=1; -- Atualiza apenas o
registro com id=1 para Verdadeiro.
sqlite> UPDATE clientes SET bloqueado=1 WHERE UF='RJ'; -- Atualiza para
Verdadeiro todos os registros com UF='RJ'.

Faa um SELECT novamente para ver o resultado.


14.Deletando registros.
sqlite> DELETE FROM clientes WHERE id=4;

Cuidado: se voc no usar o WHERE e escolher um id voc pode deletar todos os registros da
tabela.
15.Voc pode exibir os dados na forma de coluna.
sqlite> .mode column

Backup
$ sqlite3 Clientes.db .dump > clientes.sql
$ cat clientes.sql
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE clientes(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
Nome VARCHAR(100) NOT NULL,
CPF VARCHAR(11) NOT NULL,
Email VARCHAR(20) NOT NULL,
Fone VARCHAR(20),
UF VARCHAR(2) NOT NULL
);
INSERT INTO "clientes"
VALUES(1,'Regis','00000000000','rg@email.com','1100000000','SP');
INSERT INTO "clientes"
VALUES(2,'Abigail','11111111111','abigail@email.com','1112345678','RJ');
INSERT INTO "clientes"
VALUES(3,'Benedito','22222222222','benedito@email.com','1187654321','SP');
INSERT INTO "clientes"
VALUES(4,'Zacarias','33333333333','zacarias@email.com','1199999999','RJ');
COMMIT;

Pronto, se corromper o seu banco de dados, voc pode recuper-lo:


$ mv Clientes.db Clientes.db.old
$ sqlite3 Clientes_recuperado.db < clientes.sql
$ sqlite3 Clientes_recuperado.db 'SELECT * FROM clientes;'

Faa um SELECT novamente para ver o resultado do novo banco de dados.

Relacionando tabelas
Todos devem saber que num banco de dados relacional a chave estrangeira ou FOREIGN KEY
tem um papel importante no relacionamento entre duas tabelas. Veremos aqui como relacionar duas
tabelas.
Primeiros faamos um backup do nosso bd.
$ sqlite3 Clientes.db .dump > clientes.sql

Apenas para relembrar, vamos ver qual a nossa tabela...


$ sqlite3 Clientes.db
sqlite> .tables
clientes
sqlite> .header on
sqlite> .mode column

E quais so seus registros.


sqlite> SELECT * FROM clientes;
id
bloqueado
------------------1
2
3

Nome

CPF

Email

Fone

UF

----------

-----------

------------

----------

----------

Regis
Abigail
Benedito

00000000000
11111111111
22222222222

rg@email.com
abigail@emai
benedito@ema

1100000000
1112345678
1187654321

SP
RJ
SP

1
1
0

Ento vamos criar duas novas tabelas: cidades e clientes_novo.


sqlite>
...>
...>
...>
...>
...>
...>
...>
...>
...>
...>
...>
...>
...>
...>

CREATE TABLE cidades(


id INTEGER PRIMARY KEY AUTOINCREMENT,
cidade TEXT,
uf VARCHAR(2)
);
CREATE TABLE clientes_novo(
id INTEGER PRIMARY KEY AUTOINCREMENT,
Nome VARCHAR(100) NOT NULL,
CPF VARCHAR(11) NOT NULL,
Email VARCHAR(20) NOT NULL,
Fone VARCHAR(20),
bloqueado BOOLEAN,
cidade_id INTEGER,
FOREIGN KEY (cidade_id) REFERENCES cidades(id)
);

Segundo Sqlite Drop Column, no tem como "deletar" uma coluna, ento precisamos criar uma
nova tabela clientes_novo com os campos que precisamos e copiar os dados da primeira tabela para
esta.
sqlite> INSERT INTO clientes_novo (id, Nome, CPF, Email, Fone, bloqueado)
...> SELECT id, Nome, CPF, Email, Fone, bloqueado FROM clientes;

Veja que selecionamos os campos da tabela clientes e a inserimos em clientes_novo. Note que no
copiamos o campo UF porque agora ele da tabela cidades.
Agora podemos deletar a tabela "antiga".

sqlite> DROP TABLE clientes;

E renomear a nova tabela.


sqlite> ALTER TABLE clientes_novo RENAME TO clientes;

Veja o resultado da nova tabela.


sqlite> SELECT * FROM clientes;
id
cidade_id
------------------1
2
3

Nome

CPF

Email

Fone

bloqueado

----------

-----------

------------

----------

----------

Regis
Abigail
Benedito

00000000000
11111111111
22222222222

rg@email.com
abigail@emai
benedito@ema

1100000000
1112345678
1187654321

1
1
0

Agora voc ter que popular as cidades e definir a cidade_id em cada cliente. Lembrando que a
chave AUTOINCREMENT, ento use NULL.
sqlite> INSERT INTO cidades VALUES (NULL,'Campinas','SP');
sqlite> INSERT INTO cidades VALUES (NULL,'Sao Paulo','SP');
sqlite> INSERT INTO cidades VALUES (NULL,'Rio de Janeiro','RJ');

Veja os registros da tabela cidades.


sqlite> SELECT * FROM cidades;
id
---------1
2
3

cidade
---------Campinas
Sao Paulo
Rio de Jan

uf
---------SP
SP
RJ

Agora precisamos atualizar a cidade_id de cada cliente.


sqlite> UPDATE clientes SET cidade_id = 3 WHERE id = 1;
sqlite> UPDATE clientes SET cidade_id = 1 WHERE id = 2;
sqlite> UPDATE clientes SET cidade_id = 2 WHERE id = 3;

Resultado.
sqlite> SELECT * FROM clientes;
id
cidade_id
------------------1
2
3

Nome

CPF

Email

Fone

bloqueado

----------

-----------

------------

----------

----------

Regis
Abigail
Benedito

00000000000
11111111111
22222222222

rg@email.com
abigail@emai
benedito@ema

1100000000
1112345678
1187654321

1
1
0

Faamos um INNER JOIN para visualizar todos os dados, inclusive a cidade e o uf.
sqlite> SELECT * FROM clientes INNER JOIN cidades ON clientes.cidade_id =
cidades.id;
id
cidade_id

Nome
cidade

CPF

Email
uf

Fone

bloqueado

3
1
2

---------- ---------- -------------------- -------------- -1


Regis
00000000000
Rio de Janeiro RJ
2
Abigail
11111111111
Campinas
SP
3
Benedito
22222222222
Sao Paulo
SP

------------

----------

----------

rg@email.com

1100000000

abigail@emai

1112345678

benedito@ema

1187654321

Json View - Django class-based generic views


ntroduo
# Se quer saber primeiro sobre class based generic views e esta srie de artigos que estou fazendo,
basta ler este trecho do primeiro artigo da srie.
Vamos imaginar que voc precisa serializar a Response de uma view tua para utilizar os dados e
trabalh-los com Ajax, e/ou para criar uma API, ou seja l qual for o motivo, se voc usa as class
based generic views do Django fica mais fcil e elegante. Neste artigo veremos um exemplo
simples de como faz-lo.
Reforando novamente que tomo por base o cdigo do repositrio que criei no github para os
exemplos.

Estendendo uma ListView para criarmos nossa JsonView


O arquivo views.py, ou em nosso caso list_and_detail.py, tem uma view que lista os objetos de uma
"lista de reproduo", como abaixo:
from django.views.generic importListView
from app_exemplo.models importListaDeReproducao
class ListasDeReproducao(ListView):
model = ListaDeReproducao
Com o exemplo acima teremos uma lista de objetos de

ListaDeReproducao

, porm o retorno ser

em text/html do modo convencional, em que precisamos renderizar um template e tudo mais.


Para aproveitarmos possveis comportamentos, personalizaes e/ou mesmo as definies dos
atributos j contidos em ListaDeReproducao vamos estender esta view e fazer a modificao no
mtodo

get(self, request, *args, **kwargs)

para termos um response application/json.

Em views.json (json.py) eu tenho isto:


# coding: utf-8
from django.http import HttpResponse
from django.core import serializers
from list_and_detail import ListasDeReproducao
class ListasDeReproducaoJson(ListasDeReproducao):

context_object_name = 'listas_de_reproducao'
def get(self, request, *args, **kwargs):
data = serializers.serialize("json", self.get_queryset())
return HttpResponse(data, content_type='application/json')
O contedo desta resposta o retorno de self.get_queryset() j serializado no formato JSON. Algo
como:
[
{
"pk":1,
"model":"app_exemplo.listadereproducao",
"fields":{
"modificado":"2010-03-13 12:12:45",
"audios":[
1,
2,
3
],
"criado":"2010-01-26 15:25:26",
"titulo":"Animiais",
"slug":"Animais",
"descricao":"Alguns efeitos sonoros de animais."
}
},
{
"pk":2,
"model":"app_exemplo.listadereproducao",
"fields":{
"modificado":"2010-05-30 08:16:53",
"audios":[
4,
5,
6,
7
],
"criado":"2010-01-05 14:12:28",
"titulo":"Clicks",
"slug":"clicks",
"descricao":"Efeitos sonoros de CLICKs."
}
},
// ...
]

Para um exemplo isto serve, mas talvez voc realmente queira personalizar isto antes de transformlo em Json. Por exemplo se voc quer serializar um queryset de usurios, dependendo de como sua
query foi composta, pode trazer o campo 'senha' e, talvez, voc queira omit-lo deste retorno.
Contudo creio que no haver grandes problemas para fazer isto, uma vez que com
self.get_queryset() voc consegue manipular bem estes resultados, antes de serializ-los.

Concluso
Bem, este foi um exemplo simples, em um outro artigo creio que farei algo como retornar o Json e
trabalhar ele com Jquery.
Extra:
Se voc quisesse, ao invs de Json, retornar um XML, bastaria modificar duas simples coisas, neste
simples exemplo, claro:
# ...
serializers.serialize("xml", self.get_queryset())
return HttpResponse(data, content_type='application/xml')

Use Django's Class-Based Views with Bootstrap


Modals
Twitter Bootstrap contains an exhaustive set of CSS classes, components, and jQuery plugins that
are responsive across a broad array of devices. When combined with the Django web framework,
creating responsive, data-driven websites becomes quick and easy for Python developers.
The modal dialog box is one of the components Bootstrap provides. Since Djangos class-based
views makes it easy to define templates and forms with complex validation rules, using Django to
generate the contents of these modal dialog boxes has become a common task.

Many different methods exist for accomplishing this task, but these solutions often require
duplicating template code, dont address rendering the same view as both a standalone page and the
contents of a modal, or dont account for redirects when submitting forms.

I recently found myself trying to render a Django view as both a modal and a standalone page and
had the following requirements:
minimize code repetition
update the modal with any form errors
close the modal on successful submission
I was able to accomplish this task with a few lines of Python, a few lines of JavaScript, and a minor
template change.

Server-Side Changes
The server-side changes consisted of creating an AjaxTemplateMixin to render a different
template for AJAX requests and making a small change to an existing template.

An AjaxTemplateMixin
1 class AjaxTemplateMixin(object):
2
3
def dispatch(self, request, *args, **kwargs):
4
if not hasattr(self, 'ajax_template_name'):
5
split = self.template_name.split('.html')
6
split[-1] = '_inner'
7
split.append('.html')
8
self.ajax_template_name = ''.join(split)
9
if request.is_ajax():
10
self.template_name = self.ajax_template_name
11
return super(AjaxTemplateMixin, self).dispatch(request, *args,
**kwargs)

The first step required writing a mixin to add an ajax_template_name attribute Djangos
class-based views. If this attribute is not explicitly defined, it will default to adding _inner to the
end of the template_name attribute. For example, take the following FormView class:
1 class TestFormView(SuccessMessageMixin, AjaxTemplateMixin, FormView):
2
template_name = 'test_app/test_form.html'
3
form_class = TestForm
4
success_url = reverse_lazy('home')
5
success_message = "Way to go!"

In this example, the ajax_template_name defaults to


test_app/test_form_inner.html. If the request is AJAX, then the view renders this
template. Otherwise, the view renders the test_app/test_form.html template.

Create the AJAX Template


Now that the view will render ajax_template_name for AJAX requests we have to create it.
This template could be unique, but more than likely it will be the same as template_name but
without extending the base template containing the sites header, navigation, and footer. This could
be as simple as changing test_app/test_form.html from:
1 {% extends 'test_app/home.html' %}
2
3 {% block content %}

4 {% load crispy_forms_tags %}
5 <div class="row">
6
<form class="form-horizontal" action="{% url 'test-form' %}"
method="post">
7
{% crispy form %}
8
<input type="submit" class="btn btn-submit col-md-offset-2">
9
</form>
10 </div>
11 {% endblock content %}

to:
1
2
3
4
5

{% extends 'test_app/home.html' %}
{% block content %}
{% include 'test_app/test_form_inner.html' %}
{% endblock content %}

and creating test_app/test_form_inner.html containing:


1 {% load crispy_forms_tags %}
2 <div class="row">
3
<form class="form-horizontal" action="{% url 'test-form' %}"
method="post">
4
{% crispy form %}
5
<input type="submit" class="btn btn-submit col-md-offset-2">
6
</form>
7 </div>

All weve done here is moved the HTML within the content block to its own template. The example
template uses django-crispy-forms to generate the form markup using Bootstrap CSS classes but
this is not a requirement.

Front-end Changes
At this point, rendering your view is easy, unless it contains a form.

Rendering a View in a Modal the Easy Way


Given the following modal in your HTML:
1 <div class="modal fade" id="form-modal" tabindex="-1" role="dialog" arialabelledby="myModalLabel" aria-hidden="true">
2
<div class="modal-dialog">
3
<div class="modal-content">
4
<div class="modal-header">
5
<button type="button" class="close" data-dismiss="modal" ariahidden="true">&times;</button>
6
<h4 class="modal-title">Modal title</h4>
7
</div>
8
<div id="form-modal-body" class="modal-body">
9
...
10
</div>
11
<div class="modal-footer">
12
<button type="button" class="btn btn-default" datadismiss="modal">Close</button>
13
</div>
14
</div>
15
</div>
16 </div>

rendering a Django view in it can be as simple as adding:


1 <a data-toggle="modal" href="{% url 'test-form' %}" data-target="#formmodal">Click me</a>

to your template. Behind the scenes, Bootstrap is using the data attributes to call jQuerys
.load() method to make an AJAX call to the test-form url and replace the HTML within
#form-modal. However, there are a couple problems with this:
Using data attributes replaces the entire contents of the modal, so your template will need to
contain the .modal-dialog, .modal-content and .modal-body DIVs to render
properly.
jQuerys .load() is only called once the first time the modal is opened.
Any redirects that occur, such as from submitting a form, will redirect the entire page.
If none of this matters to you, then great, youre done! Otherwise, keep reading.

Rendering a View in a Modal the Slightly Harder Way


The following JavaScript solves the problems above:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

var formAjaxSubmit = function(form, modal) {


$(form).submit(function (e) {
e.preventDefault();
$.ajax({
type: $(this).attr('method'),
url: $(this).attr('action'),
data: $(this).serialize(),
success: function (xhr, ajaxOptions, thrownError) {
if ( $(xhr).find('.has-error').length > 0 ) {
$(modal).find('.modal-body').html(xhr);
formAjaxSubmit(form, modal);
} else {
$(modal).modal('toggle');
}
},
error: function (xhr, ajaxOptions, thrownError) {
// handle response errors here
}
});
});
}
$('#comment-button').click(function() {
$('#form-modal-body').load('/test-form/', function () {
$('#form-modal').modal('toggle');
formAjaxSubmit('#form-modal-body form', '#form-modal');
});
}

This code binds to the click event on #comment-button and loads the /test-form/ HTML
asynchronously into the body of the modal. Since this is an AJAX call, the
test_form/test_form_inner.html template will be rendered and the form will be
displayed without any site navigation or footer.
Additionally, this code also calls formAjaxSubmit(). This function binds to the forms submit
event. By calling preventDefault(), the callback function prevents the form from performing

its default submit action. Instead, the forms content is serialized and sent via an AJAX call using
the forms defined action and method.
If the server sends back a successful response, the success function is called. The xhr parameter
contains the HTML received from the server. Note that a successful response from the server does
not mean that the form validated successfully. Therefore, xhr is checked to see if it contains any
field errors by looking for the has_error Bootstrap class in its contents. If any errors are found,
the modals body is updated with the form and its errors. Otherwise, the modal is closed.

class Signup(CreateView):
model = User
fields = ['first_name', 'last_name', 'email', 'password']
def get_form(self, form_class):
form = super(Signup, self).get_form(form_class)
form.fields['password'].widget = forms.PasswordInput()
return form
from django import forms
class SignupForm(forms.ModelForm):
class Meta:
model = User
fields = ['first_name', 'last_name', 'email', 'password']
widgets = {
'password': forms.PasswordInput()
}
class Signup(CreateView):
form_class = SignupForm
model = User

Summary
In conclusion, we were able to define our form and view in Django and render it correctly in a

Bootstrap modal by adding one short Django mixin, making a small change to an existing template,
and adding a few lines of JavaScript.

Вам также может понравиться