ãã®ã¬ã€ãã®ç®çã¯ãããªãæ©èœçãªãã€ã¯ãããã°ã¢ããªã±ãŒã·ã§ã³ãéçºããããšã§ãããªãªãžããªãã£ãå®å šã«æ¬ åŠããŠããããããã€ã¯ãããã°ã¢ããªã±ãŒã·ã§ã³ãšåŒã¶ããšã«ããŸããã
ç®æ¬¡
ããŒã1ïŒHello WorldïŒ
ããŒã2ïŒãã³ãã¬ãŒã
ããŒã3ïŒãã©ãŒã
ããŒã4ïŒããŒã¿ããŒã¹
ããŒã5ïŒãŠãŒã¶ãŒãã°ã€ã³
ããŒã6ïŒãããã£ãŒã«ããŒãžãšã¢ãã¿ãŒ
ããŒã7ïŒåäœãã¹ã
ããŒã8ïŒãã©ãã¯ãŒãé£çµ¡å ãåé
ããŒã9ïŒããŒãžããŒã·ã§ã³
ããŒã10ïŒå šææ€çŽ¢
ããŒã11ïŒã¡ãŒã«ãµããŒã
ããŒã12ïŒåæ§æ
ããŒã13ïŒæ¥ä»ãšæå»
ããŒã14ïŒI18nããã³L10n
ããŒã15ïŒAjax
ããŒã16ïŒãããã°ããã¹ããããã³ãããã¡ã€ãªã³ã°ïŒãã®èšäºïŒ
ããŒã17ïŒLinuxïŒããã³Raspberry Piã§ãïŒã§ã®å±é
ããŒã18ïŒHeroku Cloudã§ã®ãããã€
ããŒã2ïŒãã³ãã¬ãŒã
ããŒã3ïŒãã©ãŒã
ããŒã4ïŒããŒã¿ããŒã¹
ããŒã5ïŒãŠãŒã¶ãŒãã°ã€ã³
ããŒã6ïŒãããã£ãŒã«ããŒãžãšã¢ãã¿ãŒ
ããŒã7ïŒåäœãã¹ã
ããŒã8ïŒãã©ãã¯ãŒãé£çµ¡å ãåé
ããŒã9ïŒããŒãžããŒã·ã§ã³
ããŒã10ïŒå šææ€çŽ¢
ããŒã11ïŒã¡ãŒã«ãµããŒã
ããŒã12ïŒåæ§æ
ããŒã13ïŒæ¥ä»ãšæå»
ããŒã14ïŒI18nããã³L10n
ããŒã15ïŒAjax
ããŒã16ïŒãããã°ããã¹ããããã³ãããã¡ã€ãªã³ã°ïŒãã®èšäºïŒ
ããŒã17ïŒLinuxïŒããã³Raspberry Piã§ãïŒã§ã®å±é
ããŒã18ïŒHeroku Cloudã§ã®ãããã€
ç§ãã¡ã®è¬èãªã¢ããªã¯ãªãªãŒã¹ã®æºåãã§ããŠããå åãèŠãå§ããŠããã®ã§ãã§ããã ããããæŽçããæãæ¥ãŸããã å°ãåãŸã§ããã®ããã°ã®èªè ã®1人ïŒhello GeorgeïŒïŒãå¥åŠãªããŒã¿ããŒã¹ã®åäœãå ±åããŸãããä»æ¥ã¯ãããã°ãè©Šã¿ãŸãã ããã«ãããã³ãŒããã©ãã ã泚ææ·±ãäœæãããã¹ãããé »åºŠã«é¢ä¿ãªããäžéšã®ãšã©ãŒãæ°ä»ãããªãããšãããããšãããããŸãã æ®å¿µãªãããããããçºèŠããã®ã¯éåžžãšã³ããŠãŒã¶ãŒã§ãã
ãã®ãšã©ãŒãä¿®æ£ããŠæ¬¡ã®ãšã©ãŒãæ€åºãããã®ãåŸ ã€ã®ã§ã¯ãªããèãããããšã©ãŒãæ€åºããããã®äºé²çãè¬ããŸãã
ãã®èšäºã®æåã®éšåã§ã¯ã ãããã°ã«ã€ããŠèª¬æããè€éãªåé¡ããããã°ãããšãã«äœ¿çšããããã€ãã®ããªãã¯ãšãã¯ããã¯ã玹ä»ããŸãã
åŸã§ããã¹ãæŠç¥ã®æå¹æ§ãè©äŸ¡ããæ¹æ³ã確èªããŸãã åäœãã¹ããã«ããŒããã³ãŒãã®éã枬å®ããŸããããããã¹ãã«ãã¬ããžãšåŒã³ãŸã ã
çµè«ãšããŠãå€ãã®ã¢ããªã±ãŒã·ã§ã³ããã°ãã°ééããå¥ã®ã¯ã©ã¹ã®åé¡-äžååãªããã©ãŒãã³ã¹ã«ã€ããŠèå¯ããŸãã ãããã¡ã€ãªã³ã°ææ³ãèŠãŠãã¢ããªã±ãŒã·ã§ã³ã®é ãéšåãèŠã€ããŸãã
ããã§ããïŒ ããã§ã¯å§ããŸãããã
ãšã©ãŒ
ãã®ããã°ã®èªè ã¯ããŠãŒã¶ãŒãæçš¿ãåé€ã§ããæ°ããæ©èœãå®è£ ããåŸã«åé¡ãçºèŠããŸããã ãã€ã¯ãããã°ã®å ¬åŒããŒãžã§ã³ã«ã¯ãã®æ©èœãå«ãŸããŠããªãã®ã§ããããã°ã§ããããã«ããã«å®è£ ããŸãã
æçš¿ãåé€ãã衚瀺æ©èœïŒapp / views.pyãã¡ã€ã«ïŒïŒ
@app.route('/delete/<int:id>') @login_required def delete(id): post = Post.query.get(id) if post == None: flash('Post not found.') return redirect(url_for('index')) if post.author.id != g.user.id: flash('You cannot delete this post.') return redirect(url_for('index')) db.session.delete(post) db.session.commit() flash('Your post has been deleted.') return redirect(url_for('index'))
ãã®æ©èœãæå¹ã«ããã«ã¯ãçŸåšã®ãŠãŒã¶ãŒã«å±ãããã¹ãŠã®æçš¿ã«åé€ãªã³ã¯ãè¿œå ããŸãïŒãã¡ã€ã«app / templates / post.htmlïŒïŒ
{% if post.author.id == g.user.id %} <div><a href="{{ url_for('delete', id = post.id) }}">{{ _('Delete') }}</a></div> {% endif %}
ç§ãã¡ã«ãšã£ãŠæ°ããããšã¯äœããããŸãããããã¯ä»¥åã«ãäœåºŠãè¡ã£ãŠããŸããã
ãããã°ãç¡å¹ïŒdebug = FalseïŒã«ããŠã¢ããªã±ãŒã·ã§ã³ãå®è¡ãããŠãŒã¶ãŒã®ç®ãéããŠèŠãŠã¿ãŸãããã
Linuxããã³MacãŠãŒã¶ãŒã¯ãã³ã³ãœãŒã«ã§å®è¡ããŸãã
$ ./runp.py
WindowsãŠãŒã¶ãŒã¯ãã³ãã³ãã·ã§ã«ã§æ¬¡ã®ã³ãã³ããå®è¡ããŸãã
flask/Scripts/python runp.py
ããã§ããŠãŒã¶ãŒãšããŠãæçš¿ãäœæããŠããåé€ããŠã¿ãŸãã ãããŠãåé€ãªã³ã¯ãã¯ãªãã¯ãããšããã«...ã¯ã ïŒ
ãšã©ãŒãçºçããããšã瀺ãçãã¡ãã»ãŒãžãåä¿¡ãã管çè ã«éç¥ãããŸãã å®éããã®æçš¿ã¯500.htmlãã³ãã¬ãŒãã§ãã ãããã°ããªãã«ãããšãFlaskã¯ãªã¯ãšã¹ãåŠçäžã«çºçãããã¹ãŠã®ãšã©ãŒã«å¯ŸããŠãã®ãã³ãã¬ãŒããè¿ããŸãã ãªããªã ããããã¯ã·ã§ã³ãã¢ãŒãã«ãªã£ãŠãããããå®éã®ãšã©ãŒã¡ãã»ãŒãžãåŒã³åºãã¹ã¿ãã¯ã衚瀺ãããŸããã
ãçŸå Žãåé¡ã®ãããã°
åäœãã¹ãã«é¢ããèšäºãèŠããŠãããªããã¢ããªã±ãŒã·ã§ã³ã®ãæ¬çªãããŒãžã§ã³ã§å®è¡ããããã«ããã€ãã®ãããã°ãµãŒãã¹ãã¢ã¯ãã£ãã«ããŸããã 次ã«ãã¢ããªã±ãŒã·ã§ã³ã®å®è¡äžã«ãšã©ãŒãšèšºæã¡ãã»ãŒãžããã°ãã¡ã€ã«ã«æžã蟌ããã¬ãŒãäœæããŸããã Flaskèªäœã¯ãçºçããäŸå€ã®åŒã³åºãã¹ã¿ãã¯ãèšé²ããèŠæ±ãå®äºãããŸã§åŠçãããŸããã§ããã ããã«ããšã©ãŒããã°ã«æžã蟌ããšãã«ç®¡çè ã®ãªã¹ãã®ãã¹ãŠã®ã¡ã³ããŒãéä¿¡ãããã¬ãŒãèšå®ããŸããã
ãããã£ãŠãäžèšã®ãããªãšã©ãŒãçºçããå Žåããã°ãã¡ã€ã«ãšé»åã¡ãŒã«ã®2ã€ã®å Žæã«ãã®æ§è³ªã«é¢ããæ å ±ãäžåºŠã«è¡šç€ºãããŸãã
åŒã³åºãã¹ã¿ãã¯ã®å 容ã¯ãšã©ãŒãä¿®æ£ããã®ã«ååã§ã¯ãªããããããŸãããããããã®å Žåãäœããªãããã¯ãŸãã§ãã æ¢åã®åé¡ã«ã€ããŠäœãç¥ããªããšããŸãã ãããŠãä»åºŠã¯ãã³ãŒã«ã¹ã¿ãã¯ã®å°å·ã®ã¿ã«åºã¥ããŠãäœãèµ·ãã£ãããå€æããå¿ èŠããããŸãã åŒã³åºãã¹ã¿ãã¯ã¯æ¬¡ã®ãšããã§ãã
127.0.0.1 - - [03/Mar/2013 23:57:39] "GET /delete/12 HTTP/1.1" 500 - Traceback (most recent call last): File "/home/microblog/flask/lib/python2.7/site-packages/flask/app.py", line 1701, in __call__ return self.wsgi_app(environ, start_response) File "/home/microblog/flask/lib/python2.7/site-packages/flask/app.py", line 1689, in wsgi_app response = self.make_response(self.handle_exception(e)) File "/home/microblog/flask/lib/python2.7/site-packages/flask/app.py", line 1687, in wsgi_app response = self.full_dispatch_request() File "/home/microblog/flask/lib/python2.7/site-packages/flask/app.py", line 1360, in full_dispatch_request rv = self.handle_user_exception(e) File "/home/microblog/flask/lib/python2.7/site-packages/flask/app.py", line 1358, in full_dispatch_request rv = self.dispatch_request() File "/home/microblog/flask/lib/python2.7/site-packages/flask/app.py", line 1344, in dispatch_request return self.view_functions[rule.endpoint](**req.view_args) File "/home/microblog/flask/lib/python2.7/site-packages/flask_login.py", line 496, in decorated_view return fn(*args, **kwargs) File "/home/microblog/app/views.py", line 195, in delete db.session.delete(post) File "/home/microblog/flask/lib/python2.7/site-packages/sqlalchemy/orm/scoping.py", line 114, in do return getattr(self.registry(), name)(*args, **kwargs) File "/home/microblog/flask/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1400, in delete self._attach(state) File "/home/microblog/flask/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1656, in _attach state.session_id, self.hash_key)) InvalidRequestError: Object '<Post at 0xff35e7ac>' is already attached to session '1' (this is '3')
ä»ã®ããã°ã©ãã³ã°èšèªã䜿çšããŠãããšãã«ãã®ãããªãšã©ãŒã¡ãã»ãŒãžãæ¢ã«èªãã§ããå Žåã¯ãPythonãåŒã³åºãã¹ã¿ãã¯ãéã®é åºã§è¡šç€ºããããšã«æ³šæããŠãã ãããã€ãŸãããšã©ãŒã®åå ãšãªã£ããã¬ãŒã ã¯äžçªäžã«ãããŸãã
ä»ãããã«ã©ã®ããã«å¯ŸåŠããŸããïŒ
åŒã³åºãã¹ã¿ãã¯ã®ããªã³ãã¢ãŠãããå€æãããšãsqlalchemy / orm / session.pyã«ããSQLAlchemyã»ãã·ã§ã³åŠçã³ãŒãã«ãã£ãŠäŸå€ãã¹ããŒãããŸããã
åŒã³åºãã¹ã¿ãã¯ã䜿çšããå Žåãç¬èªã®ã³ãŒãããæåŸã«å®è¡ãããåŒãèŠã€ããããšã¯åžžã«åœ¹ç«ã¡ãŸãã äžããå§ããŠããã¬ãŒã ããšã«åŸã ã«ãã©ãã¯ãç»ã£ãŠãããšãapp / views.pyãã¡ã€ã«ããŸãã¯ãããdeleteïŒïŒãã¥ãŒé¢æ°ã®db.session.deleteïŒpostïŒåŒã§4çªç®ã®ãã¬ãŒã ãèŠã€ãããŸãã
ããã§ãSQLAlchemyã¯çŸåšã®ããŒã¿ããŒã¹ã»ãã·ã§ã³ã§ãã®æçš¿ãåé€ã§ããªãããšãããããŸããã ãããããŸã çç±ã¯ããããŸããã
äžéšã®äŸå€ã®ããã¹ããèŠããšãåé¡ã¯Postãªããžã§ã¯ããã»ãã·ã§ã³ã1ãã«å±ããŠãããããåããªããžã§ã¯ããå¥ã®ã»ãã·ã§ã³ã3ãã«ã¢ã¿ããããããšããŠããããã§ãã
Googleã«å©ããæ±ãããšããããã®åé¡ã®ã»ãšãã©ã¯ããªããžã§ã¯ãã2ã€ã®ç°ãªãã»ãã·ã§ã³ã«äžåºŠã«æ¥ç¶ããããšãã2ã€ã®èŠæ±ãå®è¡ãããã«ãã¹ã¬ããWebãµãŒããŒã䜿çšãããšãã«çºçããããšãããããŸãã ãã ããã·ã³ã°ã«ã¹ã¬ããã®Pythonãããã°ãµãŒããŒã䜿çšããŠãããããããã¯åœãŠã¯ãŸããŸããã ãã®ããã1ã€ã§ã¯ãªã2ã€ã®ã¢ã¯ãã£ããªã»ãã·ã§ã³ãäœæããä»ã®åé¡ããããŸãã
åé¡ã®è©³çŽ°ã«ã€ããŠã¯ãããå¶åŸ¡ãããç°å¢ã§ãšã©ãŒãç¹°ãè¿ããŠã¿ãŠãã ããã 幞ããªããšã«ããã®ãšã©ãŒã¯ã¢ããªã±ãŒã·ã§ã³ã®ãéçºãããŒãžã§ã³ã«ã衚瀺ãããŸããããã¯ã500.htmlãã³ãã¬ãŒãã§ã¯ãªããFlaskèªäœãæäŸããWebããŒãžã§ã³ã®ã³ãŒã«ã¹ã¿ãã¯ã«ã¢ã¯ã»ã¹ã§ããããããããã«åªããŠããŸãã
ã³ãŒã«ã¹ã¿ãã¯ã®WebããŒãžã§ã³ã¯ãã³ãŒãã衚瀺ããåŒã®å®è¡ã®çµæãããã«ãã©ãŠã¶ãããã¹ãŠèŠãããšãã§ãããšããç¹ã§æ³šç®ã«å€ããŸãã ã³ãŒãã§äœãèµ·ãã£ãŠããã®ããååã«ç解ããŠããªããããäœããã®çç±ã§ããããäœæãããªã¯ãšã¹ãã®æåŸã®éåžžã®ã»ãã·ã§ã³ã®ããã«åé€ãããªãã£ãã1ãã®ã»ãã·ã§ã³ïŒãããäœæãããæåã®ã»ãã·ã§ã³ã§ãããšä»®å®ããããšãã§ããŸãïŒããããšæšæž¬ããŸãã»ãã·ã§ã³ã ãããã£ãŠãåé¡ã解決ããããã«åé²ããã«ã¯ã誰ããã®éå ¬éã»ãã·ã§ã³ãäœæããããç¥ãã®ãããã§ãããã
Pythonãããã¬ãŒã®äœ¿çš
ãªããžã§ã¯ãã®äœæè ãèŠã€ããæãç°¡åãªæ¹æ³ã¯ããªããžã§ã¯ãã®ã³ã³ã¹ãã©ã¯ã¿ãŒã«ãã¬ãŒã¯ãã€ã³ããèšå®ããããšã§ãã ãã¬ãŒã¯ãã€ã³ãã¯ãç¹å®ã®æ¡ä»¶ãæºãããããšãã«ããã°ã©ã ã®å®è¡ãäžæåæ¢ããã³ãã³ãã§ãã ãããŠããã®æç¹ã§ãããã°ã©ã ã調ã¹ããããã®ç¹å®ã®å®è¡ãã€ã³ãã§ã³ãŒã«ã¹ã¿ãã¯ã衚瀺ããããå€æ°ã®å 容ã衚瀺ããããå€æŽããããšããã§ããŸã ããã¬ãŒã¯ãã€ã³ãã¯ã ãããã¬ã®åºæ¬æ©èœã®1ã€ã§ãã ä»åã¯ãpdbãšåŒã°ããPythonã€ã³ã¿ãŒããªã¿ãŒã«ä»å±ã®ãããã¬ãŒã䜿çšããŸãã
ããããã©ã®ã¯ã©ã¹ãæ¢ããŠããŸããïŒ WebããŒãžã§ã³ã®ãã©ãã¯ã«æ»ã£ãŠèŠãŠã¿ãŸãããã ãã¬ãŒã¹ã®åãã¬ãŒã ã®äžéšã«ã¯ãã»ãã·ã§ã³ã䜿çšããã¯ã©ã¹ãèŠã€ããããã®ã³ãŒããã¥ãŒã¢ãŒãšPythonã³ã³ãœãŒã«ãžã®ãªã³ã¯ããããŸãïŒã¢ã€ã³ã³ã¯å³åŽã«ããããã¬ãŒã ã«ã«ãŒãœã«ãåããããšè¡šç€ºãããŸãïŒã ã³ãŒãããã«ã§ã¯ãæããã«SQLAlchemyããŒã¿ããŒã¹ã»ãã·ã§ã³ã®åºæ¬ã¯ã©ã¹ã§ããSessionã¯ã©ã¹å ã«ããããšãããããŸãã ã¹ã¿ãã¯ã®äžéšãã¬ãŒã ã®ã³ã³ããã¹ãã¯ã»ãã·ã§ã³ãªããžã§ã¯ãå ã«ãããããã³ã³ãœãŒã«ã§å®éã®ã»ãã·ã§ã³ã¯ã©ã¹ãååŸã§ããŸãã
>>> print self <flask_sqlalchemy._SignallingSession object at 0xff34914c>
ããã§ã䜿çšããã»ãã·ã§ã³ãFlask-SQLAlchemyã§å®çŸ©ãããŠããããšãããããŸãããããã¯ããã®æ¡åŒµæ©èœãSQLAlchemyããã±ãŒãžã®Sessionã¯ã©ã¹ãç¶æ¿ããç¬èªã®ã»ãã·ã§ã³ã¯ã©ã¹ãå®çŸ©ããŠããããã§ãã
ããã§ããã¡ã€ã«flask / lib / python2.7 / site-packages / flask_sqlalchemy.pyã«ããFlask-SQLAlchemyæ¡åŒµæ©èœã®ãœãŒã¹ã³ãŒãã調ã¹ã_SignallingSessionã¯ã©ã¹ã®__init __ïŒïŒã³ã³ã¹ãã©ã¯ã¿ãŒãèŠã€ããããšãã§ããŸãã ããã§ããããã°ã®æºåãæŽããŸããã
Pythonã¢ããªã±ãŒã·ã§ã³ã«ãã¬ãŒã¯ãã€ã³ããèšå®ããã«ã¯ãããã€ãã®æ¹æ³ããããŸãã æãç°¡åãªã®ã¯ãããã°ã©ã ãåæ¢ããå Žæã«æ¬¡ã®ã³ãŒããè¿œå ããããšã§ãã
import pdb; pdb.set_trace()
_SignallingSessionã¯ã©ã¹ã®ã³ã³ã¹ãã©ã¯ã¿ãŒã«äžæçã«ãã¬ãŒã¯ãã€ã³ããè¿œå ããããšã§è¡ãããšïŒãã¡ã€ã«flask / lib / python2.7 / site-packages / flask_sqlalchemy.pyïŒïŒ
class _SignallingSession(Session): def __init__(self, db, autocommit=False, autoflush=False, **options): import pdb; pdb.set_trace() # <-- this is temporary! self.app = db.get_app() self._model_changes = {} Session.__init__(self, autocommit=autocommit, autoflush=autoflush, extension=db.session_extensions, bind=db.engine, binds=db.get_binds(self.app), **options) # ...
ããã§ã¯ãã¢ããªã±ãŒã·ã§ã³ãå床å®è¡ããŠãäœãèµ·ãããèŠãŠã¿ãŸãããã
$ ./run.py > /home/microblog/flask/lib/python2.7/site-packages/flask_sqlalchemy.py(198)__init__() -> self.app = db.get_app() (Pdb)
ãªããªã ãRunning on ...ããšããã¡ãã»ãŒãžã¯è¡šç€ºãããŸããã§ãããããµãŒããŒããŸã èµ·åããŠããªãããšãããããŸãã ã³ãŒãã®äžéšã§èª°ãããç¥ç§çãªãã»ãã·ã§ã³ã®äœæãèŠæ±ãããããããã°ã©ã ã®å®è¡ãäžæãããŸããïŒ
ããã«çããªããã°ãªããªãæãéèŠãªè³ªåã¯ãã¢ããªã±ãŒã·ã§ã³ã®ã©ãã«ããã®ããšããããšã§ããããã¯ãããã°ã©ã ã®éäžã§åãé€ãããšãã§ããªãã»ãã·ã§ã³ã1ãã®äœæã誰ãèŠæ±ããããæããŠãããããã§ãã ã³ãŒã«ã¹ã¿ãã¯ã®å 容ãååŸããã«ã¯ãbtã³ãã³ãïŒbacktraceã®ç¥ïŒã䜿çšããŸãã
(Pdb) bt /home/microblog/run.py(2)<module>() -> from app import app /home/microblog/app/__init__.py(44)<module>() -> from app import views, models /home/microblog/app/views.py(6)<module>() -> from forms import LoginForm, EditForm, PostForm, SearchForm /home/microblog/app/forms.py(4)<module>() -> from app.models import User /home/microblog/app/models.py(92)<module>() -> whooshalchemy.whoosh_index(app, Post) /home/microblog/flask/lib/python2.6/site-packages/flask_whooshalchemy.py(168)whoosh_index() -> _create_index(app, model)) /home/microblog/flask/lib/python2.6/site-packages/flask_whooshalchemy.py(199)_create_index() -> model.query = _QueryProxy(model.query, primary_key, /home/microblog/flask/lib/python2.6/site-packages/flask_sqlalchemy.py(397)__get__() -> return type.query_class(mapper, session=self.sa.session()) /home/microblog/flask/lib/python2.6/site-packages/sqlalchemy/orm/scoping.py(54)__call__() -> return self.registry() /home/microblog/flask/lib/python2.6/site-packages/sqlalchemy/util/_collections.py(852)__call__() -> return self.registry.setdefault(key, self.createfunc()) > /home/microblog/flask/lib/python2.6/site-packages/flask_sqlalchemy.py(198)__init__() -> self.app = db.get_app() (Pdb)
åãšåãããã«ãäžããå§ããŠãã³ãŒããæ¢ããŠäžã«ç§»åããŸãã ãããŠãããã¯ãå šææ€çŽ¢ãåæåãããmodels.pyã¢ãã«ãã¡ã€ã«ã®è¡92ã«ãªããŸãã
whooshalchemy.whoosh_index(app, Post)
å€ã§ãã ããŒã¿ããŒã¹ã»ãã·ã§ã³ãäœæããããšãããã®ã»ãã·ã§ã³ãäœæããããšãè¡ããŸããããFlask-WhooshAlchemyã®åæåèªäœãã»ãã·ã§ã³ãäœæããããã§ãã
ããã¯ç§ãã¡ã®ééãã§ã¯ãªããSQLAlchemyãšWhooshã®ã©ãããŒæ¡åŒµã®éã«äœããã®ç«¶åãããããã§ãã ããã§ãããŠãããã2ã€ã®ãã°ãããæ¡åŒµæ©èœã®éçºè ãŸãã¯ã³ãã¥ããã£ã«å©ããæ±ããããšãã§ããŸãã ãŸãã¯ããããã°ãç¶è¡ããŠãããã§åé¡ã解決ã§ãããã©ããã確èªã§ããŸãã ãããã£ãŠããããã°ãç¶è¡ããŸããèå³ããªãå Žåã¯ã次ã®ã»ã¯ã·ã§ã³ã«èªç±ã«é²ãã§ãã ããã
ã³ãŒã«ã¹ã¿ãã¯ãããäžåºŠèŠãŠã¿ãŸãããã call whoosh_indexïŒïŒãåŒã³åºãã次ã«_create_indexïŒïŒãåŒã³åºããŸãã ç¹å®ã®è¡_create_indexïŒïŒã¯æ¬¡ã®ããã«ãªããŸãã
model.query = _QueryProxy(model.query, primary_key, searcher, model)
ãã®ã³ã³ããã¹ãã®ã¢ãã«å€æ°ã¯Postã¯ã©ã¹ãè¡šããwhoosh_indexïŒïŒé¢æ°ã®åŒæ°ãšããŠæž¡ããŸãã ãããèãããšãFlask-WhooshAlchemyã¯ãå ã®Post.queryãåŒæ°ã«å ããWooshåºæã®ã³ã³ãã³ããåãåãPost.queryã©ãããŒãäœæããããã§ãã ããããããã¯ãã§ã«èå³æ·±ãã§ãã äžèšã®ãã©ãã¯ããå€æãããšããã¥ãŒå ã®æ¬¡ã®é¢æ°ã¯ãPythonèšèªèšè¿°åã¡ãœããã® 1ã€ã§ãã__get __ïŒïŒã§ãã
__get __ïŒïŒã¡ãœããã¯ãå€ã«å ããŠç¹å®ã®åäœãæã€å±æ§ã§ããèšè¿°åãå®è£ ããããã«äœ¿çšãããŸãã èšè¿°åãèšåããããã³ã«ã__ get __ïŒïŒé¢æ°ãåŒã³åºãããŸãã 次ã«ãé¢æ°ã¯å±æ§å€ãè¿ãå¿ èŠããããŸãã ãã®ã³ãŒãè¡ã§èšåãããŠããå±æ§ã¯ã¯ãšãªã®ã¿ã§ããããã以åã¯ããŒã¿ããŒã¹ã¯ãšãªãçæããããã«äœ¿çšããŠããäžèŠã·ã³ãã«ãªå±æ§ãå®éã«ã¯èšè¿°åã§ãããå±æ§ã§ã¯ãªãããšãããããŸããã åŒã³åºãã¹ã¿ãã¯ã®æ®ãã¯ãmodel.queryåŒã®å€ãèšç®ãã_QueryProxyãªããžã§ã¯ãã®ã³ã³ã¹ãã©ã¯ã¿ãŒãäœæããæºåãããŸãã
ããã§ã¯ãå°ãäžã®ã¹ã¿ãã¯ãäžã£ãŠã次ã«äœãèµ·ãããèŠãŠã¿ãŸãããã __get __ïŒïŒã¡ãœããããã®åœä»€ã以äžã«ç€ºããŸãã
return type.query_classïŒããããŒãã»ãã·ã§ã³= self.sa.sessionïŒïŒïŒ
ãããŠãããã¯éåžžã«å¥åŠãªã³ãŒãã§ãã ããšãã°ãUser.query.getïŒidïŒãåŒã³åºããšãéæ¥çã«__get __ïŒïŒã¡ãœãããåŒã³åºããŠã¯ãšãªãªããžã§ã¯ããååŸããããã䜿çšããŠã»ãã·ã§ã³ãååŸããŸãã
Flask-WhooshAlchemyãmodel.queryãå®è¡ãããšãã»ãã·ã§ã³ãäœæãããã¯ãšãªãªããžã§ã¯ãã«ã¢ã¿ãããããŸãã ããããFlask-WhooshAlchemyã«ãã£ãŠãªã¯ãšã¹ãããããªã¯ãšã¹ããªããžã§ã¯ãã¯ããã¬ãŒã³ããŒã·ã§ã³é¢æ°å ã§å®è¡ãããã®ã»ã©çåœã§ã¯ãããŸããã Flask-WhooshAlchemyã¯ããã®ã¯ãšãªãªããžã§ã¯ããç¬èªã®ã¯ãšãªãªããžã§ã¯ãã§ã©ããããmodel.queryã«ä¿åãçŽããŸãã ãªããªã __set __ïŒïŒã¡ãœãããååšããªãå Žåãæ°ãããªããžã§ã¯ãã¯å±æ§ãšããŠä¿åãããŸãã Postã¯ã©ã¹ã®å Žåãããã¯ãFlask-WhooshAlchemyã®åæåãå®äºããåŸãåãååã®èšè¿°åãšå±æ§ãããããšãæå³ããŸãã åªå é äœã«åŸã£ãŠããã®å Žåãå±æ§ãåªå ãããŸããããã§ãªãå ŽåãWhooshã«åºã¥ãæ€çŽ¢ã¯æ©èœããŸããã
ããã®éèŠãªè©³çŽ°ã¯ãäžèšã®ã³ãŒããã1ãã»ãã·ã§ã³å ã«å«ãŸããå®æ°å±æ§ãèšå®ããããšã§ãã ã¢ããªã±ãŒã·ã§ã³ã«ãã£ãŠåŠçãããæåã®ãªã¯ãšã¹ãããã®ã»ãã·ã§ã³ã䜿çšããå®äºåŸããã«ãããå¿ãããšããäºå®ã«ãããããããPost.queryå±æ§ããããåç §ãç¶ãããããã»ãã·ã§ã³èªäœã¯ã©ãã«ãè¡ããŸããã ããã¯ãŸãã«ééãã§ãïŒ
ãã®ãšã©ãŒã¯ãïŒç§ã®æèŠã§ã¯ïŒèšè¿°åã®çŽããããæ§è³ªãåå ã§ãã ãããã¯æ®éã®å±æ§ã®ããã«èŠãã人ã ã¯ãã®ããã«äœ¿çšããŸãã Flask-WhooshAlchemyã®éçºè ã¯ãåã«ãªã¯ãšã¹ããå®è¡ããããã®æçšãªæ å ±ãä¿åããæ¡åŒµãªã¯ãšã¹ããªããžã§ã¯ããäœæãããã£ãã®ã§ãããã¯ãšãªã¢ãã«å±æ§ã䜿çšãããšãã»ãã·ã§ã³ãéãåäœãé ãããŠãããããèŠãç®ãããå°ãã ãå€ãã®ããšãè¡ãããããšã«æ°ä»ããŸããã§ããããŒã¿ããŒã¹ã§ã
ååž°ãã¹ã
å€ãã®å Žåããã®ç¶æ³ã§æãè«ççãªã¹ãããã¯ãFlask-WhooshAlchemyãšã©ãŒãä¿®æ£ããŠå ã«é²ãããã§ãã ãããããããè¡ã£ãå Žåãå°æ¥ãã®ãããªééãããªãããšãä¿èšŒãããã®ã¯äœã§ããïŒ ããšãã°ã1幎ã§Flask-WhooshAlchemyãæ°ããããŒãžã§ã³ã«ã¢ããã°ã¬ãŒãããç·šéãå¿ããå Žåã¯ã©ããªããŸããïŒ
ãšã©ãŒãæ€åºããããã®æè¯ã®ãªãã·ã§ã³ã¯ããšã©ãŒãå°æ¥åçºããã®ãé²ãããã®åäœãã¹ãïŒããããååž° ïŒãäœæããããšã§ãã
ãã ããåããã¹ãå ã§2ã€ã®ã¯ãšãªããšãã¥ã¬ãŒãããå¿ èŠãããããããã®ãšã©ãŒã®ãã¹ããäœæããã®ã¯å€å°å°é£ã§ãã æåã®ãªã¯ãšã¹ãã¯Postãªããžã§ã¯ãã«ã¢ã¯ã»ã¹ããããŒãžã«ããŒã¿ã衚瀺ããããã«äœæãããªã¯ãšã¹ãããšãã¥ã¬ãŒãããŸãã ãããæåã®ãªã¯ãšã¹ãã§ãããããã»ãã·ã§ã³ã1ãã䜿çšããŸãã 次ã«ããã®ã»ãã·ã§ã³ãå¿ããŠãFlask-SQLAlchemyãšãŸã£ããåãããã«æ°ããã»ãã·ã§ã³ãäœæããå¿ èŠããããŸãã æåã®ã»ãã·ã§ã³ãæåŸ ã©ããã«çµäºããªãããã2çªç®ã®ã»ãã·ã§ã³ã§Postãªããžã§ã¯ããåé€ããããšãããšããã®ãšã©ãŒãå床çºçããŸãã
Flask-SQLAlchemyã®ãœãŒã¹ã³ãŒããããäžåºŠèŠããšãdb.create_scoped_sessionïŒïŒé¢æ°ã«ãã£ãŠæ°ããã»ãã·ã§ã³ãäœæããããªã¯ãšã¹ããå®äºãããšãdb.session.removeïŒïŒé¢æ°ãåŒã³åºãããšã§ã»ãã·ã§ã³ãç Žæ£ãããããšãããããŸãã ãããç¥ã£ãŠããã®ã§ããã®ãšã©ãŒã®ãã¹ããæžãã®ã¯éåžžã«ç°¡åã§ãã
def test_delete_post(self): # create a user and a post u = User(nickname = 'john', email = 'john@example.com') p = Post(body = 'test post', author = u, timestamp = datetime.utcnow()) db.session.add(u) db.session.add(p) db.session.commit() # query the post and destroy the session p = Post.query.get(1) db.session.remove() # delete the post using a new session db.session = db.create_scoped_session() db.session.delete(p) db.session.commit()
ãããŠããã¡ããããã¹ããå®è¡ãããšã倱æãããã¹ãã«é¢ããã¡ãã»ãŒãžã衚瀺ãããŸãã
$ ./tests.py .E.... ====================================================================== ERROR: test_delete_post (__main__.TestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "./tests.py", line 133, in test_delete_post db.session.delete(p) File "/home/microblog/flask/lib/python2.7/site-packages/sqlalchemy/orm/scoping.py", line 114, in do return getattr(self.registry(), name)(*args, **kwargs) File "/home/microblog/flask/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1400, in delete self._attach(state) File "/home/microblog/flask/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1656, in _attach state.session_id, self.hash_key)) InvalidRequestError: Object '<Post at 0xff09b7ac>' is already attached to session '1' (this is '3') ---------------------------------------------------------------------- Ran 6 tests in 3.852s FAILED (errors=1)
èšæ£
ãã®åé¡ã解決ããã«ã¯ãFlask-WhooshAlchemyãªã¯ãšã¹ããªããžã§ã¯ããã¢ãã«ã«ãã€ã³ãããå¥ã®æ¹æ³ãèŠã€ããå¿ èŠããããŸãã
Flask-SQLAlchemyã®ããã¥ã¡ã³ãã«ã¯ãã¯ãšãªã®å®è¡ã«äœ¿çšãããã¯ã©ã¹ãå«ãmodel.query_classå±æ§ãèšèŒãããŠããŸãã å®éãããã¯ãFlask-WhooshAlchemyã§äœ¿çšããããã®ããããFlask-SQLAlchemyãå€æŽãããã¯ãšãªã¯ã©ã¹ã䜿çšããããã«åŒ·å¶ãããã¯ããã«ééçã§ç解å¯èœãªæ¹æ³ã§ãã Flask-SQLAlchemyãèšå®ããŠWhooshã¯ãšãªã¯ã©ã¹ïŒFlask-SQLAlchemyããBaseQueryã¯ã©ã¹ãç¶æ¿ïŒã䜿çšããŠã¯ãšãªãäœæããå Žåãçµæã¯å€ãããŸãããããšã©ãŒã¯æ¶ããŸãã
ãããã®å€æŽãå®è£ ããgithubã§Flask-WhooshAlchemyãããžã§ã¯ãã®ãã©ãŒã¯ãäœæããŸããã å€æŽãç解ãããå Žå㯠ãã³ãããã®github diffãèŠãããæ¡åŒµæ©èœå šäœãããŠã³ããŒãããŠå ã®flask_whooshalchemy.pyãã¡ã€ã«ã«çœ®ãæããããšãã§ããŸãã
ç§ã¯éçºè Flask-WhooshAlchemyã«è¡ã£ãå€æŽãéä¿¡ããŸããããä»åºŠã¯ããããå ¬åŒããŒãžã§ã³ã«å«ãŸããããšãé¡ã£ãŠããŸãã
ãã¹ãã«ãã¬ããž
ã¢ããªã±ãŒã·ã§ã³ããµãŒããŒã«ãããã€ããåŸã«ãšã©ãŒã®å¯èœæ§ãå€§å¹ ã«æžãã1ã€ã®æ¹æ³ã¯ããã¹ãç¯å²ãå³ããããããšã§ãã æ¢ã«ãã¹ããã¬ãŒã ã¯ãŒã¯ããããŸãããã¢ããªã±ãŒã·ã§ã³ã®ã©ã®éšåãå®éã«äœ¿çšãããŠããããã©ã®ããã«ãã¹ãããããã©ã®ããã«ããŠç¥ãã®ã§ãããã
ãã¹ãã«ãã¬ããžæž¬å®ããŒã«ã¯ãå®è¡äžã®ã¢ããªã±ãŒã·ã§ã³ã調ã¹ãŠãå®è¡äžãŸãã¯å®è¡äžã§ãªãã³ãŒãè¡ãããŒã¯ã§ããŸãã å®äºåŸãéå§ãããªãã£ãã³ãŒãã®è¡ã瀺ãã¬ããŒããçºè¡ããŸãã ãã¹ãçšã«ãã®ãããªã¬ããŒããåãåã£ãããå®äºãããã¹ãã«ãã£ãŠã³ãŒãã®ã©ã®éšåã圱é¿ãåããªãã£ãããæ£ç¢ºã«å€æã§ããŸãã
Pythonã«ã¯ãç°¡åãªååcoverageãåããç¬èªã®ãã¹ãã«ãã¬ããžæž¬å®ããŒã«ããããŸãã ã€ã³ã¹ããŒã«ããŸãããïŒ
flask/bin/pip install coverage
ãã®ããŒã«ã¯ãã³ãã³ãã©ã€ã³ãã䜿çšããããã¹ã¯ãªããã«çŽæ¥åŒã³åºããåã蟌ãããšãã§ããŸãã 誀ã£ãŠå®è¡ããã®ãå¿ããªãããã«ãæåŸã®ãªãã·ã§ã³ãéžæããŸãã
ã¬ããŒãïŒtests.pyãã¡ã€ã«ïŒãçæããããã«ãã¹ãã«è¿œå ããå¿ èŠãããå€æŽã¯æ¬¡ã®ãšããã§ãã
from coverage import coverage cov = coverage(branch = True, omit = ['flask/*', 'tests.py']) cov.start() # ... if __name__ == '__main__': try: unittest.main() except: pass cov.stop() cov.save() print "\n\nCoverage Report:\n" cov.report() print "HTML version: " + os.path.join(basedir, "tmp/coverage/index.html") cov.html_report(directory = 'tmp/coverage') cov.erase()
ã¹ã¯ãªããã®æåã«ã«ãã¬ããžã¢ãžã¥ãŒã«ãåæåããããšããå§ããŸãã branch = Trueãã©ã¡ãŒã¿ãŒã¯ãéåžžã®è¡ããšã®ã«ãã¬ããžãã§ãã¯ã«å ããŠãå®è¡ãã©ã³ãã®åæã®å¿ èŠæ§ã瀺ããŸãã é€å€ãã©ã¡ãŒã¿ãŒã¯ãä»®æ³ç°å¢ã«ã€ã³ã¹ããŒã«ãããŠãããã¹ãŠã®ãµãŒãããŒãã£ã¢ãžã¥ãŒã«ãšãã¹ããã¬ãŒã ã¯ãŒã¯èªäœããã¹ãããé€å€ããããã«å¿ èŠã§ããããã¯ãã¢ããªã±ãŒã·ã§ã³ã®ã¿ã®ã³ãŒããåæããããšã«é¢å¿ãããããã§ãã
ã«ãã¬ããžçµ±èšãåéããã«ã¯ãcov.startïŒïŒãåŒã³åºããŠãããŠããããã¹ããå®è¡ããŸããããããªããšããã¹ãã«ãã¬ããžã¬ããŒããçæããã«ã¹ã¯ãªãããçµäºããããããã¹ããã¬ãŒã ã¯ãŒã¯ã§çºçããäŸå€ããã£ããããŠã¹ãããããå¿ èŠããããŸãããã¹ããçµäºããããcov.stopïŒïŒã§ã«ãã¬ããžãåæ¢ããcov.saveïŒïŒã§çµæãèšé²ããŸããæçµçã«ãcov.reportïŒïŒã¯çµ±èšãã³ã³ãœãŒã«ã«åºåããcov.html_reportïŒïŒã¯åãããŒã¿ã§ããé åçãªHTMLã¬ããŒããçæããcov.eraseïŒïŒã¯ããŒã¿ãã¡ã€ã«ãåé€ããŸãã
以äžã¯ãã¬ããŒãçæãã¢ã¯ãã£ãã«ããŠãã¹ããå®è¡ããäŸã§ãïŒæ³šæïŒèœäžãã¹ãã¯çµäºããŸããïŒã
$ ./tests.py .....F ====================================================================== FAIL: test_translation (__main__.TestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "./tests.py", line 143, in test_translation assert microsoft_translate(u'English', 'en', 'es') == u'Inglés' AssertionError ---------------------------------------------------------------------- Ran 6 tests in 3.981s FAILED (failures=1) Coverage Report: Name Stmts Miss Branch BrMiss Cover Missing ------------------------------------------------------------ app/__init__ 39 0 6 3 93% app/decorators 6 2 0 0 67% 5-6 app/emails 14 6 0 0 57% 9, 12-15, 21 app/forms 30 14 8 8 42% 15-16, 19-30 app/models 63 8 10 1 88% 32, 37, 47, 50, 53, 56, 78, 90 app/momentjs 12 5 0 0 58% 5, 8, 11, 14, 17 app/translate 33 24 4 3 27% 10-36, 39-56 app/views 169 124 46 46 21% 16, 20, 24-30, 34-37, 41, 45-46, 53-67, 75-81, 88-109, 113-114, 120-125, 132-143, 149-164, 169-183, 188-198, 203-205, 210-211, 218 config 22 0 0 0 100% ------------------------------------------------------------ TOTAL 388 183 74 61 47% HTML version: /home/microblog/tmp/coverage/index.html
ã¬ããŒãã«ãããšãã¢ããªã±ãŒã·ã§ã³ã®47ïŒ ããã¹ãã§ã«ããŒããŸããããŸãããã¹ãã«ãã£ãŠèµ·åãããªãã£ãè¡ã®ãªã¹ããååŸããŸãããã€ãŸãããããã®è¡ã調ã¹ãŠãã©ã®ãã¹ããããããã«ããŒã§ããããèããå¿ èŠããããŸãã
ãŸããäž»ã«ã¢ãã«ã®ãã¹ãã«éç¹ã眮ããŠãããããapp / models.pyã¢ãžã¥ãŒã«ã®ã«ãã¬ããžãããªãé«ãïŒ88ïŒ ïŒããšãããããŸãããçŸåšããã¹ãã§ã¯è¡šçŸã®æ©èœãå®è¡ããŠããªããããapp / views.pyã¢ãžã¥ãŒã«ã®ã«ãã¬ããžã¯æ¯èŒçäœããªã£ãŠããŸãïŒ21ïŒ ïŒã
ãã®ããŒã«ã¯ããã¹ãã§ã¹ããããããè¡ã«å ããŠãBranchåãšBrMissåã®å®è¡ãã©ã³ãã®ã«ãã¬ããžã«é¢ããæ å ±ãæäŸããŸãã次ã®ã¹ã¯ãªããäŸãæ€èšããŠãã ããã
def f(x): if x >= 0: x = x + 1 return x f(1)
ãã®åçŽãªé¢æ°ã®ã«ãã¬ããžãå®è¡ãããšã100ïŒ ã®ã«ãã¬ããžãåŸãããŸããå ¥åã§1ãååŸãããšãé¢æ°ã¯3è¡ãã¹ãŠãå®è¡ããŸãããããã0æªæºã®åŒæ°ã§ãã®é¢æ°ãå®è¡ããªãã£ããããç°ãªãåäœãçºçããå¯èœæ§ããããŸããããè€éãªã±ãŒã¹ã§ã¯ãããã«ãããšã©ãŒãçºçããå¯èœæ§ããããŸãã
å®è¡ãã©ã³ãã®ã«ãã¬ããžããã§ãã¯ãããšãã¹ãããããå®è¡ãã©ã³ãã®æ°ãããããŸããããã¯ãååã§ãªãå¯èœæ§ããããã¹ãã«ã€ããŠèãããã1ã€ã®çç±ã§ãã
ã«ãã¬ããžã¢ãžã¥ãŒã«ã¯ããã¹ãã§èŠããã¹ããããããè²ä»ãã®è¡ãšå®è¡ãã©ã³ããå«ããœãŒã¹ã³ãŒãã衚瀺ããHTMLã¬ããŒããçæããŸãã
äž»ã«ã¢ãã«ãã¹ãã«çŠç¹ãåœãŠãæŠç¥ãç¶ç¶ãããã¹ãã§ã«ããŒãããŠããªãapp / models.pyãã¡ã€ã«ã®éšåãèæ ®ããããšãã§ããŸããããã¯HTMLã¬ããŒãã䜿çšããŠéåžžã«ç°¡åã«è¡ãããšãã§ãããããã次ã®ãªã¹ããååŸããŸãã
- User.make_valid_nicknameïŒïŒ
- User.is_authenticatedïŒïŒ
- User.is_activeïŒïŒ
- User.is_anonymousïŒïŒ
- User.get_idïŒïŒ
- ãŠãŒã¶ãŒ.__ repr __ïŒïŒ
- æçš¿.__ repr __ïŒïŒ
User.make_unique_nicknameïŒïŒïŒçµæã®ååãäžæã§ãããå€æ
ãå¿ èŠãšããªãå Žåãå®è¡ãã©ã³ãã®ã¿ïŒæ¬¡ã®ãã¹ãã§æåã®5ã€ã®ãã©ã°ã¡ã³ããçµåã§ããŸãã
def test_user(self): # make valid nicknames n = User.make_valid_nickname('John_123') assert n == 'John_123' n = User.make_valid_nickname('John_[123]\n') assert n == 'John_123' # create a user u = User(nickname = 'john', email = 'john@example.com') db.session.add(u) db.session.commit() assert u.is_authenticated() == True assert u.is_active() == True assert u.is_anonymous() == False assert u.id == int(u.get_id())
__repr __ïŒïŒé¢æ°ã¯å éšäœ¿çšå°çšã§ããããããã¹ãããå¿ èŠã¯ãããŸããããã®ãããããããç¡èŠãšããŠããŒã¯ã§ããŸãã
def __repr__(self): # pragma: no cover return '<User %r>' % (self.nickname)
æåŸã«ãmake_unique_nicknameïŒïŒã®ãã¹ããäœæãããšããååã®ç«¶åã®åŠçã«çŠç¹ãåãããŸããããååãäžæã§ãããåŠçãå¿ èŠãšããªãå Žåã®ãã¹ããè¿œå ããã®ãå¿ããŸããããã®ã±ãŒã¹ãã«ããŒããããã«æ¢åã®ãã¹ããæ¡åŒµã§ããŸãã
def test_make_unique_nickname(self): # create a user and write it to the database u = User(nickname = 'john', email = 'john@example.com') db.session.add(u) db.session.commit() nickname = User.make_unique_nickname('susan') assert nickname == 'susan' nickname = User.make_unique_nickname('john') assert nickname != 'john' #...
ãããŠããããã®ç°¡åãªä¿®æ£ã®ãããã§ãmodels.pyã¢ãã«ãã¡ã€ã«ã100ïŒ ã«ããŒããŠããŸãã
ãšãããããããã§åæ¢ããŸãããããããããã€ããã«ãã¬ããžã®äœæ¥ãç¶è¡ãããã¬ãŒã³ããŒã·ã§ã³æ©èœããã¹ãããããã®è¯ãæ¹æ³ãèãåºãããšã決å®ããã§ãããããä»ã®ãšãããããŒã¿ããŒã¹äœæ¥ã³ãŒãããã¹ãã§å®å šã«ã«ããŒããããšãç¥ãã ãã§ååã§ãããã
ããã©ãŒãã³ã¹ãããã¡ã€ãªã³ã°
ãã®æ¥ã®æ¬¡ã®ãããã¯ã¯ããã©ãŒãã³ã¹ã§ããé·ãããŒãžã®èªã¿èŸŒã¿ã»ã©ããµã€ãã®ãŠãŒã¶ãŒã«ãšã£ãŠã²ã©ããã®ã¯ãããŸãããã¢ããªã±ãŒã·ã§ã³ãå¯èœãªéãé«éã§å®è¡ããããšã確èªãããã®ã§ãããã©ãŒãã³ã¹ã®åé¡ã解決ããããã«æºåããããã«ããã€ãã®å¯Ÿçãè¬ããå¿ èŠããããŸãã
é©çšããææ³ã¯ããããã¡ã€ãªã³ã°ãšåŒã°ããŸããããããã¡ã€ã©ãŒã¯ãã«ãã¬ããžãšåãæ¹æ³ã§å®è¡å¯èœããã°ã©ã ãç£èŠããŸãããå®è¡äžã®è¡ãã¹ããããããè¡ãèæ ®ãã代ããã«ãåé¢æ°ã«è²»ããããæéãèæ ®ããŸãããããã¡ã€ãªã³ã°ã®æåŸã«ãå®è¡ããããã¹ãŠã®æ©èœã®ãªã¹ããšãããããã«è²»ããããæéã«é¢ããæ å ±ã衚瀺ãããŸãããã®ãªã¹ãã¯ãã©ã³ã¿ã€ã ã§ãœãŒããããäž»ã«ã©ã³ã¿ã€ã ãæé©åããããã«æ³šæããã³ãŒãã®ã»ã¯ã·ã§ã³ã瀺ããŸãã
Pythonã«ã¯é©åãªcProfileãããã¡ã€ã©ãŒãä»å±ããŠããŸãããã®ãããã¡ã€ã©ãŒãã¢ããªã±ãŒã·ã§ã³ã«çŽæ¥åã蟌ãããšãã§ããŸããããããè¡ãåã«ãæ¢è£œã®ãœãªã¥ãŒã·ã§ã³ãæ¢ã䟡å€ããããŸãããFlaskãããã¡ã€ã©ãŒããšãããã¬ãŒãºããã°ããæ€çŽ¢ãããšãFlaskã§äœ¿çšãããWerkzeugã«ã¯ããã«äœ¿çšã§ãããããã¡ã€ã©ãŒã¢ãžã¥ãŒã«ãä»å±ããŠããããã䜿çšã§ããã®ã¯ããã ãã§ããããšãããããŸãã
Werkzeugãããã¡ã€ã©ãŒãã¢ã¯ãã£ãã«ããã«ã¯ãrun.pyãªã©ã®å¥ã®ã¹ã¿ãŒãã¢ããã¹ã¯ãªãããäœæããŸããprofile.pyãšåŒã³ãŸãããïŒ
#!flask/bin/python from werkzeug.contrib.profiler import ProfilerMiddleware from app import app app.config['PROFILE'] = True app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions = [30]) app.run(debug = True)
ãã®ã¹ã¯ãªããã䜿çšããŠã¢ããªã±ãŒã·ã§ã³ãèµ·åãããšããããã¡ã€ã©ãŒã¯åãªã¯ãšã¹ãã®æé·30åã®é¢æ°ã衚瀺ã§ããŸãïŒå¶éã®è©³çŽ°ã«ã€ããŠã¯ãããã¥ã¡ã³ããã芧ãã ããïŒã
ã¢ããªã±ãŒã·ã§ã³ãèµ·åãããšãåãªã¯ãšã¹ãã«ãããã¡ã€ã©ãŒæ å ±ã衚瀺ãããŸãã以äžã«äŸã瀺ããŸãã
-------------------------------------------------------------------------------- PATH: '/' 95477 function calls (89364 primitive calls) in 0.202 seconds Ordered by: internal time, call count List reduced from 1587 to 30 due to restriction <30> ncalls tottime percall cumtime percall filename:lineno(function) 1 0.061 0.061 0.061 0.061 {method 'commit' of 'sqlite3.Connection' objects} 1 0.013 0.013 0.018 0.018 flask/lib/python2.7/site-packages/sqlalchemy/dialects/sqlite/pysqlite.py:278(dbapi) 16807 0.006 0.000 0.006 0.000 {isinstance} 5053 0.006 0.000 0.012 0.000 flask/lib/python2.7/site-packages/jinja2/nodes.py:163(iter_child_nodes) 8746/8733 0.005 0.000 0.005 0.000 {getattr} 817 0.004 0.000 0.011 0.000 flask/lib/python2.7/site-packages/jinja2/lexer.py:548(tokeniter) 1 0.004 0.004 0.004 0.004 /usr/lib/python2.7/sqlite3/dbapi2.py:24(<module>) 4 0.004 0.001 0.015 0.004 {__import__} 1 0.004 0.004 0.009 0.009 flask/lib/python2.7/site-packages/sqlalchemy/dialects/sqlite/__init__.py:7(<module>) 1808/8 0.003 0.000 0.033 0.004 flask/lib/python2.7/site-packages/jinja2/visitor.py:34(visit) 9013 0.003 0.000 0.005 0.000 flask/lib/python2.7/site-packages/jinja2/nodes.py:147(iter_fields) 2822 0.003 0.000 0.003 0.000 {method 'match' of '_sre.SRE_Pattern' objects} 738 0.003 0.000 0.003 0.000 {method 'split' of 'str' objects} 1808 0.003 0.000 0.006 0.000 flask/lib/python2.7/site-packages/jinja2/visitor.py:26(get_visitor) 2862 0.003 0.000 0.003 0.000 {method 'append' of 'list' objects} 110/106 0.002 0.000 0.008 0.000 flask/lib/python2.7/site-packages/jinja2/parser.py:544(parse_primary) 11 0.002 0.000 0.002 0.000 {posix.stat} 5 0.002 0.000 0.010 0.002 flask/lib/python2.7/site-packages/sqlalchemy/engine/base.py:1549(_execute_clauseelement) 1 0.002 0.002 0.004 0.004 flask/lib/python2.7/site-packages/sqlalchemy/dialects/sqlite/base.py:124(<module>) 1229/36 0.002 0.000 0.008 0.000 flask/lib/python2.7/site-packages/jinja2/nodes.py:183(find_all) 416/4 0.002 0.000 0.006 0.002 flask/lib/python2.7/site-packages/jinja2/visitor.py:58(generic_visit) 101/10 0.002 0.000 0.003 0.000 flask/lib/python2.7/sre_compile.py:32(_compile) 15 0.002 0.000 0.003 0.000 flask/lib/python2.7/site-packages/sqlalchemy/schema.py:1094(_make_proxy) 8 0.002 0.000 0.002 0.000 {method 'execute' of 'sqlite3.Cursor' objects} 1 0.002 0.002 0.002 0.002 flask/lib/python2.7/encodings/base64_codec.py:8(<module>) 2 0.002 0.001 0.002 0.001 {method 'close' of 'sqlite3.Connection' objects} 1 0.001 0.001 0.001 0.001 flask/lib/python2.7/site-packages/sqlalchemy/dialects/sqlite/pysqlite.py:215(<module>) 2 0.001 0.001 0.002 0.001 flask/lib/python2.7/site-packages/wtforms/form.py:162(__call__) 980 0.001 0.000 0.001 0.000 {id} 936/127 0.001 0.000 0.008 0.000 flask/lib/python2.7/site-packages/jinja2/visitor.py:41(generic_visit) -------------------------------------------------------------------------------- 127.0.0.1 - - [09/Mar/2013 19:35:49] "GET / HTTP/1.1" 200 -
ãã®ã¬ããŒãã®åã¯æ¬¡ã®ããšã瀺ããŠããŸãã
- ncalls: .
- tottime: , .
- percall: tottime ncalls.
- cumtime: .
- percall: cumtime ncalls.
- filename:lineno(function): .
ããã§ãã³ãã¬ãŒããé¢æ°ã®åœ¢ã§ããã«ããããšã泚ç®ã«å€ããŸããããã¯ãJinja2ããã³ãã¬ãŒããPythonã³ãŒãã«ã³ã³ãã€ã«ããããã§ããããã¯ããããã¡ã€ã©ãŒãé ãã³ãŒãã ãã§ãªããé ããã¿ãŒã³ã«ã€ããŠãæããŠãããããšãæå³ããŸãïŒ
çŸæç¹ã§ã¯ãããã©ãŒãã³ã¹ã«é¢ããç¹å¥ãªåé¡ã¯ãããŸãããå°ãªããšããã®ç¹å®ã®ãªã¯ãšã¹ãã§ã¯ãééããªãããã§ãã sqlite3ããŒã¿ããŒã¹ãæäœããé¢æ°ãšJinja2ãã³ãã¬ãŒãã®è¡šç€ºã«æãæéããããããšãããããŸããã¿ã€ãã«ã¯ããã®ãªã¯ãšã¹ãã®å®è¡ã«0.2ç§ããããããªãã£ãããšã瀺ããŠããããšã«æ³šæããŠãã ãããåã ã®é¢æ°ã®å®è¡æéã¯ç¡èŠã§ããŸãã
ã¢ããªã±ãŒã·ã§ã³ãæé·ããã«ã€ããŠããããã¡ã€ã©ã䜿çšããŠæ°ããè¿œå ãªã¯ãšã¹ããèµ·åããé©åãªè»éã«ä¹ã£ãŠããããšã確èªããããšã圹ç«ã€å ŽåããããŸãã
ããŒã¿ããŒã¹ã®ããã©ãŒãã³ã¹
ãããŠããã®èšäºã®æåŸã§ãããŒã¿ããŒã¹ã®ããã©ãŒãã³ã¹ã®åé¡ãèŠãŠã¿ãŸããããäžèšã§ãããŒã¿ããŒã¹ã®æäœããããã¡ã€ã©ãŒã¬ããŒãã®äžçªäžã«ããããšã«æ°ã¥ããã®ã§ãããã«ãµãŒããŒã§ããŒã¿ããŒã¹ã®åäœãé ããªããããå ŽåïŒããã³ãã®å ŽåïŒã«èŠåããçµ±åã·ã¹ãã ããããšäŸ¿å©ã§ãã
Flask-SQLAlchemyã®ããã¥ã¡ã³ãã«ã¯get_debug_queriesé¢æ°ãèšèŒãããŠãããå®è¡æéãšãšãã«å®è¡ãããã¯ãšãªã®ãªã¹ããè¿ããŸãã
ããã¯éåžžã«åœ¹ç«ã€æ å ±ã§ããã¢ããªã±ãŒã·ã§ã³ã®å¯Ÿè±¡é åã¯ãéçºããã³ãã¹ãäžã«ã¯ãšãªã®å®è¡æéã枬å®ããŸããããã¹ãŠã®ãªã¯ãšã¹ãã®å®è¡æéããããã«å¢å ãããšããŠãããªã¯ãšã¹ããé·æéå®è¡ããããšãã«ä¿¡å·ãéä¿¡ããæ©èœã¯ãç£æ¥éçšäžã«ã圹ç«ã¡ãŸãã
æ¬çªç°å¢ã§ãã®ããããã£ã䜿çšããã«ã¯ãæ§æãã¡ã€ã«ïŒconfig.pyãã¡ã€ã«ïŒã§æ瀺çã«æå¹ã«ããå¿ èŠããããŸãã
SQLALCHEMY_RECORD_QUERIES = True
ããã«ããªã¯ãšã¹ãã®æéã«å¶éãèšå®ããå¿ èŠããããŸããããã«ãããæéãããããã®ã¯ãã¹ãŠãé ãããšèŠãªãããŸãïŒconfig.pyãã¡ã€ã«ïŒã
# slow database query threshold (in seconds) DATABASE_QUERY_TIMEOUT = 0.5
ã·ã°ãã«ãéä¿¡ããå¿ èŠæ§ã確èªããããã«ãåãªã¯ãšã¹ãã®åŸã«ãã§ãã¯ãè¿œå ããŸããFlaskã®äœ¿çšã¯ç°¡åã§ããafter_requestãã³ãã©ãŒïŒapp / views.pyãã¡ã€ã«ïŒãæ§æããã ãã§ãã
from flask.ext.sqlalchemy import get_debug_queries from config import DATABASE_QUERY_TIMEOUT @app.after_request def after_request(response): for query in get_debug_queries(): if query.duration >= DATABASE_QUERY_TIMEOUT: app.logger.warning("SLOW QUERY: %s\nParameters: %s\nDuration: %fs\nContext: %s\n" % (query.statement, query.parameters, query.duration, query.context)) return response
ãããã£ãŠã0.5ç§ããé·ãå®è¡ãããŠãããã¹ãŠã®èŠæ±ããã°ã«èšé²ãããŸãããã°å ã®æ å ±ã«ã¯ãSQLåŒã䜿çšãããå®éã®ãã©ã¡ãŒã¿ãŒãæéãããã³ãã®èŠæ±ãåŒã³åºããããœãŒã¹ã³ãŒãå ã®å Žæãå«ãŸããŸãã
ç§ãã¡ã®å Žåã®ããã«ããŒã¿ããŒã¹ã®ãµã€ãºãå°ããå Žåãã¯ãšãªãé ããªããšããåé¡ã¯çºçããªãããã§ãããã¢ããªã±ãŒã·ã§ã³ãšããŒã¿ããŒã¹ã®æé·ã«äŒŽããäžéšã®ã¯ãšãªã¯ãããšãã°è¿œå ã®ã€ã³ããã¯ã¹ãäœæããããšã§æé©åãå¿ èŠã§ããããšãããããŸãããã®ãã°ãéæ確èªãããªã¯ãšã¹ãã®æé©åãå¿ èŠãã©ããã確èªããŸãã
ãããã«
ä»æ¥ãç§ãã¡ã¯ããã€ãã®æ·±å»ãªæªçœ®ãè¬ããŸãããããã¯ãä¿¡é Œã§ããã¢ããªã±ãŒã·ã§ã³ã«ãšã£ãŠãéåžžã«éèŠã§ããæŽæ°ãããã¢ããªã±ãŒã·ã§ã³ã³ãŒãã¯ã
ããŠã³ããŒãmicroblog-0.16.zipã§ããŠã³ããŒãã§ããŸãã
GitHubã«ç²ŸéããŠããèªè ã¯ãããããæ°ããããŒãžã§ã³ãå ¥æã§ããŸãã
ç§ã¯ãã®ã·ãªãŒãºã®çµããã«å®¹èµŠãªãè¿ã¥ããŠããããã§ãããªããªããç§ã¯ä»ã«äœãäŒããããšãã§ãããã«ã€ããŠã®ã¢ã€ãã¢ã䜿ãæãããããã§ãã次ã®ãããããæåŸã®éšåã§ã¯ãåŸæ¥åãšã¯ã©ãŠãã®äž¡æ¹ã§ã¢ããªã±ãŒã·ã§ã³ããããã€ããããã»ã¹ãèŠãŠãããŸãã
ãã®äžé£ã®èšäºã§èŠèœãšããåé¡ã«ã€ããŠãæèŠããããŸãããã以äžã®ã³ã¡ã³ãã§ãç¥ãããã ããã
ããããïŒ
ãã²ã«