Posts mit dem Label rest werden angezeigt. Alle Posts anzeigen
Posts mit dem Label rest werden angezeigt. Alle Posts anzeigen

Sonntag, 5. August 2012

django REST tastypie jQuery

Nach dem wir so schön und einfach django um REST erweitert haben befassen wir uns damit diese über JavaScript um konkreter zu sein jQuery aufzurufen.

beim googlen nach "jQuery rest" kommt man nicht um das Script vom "Sasa Jovancic" vorbei, daher habe ich mir erlaubt sein Script als Vorlage zu nehmen und es zu erweitern.

Für dieses Beispiel wird eine funktionieren tastypie-Anwendung benötigt wie z.b. hier.
In der Standartkonfiguration ist die default "Authentication" eingestellt, also keine.
Als autorisation ist "DjangoAuthorization" eingestellt, also kann man nur Anfragen absetzen wenn man sich eingeloggt hat

Hier die JS-Klasse
Danke an Sasa Jovancic für diesen Code-Snippets

/* JS REST.js */
function RestServiceJs(newurl) {
 this.myurl = newurl;

 this.add = function(model, callback) {
  console.log(JSON.stringify(model));
  $.ajax({
   type : 'POST',
   url : this.myurl,
   data : JSON.stringify(model), // '{"name":"' + model.name + '"}',
   dataType : 'text',
   processData : false,
   contentType : 'application/json',
   success : callback,
   error : function(req, status, ex) {
   },
   timeout : 60000
  });
 };

 this.update = function(model, callback) {
  $.ajax({
   type : 'PUT',
   url : this.myurl,
   data : JSON.stringify(model), // '{"name":"' + model.name + '"}',
   dataType : 'text',
   processData : false,
   contentType : 'application/json',
   success : callback,
   error : function(req, status, ex) {
   },
   timeout : 60000
  });
 };

 this.findById = function(id, callback) {
  $.ajax({
   type : 'GET',
   url : this.myurl + id,
   contentType : 'application/json',
   success : callback,
   error : function(req, status, ex) {
   },
   timeout : 60000
  });
 };

 this.filter = function(query, callback) {
  
  $.ajax({
   type : 'GET',
   url : this.myurl + "?" + $.param(query),
   contentType : 'application/json',
   success : callback,
   error : function(req, status, ex) {
   },
   timeout : 60000
  });
 };

 this.findAll = function(callback) {
  $.ajax({
   type : 'GET',
   url : this.myurl,
   contentType : 'application/json',
   success : callback,
   error : function(req, status, ex) {
   },
   timeout : 60000
  });
 };

 this.remove = function(id, callback) {
  $.ajax({
   type : 'DELETE',
   url : this.myurl + id,
   contentType : 'application/json',
   success : callback,
   error : function(req, status, ex) {
   },
   timeout : 60000
  });
 };

 this.loadTmpl = function(turl, callback) {
  $.ajax({
   url : turl,
   success : callback,
   error : function(req, status, ex) {
   },
   timeout : 60000
  });
 }
}



Die REST.js und natürlich jQuery binden wir ein und los geht's.
Nach dem die Seite geladen ist können wir gleich mit der Browser-Konsole spielen.

p = new RestServiceJs("/api/v1/poll/") // mit slash am ende, da sonst kein POST von django akzeptiert wird


// fetche alle Daten
r.findAll(function(data){ 
    console.log(data); 
}) 


// fetch by id
r.findById(1, function(data){ 
    console.log(data); 
})


// django filter mit (i)startswith, (i)endswith, (i)contains, gt, lt ...
r.filter(
    { bezeichnung__istartswith:'1008'}, 
    function(data){ 
        console.log(data); 
})


beim filter kann django-db-filter-notation verwendet werden.

also viel spaß beim ausprobieren!



Freitag, 3. August 2012

Django REST TastyPie helper

Heute habe ich mir django und REST-API angeschaut, nach dem Google ein Dutzen django-rest-projekte ausgespuckt hat, bin ich auf tastypie (git) gestoßen, da das Projekt schon seit einigen Jahren existiert und munter weiter entwickelt wird habe ich mir das näher angeschaut.

