Life is Like a Boat

忘備録や投資日記、プログラミングに関するメモやtipsなど

Django x Salesforce

この記事はDjango Advent Calendar 2019の記事です。

Djangoは2019年にちょっと時間割いて勉強しておこうと思ったPythonのWebフレームワークです。

きっかけはFinpyに参加されていたakiyokoさんのLTで、氏が著した下記3本やチュートリアルをみながら学習していった感じです。

現場で使える Django の教科書《基礎編》

現場で使える Django の教科書《基礎編》

現場で使える Django の教科書《実践編》

現場で使える Django の教科書《実践編》

Advent Calenderは初参加で何か書くぞと宣言したはいいもの、このネタを思いついたのが実は昨日ですw

Djangoと何かを掛け合わせて相乗効果がでるようなネタがいいなと、自分のSalesforce開発に関わった経験とDjangoを繋ぐためのライブラリを探したところ、見つけたのがdjango-salesforceです。

このライブラリのメリットを一言で表すと、「Django ORMと統合することでSalesforce上のレコードをPostgreSQLやMySQLをBackendに使う場合の様に操作できる点」だと思います。

github.com

セットアップ

基本公式のREADMEに全て書いてあります。

django-salesforce/README.rst at master · django-salesforce/django-salesforce · GitHub

セットアップの肝としては、Django側とSalesforce側のやり取りはConnected Appを通して行われます。Connected AppはOAuth認証を使用して外部アプリとSalesforce APIを統合するためのものです。この辺の解説が詳しいです。

Help | Training | Salesforce

Connected Appの具体的な使用事例としては、外部のBIツールからこれを介してSalesforce内のCRMデータにアクセスし、BIツール側でレポートを作るなどがあります。

Model

DjangoでのModelがSalesforceでのオプジェクトになります。ドキュメントによれば全てのModelはsalesforce.models.Modelを継承していなければなりません。 また、Salesforce上に既にオブジェクトが存在している必要があります。例えば、Djangoチュートリアルで使われるPollアプリの質問や選択肢がSalesforce上にカスタムオブジェクトとして(Questionc, Choicec)として存在することが期待されます。これはdjango-salesforceがメタデータの作成までサポートしていないからだと思います。githubのissuesをざっくり眺めたのですが、この辺り将来的なロードマップへの言及は見つけられませんでした。

Salesforce側にModelが既にあれば、Django既存のデータベースをスキャンしてModelを自動作成してくれるinspectdbコマンドを使うことでmodels.pyへの記述が楽になります。ドキュメントによると、このコマンドを使ってmodels.pyを記述していく方法が推奨されている様です。 例: > python manage.py inspectdb Opportunity --database=salesforce

ここではDjangoチュートリアルのPollアプリを基に考えます。

Pollアプリ - Question

DjangoチュートリアルのPollアプリはQuestionとChoiceから構成されます。

先にSalesforce側でQuestion__cというカスタムオブジェクトを作成します。

f:id:nerimplo:20191216105612p:plain

f:id:nerimplo:20191216105617p:plain

次にinspectdbコマンドでQuestion__cのModelを自動生成します。

python manage.py inspectdb Question__c --database=salesforce

class Question(models.Model):
    name = models.CharField(max_length=80, verbose_name='質問名', default=models.DEFAULTED_ON_CREATE, blank=True, null=True)
    question_text = models.CharField(custom=True, db_column='question_text__c', max_length=200, verbose_name='question_text', blank=True, null=True)
    pub_date = models.DateTimeField(custom=True, db_column='pub_date__c', verbose_name='pub_date', blank=True, null=True)
    class Meta(models.Model.Meta):
        db_table = 'Question__c'
        verbose_name = '質問'
        verbose_name_plural = '質問'
        # keyPrefix = 'a07'

