Quantcast
Channel: Planet Python
Viewing all articles
Browse latest Browse all 24332

PythonClub - A Brazilian collaborative blog about Python: Criando novos comandos no django-admin

$
0
0

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 já tem um bocado de comandos interessantes, os mais utilizados são:

  • startproject - cria novos projetos.
  • startapp - cria novas apps.
  • makemigrations - cria novas migrações baseadas nas mudanças detectadas nos modelos Django.
  • migrate - sincroniza o banco de dados com as novas migrações.
  • createsuperuser - cria novos usuários.
  • test - roda os testes da aplicação.
  • loaddata - carrega dados iniciais a partir de um json, por exemplo, python manage.py loaddata fixtures.json
  • shell - 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 - 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.

Começando do começo

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

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 informações em [module-argparse][19].

fromdjango.core.management.baseimportBaseCommand,CommandErrorfromoptparseimportmake_optionclassCommand(BaseCommand):help='Texto de ajuda aqui.'option_list=BaseCommand.option_list+(make_option('--awards','-a',action="store_true",help='Ajuda da opção aqui.'),)defhandle(self,**options):self.stdout.write('Hello world.')ifoptions['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.

$ python manage.py hello
Hello World

--awardsé um argumento opcional, você também 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 são baseados em **options.

Veja uma outra forma de escrever

fromdjango.core.management.baseimportBaseCommand,CommandErrorclassCommand(BaseCommand):defadd_arguments(self,parser):# Argumento nomeado (opcional)parser.add_argument('--awards','-a',action='store_true',help='Ajuda da opção aqui.')defhandle(self,*args,**options):self.stdout.write('Hello world.')ifoptions['awards']:self.stdout.write('Awards')

A diferença é que aqui usamos parser.add_argument ao invés de make_option.

hello.py

fromdjango.core.management.baseimportBaseCommand,CommandError# minimalistaclassCommand(BaseCommand):help='Print hello world'defhandle(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

fromdjango.dbimportmodelsclassMovie(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)classMeta:ordering=['title']verbose_name='filme'verbose_name_plural='filmes'def__str__(self):returnself.title

Não se esqueça de fazer

python manage.py makemigrations
python manage.py migrate

admin.py

Vamos visualizar pelo admin.

fromdjango.contribimportadminfromcore.modelsimportMovieadmin.site.register(Movie)

Instale o requests

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.
# -*- coding: utf-8 -*- #importrandomimportstringimportrequestsfromdjango.core.management.baseimportBaseCommand,CommandErrorfromdjango.core.exceptionsimportValidationErrorfromoptparseimportmake_optionfromcore.modelsimportMovieclassCommand(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 lançamento do filme.'),)defprint_red(self,name):"""imprime em vermelho"""print("\033[91m {}\033[00m".format(name))defget_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 aleatoriamenteletters=''.join(random.choice(string.ascii_lowercase)for_inrange(2))# Se não for definido o ano, então escolhe um randomicamenteifyearisNone: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))returnrequests.get(url).json()defget_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.whilemovie['Response']=='False'andj<100:movie=self.get_html(year)self.print_red('Tentanto %d vezes\n'%j)j+=1returnmoviedefsave(self,**kwargs):"""SALVA os dados"""try:Movie.objects.create(**kwargs)exceptValidationErrorase:self.print_red(e.messages)self.print_red('O objeto não foi salvo.\n')defhandle(self,movies,year,**options):""" se "movies" não for nulo, transforma em inteiro """self.verbosity=int(options.get('verbosity'))ifmoviesisnotNone:movies=int(movies)# busca os filmes n vezes, a partir da variavel "movies"foriinrange(movies):# verifica as validaçõesm=self.get_movie(year)ifm['imdbRating']=="N/A":m['imdbRating']=0.0# Transforma "year" em inteiroif"–"inm['Year']:m['Year']=yeardata={"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)ifself.verbosity>0:self.stdout.write('\n {0} {1} {2}'.format(i+1,data['year'],data['title']))ifself.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 título ou ano de lançamento.

fromdjango.core.management.baseimportBaseCommand,CommandErrorfromoptparseimportmake_optionfromcore.modelsimportMovieclassCommand(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.'),)defhandle(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:valueforkey,valueinfilters.items()ifvalueisnotNone}queryset=Movie.objects.filter(**filter_by)ifself.verbosity>0:formovieinqueryset: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 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 tem um exemplo legal que eu usei como ideia pra fazer este post.

Mais algumas referências:

Writing custom django-admin commands

Zachary Voase: Fixing Django Management Commands

Adding Custom Commands to manage.py and django-admin.py by dave


Viewing all articles
Browse latest Browse all 24332

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>