วันพุธที่ 23 มีนาคม พ.ศ. 2559

Heroku with Python 3 !!

โดยปกติทั่วไป เมื่อ Deploy ขึ้น Heroku แล้ว version ของ Python จะเป็น Python 2.7.11

แต่ถ้าหากเราต้องการจะเปลี่ยนเป็น Python 3 สามารถทำได้

โดยการสร้างไฟล์ชื่อ runtime.txt ขึ้นใน root directory ของ project

และใส่ version ที่ต้องการจะใช้ไป เช่น python-3.5.1 ก็จะเป็นการใช้ Python 3.5.11 ใน App ของเรา

รายละเอียดเพิ่มเติม https://devcenter.heroku.com/articles/python-runtimes

วันอาทิตย์ที่ 20 มีนาคม พ.ศ. 2559

Deploy Web App to Heroku

สมัคร Heroku account

https://www.heroku.com/

ลง Virtualenv


$ sudo pip install virtualenv
 
ลง Postgres
 
$ sudo apt-get install postgresql postgresql-contrib 

สร้าง user account Postgres

$ sudo -u postgres createuser --superuser $USER
$ sudo -u postgres createdb $USER

cd ไปที่ /home

$ touch .psql_history
 
ลง Heroku Toolbelt
 
$ wget -O- https://toolbelt.heroku.com/install-ubuntu.sh | sh   
 
ทำการเริ่ม Deploy App ลง Heroku
 
สร้างไฟล์ชื่อ requirements.txt ใน directory project
ใส่ข้อความบอกว่าต้องการโปรแกรมอะไรบ้าง
 
dj-database-url==0.4.0
Django==1.9.2
gunicorn==19.4.5
psycopg2==2.6.1
whitenoise==2.0.6
 
สร้าง virtual env ลงใน directory project
 
$ virtualenv venv
 
จากนั้นเปิดใช้งาน virtualenv
 
$ source venv/bin/activate
 
สร้าง Procfile ขึ้นมา
 ใส่ข้อความว่า web: gunicorn ชื่อโปรเจค.wsgi --log-file -
เช่น web: gunicorn wordbankwebsite.wsgi --log-file - 
 
ทำการลง psycopg2
$ sudo apt-get install libpq-dev && sudo apt-get install python3-dev
 
ติดตั้งไฟล์ที่จำเป็นในการรัน app ใน requirements.txt
 
$ sudo pip3 install -r requirements.txt
 
ลองรันในเครื่องตัวเอง
 $ heroku local web
 
ตั้งค่า static files
 
ไป directory project
 
เปิด setting.py 
ใส่ import dj_database_urlว้ด้านบนของไฟล์
เลื่อนลงมาล่างสุด
ลบ
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/
STATIC_URL = '/static/'
ใส่
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) 
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/ 
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'staticfiles')
STATIC_URL = '/static/'# Extra places for collectstatic to find static files.
STATICFILES_DIRS = (
    os.path.join(PROJECT_ROOT, 'static'),
) 
# Simplified static file serving.
# https://warehouse.python.org/project/whitenoise/
STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage'  
 
สร้าง folder static ใน directory project
ใส่ไฟล์อะไรก็ได้ใน folder
 
จากนั้นทำการ collect static
python manage.py collectstatic
 
เปิดไฟล์ wsgi.py
ใส่ from whitenoise.django import DjangoWhiteNoise ว้ด้านบน
ใส่ application = DjangoWhiteNoise(application) ไว้ด้านล่าง   
 
จะเริ่ม deploy
 
สร้าง gitignore
กลับมา root dir ของโปรเจค
$  gedit .gitignore
ใส่ข้อความ
venv
*.pyc
staticfiles
.env 

จากนั้น commit
 
แล้วสร้าง app ใน heroku
$ heroku create
 
แล้ว push project ขึ้น heroku

เสร็จสิ้นเรียบร้อย

https://wordbank5720144.herokuapp.com/

ปัญหาจากการ Deploy ขึ้น Heroku

เมื่อใช้คำสั่ง sudo apt-get install จะขึ้นแบบนี้เสมอ


วิธีแก้ไขคือ ลง Ubuntu ใหม่

วันพุธที่ 24 กุมภาพันธ์ พ.ศ. 2559

Assignment 1 : Word Bank (Part 1)

ออกแบบหน้าเว็บไซต์ที่เราต้องการจะให้เป็น

หน้าแรกที่เข้าเว็บไซต์มา


เมื่อกด Search



Start Project ชื่อ WordBankWebsite

สร้าง git repository

และสร้าง app ชื่อ WordBank

จากนั้นสร้างไฟล์สำหรับ functional test


จะเริ่มจากการ functional test GUI ของ หน้า homepage ให้ได้แบบที่ต้องการ

User story



เริ่มเขียน functional test

เริ่มจากการทดสอบ title ว่ามีคำว่า Word Bank Website ไหม


พอลองรันเทส



