ためしにTodoリストを作っているのですが、ちょっとハマってます。
グループに案件を登録すると、そのグループのメンバー全員のTodoリストに
その案件が登録されるようにしたいのですが、
Todoモデルのdef save(self):Super(Todo, self).save()
の後にその処理を書こうと思ったところ、
どうもManyToManyで登録したGroupがcreateの時に取得できません
(updateの時は取得できる)
こういうときはどうすればいいのでしょうか?
[11/Dec/2006 20:44:46] "GET /admin/todo/todo/ HTTP/1.1" 200 1485
[11/Dec/2006 20:44:48] "GET /admin/todo/todo/add/ HTTP/1.1" 200 4882
[11/Dec/2006 20:44:48] "GET /admin/jsi18n/ HTTP/1.1" 200 2180
(createの時)
[]
[11/Dec/2006 20:45:06] "POST /admin/todo/todo/add/ HTTP/1.1" 302 0
[11/Dec/2006 20:45:06] "GET /admin/todo/todo/ HTTP/1.1" 200 1943
[11/Dec/2006 20:45:17] "GET /admin/todo/todo/1/ HTTP/1.1" 200 5100
[11/Dec/2006 20:45:18] "GET /admin/jsi18n/ HTTP/1.1" 200 2180
(updateの時)
[<Group: System_G>]
[11/Dec/2006 20:45:21] "POST /admin/todo/todo/1/ HTTP/1.1" 302 0
[11/Dec/2006 20:45:22] "GET /admin/todo/todo/ HTTP/1.1" 200 1943
models.py
from django.db import models
from django.contrib.auth.models import User, Group
from django.dispatch import dispatcher
import datetime
class Todo(models.Model):
create_user = models.ForeignKey(User)
related_group = models.ManyToManyField(Group)
title = models.CharField(blank=False, maxlength=200)
note = models.TextField(blank=True)
due= models.DateField(blank=True)
created = models.DateTimeField(blank=True, default=datetime.datetime.now())
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return "/todo/detail/%s" % str(self.id)
def save(self):
super(Todo, self).save()
print self.related_group.all() #print debug ここに処理を書く予定
class Admin:
list_display = ('create_user', 'title', 'due')
class Meta:
ordering = ['due', 'create_user']
ちょっと状況がつかみきれないのと、試してみてないのであてずっぽうします。
こうするとどうですか?
もし下記のコードで出力されるようになるとするならば、現在updateの際に出力
されている値は古い値である可能性が高いですが。
def save(self):
super(Todo, self).save()
new_obj = Todo.objects.get(pk=self.id)
print new_obj.related_group.all()
06/12/12 に Makoto Uemura<makoto...@gmail.com> さんは書きました:
上手くいきません。。。。。
def save(self):
super(Todo, self).save()
new_obj = Todo.objects.get(pk=self.id)
print self.id
print new_obj
print new_obj.related_group.all()
print "\n".join([str(i) for i in connection.queries])
にしてSQLを調べると
[12/Dec/2006 13:07:51] "POST /admin/todo/todo/add/ HTTP/1.1" 302 0
[12/Dec/2006 13:07:51] "GET /admin/todo/todo/add/ HTTP/1.1" 200 5031
[12/Dec/2006 13:07:51] "GET /admin/jsi18n/ HTTP/1.1" 200 2180
14
Test 3
[]
{'time': '0.001', 'sql': 'SELECT
`django_session`.`session_key`,`django_session`.`session_data`,`django_session`.`expire_date`
FROM `django_session` WHERE (`django_session`.`session_key` =
614fd3e6e7ff602611a1bd4bcdc5e327 AND `django_session`.`expire_date` >
2006-12-12 13:10:18.826772)'}
{'time': '0.000', 'sql': 'SELECT
`auth_user`.`id`,`auth_user`.`username`,`auth_user`.`first_name`,`auth_user`.`last_name`,`auth_user`.`email`,`auth_user`.`password`,`auth_user`.`is_staff`,`auth_user`.`is_active`,`auth_user`.`is_superuser`,`auth_user`.`last_login`,`auth_user`.`date_joined`
FROM `auth_user` WHERE (`auth_user`.`id` = 1)'}
{'time': '0.001', 'sql': 'SELECT
`auth_user`.`id`,`auth_user`.`username`,`auth_user`.`first_name`,`auth_user`.`last_name`,`auth_user`.`email`,`auth_user`.`password`,`auth_user`.`is_staff`,`auth_user`.`is_active`,`auth_user`.`is_superuser`,`auth_user`.`last_login`,`auth_user`.`date_joined`
FROM `auth_user` ORDER BY `auth_user`.`username` ASC'}
{'time': '0.000', 'sql': 'SELECT `auth_group`.`id`,`auth_group`.`name`
FROM `auth_group` ORDER BY `auth_group`.`name` ASC'}
##########################################
## INSERT文
{'time': '0.001', 'sql': 'INSERT INTO `todo_todo`
(`create_user_id`,`title`,`note`,`due`,`created`,`updated`) VALUES
(2,Test 3,Test 3 test 3,2006-12-12,2006-12-12 13:07:44,2006-12-12
13:10:18)'}
###########################################
{'time': '0.001', 'sql': 'SELECT
`todo_todo`.`id`,`todo_todo`.`create_user_id`,`todo_todo`.`title`,`todo_todo`.`note`,`todo_todo`.`due`,`todo_todo`.`created`,`todo_todo`.`updated`
FROM `todo_todo` WHERE (`todo_todo`.`id` = 14)'}
{'time': '0.001', 'sql': 'SELECT `auth_group`.`id`,`auth_group`.`name`
FROM `auth_group` LEFT OUTER JOIN `todo_todo_related_group` AS
`m2m_auth_group__todo` ON `auth_group`.`id` =
`m2m_auth_group__todo`.`group_id` WHERE
(`m2m_auth_group__todo`.`todo_id` = 14) ORDER BY `auth_group`.`name`
ASC'}
と insert文に
そもそもManyToManyが無いです。
直後にpython manage.py dbshell
で最後のSQLをコピペして実行すると表示されるので
どこか別のタイミングでManyToManyの処理をしてるっぽいです。
ん
どうしたものだか、、、、
06/12/12 に tsuyuki makoto<mtsu...@gmail.com> さんは書きました:
えっとー。なんとなくアレ?って思ったんですけど。
Groupたんってこの後addするんですよね??
1.Todoを登録
2.登録されたTodoに対し、Groupを紐つけしていく
って感じじゃないのかな?
きっと、TodoとGroupを同時に登録しようとしているんですよね?
django.db.models.manipulator#AutomaticManipulator.saveの
for f in self.opts.many_to_many:とTodoのSuper(Todo, self).save()
の後にそれぞれデバッグアウトを仕込むと、どういう順番で出力されますか?
Todoの保存時にManyToManyも入れなおしが行われると思うんだけど。
更新時には入ってるのが見えるってことは、登録が行われないこともな
いだろうし。
んー。followってどういうオプションでしたっけ?>理解した人
06/12/12 に Yutaka Matsubara<yutaka.m...@gmail.com> さんは書きました:
を挿入して実行してみました。
Development server is running at http://0.0.0.0:8088/
Quit the server with CONTROL-C.
20
Test Title
[]
{'time': '0.001', 'sql': 'SELECT
`django_session`.`session_key`,`django_session`.`session_data`,`django_session`.`expire_date`
FROM `django_session` WHERE (`django_session`.`session_key` =
614fd3e6e7ff602611a1bd4bcdc5e327 AND `django_session`.`expire_date` >
2006-12-12 18:05:03.114038)'}
{'time': '0.001', 'sql': 'SELECT
`auth_user`.`id`,`auth_user`.`username`,`auth_user`.`first_name`,`auth_user`.`last_name`,`auth_user`.`email`,`auth_user`.`password`,`auth_user`.`is_staff`,`auth_user`.`is_active`,`auth_user`.`is_superuser`,`auth_user`.`last_login`,`auth_user`.`date_joined`
FROM `auth_user` WHERE (`auth_user`.`id` = 1)'}
{'time': '0.001', 'sql': 'SELECT
`auth_user`.`id`,`auth_user`.`username`,`auth_user`.`first_name`,`auth_user`.`last_name`,`auth_user`.`email`,`auth_user`.`password`,`auth_user`.`is_staff`,`auth_user`.`is_active`,`auth_user`.`is_superuser`,`auth_user`.`last_login`,`auth_user`.`date_joined`
FROM `auth_user` ORDER BY `auth_user`.`username` ASC'}
{'time': '0.000', 'sql': 'SELECT `auth_group`.`id`,`auth_group`.`name`
FROM `auth_group` ORDER BY `auth_group`.`name` ASC'}
{'time': '0.001', 'sql': 'INSERT INTO `todo_todo`
(`create_user_id`,`title`,`note`,`due`,`created`,`updated`) VALUES
(2,Test Title,Test Note,2006-12-12,2006-12-12 17:59:58,2006-12-12
18:05:03)'}
{'time': '0.001', 'sql': 'SELECT
`todo_todo`.`id`,`todo_todo`.`create_user_id`,`todo_todo`.`title`,`todo_todo`.`note`,`todo_todo`.`due`,`todo_todo`.`created`,`todo_todo`.`updated`
FROM `todo_todo` WHERE (`todo_todo`.`id` = 20)'}
{'time': '0.028', 'sql': 'SELECT `auth_group`.`id`,`auth_group`.`name`
FROM `auth_group` LEFT OUTER JOIN `todo_todo_related_group` AS
`m2m_auth_group__todo` ON `auth_group`.`id` =
`m2m_auth_group__todo`.`group_id` WHERE
(`m2m_auth_group__todo`.`todo_id` = 20) ORDER BY `auth_group`.`name`
ASC'}
django.db.models.manipulator.py PRINT DEBUG
{'time': '0.001', 'sql': 'SELECT
`django_session`.`session_key`,`django_session`.`session_data`,`django_session`.`expire_date`
FROM `django_session` WHERE (`django_session`.`session_key` =
614fd3e6e7ff602611a1bd4bcdc5e327 AND `django_session`.`expire_date` >
2006-12-12 18:05:03.114038)'}
{'time': '0.001', 'sql': 'SELECT
`auth_user`.`id`,`auth_user`.`username`,`auth_user`.`first_name`,`auth_user`.`last_name`,`auth_user`.`email`,`auth_user`.`password`,`auth_user`.`is_staff`,`auth_user`.`is_active`,`auth_user`.`is_superuser`,`auth_user`.`last_login`,`auth_user`.`date_joined`
FROM `auth_user` WHERE (`auth_user`.`id` = 1)'}
{'time': '0.001', 'sql': 'SELECT
`auth_user`.`id`,`auth_user`.`username`,`auth_user`.`first_name`,`auth_user`.`last_name`,`auth_user`.`email`,`auth_user`.`password`,`auth_user`.`is_staff`,`auth_user`.`is_active`,`auth_user`.`is_superuser`,`auth_user`.`last_login`,`auth_user`.`date_joined`
FROM `auth_user` ORDER BY `auth_user`.`username` ASC'}
{'time': '0.000', 'sql': 'SELECT `auth_group`.`id`,`auth_group`.`name`
FROM `auth_group` ORDER BY `auth_group`.`name` ASC'}
{'time': '0.001', 'sql': 'INSERT INTO `todo_todo`
(`create_user_id`,`title`,`note`,`due`,`created`,`updated`) VALUES
(2,Test Title,Test Note,2006-12-12,2006-12-12 17:59:58,2006-12-12
18:05:03)'}
{'time': '0.001', 'sql': 'SELECT
`todo_todo`.`id`,`todo_todo`.`create_user_id`,`todo_todo`.`title`,`todo_todo`.`note`,`todo_todo`.`due`,`todo_todo`.`created`,`todo_todo`.`updated`
FROM `todo_todo` WHERE (`todo_todo`.`id` = 20)'}
{'time': '0.028', 'sql': 'SELECT `auth_group`.`id`,`auth_group`.`name`
FROM `auth_group` LEFT OUTER JOIN `todo_todo_related_group` AS
`m2m_auth_group__todo` ON `auth_group`.`id` =
`m2m_auth_group__todo`.`group_id` WHERE
(`m2m_auth_group__todo`.`todo_id` = 20) ORDER BY `auth_group`.`name`
ASC'}
{'time': '0.000', 'sql': 'DELETE FROM `todo_todo_related_group` WHERE
`todo_id` = 20'}
{'time': '0.000', 'sql': 'SELECT `auth_group`.`id`,`auth_group`.`name`
FROM `auth_group` WHERE (`auth_group`.`id` = 1)'}
{'time': '0.001', 'sql': 'SELECT `group_id` FROM
`todo_todo_related_group` WHERE `todo_id` = 20 AND `group_id` IN (1)'}
{'time': '0.001', 'sql': 'INSERT INTO `todo_todo_related_group`
(`todo_id`, `group_id`) VALUES (20, 1)'}
{'time': '0.000', 'sql': 'SELECT `auth_group`.`id`,`auth_group`.`name`
FROM `auth_group` WHERE (`auth_group`.`id` = 3)'}
{'time': '0.001', 'sql': 'SELECT `group_id` FROM
`todo_todo_related_group` WHERE `todo_id` = 20 AND `group_id` IN (3)'}
{'time': '0.001', 'sql': 'INSERT INTO `todo_todo_related_group`
(`todo_id`, `group_id`) VALUES (20, 3)'}
{'time': '0.000', 'sql': 'SELECT `auth_group`.`id`,`auth_group`.`name`
FROM `auth_group` WHERE (`auth_group`.`id` = 2)'}
{'time': '0.001', 'sql': 'SELECT `group_id` FROM
`todo_todo_related_group` WHERE `todo_id` = 20 AND `group_id` IN (2)'}
{'time': '0.001', 'sql': 'INSERT INTO `todo_todo_related_group`
(`todo_id`, `group_id`) VALUES (20, 2)'}
[12/Dec/2006 18:05:03] "POST /admin/todo/todo/add/ HTTP/1.1" 302 0
[12/Dec/2006 18:05:03] "GET /admin/todo/todo/add/ HTTP/1.1" 200 5036
[12/Dec/2006 18:05:03] "GET /admin/jsi18n/ HTTP/1.1" 200 2180
そもそもやろうとした実装には他にも問題がありそうなので
別な方法をとるようにしますけど、
これはこれで気になりますね。
やろうとしてるのは
例えば 人事部にAさんBさんCさんがいて、
総務部にDさん、Eさんがいる場合
人事部、総務部のタスクに「シフト表入力」と設定すると
5人のTodoリストにそれぞれ別の「シフト表入力」
のタスクが付け加えられるというものです。
(ユーザーごとのモデルも別にある)
タスク設定時に該当グループのメンバーをしらべて
それぞれのタスクを加えようとしたのですが、
上手くいかない&新しい人が増えたら対処できない(たとえばタスク設定後に総務にFさんが入社した場合)っぽいので
その人のタスクのリストを出すときに、該当するタスクが無かったら
付け加える方向に変えようかと思ってます。
面倒に考えすぎてるのかも??
ユーザが割り込めるように, dispatch などを使ってフックが設定されているかなと
思ったのですが,見当たりませんでした.
ところで,
1. ユーザは Todo を m2m でいくつかのグループに関連づけて保存できる
2. 他のユーザが自分の所属しているグループの Todo を表示する
という機能の実現を考えると,Todo のモデルで save をオーバライドするより,
ビューでユーザが提出したフォームから m2m リレーションを張っていくような実装に
した方が, Todo を特定のロジックに紐付けず,よりデータモデルとして独立させ
られるんじゃないでしょうか.
--
Yasushi Masuda
http://ymasuda.jp/
こんなのはどうでしょう.
class Todo:
group = ForeignKey(Group, ...)
task = CharField(...)
を定義して,views.new_task() で新しいタスクを追加できるようにしておきます.
new_task() は Group.objects.all() を使ってグループを複数選択リストで表示し,
ユーザが提出したフォームからグループリスト groups_to_post を作成します.
for grp in groups_to_post:
new_todo = Todo(group=grp, task=some_task_detail)
new_todo.save()
して,Todo にグループを関連づけます.ManyToMany にせず,部局毎に Todo を
作成するのは,ある部局で Todo のレコードを削除する必要ができたときに恐らく
破綻するからです.
あとは, views.task_list() で,
tasks = Todo.objects.all(group_in=request.User.group)
とでもして,ユーザのグループに応じてタスクリストを表示すればいいんじゃない
でしょうか. login_required でラップしたり,object_detail 汎用ビューを使う
よう工夫すればさらに手抜きできるかもしれません :)
AutomaticManipulatorのsave内にnew_data.save()という行がありまして、
そのずっと下でmany_to_manyのデータ関連づけを行っていました。
なので、Model.saveを上書きしても駄目なようです。
Manipulatorを軽くいじれないか、今度見てみます。
#Manipulator無くなるって?
以下蛇足
TODOのモデル案を考えようとしたのですが、断念しました。
とりあえず、人が入ってきた時に自動でなんとかしようというのは
危険な香りがします。
シンプルさが失われるコスト>人が増えたら管理画面でチコチコ追加するコスト
みたいな?
ymasudaさんの
>Todo にグループを関連づけます.ManyToMany にせず,部局毎に Todo を
作成するのは,ある部局で Todo のレコードを削除する必要ができたときに恐らく
破綻するからです.
<
と言う意味がよく分からなかったのですが、
とりあえず作っていた
ユーザーごとのリストを出すときに、存在してないタスクがあったら
追加するManyToMany版
で複数のグループを持つタスクから、一部のグループを削除する時に、
フックがないので手がつまりました、、、、
このことでしょうか?
タスクそのものを管理画面から削除すると
関連するものもキレイに削除されるのでこっちは大丈夫でした。
とりあえず両方書いてみようかと思います。
有る程度まとまったらBLOGにも書くのでアドバイスお願いします。
views.py
def _custom_object_list(request, queryset):
""" チェックボックスcheckedに値が合った場合は日付を入れる """
if request.POST:
new_data = request.POST.copy()
#print new_data
checked_list = new_data.getlist("checked")
print checked_list
for todo_id in checked_list:
todo = Todo.objects.get(pk=todo_id)
todo.checked = datetime.datetime.now()
todo.save()
return object_list(request, queryset, allow_empty=True)
def list_by_user(request, slug):
user = User.objects.get(username=slug)
groups = [i['id'] for i in user.groups.values()]
#print groups
if groups:
q = Todo.objects.filter(related_group__in=groups)
create_userstodo(user, q)
queryset = UsersTodo.objects.filter(user=user)
return _custom_object_list(request, queryset)
else:
raise Http404
def create_userstodo(user, queryset):
""" タスクが無い場合に追加 """
if queryset:
for todo in queryset:
try:
UsersTodo.objects.get(user=user, todo=todo)
except:
ut = UsersTodo(user=user, todo=todo)
ut.save()
06/12/13 に tsuyuki makoto<mtsu...@gmail.com> さんは書きました:
「破綻する」は設計としての話で,複数のグループに Todo を割り当てる
ケースに対して,グループと Todo を ManyToMany で結ぶモデルでは,
特定のグループと Todo の関連づけを解消しようとしたときの処理が実質的に
破綻しませんか,ということです.
上村さんのメールから,「あるユーザが Todo を作成して登録し,別の
ユーザが自分の所属するグループの Todo 一覧を表示できる」ような
アプリを作りたいのだと推察しました.
そこで,まず ManyToMany で組んでみます.
class Todo(models.Model):
groups = models.ManyToManyField(...)
detail = models.CharField(...)
...
各グループごとに Todo.objects をフィルタすれば,あるグループの Todo を
表示できますよね.Admin の動作も完璧で,Admin を使っている限りは,Todo
にどんなグループを関連づけるかも自由に操作できます.
私が懸念したのは,この後で各グループに所属しているユーザ向けのインタフェー
スを作る場合,とりわけ「この Todo を削除する」のような機能を付けたくなっ
た場合です.
あるTodo (T)を登録して,複数のグループ(A, B)に関連づけたとします.
グループ A のユーザがそのTodoを完遂して,Tを削除したいとしましょう.
「A, B で共有の Todo」,つまり,グループ A での Todo の完了がグループ B
での完了も意味するような Todo なら,単純に T を削除すればよいの
ですが,「A と B で別個に完了する Todo」の場合, A が先に Todo を
完了して,問答無用で T を削除してしまったら, T から B への関連も
一緒に削除されてしまいますよね.これを防ごうとすると,あるグループと Todo
の関連づけが変更される度に呼び出されるフックを書かねばなりません.
例えば T.groups が空かどうかを判別するようなコードで乗り切ろうとすると,
今度はグループの関連づけられていない空の Todo を作成できなくなってしまう
かもしれません.
...と色々と妄想して,「ああ,これは破綻するんじゃないか」と思った
わけです.お騒がせしてすみません.
Makoto Uemura wrote:
> 上村です。
>
> ymasudaさんの
>
>> Todo にグループを関連づけます.ManyToMany にせず,部局毎に Todo を
> 作成するのは,ある部局で Todo のレコードを削除する必要ができたときに恐らく
> 破綻するからです.
> <
>
> と言う意味がよく分からなかったのですが、
>
> とりあえず作っていた
> ユーザーごとのリストを出すときに、存在してないタスクがあったら
> 追加するManyToMany版
> で複数のグループを持つタスクから、一部のグループを削除する時に、
> フックがないので手がつまりました、、、、
>
> このことでしょうか?
>
> タスクそのものを管理画面から削除すると
> 関連するものもキレイに削除されるのでこっちは大丈夫でした。
--
Yasushi Masuda
http://ymasuda.jp/