Django Weblog: DjangoCon Australia 2019: Tickets on sale 🎟️

For the 7th year running, DjangoCon Australia is coming up on August 2nd. Just like last year, the sibling conference to DjangoCons EU and US, is on in Sydney at the International Convention Centre.

DjangoCon Australia is a one-day event, organized as a specialist track as part of PyCon AU. Packed with talks about best practices, communities, contributions, and the present and future of Django, DjangoCon Australia 2019 will be bigger than ever.

There are still tickets available for DjangoCon Australia and PyCon AU. You can join for one day with tickets starting at AU$ 150 for just the DjangoCon AU day, or AU$ 490 for all three days. We also have significant discounts for student attendees, and we also have Contributor ✨ tickets for those who want to help financially support the conference.

The schedule for DjangoCon Australia and all of PyCon AU is already live, so take a look at what we have in store.

Buy your ticket before July 9 to ensure you get one of the famous PyCon AU t-shirts in a size that fits you. Shirts for DjangoCon Australia will be revealed and details announced on the day.

We hope to see you in Sydney next month!

Leigh Brenecki, Markus Holtermann, DjangoCon Australia organizers

Planet Python

PythonClub – A Brazilian collaborative blog about Python: Tutorial Django 2.2

Este tutorial é baseado no Intro to Django que fica na parte de baixo da página start do Django project.

Até a data deste post o Django está na versão 2.2.2, e requer Python 3.

O que você precisa?

Python 3.6 ou superior, pip e virtualenv.

Considere que você tenha instalado Python 3.6 ou superior, pip e virtualenv.

Criando o ambiente

Crie uma pasta com o nome django2-pythonclub

$   mkdir django2-pythonclub $   cd django2-pythonclub 

A partir de agora vamos considerar esta como a nossa pasta principal.

Considerando que você está usando Python 3, digite

python3 -m venv .venv 

Lembre-se de colocar esta pasta no seu .gitignore, caso esteja usando.

echo .venv >> .gitignore 

Depois ative o ambiente digitando

source .venv/bin/activate 

Lembre-se, sempre quando você for mexer no projeto, tenha certeza de ter ativado o virtualenv, executando o comando source .venv/bin/activate. Você deve repetir esse comando toda a vez que você abrir um novo terminal.

Instalando Django 2.2.2

Basta digitar

pip install django==2.2.2 

Dica: se você digitar pip freeze você verá a versão dos programas instalados.

É recomendável que você atualize a versão do pip

pip install -U pip 

Se der erro então faça:

python -m pip install --upgrade pip 

Instalando mais dependências

Eu gosto de usar o django-extensions e o django-widget-tweaks, então digite

pip install django-extensions django-widget-tweaks python-decouple 

Importante: você precisa criar um arquivo requirements.txt para instalações futuras do projeto em outro lugar.

pip freeze > requirements.txt 

Este é o resultado do meu até o dia deste post:

(.venv):$   cat requirements.txt   django-extensions==2.1.6 django-widget-tweaks==1.4.3 python-decouple==3.1 pytz==2018.9 six==1.12.0 

Escondendo a SECRET_KEY e trabalhando com variáveis de ambiente

É muito importante que você não deixe sua SECRET_KEY exposta. Então remova-o imediatamente do seu settings.py ANTES mesmo do primeiro commit. Espero que você esteja usando Git.

Vamos usar o python-decouple escrito por Henrique Bastos para gerenciar nossas variáveis de ambiente. Repare que já instalamos ele logo acima.

Em seguida você vai precisar criar um arquivo .env, para isso rode o comando a seguir, ele vai criar uma pasta contrib e dentro dele colocar um arquivo env_gen.py

if [ ! -d contrib ]; then mkdir contrib; fi; git clone https://gist.github.com/22626de522f5c045bc63acdb8fe67b24.git contrib/ rm -rf contrib/.git/  # remova a pasta .git que está dentro de contrib. 

Em seguida rode

python contrib/env_gen.py 

que ele vai criar o arquivo .env.

Supondo que você está versionando seu código com Git, é importante que você escreva isso dentro do seu arquivo .gitignore, faça direto pelo terminal

echo .env >> .gitignore echo .venv >> .gitignore echo '*.sqlite3' >> .gitignore 

Pronto, agora você pode dar o primeiro commit.

Criando o projeto e a App