Schnell pip install tastypie in die virtualenv eingegeben, ein bisschen konfigurieren und voila! es klappt.

Nur leider ist es doch ein bisschen mühsam für jede ORM-Klasse das Tastypie-Gegenstück (Resource-Klasse) zu schreiben, auch wenn die Klasse trivial aufgebaut ist.

Der Ansatz jede Resource-Klasse in der urls.py zu registrieren erscheint mir nicht sehr elegant, das fürht bei größeren Projekten mit mehreren "Apps" und Klassen zu einem riesen Import-Schlacht (korrigiert mich, falls ich da falsch lege).

Also habe ich mich kurzer Hand entschieden eine API-POOL zu schreiben, der sich drum kümmert triviale Resource-Klassen dynamisch zu erstellen und komplexe Klassen zu sammeln.

#Datei: TastyPiePool
#coding: utf-8
'''
Created on 03.08.2012

@author: trungphanan
@contact: info@level96.de

'''

from tastypie.api import Api
from tastypie.resources import ModelResource, ALL, ALL_WITH_RELATIONS
from tastypie.authentication import Authentication
from tastypie.authorization import DjangoAuthorization
import tastypie.fields



class TastyPiePool(object):
    '''
    '''

    def __init__(self, api_name="v1"):
        self.api = Api(api_name=api_name)
        self.allowed_methods = ("get", "post", "put", "delete")
        

    def register(self, orm_class, resource_name, foreign_keys={}, filtering={}, manager=None, excludes=None, fields=None, allowed_methods=None, authentication=None, authorization=None):
        '''
        Dynamicly creates Resource Class from Django-ORM-Model
        '''
        
        resource_attrs = {"Meta": type(
            "Meta", (), 
            {
                "queryset": orm_class.objects.all() if not manager else manager,
                "resource_name": resource_name,
                "excludes": excludes, 
                "fields": fields, 
                "allowed_methods": allowed_methods if allowed_methods else self.allowed_methods,
                "authentication": Authentication() if not authentication else authentication,
                "authorization": DjangoAuthorization() if not authorization else authorization,
                "filtering": dict([ (f, ALL_WITH_RELATIONS if filtering[f]=="all" else filtering[f]) for f in filtering ])
            }
        )}
        
        for fk in foreign_keys:
            resource_attrs.update({ 
                fk: tastypie.fields.ForeignKey(foreign_keys[fk], 
                getattr(foreign_keys[fk].Meta, "resource_name")) 
           })
            
        
        cls = type(
            "%sResource" % type(orm_class).__name__,
            (ModelResource, object), 
            resource_attrs
        )
        
        self.register_resource(cls())
        
        return cls
    
    
    def register_resource(self, resource, canonical=True):
        self.api.register(resource, canonical)
    
    
    @property
    def urls(self):
        return self.api.urls

        


api_pool = TastyPiePool()


in der models.py importieren wir die API-POOL und registrieren die Klasse und den resource-name, letzteres wird als Slug für die API verwendet.
Die Funktion register generiert dynamisch eine Resource-Klasse mit dem Namen: orm-klasse-name Resource (tastypie namens-konvention) mit der entsprechenden Meta-Klasse.
Dabei kann ein abweichenden Object-Manager, excludes, fields oder allowed_methods angegeben werden (siehe tastypie doku)

Für den Fall das ihr komplexere Resource-Klassen benötigt und geschrieben habt, könnt ihr die Funktion "register_resource(CLASS)" benutzen.

from TastyPiePool import api_pool

class Poll(models.Model):
    name = models.TextField()

api_pool.register(Poll, 'poll' ) # args: orm-class, resource_name


Nach dem die Klasse registiert wurde müssen wir die api_pool nur noch der urls.py registriert werden damit die urls erreichtbar sind.


from django.conf.urls.defaults import *

from TastyPiePool import api_pool


urlpatterns = patterns('',
    (r'^api/', include(api_pool.urls)),
)


danach server neustarren und ihr erreicht die API unter:
http://host:port/api/v1/RESCOURCE-NAME/?format=json

viel spaß beim Testen und Erweitern des Scripts


für jQuery-API klickt ihr hier.