コマンド実行後のアウトプットは標準項目も含んでいるのですが、好きな様に簡略化しろとドキュメントにはあるので、この様にしました。models.pyにはname, question_text, pub_dateのみの項目とします。なお、inspectdbコマンドでカスタムオブジェクトのモデルを作成するとCが必ず最後につく様です。これは削除するようにしました。

ここまで、確認を兼ねてCRUDができるかやってみました。

Pollアプリ - CRUD

q = Question(name=4, question_text='どこにいきたい?', pub_date=datetime.today())
q.save()

f:id:nerimplo:20191216100746p:plain

ちゃんと保存されています!所有者は接続アプリのログインユーザーになっています。

> q = Question.objects.filter(name=2)
> q
<SalesforceQuerySet [<Question: Question object (a072v00001AWH9XAAX)>]>
> q[0].name
'2'
> q[0].question_text
'夏休み旅行の予算はいくらですか'

UpdateやDeleteも通常のDjangoモデルのそれと同じに扱える様です。deleteメソッドの場合、Salesforce側では論理削除になっています(削除済みのフラグが付けられ、ごみ箱入り)

Pollアプリ - Choice

Djangoチュートリアルも参考に選択肢(Choice)をmodels.pyに加えてみます。

class Choice(models.SalesforceModel):
    name = models.CharField(max_length=80, verbose_name='選択肢名', default=models.DEFAULTED_ON_CREATE, blank=True, null=True)
    question = models.ForeignKey('Question', models.DO_NOTHING, custom=True, blank=True, null=True, db_column='Question__c')
    choice_text = models.CharField(custom=True, db_column='choice_text__c', max_length=20, verbose_name='choice_text', blank=True, null=True)
    votes = models.DecimalField(custom=True, db_column='votes__c', max_digits=18, decimal_places=0, verbose_name='votes', blank=True, null=True)
    class Meta(models.Model.Meta):
        db_table = 'Choice__c'
        verbose_name = '選択肢'
        verbose_name_plural = '選択肢'

質問レコードを選んで、選択肢を加えてみます。

> q = Question.objects.get(name=1)
> q.id
'a072v00001AWH9SAAX'
> c = Choice(question_id=q.id, name='A', choice_text='A', votes=10)
> c.save()
> c = Choice(question_id=q.id, name='B', choice_text='B', votes=1)
> c.save()
> c = Choice(question_id=q.id, name='C', choice_text='C', votes=1)
> c.save()

f:id:nerimplo:20191216130238p:plain

関連リストにちゃんと選択肢が表示されています。

選択肢nameにAを含む質問

> Question.objects.filter(choice__name='A')
<SalesforceQuerySet [<Question: Question object (a072v00001AWH9SAAX)>]>

選択肢のvotesが5以上の質問

> Question.objects.filter(choice__votes__gt=5)
<salesforce.backend.models_sql_query.SalesforceQuery object at 0x10b8a9d90>
>>> q = Question.objects.get(name=1)
>>> q
<Question: Question object (a072v00001AWH9SAAX)>
>>> q.choice_set.all()
<SalesforceQuerySet [<Choice: Choice object (a082v00002pwq0hAAA)>, <Choice: Choice object (a082v00002pwq2YAAQ)>, <Choice: Choice object (a082v00002pwq2OAAQ)>]>

_setを使ったクエリもDjangoライクにできます。ライブラリ側でORM->SOQLクエリにしている様です。

活用案

ざっくりdjango-salesforceを使ってみた感じですが、このライブラリの活用案を考えてみたところ、

  1. 既にDjangoベースのtoC向けのシステムがあって、CRM側のデータを使ってフロントで見せたい。
  2. Django側にCRM側のデータを引っ張り出してユーザー認証後の画面で編集
  3. ライセンスをケチる必要がある(爆

があると思います。 このライブラリを使わなくても解決はできそうですが、必要は発明の母というので私が認識していない活用事例があるはずだ思っています。思いついて語りたい方、Twitterまで連絡ください!