Para criar o projeto digite

$   django-admin startproject myproject . 

repare no ponto no final do comando, isto permite que o arquivo manage.py fique nesta mesma pasta django2-pythonclub .

Agora vamos criar a app bands, mas vamos deixar esta app dentro da pasta myproject. Então entre na pasta

$   cd myproject 

e digite

$   python ../manage.py startapp bands 

A intenção é que os arquivos tenham a seguinte hierarquia nas pastas:

. ├── manage.py ├── myproject │   ├── bands │   │   ├── admin.py │   │   ├── apps.py │   │   ├── models.py │   │   ├── tests.py │   │   └── views.py │   ├── settings.py │   ├── urls.py │   └── wsgi.py └── requirements.txt 

Agora permaneça sempre na pasta django2-pythonclub

cd .. 

e digite

$   python manage.py migrate 

para criar a primeira migração (isto cria o banco de dados SQLite), e depois rode a aplicação com

$   python manage.py runserver 

e veja que a aplicação já está funcionando. Veja o endereço da url aqui

Django version 2.2.2, using settings 'myproject.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C. 

Editando settings.py

Em INSTALLED_APPS acrescente as linhas abaixo.

INSTALLED_APPS = (     ...     'widget_tweaks',     'django_extensions',     'myproject.bands', ) 

E mude também o idioma.

LANGUAGE_CODE = 'pt-br'

E caso você queira o mesmo horário de Brasília-BR

TIME_ZONE = 'America/Sao_Paulo'

Já que falamos do python-decouple, precisamos de mais alguns ajustes

from decouple import config, Csv  # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = config('SECRET_KEY')  # SECURITY WARNING: don't run with debug turned on in production! DEBUG = config('DEBUG', default=False, cast=bool)  ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=[], cast=Csv()) 

Veja que é importante manter sua SECRET_KEY bem guardada (em outro lugar).

Então crie um arquivo .env e guarde sua SECRET_KEY dentro dele, exemplo:

SECRET_KEY=your_secret_key DEBUG=True ALLOWED_HOSTS=127.0.0.1,.localhost 

Editando models.py

from django.db import models from django.urls import reverse_lazy   class Band(models.Model):      """A model of a rock band."""     name = models.CharField(max_length=200)     can_rock = models.BooleanField(default=True)      class Meta:         ordering = ('name',)         verbose_name = 'band'         verbose_name_plural = 'bands'      def __str__(self):         return self.name      def get_absolute_url(self):         # retorna a url no formato /bands/1/         return reverse_lazy('band_detail', kwargs={'pk': self.pk})      def get_members_count(self):         # count members by band         # conta os membros por banda         return self.band.count()   class Member(models.Model):      """A model of a rock band member."""     name = models.CharField("Member's name", max_length=200)     instrument = models.CharField(choices=(         ('g', "Guitar"),         ('b', "Bass"),         ('d', "Drums"),         ('v', "Vocal"),         ('p', "Piano"),     ),         max_length=1     )      band = models.ForeignKey("Band", related_name='band', on_delete=models.CASCADE)      class Meta:         ordering = ('name',)         verbose_name = 'member'         verbose_name_plural = 'members'      def __str__(self):         return self.name 

Tem algumas coisas que eu não estou explicando aqui para o tutorial ficar curto, mas uma coisa importante é que, como nós editamos o models.py vamos precisar criar um arquivo de migração do novo modelo. Para isso digite

python manage.py makemigrations python manage.py migrate 

O primeiro comando cria o arquivo de migração e o segundo o executa, criando as tabelas no banco de dados.

Editando urls.py