แน่นอนว่าต้อง Error !!

มาทำให้เทสผ่านกันเถอะ

มาวิเคราะห์ดูแล้ว ที่ยังรันเทสไม่ผ่าน เพราะยังไม่มีหน้าแรกเลยด้วยซ้ำ

แสดงว่าเราต้องสร้าง index view

เขียน unit test เพื่อทำการเทสว่าเรามี index view แล้วหรือยัง


ลองเทส


ไม่พบ index

เพิ่ม index view ใน /WordBankWebsite/WordBank/views.py



จากตรง RegexURLPattern ดูเหมือนกับว่า มีปัญหาที่ URL

เรายังไม่ได้ URL Config เลย

สร้าง /WordBankWebsite/WordBank/urls.py


และแก้ไขไฟล์ /WordBankWebsite/WordBankWebsite/urls.py


ลองรันเทส


เทสผ่าน

ลองรัน functional test



เหตุผลที่ Unit Test ผ่าน เพราะ resolve ไปที่ '/' ได้ แต่ Functional Test ไม่ผ่าน เพราะถึงจะ resolve ไปได้ แต่เราก็ยังไม่มีหน้าเพจตามที่เราต้องการอยู่ดี

เขียน Unit Test เพื่อเทสว่า view return หน้าเพจ html ที่เราต้องการ




ทำการเขียนโค้ดเพื่อให้เทสผ่าน


ลองรันเทส


รัน Unit Test ผ่านแล้ว ลองรัน Functional Test




Functional Test ก็ผ่าน เย้ !!

แต่ว่า ด้วยความรู้ Django พื้นฐานที่มี เราสามารถใช้ Template ได้

จะทำการ Refactor index view

เซ็ต WordBank app ให้อยู่ใน INSTALLED_APPS

เปิด WordBankWebsite/WordBankWebsite/settings.py

เพิ่ม 'WordBank.apps.WordbankConfig', เข้าไป


สร้าง Directory /WordBankWebsite/WordBank/templates/WordBank/

และสร้างไฟล์ index.html ไว้ข้างใน


แต่เมื่อลองรันเทส


Unit Test เทสไม่ผ่าน แต่ Functional Test เทสผ่าน !!!

เป็นเพราะ response ที่ได้กลับมา ไม่ได้อยู่ในรูปของ text

ต้องแก้ไขเทส


 และแล้ว ก็รันเทสผ่าน


ในเมื่อเทสผ่านแล้ว รีแฟคเตอร์แล้ว ก็ทำการเขียน Functional test ต่อ และทำตาม TDD Cycle ต่อไป !!

วันอาทิตย์ที่ 14 กุมภาพันธ์ พ.ศ. 2559

Test-Driven Development with Python Chapter 4

โปรแกรมเมอร์เหมือนกับตักน้ำขึ้นมาจากบ่อ

ในการตักน้ำขึ้นมา แรกๆอาจจะยังง่ายๆอยู่ แต่เมื่อตักไปเรื่อยๆ คุณอาจจะเหนื่อย ต้องการการพัก คุณก็จะใช้วงล้อมาล็อคไว้ และทำให้คุณได้พัก

#TDDก็เช่นกัน

เพราะการ Programming แรกๆอาจจะยังง่ายๆอยู่ แต่เมื่อคุณทำไปเรื่อยๆ คุณต้องการการพัก TDD จะช่วยรักษาขั้นตอนและกระบวนการของคุณไว้ เพราะคุณมีเทสไว้ เมื่อกลับมาพัฒนาต่อ ก็จะไม่หลุดประเด็นจากที่ตั้งไว้


ในบางครั้งคุณอาจจะคิดว่า "ทำเกินไปหรือเปล่า ?" ที่มานั่งเทสสิ่งเล็กๆ ไปทีละสเต็ปเล็กๆ

แน่นอนแหละที่คุณคิดแบบนั้น มันเป็นธรรมชาติ แต่ TDD เป็นเหมือนระเบียบวินัย ที่คุณต้องบังคับตัวเองให้ปฏิบัติตาม เพราะมันจะส่งต่อคุณในระยะยาว


วันพุธที่ 10 กุมภาพันธ์ พ.ศ. 2559

Test-Driven Development with Python Chapter 3

ทดสอบ Homepage อย่างง่ายด้วย Unit Tests !!

จาก chapter ที่แล้ว ที่เรารัน functional test ไม่ผ่านเพราะเราต้องการให้มีคำว่า To-Do ใน title ของ homepage
คราวนี้แหละ จะถึงเวลาที่เราจะเริ่มทำแอพลิเคชั่น

เริ่มทำ Django App และทำ Unit Tests !!

สร้าง app ชื่อ lists สำหรับทำ To-Do lists

$ python3 manage.py startapp lists




ความแตกต่างระหว่าง Functional Tests และ Unit Tests

Functional Test เป็นการเทสจากภายนอก ในมุมมองของผู้ใช้ ส่วน Unit Test จะเป็นการเทสจากภายใน ในมุมมองของ Programmer

