Differences

This shows you the differences between two versions of the page.

py:web5_rev [2011/05/17 17:09] (current)
Line 1: Line 1:
 +[[python_curriculum|目次へ]]
 +
 +====== 練習問題2・指南編 ======
 +
 +[TODO:動物さん]
 +
 +回答をくださった方がいます。いつもありがとう。
 +
 +<box 100%>
 +<code python>
 +#!C:\python26\python.exe
 +#-*- coding: cp932 -*-
 +
 +print "Content-Type: text/html"
 +print ""
 +
 +import cgi
 +import datetime
 +import calendar
 +
 +#今日の年月を調べておく
 +cur_dt = datetime.date.today()
 +cur_year = cur_dt.year
 +cur_month = cur_dt.month
 +
 +#カレンダーの表示方法
 +##日曜日始まりで表示されるようにする
 +calendar.setfirstweekday(calendar.SUNDAY)
 +
 +##デフォルトでは今月のカレンダーを表示するよう設定
 +f = cgi.FieldStorage()
 +view_year = f.getfirst("year", cur_year)
 +view_month = f.getfirst("month", cur_month)
 +
 +##表示するカレンダーの年月に指定があった場合、その値でカレンダーを表示できるか調べる
 +try:
 +  view_year = int(view_year)
 +  view_month = int(view_month)
 +  print_cal = calendar.monthcalendar(view_year, view_month)
 +##表示できない(エラーになる)場合、今月のカレンダーを表示するよう設定
 +except (ValueError, OverflowError):
 +  view_year = cur_year
 +  view_month = cur_month
 +  print_cal = calendar.monthcalendar(view_year, view_month)
 +
 +#リンク先の年月を計算しておく
 +prev_month=view_month - 1
 +if prev_month >= 1:
 +  prev_year = view_year
 +else:
 +  prev_month = 12
 +  prev_year = view_year - 1
 +
 +next_month = view_month + 1
 +if next_month <= 12:
 +  next_year = view_year
 +else:
 +  next_month = 1
 +  next_year = view_year + 1
 +
 +#表示するページの内容
 +##ヘッダーとカレンダーの見出し
 +print """<html>
 +<head>
 +<title>calendar</title>
 +</head>
 +<body>
 +<h1>calendar</h1>"""
 +print "<h2>%s %d</h2>""" % (calendar.month_abbr[view_month], view_year)
 +
 +##前月・来月のカレンダーへのリンク
 +print "<p><a href=http://localhost/cgi-bin/calendar.cgi?year=%d&month=%d>" % (prev_year, prev_month)
 +print "<< previous month</a></p>"
 +print "<p><a href=http://localhost/cgi-bin/calendar.cgi?year=%d&month=%d>" % (next_year, next_month)
 +print "next month >></a></p>"
 +
 +##カレンダー本体
 +print """<table border = "1">
 +<tr>"""
 +###見出し(曜日略称)部分
 +print "<th bgcolor=#ffd700>Sun</th>"
 +for i in range(0, 6):
 +  print "<th>%s</th>" % calendar.day_abbr[i]
 +print "</tr>"
 +###本体(日付)部分
 +for wk in print_cal:
 +  print "<tr>"
 +  for dy in wk:
 +    dy = int(dy)
 +    if dy != 0:
 +      if dy == wk[0]:
 +        print "<td bgcolor=#ffd700>%d</td>" % dy
 +      else:
 +        print "<td>%d</td>" % dy
 +    else:
 +      print "<td>--</td>"
 +  print "</tr>"
 +print "</table>"
 +###今月のカレンダーへのリンク
 +print "<p><a href=http://localhost/cgi-bin/calendar.cgi?year=%d&month=%d>" % (cur_year, cur_month)
 +print "this month</a></p>"
 +
 +print """</body>
 +</html>"""
 +</code>
 +</box>
 +
 +こちらの環境で実行してみると、全く問題ありませんでした。設置するときのスクリプト名は、calendar.cgi ですね。お疲れ様でした。
 +
 +コードの要所ごとにコメントを書きいれてくれていますので、こちらとしても読みやすいです。
 +
 +指定された年月のカレンダーを試しに作ってみて、例外が生じる場合には今月のカレンダーを表示してあげるという部分もよいです。発生する可能性がある例外は二種類ですね、なるほど。
 +
 +カレンダーの月名を出すために、calendarモジュールの month_abbr 件数を探しだしたようで、これを活用してくれました。これは、1 なら Jan、2 なら Feb とかいう文字に直してくれるものなんですね。筆者も知りませんでしたよ。へえこう使うんですねえ。で、曜日名を出すのは、同様に day_abbr なんというものがあると。へー、へー。
 +
 +ひとつアドバイスを差し上げたいと思います。このCGIが生成するリンクは「前月」「来月」「今月」へのものですが、それぞれ、http://localhost/ で始まる、絶対URLという書き方であらわしてくれています。回答をくださった方の環境(An HTTPDが動いてる)では問題なく動くんですが、これを実際にどこかのサーバーに載せてみようとすると、そのサイトのドメイン名にあわせてスクリプト自体を書き変える必要が出てきます。(例:nantoka.com という名前のサーバーに載せたら、この名前に直さないとね。)
 +
 +これはいちいち煩雑になりますので、絶対URLではなくて、相対URLというリンク記述の方法を採るほうがこの場合は自然です。
 +
 +リンク先として、<a href="http://localhost/calendar.cgi?......"> じゃなくて、<a href="calendar.cgi?...."> と書いてしまう。こうすれば、どのサイトに載せてもリンク先は相対的にのみ決定されますから、より都合がよいのです。スクリプトの量もちょっと減りますし、たぶんこちらがいいですよ。ご参考に。
 +
 +<box 100%>
 +別のかたより。
 +<code python>
 +#!C:\python26\python.exe
 +# coding: cp932
 +
 +#print " show page # %s !" % pagenum
 +
 +import cgi, calendar
 +calendar.setfirstweekday(calendar.SUNDAY)
 +f = cgi.FieldStorage()
 +current_year = f.getfirst('year', 2011)
 +current_month = f.getfirst('month', 4)
 +calendar_nakami = calendar.monthcalendar(int(current_year), int(current_month))
 +
 +print "Content-Type: text/html"
 +print
 +print """<html>
 +<head>
 +<meta http-equiv="Content-Type" content="text/html; charset=shift_jis">"""
 +
 +print"    <title>%s年 %s月の暦</title>" % (current_year, current_month)
 +
 +print """<STYLE TYPE="text/css">
 +    TH {background-color:#007700; color:#ffffff;}
 +    TH.SUN {background-color:#770000}
 +    TH.SAT {background-color:#000077}
 +    TD {background-color:#eeffee; text-align: center}
 +    TD.SUN {color:#ff0000; background-color:#ffeeee}
 +    TD.SAT {color:#0000ff; background-color:#eeeeff}
 +    CAPTION {font-size: 200%; line-height: 200%;}
 +</STYLE>
 +</head>
 +<body>
 +
 +<table summary="カレンダー" border="0" cellpadding="10" cellspacing="1" align="center">"""
 +
 +print"    <caption>%s年 %s月の暦</caption>" % (current_year, current_month)
 +
 +print"""    <tr><th class="SUN">SUN</th>
 +        <th>MON</th>
 +        <th>TUE</th>
 +        <th>WED</th>
 +        <th>THU</th>
 +        <th>FRI</th>
 +        <th class="SAT">SAT</th>"""
 +
 +for current_week in calendar_nakami:
 +    i=0
 +    print "    <tr>"
 +    for dy in current_week:
 +        if i == 0:
 +            print "        <td class=\"SUN\">"
 +        elif i == 6:
 +            print "        <td class=\"SAT\">"
 +        else:
 +            print "        <td>"
 +        i=i+1
 +        if dy != 0:
 +            print "            %d</td>" % dy
 +        else:
 +            print "            &nbsp;</td>"
 +    print "    </tr>"
 +
 +#前月算出
 +if current_month == "1":
 +    last_year = str(int(current_year) - 1)
 +    last_month = 12
 +else:
 +    last_year = current_year
 +    last_month = str(int(current_month) - 1)
 +
 +#次月算出
 +if current_month == "12":
 +    next_year = str(int(current_year) + 1)
 +    next_month = 1
 +else:
 +    next_year = current_year
 +    next_month = str(int(current_month) +1)
 +
 +print "</table><br>"
 +print "<div align=\"center\"><a href=\"calendar.cgi?year=%s&month=%s\">前月</a>   <a href=\"calendar.cgi?year=%s&month=%s\">次月</a></div>" % (last_year, last_month, next_year, next_month)
 +print "</body></html>"
 +</code>
 +</box>
 +
 +この例も、スクリプト名を calendar.cgi として設置して、動作を確認させていただきました。はい、ばっちりです。表示が妙にキレイに仕上がっています。土曜日にまで色がついてらあ。凝りますなあ。スタイルシート記述(styleタグ)のことは、得に説明していないことですのでここでは触れませんが。
 +
 +このスクリプトは、リンク先を相対URLで書いてくれていまずので、筆者のテスト環境にも簡単に搭載して試すことができました。いいですね。「前月」と「来月」へのリンクがそれぞれ準備されていて、年をまたぐときも全く問題なく動作します。
 +
 +calendar_nakami なんて変数名はいいですね。カレンダーの中身をここに入れておくんだという意識が明確です。変に英語っぽくしようとこだわらない潔さです。読みやすいんだから文句なしです。
 +
 +あ、カレンダーの年月を指定しなかったときは、決め打ちで2011年4月となっているのですね。(この記事を執筆しているのは2011年5月。)ごく丁寧にやるなら、datetimeモジュールを使って「今の年月」を指定してもよかったかも知れません…が、これはこれで別に間違ったアプローチではありません。
 +
 +あと、あまり起きえないことではありますが、このスクリプトが動いているときに、ブラウザのアドレス欄を直接いじって、month=13 とかいう指定をこっそりしてみると、スクリプトはまともに実行できずにサーバーからのエラーメッセージが表示されてしまいます。まあ、わざわざそんな悪さをするやつはいないでしょうし、今は特に問題ではないです。もしもWebアプリを作るプロとかに任せるとしたら、彼らはこういうイタズラを防ぐための処理は忘れません。ユーザーは悪意を持ってアプリケーションをいじくりまわすに決まっているという発想をしますから、ちゃんと例外処理を書いたりするものです。「それやると、面倒だし、スクリプトは長くなるし、もしかしたら遅くなるし…」と、筆者でもつい考えてしまいがちですが、まあ、イタズラ対策にコストが必要になるというのは、Webアプリケーションというものの宿命ですね。
 +
 +さて、回答の例として今のところふたつ挙げさせていただきましたが、前月(年・月)と来月(年・月)を導出するときの処理はほぼ同じ発想ですね。丁寧に場合分けしてありますので、書くときも読むときも間違いが少なくなっています。たいへんよいです。回答をくださってありがとうございました。
 +
 
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki