2019年3月27日14:08

yusk

django heroku OseroBattle python

環境構築から初めて OseroBattle 用の API を作成し heroku にデプロイするまで

環境構築

brew install pyenv
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
source ~/.bash_profile
pyenv install 3.7.1
pyenv global 3.7.1
pip install pipenv
pip install django

Django の初期化

cd /path/to/workspace/
django-admin startproject config
mv config oseroapi
cd oseroapi
pipenv install django
pipenv run python manage.py migrate
pipenv run python manage.py runserver
open localhost:8000 # デフォルトページの起動確認

main app の初期化

pipenv install djangorestframework
pipenv run python manage.py startapp main
vim config/settings.py



# config/settings.py
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', #追加
'main', # 追加
]
...

views の編集

vim main/views.py


from rest_framework.views import APIView
from rest_framework.response import Response


class OseroView(APIView):
def post(self, request):
return Response({
"x": 0,
"y": 0,
})

urls の編集

vim main/urls.py


from django.urls import path

from . import views

app_name = 'main'
urlpatterns = [
path('api/osero/', views.OseroView.as_view(), name="osero"),
]



vim config/urls.py


from django.urls import path, include

urlpatterns = [
path('', include('main.urls'), name='main'),
]

動作確認

curl -X POST http://localhost:8000/api/osero/ # res: {"x":0,"y":0}

データを受け取る

vim main/views.py


from rest_framework.views import APIView
from rest_framework.response import Response


class OseroView(APIView):
def post(self, request):
print(request.data)
your_stone = request.data["your_stone"]
squares = request.data["squares"]
print(your_stone)
print(squares)
return Response({
"x": 0,
"y": 0,
})



curl -X POST http://localhost:8000/api/osero/ -H "Content-Type: application/json" -d "{ \"your_stone\": 1, \"squares\": [ [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, -1, 0, 0, 0], [0, 0, 0, -1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0] ]}"

helpers を作る

vim main/helpers.py


class BoardHelper:
VECS = [(0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0), (-1,
1)]
STONES = [1, -1]
EMPTY_STONE = 0

@classmethod
def can_put(cls, squares, stone, x, y):
if squares[y][x] != cls.EMPTY_STONE:
return False

for vec in cls.VECS:
i = x
j = y
try:
i += vec[0]
j += vec[1]
if i < 0 or j < 0:
raise IndexError(f"i: {i}, j: {j}")
if squares[j][i] in (stone, cls.EMPTY_STONE):
continue
while True:
i += vec[0]
j += vec[1]
if i < 0 or j < 0:
raise IndexError(f"i: {i}, j: {j}")
if squares[j][i] == stone:
return True
elif squares[j][i] == cls.EMPTY_STONE:
break
except IndexError:
continue

return False

@classmethod
def get_can_put_list(cls, squares, stone):
can_put_list = []
for x in range(8):
for y in range(8):
if cls.can_put(squares, stone, x, y):
can_put_list.append((x, y))
return can_put_list



vim main/views.py


from rest_framework.views import APIView
from rest_framework.response import Response

from .helpers import BoardHelper


class OseroView(APIView):
def post(self, request):
print(request.data)
your_stone = request.data["your_stone"]
squares = request.data["squares"]
print(your_stone)
print(squares)
can_put_list = BoardHelper.get_can_put_list(squares, your_stone)
print(can_put_list)
return Response({
"x": 0,
"y": 0,
})



curl -X POST http://localhost:8000/api/osero/ -H "Content-Type: application/json" -d "{ \"your_stone\": 1, \"squares\": [ [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, -1, 0, 0, 0], [0, 0, 0, -1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0] ]}"

ランダムに手を打つ

vim main/views.py


import random

from rest_framework.views import APIView
from rest_framework.response import Response

from .helpers import BoardHelper


class OseroView(APIView):
def post(self, request):
your_stone = request.data["your_stone"]
squares = request.data["squares"]
can_put_list = BoardHelper.get_can_put_list(squares, your_stone)

point = random.choice(can_put_list)

return Response({
"x": point[0],
"y": point[1],
})



curl -X POST http://localhost:8000/api/osero/ -H "Content-Type: application/json" -d "{ \"your_stone\": 1, \"squares\": [ [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, -1, 0, 0, 0], [0, 0, 0, -1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0] ]}"

Heroku の準備

brew tap heroku/brew && brew install heroku
pipenv install gunicorn
vim Procfile



web: gunicorn config.wsgi --log-file -

gitignore の設定

vim .gitignore


# Created by https://www.gitignore.io/api/macos,python,django
# Edit at https://www.gitignore.io/?templates=macos,python,django

### Django ###
*.log
*.pot
*.pyc
__pycache__/
local_settings.py
db.sqlite3
media

# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/
# in your Git repository. Update and uncomment the following line accordingly.
# <django-project-name>/staticfiles/

### Django.Python Stack ###
# Byte-compiled / optimized / DLL files
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo

# Django stuff:

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don’t work, or not
# install all needed dependencies.
#Pipfile.lock

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

# End of https://www.gitignore.io/api/macos,python,django

Heroku にデプロイ

git init
heroku login
heroku create oseroapi
vim config/settings.py



...
ALLOWED_HOSTS = ["localhost", "127.0.0.1", "oseroapi.herokuapp.com"]
...



git add .
git commit -m "initial commit"

heroku config:set DISABLE_COLLECTSTATIC=1
git push heroku master
heroku ps:scale web=1
heroku run python manage.py migrate
heroku open



curl -X POST -H "Content-Type: application/json" -d "{ \"your_stone\": 1, \"squares\": [ [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, -1, 0, 0, 0], [0, 0, 0, -1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0] ]}" https://oseroapi.herokuapp.com/api/osero/

ログを確認

heroku logs --tail