TDD ประกอบด้วยเทสทั้งสองแบบ โดยจะมีลำดับการทำงานดังนี้
1.เราจะเริ่มจากการเขียน Functional Test เพื่อระบุการทำงานของแอพพลิชั่น จากมุมมองของผู้ใช้
2.หลังจากเราเทส Functional Tests ไม่ผ่าน เราก็จะคิดถึงวิธีการที่จะเขียนโค้ดเพื่อให้เทสผ่าน โดยเราจะใช้ Unit test อย่างน้อย 1 Unit Tests เพื่อกำหนดว่าโค้ดของเราควรจะเป็นยังไง โดยแนวคิดคือ โค้ดแต่ละบรรทัดควรจะถูกเทสโดย Unit tests อย่างน้อย 1 Unit test
3.หลังจากเราเทส Unit Tests ไม่ผ่าน ให้เขียนโค้ดให้น้อยที่สุดเพื่อให้เทสผ่าน เราอาจจะทำ step 2 และ 3 หลายๆครั้ง จนกว่าเราคิดว่า Functional Test น่าจะเทสผ่านเยอะขึ้น
4.ลองเทส Functional Tests อีกรอบ และดูว่าเทสผ่านหรือยัง ถ้ายังไม่ผ่านให้เขียน Unit Tests และเขียนโค้ดเพิ่ม

Unit Tests ใน Django

เปิดไฟล์ tests.py ใน /superlists/lists

จะเห็นได้ว่า Django แนะนำให้เราใช้ TestCase ซึ่งเป็น unittest พื้นฐานที่ Django เพิ่มเติมบางส่วนมาเพื่อใช้งานกับ Django โดยเฉพาะ
สำหรับ Functional Tests เรารันด้วยตัวเองโดยตรง แต่ Unit test ที่เรากำลังเขียนจะรันโดยอัตโนมัติโดย Test runner

แก้ไขไฟล์ tests.py เป็นดังนี้

from django.test import TestCase

class SmokeTest(TestCase):
  def test_bad_maths(self):
    self.assertEqual(1 + 1, 3)

รันจากนั้นให้เรารันเทส โดย $ python3 manage.py test

จะเกิด Error ขึ้นมา

 

ดูเหมือนว่า test จะใช้งานได้

ให้ commit เก็บไว้

$ git add lists
$ git commit -m"Add app for lists, with deliberately failing unit test"

ต่อมา เราตั้งเป้าจะเทส 2 อย่าง
1.เราสามารถ resolve URL ที่ / ไปหา view ที่เราสร้างได้ไหม
2.เราสามารถทำให้ view return HTML ที่ทำให้ Functional Test ผ่านได้

เริ่มจากอย่างแรก แก้ไขไฟล์ tests.py

from django.core.urlresolvers import resolve
from django.test import TestCase
from lists.views import home_page #

class HomePageTest(TestCase):
  def test_root_url_resolves_to_home_page_view(self):
    found = resolve('/') #
    self.assertEqual(found.func, home_page)

เมื่อเราลองรันเทส เราจะรันไม่ผ่าน


ให้เราเขียนโค้ดเพื่อให้เทสผ่าน

แก้ไฟล์ views.py ใน /superlists/lists

from django.shortcuts import render

# Create your views here.
home_page = None

เมื่อรันเทส


จากการเทสด้านบน ทำให้เรารู้ว่า Django หา URL mapping ของ / ไม่เจอ

แก้ไฟล์ urls.py ใน /superlists/superlists

urlpatterns = patterns('',
  url(r'^$', 'lists.views.home_page', name='home'),
)

และแก้ views.py ใน /superlists/lists

from django.shortcuts import render

# Create your views here.
def home_page():
  pass

เมื่อลองรันเทสดู


เทสผ่าน เย้ !!

commit เก็บไว้

ต่อมาเราเทสอย่างที่สองกัน

ใส่โค้ด from django.http import HttpRequest เข้าไปใน Unit test

เพิ่ม Method ที่ไว้เทส Unit Test นี้เข้าไป

def test_home_page_returns_correct_html(self):
  request = HttpRequest() #
  response = home_page(request) #
  self.assertTrue(response.content.startswith(b'<html>')) #
  self.assertIn(b'<title>To-Do lists</title>', response.content) #
  self.assertTrue(response.content.endswith(b'</html>')) #

รันเทส


แก้ไข views.py เพื่อให้เทสผ่าน ดูว่าเทสไม่ผ่านเพราะอะไร และเพิ่มโค้ดไปเรื่อยๆ จนสุดท้ายใน views.py จะมีหน้าตาแบบนี้

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.
def home_page(request):
    return HttpResponse('<html><title>To-Do lists</title></html>')

แล้วเราก็จะเทสผ่าน

หลังจากนั้น ลองรัน Functional Test

ก็จะพบว่าเทสผ่าน !!

commit เก็บไว้ !!