| Author: | voluntas(Twisted Mind) |
| Title: | Django and AJAX |
| Keywords: | Django, Python, JSON, jQuery, YUI |
| Version: | 1.0.1 |
| License: | GNU Free Document License |
| Date: | 6 Dec 2006 |
Note
Python Workshop 04 presentation materials.
Contents
自分が短時間で思いついたモノをいくつか。 短時間で思いつく→現実的
魅力
ドキュメントが充実してます
そのドキュメントすべての日本語翻訳があります
gettextによる日本語化が殆ど終ってます
フルスクラッチなのでまとまっています
XMLによる設定ファイル?YAMLよる設定ファイル?なんですかそれ?
利点
強力なActiveRecordパターンのO/R Mapper
高速なテンプレートエンジン
URLによるコントローラーとの関連付け
MySQL PostgreSQL Oracle MSSQL SQLiteに対応
管理画面があるので開発が楽
強力なデバッグスクリーン
欠点
マルチDBが使用できない
AJAXが組み込まれてない?後ほどお話しします。
図はありません
request -> URL Dispatcher -> Views and Models -> Template -> request
しんぷるいずべすと
コードは自分で書け
最初に生成されるファイルは少ない、そのうえ中身はほとんど空っぽ
Tip
Installing minimal Django on Win32 environemnt
| Django: | 0.96-pre 4150 + patch |
| Python: | 2.5 or 2.4.4 |
| PostgreSQL: | 8.1.5 |
$ django-admin.py startproject workshop $ cd workshop $ python manage.py startapp firststep $ cp urls.py ./firststep $ python manage.py runserver
| It worked!: | http://localhost:8000/ |
workshop/
firststep/
__init__.py
models.py
urls.py
views.py
__init__.py
manage.py
settings.py
urls.py
/workshop/settings.py:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'workshop.firststep',
)
/workshop/urls.py:
from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^firststep/', include('workshop.firststep.urls')), # Uncomment this for admin: # (r'^admin/', include('django.contrib.admin.urls')), )
/workshop/firststep/views.py:
from django.http import HttpResponse def hello_django(request): return HttpResponse('Hello, django!')
/workshop/firststep/urls.py:
from django.conf.urls.defaults import * from workshop.firststep.views import hello_django urlpatterns = patterns('', (r'^hello/django/$', hello_django), )
| Hello, world!: | http://localhost:8000/firststep/hello/django/ |
/workshop/firststep/urls.py:
from django.conf.urls.defaults import * from workshop.firststep.views import * urlpatterns = patterns('', (r'^hello/python/$', hello_python), (r'^hello/ruby/$', hello_ruby), (r'^hello/django/$', hello_django), )
/workshop/firststep/views.py:
from django.http import HttpResponse, HttpResponseRedirect def hello_django(request): return HttpResponse('Hello, django!') def hello_python(request): data = 'Simple is better than complex.' return HttpResponse(data) def hello_ruby(request): return HttpResponseRedirect('/firststep/hello/python/')
| Hello, Python!: | http://localhost:8000/firststep/hello/python/ |
| Hello, Ruby?: | http://localhost:8000/firststep/hello/ruby/ |
/workshop/firststep/urls.py:
from django.conf.urls.defaults import * from workshop.firststep.views import * urlpatterns = patterns('', (r'^hello/django/$', hello_django), (r'^hello/python/$', hello_python), (r'^hello/ruby/$', hello_ruby), (r'^which/(Python|ruby)/$', python_or_ruby), (r'^select/(?P<language>.+)/$', select_language), )
/workshop/django/views.py:
from django.http import HttpResponse, HttpResponseRedirect def hello_django(request): return HttpResponse('Hello, django!') def hello_python(request): data = 'Simple is better than complex.' return HttpResponse(data) def hello_ruby(request): return HttpResponseRedirect('/firststep/hello/python/') def python_or_ruby(request, language): return HttpResponse('%s is nice programing language...' % language) def select_language(request, language): return HttpResponse('%s is bad programing language...' % language)
Python? Ruby? Others?:
Any language:
| Java: | http://localhost:8000/firststep/select/Java/ |
| Perl: | http://localhost:8000/firststep/select/Perl/ |
$ python manage.py startapp blog $ cp urls.py ./blog
/workshop/blog/models.py:
from django.db import models from django.contrib.auth.models import User class Label(models.Model): name = models.CharField(maxlength=128) created = models.DateTimeField(auto_now_add=True) class Admin: pass class Entry(models.Model): title = models.CharField(maxlength=32) user = models.ForeignKey(User) body = models.TextField() created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) tag = models.ManyToManyField(Label) class Admin: pass class Meta: verbose_name_plural = 'entries' def get_absolute_url(self): return '/blog/%s/' % self.id class Comment(models.Model): name = models.CharField(maxlength=32) url = models.URLField() mail = models.EmailField() body = models.TextField() created = models.DateTimeField(auto_now_add=True) entry = models.ForeignKey(Entry) class Admin: pass
/workshop/settings.py:
DATABASE_ENGINE = 'postgresql_psycopg2' DATABASE_NAME = 'workshop' DATABASE_USER = 'django' DATABASE_PASSWORD = 'django' TIME_ZONE = 'Asia/Tokyo' LANGUAGE_CODE = 'ja' INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'workshop.firststep', 'workshop.blog', )
$ python manage.py syncdb Creating table auth_message Creating table auth_group Creating table auth_user Creating table auth_permission Creating table django_content_type Creating table django_session Creating table django_site Creating table blog_comment Creating table blog_entry Creating table blog_label You just installed Django's auth system, \ which means you don't have any superusers defined. Would you like to create one now? (yes/no): yes Username: admin E-mail address: admin@django.ja Password: admin Password (again): admin Superuser created successfully. Installing index for auth.Message model Installing index for auth.Permission model Installing index for blog.Comment model Installing index for blog.Entry model
CREATE TABLE SQL 文を出力。
$ python manage.py sql blog
BEGIN; CREATE TABLE "blog_comment" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(32) NOT NULL, "url" varchar(200) NOT NULL, "mail" varchar(75) NOT NULL, "body" text NOT NULL, "created" timestamp with time zone NOT NULL, "entry_id" integer NOT NULL ); CREATE TABLE "blog_entry" ( "id" serial NOT NULL PRIMARY KEY, "title" varchar(32) NOT NULL, "user_id" integer NOT NULL REFERENCES "auth_user" ("id"), "body" text NOT NULL, "created" timestamp with time zone NOT NULL, "updated" timestamp with time zone NOT NULL ); ALTER TABLE "blog_comment" ADD CONSTRAINT entry_id_refs_id_2ebcee0 FOREIGN KEY ( "entry_id") REFERENCES "blog_entry" ("id"); CREATE TABLE "blog_label" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(128) NOT NULL, "created" timestamp with time zone NOT NULL ); CREATE TABLE "blog_entry_tag" ( "id" serial NOT NULL PRIMARY KEY, "entry_id" integer NOT NULL REFERENCES "blog_entry" ("id"), "label_id" integer NOT NULL REFERENCES "blog_label" ("id"), UNIQUE ("entry_id", "label_id") ); COMMIT;
DROP TABLE SQL 文を出力
$ python manage.py sqlclear blog
BEGIN; DROP TABLE "blog_entry_tag"; DROP TABLE "blog_label"; ALTER TABLE "blog_comment" DROP CONSTRAINT "entry_id_refs_id_2ebcee0"; DROP TABLE "blog_entry"; DROP TABLE "blog_comment"; COMMIT;
$ python manage.py shell >>> from workshop.blog.models import Entry >>> entry = Entry(title='Today is ...', user='admin', body='etc...') >>> entry.save()
/workshop/settings.py:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sites',
'django.contrib.admin',
'workshop.firststep',
'workshop.blog',
)
/workshop/urls.py:
from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^firststep/', include('workshop.firststep.urls')), (r'^admin/', include('django.contrib.admin.urls')), )
$ python manage.py syncdb Creating table django_admin_log Installing index for admin.LogEntry model
| Django administration: | http://localhost:8000/admin/ |
/workshop/blog/models.py:
from django.db import models from django.contrib.auth.models import User class Label(models.Model): name = models.CharField(maxlength=128) created = models.DateTimeField(auto_now_add=True) class Admin: list_display = ('name', 'created') def __str__(self): return self.name class Entry(models.Model): title = models.CharField(maxlength=32) user = models.ForeignKey(User) body = models.TextField() created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) tag = models.ManyToManyField(Label, filter_interface=models.HORIZONTAL) class Admin: list_display = ('title', 'user', 'created', 'updated') list_filter = ('created', 'updated') list_select_related = True search_fields = ('title', 'body') date_hierarchy = 'created' ordering = ('created', 'updated') class Meta: verbose_name_plural = 'entries' def __str__(self): return self.title def get_absolute_url(self): return '/blog/%s/' % self.id class Comment(models.Model): name = models.CharField(maxlength=32) url = models.URLField(null=True, blank=True) mail = models.EmailField(null=True, blank=True) body = models.TextField() created = models.DateTimeField(auto_now_add=True) entry = models.ForeignKey(Entry) class Admin: list_display = ('name', 'entry', 'created') list_filter = ('created', 'entry') search_fields = ('name', 'body') date_hierarchy = 'created' ordering = ('created', ) def __str__(self): return self.name
| Django administration: | http://localhost:8000/admin/ |
$ python manage.py shell >>> from workshop.blog.models import * >>> entries = Entry.objects.all() >>> entry = entries[0] >>> entry.user.username >>> entry.title >>> entry = Entry.objects.get(1) >>> entry.body >>> entry.created
Note
www.ymasuda.jp
Creating models: http://www.ymasuda.jp/python/django/docs/model-api.html
The database API: http://www.ymasuda.jp/python/django/docs/db-api.html
$ mkdir templates $ cd templates $ mkdir blog
workshop/
__init__.py
manage.py
settings.py
urls.py
firststep/
templates/
blog/
base.html
entry_detail_template.html
entry_list_template.html
entry_detail_filter.html
blog/
__init__.py
models.py
urls.py
views.py
/wrokshop/settings.py:
import os.path TEMPLATE_DIRS = ( os.path.join(os.path.dirname(__file__), 'templates'), )
/workshop/urls.py:
from django.conf.urls.defaults import * urlpatterns = patterns('', (r'^firststep/', include('workshop.firststep.urls')), (r'^blog/', include('workshop.blog.urls')), (r'^admin/', include('django.contrib.admin.urls')), )
/workshop/blog/views.py:
from django.shortcuts import render_to_response from workshop.blog.models import Entry def entry_detail(request, entry_id): entry = Entry.objects.get(pk=entry_id) return render_to_response('blog/entry_detail_template.html', dict(entry=entry)) def entry_list(request): entries = Entry.objects.all() return render_to_response('blog/entry_list_template.html', dict(entries=entries)) def entry_detail_and_filter(request, entry_id): entry = Entry.objects.get(pk=entry_id) return render_to_response('blog/entry_detail_filter.html', dict(entry=entry))
/workshop/blog/urls.py:
from django.conf.urls.defaults import * from workshop.blog.views import * urlpatterns = patterns('', (r'^(?P<entry_id>\d)/$', entry_detail), (r'^$', entry_list), (r'^filter/(?P<entry_id>\d)/$', entry_detail_and_filter) )
/workshop/blog/views.py:
def entry_detail(request, entry_id): entry = Entry.objects.get(entry_id) return render_to_response('blog/entry_detail_template.html', dict(entry=entry))
/workshop/templates/blog/base_template.html:
<html> <head> <title>{% block title %}{% endblock %}</title> </head> <body> {% block contents %} {% endblock %} </body> </html>
/workshop/templates/blog/entry_detail_template.html:
{% extends "blog/base_template.html" %} {% block title %}{{ entry.title }}{% endblock %} {% block contents %} <p>{{ entry.title }}<p> <p>{{ entry.body }}</p> <p>{{ entry.created }} {{ entry.user.username }}</p> {% endblock %}
| Entry Detail: | http://localhost:8000/blog/1/ |
| Page not found (404): | http://localhost:8000/blog/10/ |
/workshop/blog/views.py:
def entry_list(request): entries = Entry.objects.all() return render_to_response('blog/entry_list_template.html', dict(entries=entries))
/workshop/template/blog/entry_list_template.html:
{% extends "blog/base_template.html" %} {% block title %}Blog Entry List{% endblock %} {% block contents %} <b>Blog Entry List</b> {% if entries %} <ul> {% for entry in entries %} <li> <a href="{{ entry.get_absolute_url }}"> {{ entry.title }} by {{ entry.user.username }}</a> </li> {% endfor %} </ul> {% endif %} {% endblock %}
| Entry List: | http://localhost:8000/blog/ |
/workshop/blog/views.py:
def entry_detail_and_filter(request, entry_id): entry = Entry.objects.get(pk=entry_id) return render_to_response('blog/entry_detail_filter.html', dict(entry=entry))
/workshop/templates/blog/entry_detail_filter.html:
{% extends "blog/base_template.html" %} {% block title %}{{ entry.title }}{% endblock %} {% block contents %} <p>{{ entry.title }}<p> <p>{{ entry.body|linebreaksbr }}</p> <p>{{ entry.created|date:"Y/m/d H:i:s" }} {{ entry.user.username|upper }}</p> {% endblock %}
| Entry Detail and Filter: | http://localhost:8000/blog/filter/1/ |
| Entry Detail not Filter: | http://localhost:8000/blog/1/ |
Note
www.ymasuda.jp
Guide for HTMLLauthors: http://www.ymasuda.jp/python/django/docs/templates.html
Guide for Python programmers: http://www.ymasuda.jp/python/django/docs/templates_python.html
direct_to_template
direct_to:
from django.conf.urls.defaults import * from django.views.generic import simple urlpatterns = patterns('', (r'^hello/ruby/$', simple.direct_to, dict(url='/firststep/hello/python/')), )
archive_index
archive_year
archive_month
archive_week
archive_day
archive_today
object_detail
object_detail:
from django.conf.urls.defaults import * from django.views.generic import object_detail from workshop.blog.models import Entry queryset = dict(queryset=Entry.objects.all()) urlpatterns = patterns('', (r'^(?P<object_id>\d)/$', list_detail.object_detail, queryset), )
object_list:
from django.conf.urls.defaults import * from django.views.generic import object_detail from workshop.blog.models import Entry queryset = dict(queryset=Entry.objects.all()) urlpatterns = patterns('', (r'^$', list_detail.object_list, queryset), )
create_object:
from django.conf.urls.defaults import * from django.views.generic import create_update from workshop.blog.models import Entry model = dict(model=Entry) urlpatterns = patterns('', (r'^create/$', create_update.object_create, model), )
update_object:
from django.conf.urls.defaults import * from django.views.generic import create_update from workshop.blog.models import Entry model = dict(model=Entry) urlpatterns = patterns('', (r'^update/(?P<object_id>\d+)/$', create_update.object_update, model), )
delete_object
workshop/
__init__.py
manage.py
settings.py
urls.py
templates/
blog/
base.html
entry_detail.html
entry_list.html
entry_detail_filter.html
firststep/
blog/
__init__.py
urls.py
views.py
models.py
Tip
www.ymasuda.jp
Serving static/media files: http://ymasuda.jp/python/django/docs/static_files.html
/workshop/blog/urls.py:
from django.conf.urls.defaults import * from workshop.blog.models import Entry from workshop.blog.views import * entry_queryset = dict(queryset=Entry.objects.all()) urlpatterns = patterns('', (r'^(?P<entry_id>\d+)/ajax/xml/$', entry_ajax), (r'^(?P<entry_id>\d+)/ajaj/json/$', entry_ajaj), (r'^(xml|json)/$', entry_list_api), )
/workshop/blog/views.py:
from django.http import HttpResponse from django.core import serializers from django.utils import simplejson from workshop.blog.models import Entry, Comment def entry_ajax(request, entry_id): entry = Entry.objects.filter(id=entry_id) data = serializers.serialize('xml', entry) return HttpResponse(data, mimetype='text/plain') # return HttpResponse(data, mimetype='appllication/xml') def entry_ajaj(request, entry_id): entry = Entry.objects.filter(id=entry_id) data = serializers.serialize('json', entry, ensure_ascii=False) return HttpResponse(data, mimetype='text/plain') # return HttpResponse(data, mimetype='appllication/json') def entry_list_api(request, output): entries = Entry.objects.all() if output == 'json': data = serializers.serialize(output, entries, ensure_ascii=False) else: data = serializers.serialize(output, entries) return HttpResponse(data, mimetype='text/plain') # return HttpResponse(data, mimetype='appllication/%s' % output)
$ python manage.py syncdb
/workshop/bbs/views.py:
from django.http import HttpResponse from django.core import serializers def serialize_sample(request, type): queryset = Models.objects.all() data = serializers.serialize(type, queryset) return HttpResponse(data, mimetype='application/%s' % type)
Warning
日本人は何も気にせずに ensure_ascii=False を設定しましょう。
Note
www.ymasuda.jp
Serializing Django objects: http://www.ymasuda.jp/python/django/docs/serialization.html
/workshop/blog/views.py:
def entry_ajax(request, entry_id): entry = Entry.objects.filter(id=entry_id) data = serializers.serialize('xml', entry) return HttpResponse(data, mimetype='text/plain') # return HttpResponse(data, mimetype='appllication/xml')
/workshop/blog/urls.py:
(r'^(?P<entry_id>\d+)/ajax/xml/$', entry_ajax),
| xml: | http://localhost:8000/blog/1/ajax/xml/ |
<?xml version="1.0" encoding="utf-8"?> <django-objects version="1.0"> <object pk="1" model="blog.entry"> <field type="CharField" name="title">ルースカップリング</field> <field to="auth.user" name="user" rel="ManyToOneRel">1</field> <field type="TextField" name="body">Django のスタックが目指す基本的なゴールはルースカップリングとタイトコヒージョンの実現にあります.フレームワークの様々なレイヤは,本当に必要な場合を除き,お互いの事情を知らなくてもよいという考え方です. 例えば,テンプレートシステムは Web リクエストがどのようなものか関知せず,データベースレイヤはデータをどう表示するかに関知せず,ビューシステムはプログラマがどんなテンプレートシステムを使うかに関知しません. 利便性のため, Django には全てのスタックがついてきますが,スタックの各部分は可能な限り互いに独立になっています.</field> <field type="DateTimeField" name="created">2006-11-30 23:06:08</field> <field type="DateTimeField" name="updated">2006-11-30 23:06:08</field> </object> </django-objects>
/workshop/blog/views.py:
def entry_ajaj(request, entry_id): entry = Entry.objects.filter(id=entry_id) data = serializers.serialize('json', entry, ensure_ascii=False) return HttpResponse(data, mimetype='text/plain') # return HttpResponse(data, mimetype='appllication/json')
/workshop/blog/urls.py:
(r'^(?P<entry_id>\d+)/ajaj/json/$', entry_ajaj),
| json: | http://localhost:8000/blog/1/ajaj/json/ |
[ { "pk": "1", "model": "blog.entry", "fields": { "body": "Django のスタックが目指す基本的なゴールはルースカップリングとタイトコヒージョンの実現にあります.フレームワークの様々なレイヤは,本当に必要な場合を除き,お互いの事情を知らなくてもよいという考え方です.\r\n\r\n例えば,テンプレートシステムは Web リクエストがどのようなものか関知せず,データベースレイヤはデータをどう表示するかに関知せず,ビューシステムはプログラマがどんなテンプレートシステムを使うかに関知しません.\r\n\r\n利便性のため, Django には全てのスタックがついてきますが,スタックの各部分は可能な限り互いに独立になっています.", "updated": "2006-11-30 23:06:08", "created": "2006-11-30 23:06:08", "user": 1, "title": "ルースカップリング" } } ]
/workshop/blog/views.py:
def entry_list_api(request, output): entries = Entry.objects.all() if output == 'json': data = serializers.serialize(output, entries, ensure_ascii=False) else: data = serializers.serialize(output, entries) return HttpResponse(data, mimetype='text/plain') # return HttpResponse(data, mimetype='appllication/%s' % output)
/workshop/blog/urls.py:
(r'^(xml|json)/$', entry_list_api),
| json or xml: | http://localhost:8000/blog/xml/ |
| json or xml: | http://localhost:8000/blog/json/ |
/workshop/blog/urls.py:
(r'^(?P<entry_id>\d+)/comments/json/$', comment_list_json),
/workshop/blog/views.py:
def comment_list_json(request, entry_id): comments = Comment.objects.filter(entry__id=entry_id) data = [dict(name=c.name, url=c.url, body=c.body) for c in comments] values = simplejson.dumps(data, ensure_ascii=False) return HttpResponse(values, mimetype='text/plain') # return HttpResponse(simplejson.dumps(data), mimetype='application/json')
Note
A simple, fast, extensible JSON encoder and decoder
| return json: | http://localhost:8000/blog/1/comments/json/ |
[ { "body": "models はパッケージではなくモジュールになったので,models の設計は,パッケージ中に複数のモジュールファイルを置いてモデル実装を収められるような設計から,単一のモジュールを使ったよりフラットな設計に有利になりました.", "url": "", "name": "ymasuda" }, { "body": "django.views.generic.simple モジュールには,簡単なビューが入っていて,ビューロジックの必要がないときのレンダリングと,リダイレクトの発行というよくある二つのケースを処理できるようになっています:", "url": "http://www.ymasuda.jp/", "name": "ymasuda" } ]
$ mkdir static/js
workshop/
__init__.py
manage.py
settings.py
urls.py
templates/
blog/
base.html
entry_list_jquery.html
entry_list_yui.html
entry_detail.html
firststep/
blog/
__init__.py
urls.py
views.py
models.py
static/
css/
js/
yui/
yui-ext/
jquery/
/workshop/settings.py:
MEDIA_ROOT = os.path.join(os.path.dirname(__file__), 'static')
/workshop/urls.py:
from django.conf.urls.defaults import * from django.conf import settings urlpatterns = patterns('', (r'^firststep/', include('workshop.firststep.urls')), (r'^blog/', include('workshop.blog.urls')), (r'^admin/', include('django.contrib.admin.urls')), ) if settings.DEBUG: urlpatterns += patterns('', (r'^static/(?P<path>.*)$', 'django.views.static.serve', dict(document_root=settings.MEDIA_ROOT)), )
/workshop/blog/urls.py:
from django.conf.urls.defaults import * from django.views.generic import list_detail from workshop.blog.models import Entry from workshop.blog.views import * entry_queryset = dict(queryset=Entry.objects.all()) urlpatterns = patterns('', # Generic Views (r'^jquery/$', list_detail.object_list, dict(entry_queryset, template_name="blog/entry_list_jquery.html")), (r'^yui/$', list_detail.object_list, dict(entry_queryset, template_name="blog/entry_list_yui.html")), (r'^(?P<object_id>\d+)/$', list_detail.object_detail, entry_queryset), (r'^(?P<entry_id>\d+)/ajax/xml/$', entry_ajax), (r'^(?P<entry_id>\d+)/ajaj/json/$', entry_ajaj), (r'^(xml|json)/$', entry_list_api), )
/workshop/templates/blog/base.html:
{% comment %} vim: syntax=htmldjango {% endcomment %} <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja"> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Language" content="ja" /> <title>{% block title %}{% endblock %}</title> {% block css %} {% endblock %} {% block importjs %} {% endblock %} {% block js %} {% endblock %} </head> <body> {% block body %} {% endblock %} </body> </html>
$.getJSON:
/workshop/templates/blog/entry_list_jquery.html:
{% comment %} vim: syntax=htmldjango {% endcomment %} {% extends "blog/base.html" %} {% block title %}Django and jQuery and JSON{% endblock %} {% block importjs %} <script type="text/javascript" src="/static/js/jquery/jquery.js"></script> {% comment %} <script type="text/javascript" src="/static/js/jquery/jquery.pack.js"></script> {% endcomment %} {% endblock %} {% block js %} <script language="javaScript" type="text/javascript"> $(function(){ $.getJSON("/blog/json/", function(json){ $.each(json, function(t){ $("#" + this.pk).html(this.fields.body); }); }); }); var getEntry = function(id){ $("#" + id).toggle("slow"); } </script> {% endblock %} {% block body %} {% if object_list %} {% for object in object_list %} <p> <a href="#" onClick="getEntry({{ object.id }})">{{ object.title }}</a> <div id="{{ object.id }}" style="display: none;"></div> </p> {% endfor %} {% endif %} {% endblock %}
| jQuery and JSON: | http://localhost:8000/blog/jquery/ |
YAHOO.util.Connect.asyncRequest:
/workshop/blog/urls.py:
(r'^(?P<entry_id>\d+)/comments/json/$', comment_list_json), (r'^post/comment/$', post_comment),
/workshop/blog/views.py:
def comment_list_json(request, entry_id): comments = Comment.objects.filter(entry__id=entry_id) data = dict(comments=[dict(id=str(c.id), author=c.name, date=c.created.strftime("%Y/%m/%d %H:%M:%S"), link=c.url, text=c.body) for c in comments]) values = simplejson.dumps(data, ensure_ascii=False) return HttpResponse(values, mimetype='text/plain') # return HttpResponse(values, mimetype='application/json') def post_comment(request): if request.method == 'POST': new_data = request.POST.copy() name = new_data['author'] url = new_data['url'] body = new_data['comment'] entry_id = new_data['postId'] mail = new_data['email'] e = Entry.objects.get(pk=entry_id) c = Comment(name=name, url=url, body=body, mail=mail, entry=e) c.save() data = dict(comments=[dict(id=str(c.id), author=c.name, date=c.created.strftime("%Y/%m/%d %H:%M:%S"), link=c.url, text=c.body)]) value = simplejson.dumps(data, ensure_ascii=False) return HttpResponse(value, mimetype='text/plain') # return HttpResponse(value, mimetype='application/json') return HttpResponse(value, mimetype='text/plain')
/workshop/templates/blog/entry_detail.html:
{% comment %} vim: syntax=htmldjango ts=8 sw=2 sts=2 {% endcomment %} {% extends "blog/base.html" %} {% block title %}{{ object.title }}{% endblock %} {% block importcss %} <link rel="stylesheet" type="text/css" href="/static/css/yui-ext/resources/css/reset-min.css" /> <link rel="stylesheet" type="text/css" href="/static/css/yui-ext/resources/css/resizable.css" /> <link rel="stylesheet" type="text/css" href="/static/css/yui-ext/resources/css/tabs.css" /> <link rel="stylesheet" type="text/css" href="/static/css/yui-ext/resources/css/basic-dialog.css" /> <link rel="stylesheet" type="text/css" href="/static/css/yui-ext/resources/css/post.css" /> {% endblock %} {% block importjs %} <script type="text/javascript" src="/static/js/yui/utilities/utilities.js"></script> <script type="text/javascript" src="/static/js/yui-ext/yui-ext.js"></script> {% endblock %} {% block js %} <script language="javaScript" type="text/javascript"> var Comments = function(){ var dialog, postLink, viewLink, txtComment; var tabs, commentsList, postBtn, renderer; var wait, error, errorMsg; var posting = false; return { init : function(){ txtComment = getEl('comment'); commentsList = getEl('comments-list'); postLink = getEl('post-comment'); viewLink = getEl('view-comments'); wait = getEl('post-wait'); error = getEl('post-error'); errorMsg = getEl('post-error-msg'); this.createDialog(); postLink.addHandler('click', true, function(){ tabs.activate('post-tab'); dialog.show(postLink); }); viewLink.addHandler('click', true, function(){ tabs.activate('view-tab'); dialog.show(viewLink); }); }, submitComment : function(){ postBtn.disable(); wait.radioClass('active-msg'); var formObj = document.getElementById('comment-form') var spam = YAHOO.util.Connect.setForm(formObj); var commentSuccess = function(o){ postBtn.enable(); var data = renderer.parse(o.responseText); if(data){ wait.removeClass('active-msg'); renderer.append(data.comments[0]); dialog.hide(); }else{ error.radioClass('active-msg'); errorMsg.update(o.responseText); } }; var commentFailure = function(o){ postBtn.enable(); error.radioClass('active-msg'); errorMsg.update('Unable to connect.'); }; YAHOO.util.Connect.asyncRequest('post', '/blog/post/comment/', {success: commentSuccess, failure: commentFailure}, spam); }, createDialog : function(){ dialog = new YAHOO.ext.BasicDialog("comments-dlg", { autoTabs:true, width:500, height:400, shadow:true, minWidth:300, minHeight:300 }); dialog.addKeyListener(27, dialog.hide, dialog); dialog.addButton('Close', dialog.hide, dialog); postBtn = dialog.addButton('Post', this.submitComment, this); dialog.on('hide', function(){ wait.removeClass('active-msg'); error.removeClass('active-msg'); txtComment.dom.value = ''; }); tabs = dialog.getTabs(); var sizeTextBox = function(){ txtComment.setSize(dialog.size.width-44, dialog.size.height-264); }; sizeTextBox(); dialog.on('resize', sizeTextBox); tabs.on('tabchange', function(panel, tab){ postBtn.setVisible(tab.id == 'post-tab'); }); renderer = new CommentRenderer(commentsList); var um = commentsList.getUpdateManager(); um.setRenderer(renderer); var commentsLoaded = false; tabs.getTab('view-tab').on('activate', function(){ if(!commentsLoaded){ um.update('/blog/{{ object.id }}/comments/json/'); commentsLoaded = true; } }); } }; }(); var CommentRenderer = function(list){ var tpl = new YAHOO.ext.DomHelper.Template( '<li id="comment-{id}">' + '<div class="cheader">' + '<div class="cuser">{author}:</div>' + '<div class="commentmetadata">{date}</div>' + '</div>{text}</li>' ); this.parse = function(json){ try{ return eval('(' + json + ')'); }catch(e){} return null; }; this.render = function(el, response){ var data = this.parse(response.responseText); if(!data || !data.comments || data.comments.length < 1){ el.update('There are no comments for this post.'); return; } el.update(''); for(var i = 0, len = data.comments.length; i < len; i++){ this.append(data.comments[i]); } }; this.append = function(data){ tpl.append(list.dom, data); }; }; YAHOO.ext.EventManager.onDocumentReady(Comments.init, Comments, true); </script> {% endblock %} {% block body %} <p> <h2>{{ object.title }}</h2> <p>{{ object.body|linebreaksbr }}</p> <div id="comment-box"> <ul> <li><a id="post-comment" href="#">Post Comment</a></li> <li><a id="view-comments" href="#">View Comments</a></li> </ul> </div> </p> <!-- comments dialog --> <div id="comments-dlg" style="visibility:hidden;"> <div class="ydlg-hd">Comments</div> <div class="ydlg-bd"> <div id="post-tab" class="ydlg-tab" title="Post Comment"> <div class="inner-tab"> <form action="" method="post" id="comment-form" onsubmit="return false;"> <input id="postId" type="hidden" name="postId" value="{{ object.id }}" /> <p> <label for="author"><small>Name</small></label> <input class="textinput" type="text" name="author" id="author" value="" size="22" tabindex="1" /> </p> <p><label for="email"><small>E-mail (will not be published)</small></label> <input class="textinput" type="text" name="email" id="email" value="" size="22" tabindex="2" /> </p> <p><label for="url"><small>Website</small></label> <input class="textinput" type="text" name="url" id="url" value="" size="22" tabindex="3" /> </p> <p> <label for="comment"><small>Comment</small></label> <textarea name="comment" id="comment" tabindex="4" cols="40" rows="10"></textarea> </p> </form> </div> </div> <div id="view-tab" class="ydlg-tab" title="View Comments"> <ul id="comments-list" class="inner-tab"> </ul> </div> </div> <div class="ydlg-ft"> <div id="dlg-msg"> <span id="post-error" class="posting-msg"> <img src="/static/images/warning.gif" width="16" height="16" align="absmiddle" /> <span id="post-error-msg"></span></span> <span id="post-wait" class="posting-msg"> <img src="/static/css/yui-ext/resources/images/grid/loading.gif" width="16" height="16" align="absmiddle" /> Posting Comment...</span> </div> </div> </div> {% endblock %}
| YUI and JSON: | http://localhost:8000/blog/yui/ |
Note
Yahoo! User Interface Library
Yahoo! UI Library (YUI):
yui-ext Documentation Center:
Windowsで時間がおかしいのを修正するパッチ:
日本語用のバリデータサンプルです:
from django.core import validators from django.conf import settings import re zen_num = re.compile(u'[\uFF10-\uFF19]') zen_kana = re.compile(u'[\u30A1-\u30F6]') zen_hira = re.compile(u'[\u3041-\u3093]') han_kana = re.compile(u'[\uFF61-\uFF9F]') def isNotIncludeHalfWidthKatakana(field_data, all_data): from_data = unicode(field_data, settings.DEFAULT_CHARSET) if han_kana.search(from_data) is not None: raise validators.ValidationError(_('Half-width Kana characters' 'are not allowed.'))