Python Django芹菜任务:字典在迭代期间更改了大小
在Django 1.8中,我有这样一个视图,用户可以使用芹菜函数发布文章并通知用户的追随者,但它会产生一个相当混乱的错误:Python Django芹菜任务:字典在迭代期间更改了大小,python,django,celery,django-celery,Python,Django,Celery,Django Celery,在Django 1.8中,我有这样一个视图,用户可以使用芹菜函数发布文章并通知用户的追随者,但它会产生一个相当混乱的错误: dictionary changed size during iteration 以下是视图: @login_required def topic_reply(request, topic_id): tform = PostForm() topic = Topic.objects.get(pk=topic_id)
dictionary changed size during iteration
以下是视图:
@login_required
def topic_reply(request, topic_id):
tform = PostForm()
topic = Topic.objects.get(pk=topic_id)
args = {}
posts = Post.objects.filter(topic= topic)
posts = Paginator(posts, DJANGO_SIMPLE_FORUM_REPLIES_PER_PAGE)
if request.method == 'POST':
post = PostForm(request.POST)
if post.is_valid():
p = post.save(commit = False)
p.topic = topic
p.title = post.cleaned_data['title']
p.body = post.cleaned_data['body']
p.creator = request.user
p.user_ip = request.META['REMOTE_ADDR']
if len(p.title)< 1:
p.title=p.body[:60]
p.save()
#notify followers of the new post creation
title = 'title' #topic.title
link = 'bla' #topic.slug
flwd = request.user
flwr_ids = FollowUser.objects.filter(followed=flwd).values('follower_id')
flwrs = User.objects.filter(id__in= flwr_ids).values('username','email')
notify_new_post.delay(flwd, flwrs , title, link) #<- here the is the problem
return HttpResponseRedirect('/forum/topic/%s/?page=%s' % (topic.slug, posts.num_pages))
else:
return HttpResponseRedirect('/forum/topic/%s/?page=%s' % (topic.slug, posts.num_pages))
else:
args.update(csrf(request))
args['form'] = tform
args['topic'] = topic
return render_to_response('myforum/reply.html', args,
context_instance=RequestContext(request))
这对我来说很奇怪,因为对于“主题”视图来说,一个非常类似的任务非常有效。我真的很困惑,所以感谢你的提示
更新:这是回溯
Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/forum/reply/52/
Django Version: 1.8.3
Python Version: 2.7.3
Installed Applications:
('django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
'registration',
'aricle',
'photo',
'contact',
'captcha',
'pure_pagination',
'emoticons',
'debug_toolbar',
'django_markdown',
'myforum',
'userprofile',
'userpics',
'djcelery')
Installed Middleware:
(u'debug_toolbar.middleware.DebugToolbarMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'userprofile.middleware.ActiveUserMiddleware')
Traceback:
File "/home/mypc/.projenv/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
132. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/mypc/.projenv/local/lib/python2.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
22. return view_func(request, *args, **kwargs)
File "/home/mypc/myproj/myforum/views.py" in topic_reply
315. notify_new_post.delay(flwd, flwrs , title, link)
File "/home/mypc/.projenv/local/lib/python2.7/site-packages/celery/app/task.py" in delay
453. return self.apply_async(args, kwargs)
File "/home/mypc/.projenv/local/lib/python2.7/site-packages/celery/app/task.py" in apply_async
555. **dict(self._get_exec_options(), **options)
File "/home/mypc/.projenv/local/lib/python2.7/site-packages/celery/app/base.py" in send_task
353. reply_to=reply_to or self.oid, **options
File "/home/mypc/.projenv/local/lib/python2.7/site-packages/celery/app/amqp.py" in publish_task
305. **kwargs
File "/home/mypc/.projenv/local/lib/python2.7/site-packages/kombu/messaging.py" in publish
161. compression, headers)
File "/home/mypc/.projenv/local/lib/python2.7/site-packages/kombu/messaging.py" in _prepare
237. body) = dumps(body, serializer=serializer)
File "/home/mypc/.projenv/local/lib/python2.7/site-packages/kombu/serialization.py" in dumps
164. payload = encoder(data)
File "/usr/lib/python2.7/contextlib.py" in __exit__
35. self.gen.throw(type, value, traceback)
File "/home/mypc/.projenv/local/lib/python2.7/site-packages/kombu/serialization.py" in _reraise_errors
59. reraise(wrapper, wrapper(exc), sys.exc_info()[2])
File "/home/mypc/.projenv/local/lib/python2.7/site-packages/kombu/serialization.py" in _reraise_errors
55. yield
File "/home/mypc/.projenv/local/lib/python2.7/site-packages/kombu/serialization.py" in dumps
164. payload = encoder(data)
File "/home/mypc/.projenv/local/lib/python2.7/site-packages/kombu/serialization.py" in pickle_dumps
356. return dumper(obj, protocol=pickle_protocol)
Exception Type: EncodeError at /forum/reply/52/
Exception Value: dictionary changed size during iteration
这可能是因为为
flwrs
传递的值是django.db.models.query.ValuesQuerySet
。Django查询集是惰性计算的,我认为这不利于序列化。记住,发送到芹菜任务并从中返回的所有内容都必须序列化。因此,除了简单类型或您知道可以被序列化的类型之外,传递任何其他类型都是不可取的(例如,您自己设计的类或您已经内外检查以确保可以干净地序列化的类)
我建议的最小修复方法是传递list(flwrs)
,而不是flwrs
。这将把查询集变成一个普通的列表
。我还强烈建议将request.user.id
作为flwd
传递,而不是用户对象本身。传递ORM对象是获得意外行为的可靠方法。(芹菜文档提到了这一点。)在芹菜任务中传递一个id并重新获取对象是一种方法
但是,当我全面查看代码时,我不明白为什么数据库访问是在视图中执行的,而不是在芹菜任务中执行的。因此,除非我遗漏了某个行或变量,否则我会将您的代码更改为只传递request.user.id
asflwd
,然后在芹菜任务中执行数据库访问。因此,视图将如下调用任务:
#notify followers of the new post creation
title = 'title' #topic.title
link = 'bla' #topic.slug
notify_new_post.delay(request.user.id, title, link)
from django.contrib.auth import get_user_model
@task()
def notify_new_post(flwd_id, topic, link):
user_model = get_user_model()
flwd = user_model.objects.get(id=flwd_id)
flwr_ids = FollowUser.objects.filter(followed=flwd).values('follower_id')
flwrs = user_model.objects.filter(id__in= flwr_ids).values('username','email')
任务的开始是这样的:
#notify followers of the new post creation
title = 'title' #topic.title
link = 'bla' #topic.slug
notify_new_post.delay(request.user.id, title, link)
from django.contrib.auth import get_user_model
@task()
def notify_new_post(flwd_id, topic, link):
user_model = get_user_model()
flwd = user_model.objects.get(id=flwd_id)
flwr_ids = FollowUser.objects.filter(followed=flwd).values('follower_id')
flwrs = user_model.objects.filter(id__in= flwr_ids).values('username','email')
(注意最后一行:我假设
User
是Django项目使用的用户模型,因此我使用get\u User\u model()
的返回值(分配给User\u model
)不要直接使用用户
。如果我的假设不正确,并且用户
是其他东西,那么你必须像最初那样使用用户
。@Mikkoohtama traceback added.BTW,我使用django用户模型。因此,使用get\u user\u model()
而不是直接查询user:flwd=user.objects.get(id=flwd\u id)
是否有好处?如果您将设置AUTH\u user\u model
更改为使用自定义模型,您必须返回应用程序,并用get\u User\u model()
的返回值替换User
的所有用法。对于打算在内部使用的代码,由您决定是否接受这种可能性。(我更喜欢在我自己的代码中使用get_user_model()
)如果你编写的代码是供他人使用的,那么你必须使用get_user_model()
,除非你想让使用你的代码的人在发现他们无法在不破坏你的代码的情况下更改AUTH_user_model
。