DjangoでXMLを出力してDelphiから利用する

サーバサイド(Python/Django)とクライアントサイド(Delphi)のアプリケーションの連携をやってみる。
今回はゲストブックアプリのデータを、クライアントアプリから閲覧、投稿できるようにしてみる。

サーバサイドの用意

Djangoのアプリケーションは以前作成したゲストブックアプリを利用する。
tokibito / python-hackathon2-guestbook — Bitbucket
guestbookをINSTALLED_APPSに追加し、urls.py を書いておく。

urls.py
rom django.conf.urls.defaults import *

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    (r'^admin/', include(admin.site.urls)),
    (r'', include('guestbook.urls')), # added
)

syncdbでテーブルを作成してから、runserverコマンドでローカルサーバを実行して投稿出来るか確かめておく。

XML出力の作成

投稿データをXMLで出力するページを作成する。
XMLの生成に補助用のライブラリを使う。プロジェクトにxmlutilsディレクトリをコピーしておく。
tokibito / python-xmlutils — Bitbucket
startappコマンドで api アプリを作成して views.py と urls.py を書く。

myproject/api/views.py
from django.http import HttpResponse

from xmlutils import XMLRenderer, dict_to_node

from guestbook.models import Greeting

def index(request):
    xmldic = {'greetings':
        map(
            lambda g: {
                'greeting': {
                    'id': str(g.id),
                    'username': g.username,
                    'content':g.content
                }
            },
            Greeting.objects.all()
        )
    }
    return HttpResponse(
        XMLRenderer().render(dict_to_node(xmldic), indent=2, unichar=True),
        content_type='application/xml'
    )

PEP8なにそれな勢いのインデントだけど、気にしない。

myproject/api/urls.py
from django.conf.urls.defaults import *

urlpatterns = patterns('api.views',
    (r'^$', 'index'),
)
myproject/urls.py
from django.conf.urls.defaults import *

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    (r'^admin/', include(admin.site.urls)),
    (r'^api/', include('api.urls')), # added
    (r'', include('guestbook.urls')),
)

これで http://localhost:8000/api/ でアクセスするとXMLが出てるはず。

出力結果例
<?xml version="1.0" encoding="utf-8"?>
<greetings>
  <greeting>
    <username>&#12486;&#12473;&#12488;</username>
    <content>&#12411;&#12370;&#12405;&#12364;</content>
    <id>&#49;&#52;</id>
  </greeting>
  <greeting>
    <username>&#97;&#97;&#97;&#97;</username>
    <content>&#116;&#101;&#115;&#116;</content>
    <id>&#49;&#51;</id>
  </greeting>
</greetings>

これでサーバサイドの用意は完了。

クライアントアプリの作成

Delphi2009Proで作成した。
出力されたXMLをファイルにダウンロードして、XMLデータバインディングを利用して取り込む。
データバインディングの例は過去の記事参照。
DelphiでTwitterのタイムラインを取得する - 偏った言語信者の垂れ流し
ボタンクリックで、XMLデータをダウンロードしてリストに表示、投稿をできるようにしている。

Unit1.pas
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
  IdTCPClient, IdHTTP, XMLDoc, HTTPApp, api;

type
  TForm1 = class(TForm)
    btnReload: TButton;
    lvContents: TListView;
    txtGetXMLURL: TEdit;
    btnSend: TButton;
    txtPostURL: TEdit;
    txtUserName: TEdit;
    txtContent: TMemo;
    procedure btnReloadClick(Sender: TObject);
    procedure btnSendClick(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnReloadClick(Sender: TObject);
var
  HTTP: TIdHTTP;
  XML: TXMLDocument;
  I: Integer;
  Item: TListItem;
  Greetings: IXMLGreetingsType;
begin
  lvContents.Clear;
  HTTP := TIdHTTP.Create;
  try
    XML := TXMLDocument.Create(Self);
    try
      XML.LoadFromXML(HTTP.Get(txtGetXMLURL.Text));
      Greetings := Getgreetings(XML);
      for I := 0 to Greetings.Count - 1 do
      begin
        Item := lvContents.Items.Add;
        Item.Caption := IntToStr(Greetings.Greeting[I].Id);
        Item.SubItems.Add(Greetings.Greeting[I].UserName);
        Item.SubItems.Add(Greetings.Greeting[I].Content);
      end;
    finally
      XML.Free;
    end;
  finally
    HTTP.Free;
  end;
end;

procedure TForm1.btnSendClick(Sender: TObject);
var
  HTTP: TIdHTTP;
  PostData: TStringList;
begin
  if (txtUserName.Text <> '') and (txtContent.Lines.Text <> '') then
  begin
    HTTP := TIdHTTP.Create;
    try
      PostData := TStringList.Create;
      PostData.Add('username=' + txtUserName.Text);
      PostData.Add('content=' + txtContent.Text);
      try
        try
          HTTP.Post(txtPostURL.Text, PostData);
        except
          on e: EIdHTTPProtocolException do
            if e.ErrorCode = 302 then
              OutputDebugString('Response 302.');
        end;
        txtContent.Clear;
      finally
        PostData.Free;
      end;
    finally
      HTTP.Free;
    end;
  end;
end;

end.

投稿はフォームからPOSTするのと同じURLを使って、同様にPOSTするようにしている。

ソース

guestbook_cs.zip