from django.urls import include, path from myproject.bands import views as v from django.contrib import admin  app_name = 'bands'  urlpatterns = [     path('', v.home, name='home'),     # path('bands/', v.band_list, name='bands'),     # path('bands/<int:pk>/', v.band_detail, name='band_detail'),     # path('bandform/', v.BandCreate.as_view(), name='band_form'),     # path('memberform/', v.MemberCreate.as_view(), name='member_form'),     # path('contact/', v.band_contact, name='contact'),     # path('protected/', v.protected_view, name='protected'),     # path('accounts/login/', v.message),     path('admin/', admin.site.urls), ] 

Obs: deixei as demais urls comentada porque precisa da função em views.py para que cada url funcione. Descomente cada url somente depois que você tiver definido a função em classe em views.py a seguir.

Editando views.py

from django.shortcuts import render from django.http import HttpResponse from django.contrib.auth.decorators import login_required from django.views.generic import CreateView from django.urls import reverse_lazy from .models import Band, Member # from .forms import BandContactForm, BandForm, MemberForm 

Obs: Deixei a última linha comentada porque ainda não chegamos em forms.

A função a seguir retorna um HttpResponse, ou seja, uma mensagem simples no navegador.

def home(request):     return HttpResponse('Welcome to the site!') 

A próxima função (use uma ou outra) renderiza um template, uma página html no navegador.

def home(request):     return render(request, 'home.html') 

A função band_list retorna todas as bandas.

Para fazer a busca por nome de banda usamos o comando search = request.GET.get('search_box'), onde search_box é o nome do campo no template band_list.html.

E os nomes são retornados a partir do comando bands = bands.filter(name__icontains=search). Onde icontains procura um texto que contém a palavra, ou seja, você pode digitar o nome incompleto (ignora maiúsculo ou minúsculo).

def band_list(request):     """ A view of all bands. """     bands = Band.objects.all()     search = request.GET.get('search_box')     if search:         bands = bands.filter(name__icontains=search)     return render(request, 'bands/band_list.html', {'bands': bands}) 

Em urls.py pode descomentar a linha a seguir:

path('bands/', v.band_list, name='bands'), 

A função band_contact mostra como tratar um formulário na view. Esta função requer BandContactForm, explicado em forms.py.

def band_contact(request):     """ A example of form """     if request.method == 'POST':         form = BandContactForm(request.POST)     else:         form = BandContactForm()     return render(request, 'bands/band_contact.html', {'form': form}) 

Em urls.py pode descomentar a linha a seguir:

path('contact/', v.band_contact, name='contact'), 

A função band_detail retorna todos os membros de cada banda, usando o pk da banda junto com o comando filter em members.

def band_detail(request, pk):     """ A view of all members by bands. """     band = Band.objects.get(pk=pk)     members = Member.objects.all().filter(band=band)     context = {'members': members, 'band': band}     return render(request, 'bands/band_detail.html', context) 

Em urls.py pode descomentar a linha a seguir:

path('bands/<int:pk>/', v.band_detail, name='band_detail'), 

BandCreate e MemberCreate usam o Class Based View para tratar formulário de uma forma mais simplificada usando a classe CreateView. O reverse_lazy serve para tratar a url de retorno de página.

As classes a seguir requerem BandForm e MemberForm, explicado em forms.py.

class BandCreate(CreateView):     model = Band     form_class = BandForm     template_name = 'bands/band_form.html'     success_url = reverse_lazy('bands')   class MemberCreate(CreateView):     model = Member     form_class = MemberForm     template_name = 'bands/member_form.html'     success_url = reverse_lazy('bands') 

Em urls.py pode descomentar a linha a seguir:

path('bandform/', v.BandCreate.as_view(), name='band_form'), path('memberform/', v.MemberCreate.as_view(), name='member_form'), 

A próxima função requer que você entre numa página somente quando estiver logado.

[@login_required](https://docs.djangoproject.com/en/2.2/topics/auth/default/#the-login-required-decorator) é um decorator.

login_url='/accounts/login/' é página de erro, ou seja, quando o usuário não conseguiu logar.

E render(request, 'bands/protected.html',... é página de sucesso.

@login_required(login_url='/accounts/login/') def protected_view(request):     """ A view that can only be accessed by logged-in users """     return render(request, 'bands/protected.html', {'current_user': request.user}) 

HttpResponse retorna uma mensagem simples no navegador sem a necessidade de um template.

def message(request):     """ Message if is not authenticated. Simple view! """     return HttpResponse('Access denied!') 

Em urls.py pode descomentar a linha a seguir:

path('protected/', v.protected_view, name='protected'), path('accounts/login/', v.message), 

Comandos básicos do manage.py

Para criar novas migrações com base nas alterações feitas nos seus modelos

$ python manage.py makemigrations bands

Obs: talvez dê erro porque está faltando coisas de forms.py, explicado mais abaixo.

Para aplicar as migrações

$ python manage.py migrate

Para criar um usuário e senha para o admin

$ python manage.py createsuperuser

Para rodar a aplicação localmente

$ python manage.py runserver

Após criar um super usuário você pode entrar em localhost:8000/admin

Obs: Se você entrar agora em localhost:8000 vai faltar o template home.html. Explicado mais abaixo.

shell_plus

É o interpretador interativo do python rodando via terminal direto na aplicação do django.

Com o comando a seguir abrimos o shell do Django.

$ python manage.py shell

Mas se você está usando o django-extensions (mostrei como configurá-lo no settings.py), então basta digitar

$ python manage.py shell_plus

Veja a seguir como inserir dados direto pelo shell.

>>> from myproject.bands.models import Band, Member >>> # Com django-extensions não precisa fazer o import >>> # criando o objeto e salvando >>> band = Band.objects.create(name='Metallica') >>> band.name >>> band.can_rock >>> band.id >>> # criando uma instancia da banda a partir do id >>> b = Band.objects.get(id=band.id) >>> # criando uma instancia do Membro e associando o id da banda a ela >>> m = Member(name='James Hetfield', instrument='b', band=b) >>> m.name >>> # retornando o instrumento >>> m.instrument >>> m.get_instrument_display() >>> m.band >>> # salvando >>> m.save() >>> # listando todas as bandas >>> Band.objects.all() >>> # listando todos os membros >>> Member.objects.all() >>> # criando mais uma banda >>> band = Band.objects.create(name='The Beatles') >>> band = Band.objects.get(name='The Beatles') >>> band.id >>> b = Band.objects.get(id=band.id) >>> # criando mais um membro >>> m = Member(name='John Lennon', instrument='v', band=b) >>> m.save() >>> # listando tudo novamente >>> Band.objects.all() >>> Member.objects.all() >>> exit() 

Criando os templates

Você pode criar os templates com os comandos a seguir…

$   mkdir -p myproject/bands/templates/bands $   touch myproject/bands/templates/{menu,base,home}.html $   touch myproject/bands/templates/bands/{band_list,band_detail,band_form,band_contact,member_form,protected}.html 

… ou pegar os templates já prontos direto do Github.

mkdir -p myproject/bands/templates/bands wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/base.html -P myproject/bands/templates/ wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/home.html -P myproject/bands/templates/ wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/menu.html -P myproject/bands/templates/ wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_contact.html -P myproject/bands/templates/bands/ wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_detail.html -P myproject/bands/templates/bands/ wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_form.html -P myproject/bands/templates/bands/ wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/band_list.html -P myproject/bands/templates/bands/ wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/member_form.html -P myproject/bands/templates/bands/ wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/myproject/bands/templates/bands/protected.html -P myproject/bands/templates/bands/ 

forms.py

$ touch myproject/bands/forms.py

Edite o forms.py.

from django import forms from .models import Band, Member   class BandContactForm(forms.Form):     subject = forms.CharField(max_length=100)     message = forms.CharField(widget=forms.Textarea)     sender = forms.EmailField()     cc_myself = forms.BooleanField(required=False)   class BandForm(forms.ModelForm):      class Meta:         model = Band         fields = '__all__'   class MemberForm(forms.ModelForm):      class Meta:         model = Member         fields = '__all__' 

Lembra que eu deixei o código comentado em views.py?

Descomente ele por favor

from .forms import BandContactForm, BandForm, MemberForm 

admin.py

Criamos uma customização para o admin onde em members aparece um filtro por bandas.

from django.contrib import admin from .models import Band, Member   class MemberAdmin(admin.ModelAdmin):     """Customize the look of the auto-generated admin for the Member model."""     list_display = ('name', 'instrument')     list_filter = ('band',)   admin.site.register(Band)  # Use the default options admin.site.register(Member, MemberAdmin)  # Use the customized options 

Carregando dados de um CSV

Vamos baixar alguns arquivos para criar os dados no banco a partir de um CSV.

wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/create_data.py mkdir fix wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/fix/bands.csv -P fix/ wget https://raw.githubusercontent.com/rg3915/django2-pythonclub/master/fix/members.csv -P fix/ 

Estando na pasta principal, rode o comando

python create_data.py 

que ele vai carregar alguns dados pra você.

Veja o código de create_data.py.

Veja o código completo em https://github.com/rg3915/django2-pythonclub

git clone https://github.com/rg3915/django2-pythonclub.git

Planet Python