概要
AWSのマネージドサービスをローカル上で実行できるLocalStackとdjango-rest-frameworkを使ってSMSを送信する方法について解説していきたいと思います
前提
- Djangoのプロジェクトを作成済み
ディレクトリ構成
tree
・
├── .gitignore
├── README.md
├── backend
│ ├── application
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── apps.py
│ │ ├── fixtures
│ │ │ └── fixture.json
│ │ ├── migrations
│ │ │ ├── __init__.py
│ │ │ └── 0001_initial.py
│ │ ├── models.py
│ │ ├── urls.py
│ │ └── views.py
│ ├── manage.py
│ ├── poetry.lock
│ ├── project
│ │ ├── __init__.py
│ │ ├── asgi.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ └── pyproject.toml
└── docker-compose.yml
以下のファイルを作成・編集します
- docker-compose.yml
- settings.py
- models.py
- fixtures.py
- views.py
LocalStackの設定
まずはローカル上でAWS SNSを使用できるようにLocakStackの設定をしていきます
SERVICESにSNSを追加し、今回は
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
の環境変数をlocalstackにします
localstack:
image: localstack/localstack
container_name: localstack
ports:
- '4566:4566'
environment:
- SERVICES=sns
- AWS_ACCESS_KEY_ID=localstack
- AWS_SECRET_ACCESS_KEY=localstack
- DEBUG=1
volumes:
- localstack_data:/tmp/localstack/data
- localstack_bin:/var/lib/localstack
boto3の設定
Djangoのプロジェクト内でLocalStackとAWS SNSを使えるよう設定をしていきます
LocalStackを使う際はboto3.client内に以下の引数を指定します
引数 | 値 | 説明 |
---|---|---|
aws_access_key_id | localstack | AWSのアクセスキー LocalStack内の環境変数と同じ値にします |
aws_secret_access_key | localstack | AWSのシークレットキー LocalStack内の環境変数と同じ値にします |
region_name | ap-northeast-1 | リージョン名 |
endpoint_url | http://localstack:4566 | ローカル環境のboto3の接続先 今回だとLocalStack |
本番環境ではIAMロールを使った開発が一般的なので
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
は使用しません
import boto3
import os
if DEBUG:
SNS_CLIENT = boto3.client(
"sns",
aws_access_key_id="localstack",
aws_secret_access_key="localstack",
region_name=os.environ.get("AWS_DEFAULT_REGION_NAME"),
endpoint_url=os.environ.get("AWS_SNS_ENDPOINT_URL"),
)
else:
SNS_CLIENT = boto3.client(
"sns",
region_name=os.environ.get("AWS_DEFAULT_REGION_NAME"),
endpoint_url=os.environ.get("AWS_SNS_ENDPOINT_URL"),
)
SMS送信用APIの実装
Modelの作成
SMSを送信するお客様のModelを作成します
import uuid
from django.core.validators import RegexValidator
from django.db import models
class Customer(models.Model):
"""お客様"""
id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
editable=False,
db_comment="ID",
)
kana = models.CharField(
max_length=255,
db_comment="カナ氏名",
)
name = models.CharField(
max_length=255,
db_comment="氏名",
)
birthday = models.DateField(
db_comment="誕生日",
)
email = models.EmailField(
db_comment="メールアドレス",
)
phone_no = models.CharField(
max_length=11,
validators=[RegexValidator(r"^[0-9]{11}$", "11桁の数字を入力してください。")],
blank=True,
db_comment="電話番号",
)
created_at = models.DateTimeField(
auto_now_add=True,
db_comment="作成日時",
)
updated_at = models.DateTimeField(
auto_now=True,
db_comment="更新日時",
)
class Meta:
db_table = "Customer"
テストデータの作成
お客様用のテストデータを作成します
[
{
"model": "application.Customer",
"pk": 1,
"fields": {
"kana": "オオサカタロウ",
"name": "大阪太郎",
"birthday": "1992-01-06",
"email":"osaka@example.com",
"phone_no": "08011112222",
"created_at": "2022-07-28T00:31:09.732Z",
"updated_at": "2022-07-28T00:31:09.732Z"
}
},
{
"model": "application.Customer",
"pk": 2,
"fields": {
"kana": "キョウトジロウ",
"name": "京都二郎",
"birthday": "1994-01-06",
"email":"kyoto@example.com",
"phone_no": "08022223333",
"created_at": "2022-07-28T00:31:09.732Z",
"updated_at": "2022-07-28T00:31:09.732Z"
}
},
{
"model": "application.Customer",
"pk": 3,
"fields": {
"kana": "ヒョウゴサブロウ",
"name": "兵庫三郎",
"birthday": "1995-03-06",
"email":"hyogo@example.com",
"phone_no": "08033334444",
"created_at": "2022-07-28T00:31:09.732Z",
"updated_at": "2022-07-28T00:31:09.732Z"
}
}
]
viewの作成
今回はパラメータにお客様のIDを入れてメッセージ名にお客様の氏名が入るので動的に簡易的なメッセージを送るようにします
{{customer}}様
問い合わせありがとうございます
SMSを送信する際はSNSのClientを作成し、publishメソッドを使って送信します
日本の電話番号にSMSを送信する際は+81
が必要です
SMS送信が失敗したときのためにtry catchを使ってClientErrorの時にエラーメッセージを返すようにします
from application.models import Customer
from botocore.exceptions import ClientError
from django.conf import settings
from django.http import JsonResponse
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import ModelViewSet
class CustomerViewSet(ModelViewSet):
queryset = Customer.objects.all()
permission_classes = [IsAuthenticated]
serializer_class = None
@action(methods=["post"], detail=True)
def send_sms(self, request, pk):
customer = self.get_object()
message = f"{customer.name}様\n問い合わせありがとうございます"
try:
settings.SNS_CLIENT.publish(
PhoneNumber="+81" + customer.phone_no, Message=message
)
return JsonResponse({"msg": "SMSの送信に成功しました"},)
except ClientError as e:
return JsonResponse(
{"msg": "SMSを送信できませんでした"},
status=status.HTTP_400_BAD_REQUEST,
)
実際に送信してみよう!
先ほど作成したAPIにお客様のPKを入れてPOSTします
以下のようにSMSを送信できたら成功です
以下のようにLocalStackのlogから送信が成功したことを確認できました
2024-01-17 17:36:55 2024-01-17T08:36:55.533 DEBUG --- [ asgi_gw_0] l.services.sns.publisher : Publishing '66c16f50-ee4f-4c69-b2f6-865385453f57' to phone number '+8108011112222' with protocol 'sms'
2024-01-17 17:36:55 2024-01-17T08:36:55.535 INFO --- [ asgi_gw_0] localstack.request.aws : AWS sns.Publish => 200
2024-01-17 17:36:55 2024-01-17T08:36:55.535 INFO --- [ sns_pub_0] l.services.sns.publisher : Delivering SMS message to +8108011112222: 大阪太郎様
2024-01-17 17:36:55 問い合わせありがとうございます
参考