{ "metadata": { "name": "" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "[Python para Desenvolvedores](http://ricardoduarte.github.io/python-para-desenvolvedores/#conteudo)\n", "===================================\n", "2ª edi\u00e7\u00e3o, revisada e ampliada\n", "-----------------------------------\n", "\n", "Cap\u00edtulo 31: MVC\n", "=============================\n", "_____________________________\n", "*Model-view-controller* (MVC) \u00e9 uma arquitetura de software que divide a aplica\u00e7\u00e3o em tr\u00eas partes distintas: o modelo de dados da aplica\u00e7\u00e3o, a interface com o usu\u00e1rio e a l\u00f3gica de controle.\n", "\n", "O objetivo \u00e9 obter um baixo acoplamento entre as tr\u00eas partes de forma que uma altera\u00e7\u00e3o em uma parte tenha pouco (ou nenhum) impacto nas outras partes.\n", "\n", "![Model View Controller](files/bpypd_diags27.png)\n", "\n", "A cria\u00e7\u00e3o da aplica\u00e7\u00e3o depende da defini\u00e7\u00e3o de tr\u00eas componentes:\n", "\n", "+ Modelo (*model*): encapsula os dados da aplica\u00e7\u00e3o e a l\u00f3gica de dom\u00ednio.\n", "+ Vis\u00e3o (*view*): recupera dados do modelo e apresenta ao usu\u00e1rio.\n", "+ Controlador (*controller*): recebe e reage a poss\u00edveis eventos, como intera\u00e7\u00f5es com o usu\u00e1rio e requisita altera\u00e7\u00f5es no modelo e na vis\u00e3o.\n", "\n", "Embora a arquitetura n\u00e3o determine formalmente a presen\u00e7a de um componente de persist\u00eancia, fica impl\u00edcito que este faz parte do componente modelo.\n", "\n", "O uso mais comum para o modelo MVC \u00e9 em aplica\u00e7\u00f5es *Web* baseadas em bancos de dados, que implementam as opera\u00e7\u00f5es b\u00e1sicas chamadas CRUD (*Create, Read, Update and Delete*).\n", "\n", "Existem v\u00e1rios frameworks para aumentar a produtividade na cria\u00e7\u00e3o de aplicativos seguindo o MVC, com recursos como:\n", "\n", "+ *Scripts* que automatizam as tarefas mais comuns de desenvolvimento.\n", "+ Gera\u00e7\u00e3o autom\u00e1tica de c\u00f3digo.\n", "+ Uso de ORM.\n", "+ Uso de [CSS](http://www.w3.org/Style/CSS/) (*Cascade Style Sheets*).\n", "+ Uso de AJAX (*Asynchronous Javascript And XML*).\n", "+ Modelos de aplica\u00e7\u00f5es.\n", "+ Uso de introspec\u00e7\u00e3o para obter informa\u00e7\u00f5es sobre as estruturas de dados e gerar formul\u00e1rios com campos com as caracter\u00edsticas correspondentes.\n", "+ Diversas op\u00e7\u00f5es pr\u00e9-configuradas com *defaults* adequados para a maioria das aplica\u00e7\u00f5es.\n", "\n", "Uma das maiores vantagens oferecidas pelo MVC \u00e9 que, ao separar a apresenta\u00e7\u00e3o da l\u00f3gica de aplica\u00e7\u00e3o, se torna mais f\u00e1cil dividir as tarefas de desenvolvimento e de design da interface em uma equipe.\n", "\n", "Exemplo:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "\"\"\"\n", "Web com opera\u00e7\u00f5es CRUD\n", "\"\"\"\n", "\n", "# CherryPy\n", "import cherrypy\n", "\n", "# CherryTemplate\n", "import cherrytemplate\n", "\n", "# SQLAlchemy\n", "import sqlalchemy as sql\n", "\n", "# Conecta ao bando\n", "db = sql.create_engine('sqlite:///zoo.db')\n", "\n", "# Acesso aos metadados\n", "metadata = sql.MetaData(db)\n", "\n", "try:\n", " # Carrega metadados da tabela\n", " zoo = sql.Table('zoo', metadata, autoload=True)\n", "\n", "except:\n", " # Define a estrutura da tabela zoo\n", " zoo = sql.Table('zoo', metadata,\n", " sql.Column('id', sql.Integer, primary_key=True),\n", " sql.Column('nome', sql.String(100),\n", " unique=True, nullable=False),\n", " sql.Column('quantidade', sql.Integer, default=1),\n", " sql.Column('obs', sql.String(200), default='')\n", " )\n", "\n", " # Cria a tabela\n", " zoo.create()\n", "\n", "# Os nomes das colunas\n", "colunas = [col for col in zoo.columns.keys()]\n", "colunas.remove('id')\n", "\n", "\n", "class Root(object):\n", " \"\"\"Raiz do site\"\"\"\n", "\n", " @cherrypy.expose\n", " def index(self, **args):\n", " \"\"\"\n", " Lista os registros\n", " \"\"\"\n", "\n", " msg = ''\n", " op = args.get('op')\n", " ident = int(args.get('ident', 0))\n", " novo = {}\n", "\n", " for coluna in colunas:\n", " novo[coluna] = args.get(coluna)\n", "\n", " if op == 'rem':\n", "\n", " # Remove dados\n", " rem = zoo.delete(zoo.c.id==ident)\n", " rem.execute()\n", " msg = 'registro removido.'\n", "\n", " elif op == 'add':\n", "\n", " novo = {}\n", "\n", " for coluna in colunas:\n", " novo[coluna] = args[coluna]\n", "\n", " try:\n", " # Insere dados\n", " ins = zoo.insert()\n", " ins.execute(novo)\n", " msg = 'registro adicionado.'\n", "\n", " except sql.exceptions.IntegrityError:\n", " msg = 'registro existe.'\n", "\n", " elif op == 'mod':\n", "\n", " novo = {}\n", "\n", " for coluna in colunas:\n", " novo[coluna] = args[coluna]\n", "\n", " try:\n", " # Modifica dados\n", " mod = zoo.update(zoo.c.id==ident)\n", " mod.execute(novo)\n", " msg = 'registro modificado.'\n", "\n", " except sql.exceptions.IntegrityError:\n", " msg = 'registro existe.'\n", "\n", " # Seleciona dados\n", " sel = zoo.select(order_by=zoo.c.nome)\n", " rec = sel.execute()\n", "\n", " # Gera a p\u00e1gina principal a partir do modelo \"index.html\"\n", " return cherrytemplate.renderTemplate(file='index.html',\n", " outputEncoding='utf-8')\n", "\n", " @cherrypy.expose\n", " def add(self):\n", " \"\"\"\n", " Cadastra novos registros\n", " \"\"\"\n", "\n", " # Gera a p\u00e1gina de registro novo a partir do modelo \"add.html\"\n", " return cherrytemplate.renderTemplate(file='add.html',\n", " outputEncoding='utf-8')\n", "\n", " @cherrypy.expose\n", " def rem(self, ident):\n", " \"\"\"\n", " Confirma a remo\u00e7\u00e3o de registros\n", " \"\"\"\n", "\n", " # Seleciona o registro\n", " sel = zoo.select(zoo.c.id==ident)\n", " rec = sel.execute()\n", " res = rec.fetchone()\n", "\n", " # Gera a p\u00e1gina de confirmar exclus\u00e3o a partir do modelo \"rem.html\"\n", " return cherrytemplate.renderTemplate(file='rem.html',\n", " outputEncoding='utf-8')\n", "\n", " @cherrypy.expose\n", " def mod(self, ident):\n", " \"\"\"\n", " Modifica registros\n", " \"\"\"\n", "\n", " # Seleciona o registro\n", " sel = zoo.select(zoo.c.id==ident)\n", " rec = sel.execute()\n", " res = rec.fetchone()\n", "\n", " # Gera a p\u00e1gina de altera\u00e7\u00e3o de registro a partir do modelo \"mod.html\"\n", " return cherrytemplate.renderTemplate(file='mod.html',\n", " outputEncoding='utf-8')\n", "\n", "\n", "# Inicia o servidor na porta 8080\n", "cherrypy.quickstart(Root())" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Modelo `index.html` (p\u00e1gina principal):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "\n", "\n", "\n", "\n", "\n", " \n", "\n", "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n", "
\n", " \">modificar\n", " \n", " \">remover\n", "
\n", "
\n", "
\n", " \n", "
\n", "

\n", "\n", "

\n", "" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Modelo `add.html` (p\u00e1gina de formul\u00e1rio para novos registros):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "\n", "
\n", " \n", " \n", " \n", " \n", "
\n", " \n", " \n", " \" />\n", "
\n", "
\n", "\n", "
\n", "
\n", "[ voltar ]\n", "" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Modelo `mod.html` (p\u00e1gina de formul\u00e1rio para altera\u00e7\u00e3o de registros):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "\n", "
\" method=\"post\">\n", " \n", " \n", " \n", " \n", "
\n", " \n", " \n", " \"\n", " value=\"\" />\n", "
\n", "
\n", "\n", "
\n", "
\n", "[ voltar ]\n", "" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Modelo `rem.html` (p\u00e1gina que pede confirma\u00e7\u00e3o para remo\u00e7\u00e3o de registros):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "\n", "\n", "\n", "\n", " \n", "\n", "\n", "\n", "\n", " \n", "\n", "\n", "
\n", "
\n", "
\" method=\"post\">\n", " \n", "
\n", "
\n", "[ voltar ]\n", "" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Modelo `header.html` (cabe\u00e7alho comum a todos os modelos):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "\n", "\n", " \n", "Zoo\n", "\n", "\n", "\n", "

Zoo

\n", "
" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Modelo `footer.html` (rodap\u00e9 comum a todos os modelos):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "\n", "" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "P\u00e1gina principal:\n", "\n", "![P\u00e1gina principal de Zoo](files/zoo_pagina_principal.png)\n", "\n", "O *framework* MVC mais conhecido \u00e9 o Ruby On Rails, que ajudou a popularizar o MVC entre os desenvolvedores.\n", "\n", "Especificamente desenvolvidos em Python, existem os *frameworks* [Django](http://www.djangoproject.com/), [TurboGears](http://turbogears.org/) e [web2py](http://www.web2py.com/), entre outros." ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [ { "html": [ "\n", "" ], "output_type": "pyout", "prompt_number": 1, "text": [ "" ] } ], "prompt_number": 1 } ], "metadata": {} } ] }