Ahora que AngularJS está en producción, me dispongo a aprovechar esa nueva herramienta, con el respaldo de Turbogears.
En el homepage de AngularJS existe un demo ('Wire up a Backend') que está respaldado por MondoDB de MongoLabs y aunque funciona excelente y la plataforma de MongoDB parece muy sólida, yo quise experimentar con otras herramientas.
El modelo es bastante simple, declarado en SQLAlchemy queda así:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | # -*- coding: utf-8 -*- """ Project model module.""" from sqlalchemy import Column from sqlalchemy.types import Integer, Unicode #from sqlalchemy.orm import relation, backref from angulartests.model import DeclarativeBase, DBSession class Project(DeclarativeBase): __tablename__ = 'project' #{ Columns id = Column(Integer, primary_key=True) name = Column(Unicode(255), nullable=False) site = Column(Unicode(255), nullable=False) description = Column(Unicode(255), nullable=True) #} #{ Helpers @property def toJSON(self): return ("""{"id": %s, "name": "%s", "site": "%s", "description": "%s"}""" % self.id, self.name, self.site, self.description).encode('utf-8') @property def toDict(self): return dict( id=self.id, name=self.name, site=self.site, description=self.description) @classmethod def by_id(cls, project_id): """ Returns object whose id is ```project_id``` """ return DBSession.query(cls)\ .filter(cls.id == project_id).first() @classmethod def getAll(cls): """ Returns all objects """ return DBSession.query(cls).all() #} |
La parte del controlador me costó más trabajo ya que hay que definir un controlador con REST capabilities. TG ofrece un RestController que podemos usar.
El controllador
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | # -*- coding: utf-8 -*- """Sample REST controller module""" # turbogears imports from tg import expose, request from tg.controllers import RestController from tg.decorators import with_trailing_slash from angulartests import model import json import logging log = logging.getLogger(__name__) class WireUpResourcesController(RestController): """ Backend controller for AngularJS test """ @with_trailing_slash @expose('json') def get(self, **kw): log.debug("(GET) kw: %s", kw) id = kw.get('id', None) if id: project = model.Project.by_id(id) return json.dumps(project.toDict if project else {}) data = [o.toDict for o in model.Project.getAll()] return json.dumps(data) @with_trailing_slash @expose('json') def put(self, id, *args, **kw): # where are my parameters? kw = json.loads(request.body) if id and id != 'undefined': project = model.Project.by_id(id) else: # No id? new project then project = model.Project() project.name = kw.get('name', None) project.site = kw.get('site', None) project.description = kw.get('description', None) model.DBSession.add(project) return json.dumps(project.toDict) @with_trailing_slash @expose('json') def post(self, **kw): log.debug("(POST) kw: %s", kw) id = kw.get('id', None) # where are my parameters? kw = json.loads(request.body) if id: project = model.Project.by_id(id) else: # No id? new project then project = model.Project() project.name = kw.get('name', None) project.site = kw.get('site', None) project.description = kw.get('description', None) model.DBSession.add(project) # need an id to return, so we flush here model.DBSession.flush() log.debug("returning: %s", project.toDict) return json.dumps(project.toDict) @with_trailing_slash @expose('json') def delete(self, **kw): log.debug("(DELETE) kw: %s", kw) id = kw.get('id', None) if id: # No confirmation delete object project = model.Project.by_id(id) if project: model.DBSession.delete(project) model.DBSession.flush() return |
Notar que tengo que reconstruir mi mapa de parámetros en las líneas 37 y 60 debido a que llegan como contenido y no propiamente como parámetros del request. Esa es la única parte diferente a lo que haría con un controlador estándard
También hay que modificar un poco el módulo de angular para usar este backend, quedando projec.js así:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | 'use strict'; angular.module('project', ['project_resource']). config(function($routeProvider) { $routeProvider. when('/', {controller:ListCtrl, templateUrl:'/app/partials/wire_up_list.html'}). when('/edit/:projectId', {controller:EditCtrl, templateUrl:'/app/partials/wire_up_detail.html'}). when('/new', {controller:CreateCtrl, templateUrl:'/app/partials/wire_up_detail.html'}). otherwise({redirectTo:'/'}); }); function ListCtrl($scope, Project) { $scope.projects = Project.query(); } function CreateCtrl($scope, $location, Project) { $scope.save = function() { Project.save($scope.project, function(project) { $location.path('/edit/' + project.id); }); } } function EditCtrl($scope, $location, $routeParams, Project) { var self = this; Project.get({id: $routeParams.projectId}, function(project) { self.original = project; $scope.project = new Project(self.original); }); $scope.isClean = function() { return angular.equals(self.original, $scope.project); } $scope.destroy = function() { self.original.destroy(function() { $location.path('/list'); }); }; $scope.save = function() { $scope.project.update(function() { $location.path('/'); }); }; } |
y project-resource.js así:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 'use strict'; angular.module('project_resource', ['ngResource']). factory('Project', function($resource) { var Project = $resource('/wire_up_resources', {}, { update: { method: 'PUT' } } ); Project.prototype.update = function(cb) { return Project.update({id: this.id}, angular.extend({}, this, {id:undefined}), cb); }; Project.prototype.destroy = function(cb) { return Project.remove({id: this.id}, cb); }; return Project; }); |
Funciona bastante bien, ahora la pregunta sería ¿Vale la pena? ... hmm yo creo que si, a simple vista los requests al servidor disminuyen considerablemente agrega un poco de complejidad para crear el árbol de desarrollo...
├── config ├── controllers ├── i18n ├── lib ├── model ├── public │ ├── app <-- La 'app' de Angularjs │ │ ├── css │ │ ├── img │ │ ├── js │ │ ├── lib │ │ │ └── angular │ │ └── partials │ ├── config │ ├── css │ ├── images │ ├── javascript │ ├── logs │ ├── scripts │ └── test <-- Los tests de Angularjs │ ├── e2e │ ├── lib │ │ └── angular │ └── unit ├── templates ├── tests │ ├── functional │ └── models └── websetup
Simplemente expandí el angular-seed dentro del directorio public de mi proyecto de Turbogears. Espero que le sirva a alguien
No hay comentarios.:
Publicar un comentario