{“content”:“### 简而言之\n\n对于某些帖子,调用 PostRevisor 会将 post_id 设置为 nil。是我疯了吗?\n\n答案:不是。PostRevisor 访问 post.topic,对于已删除主题中的帖子,该值是 nil。然后它将 post.topic 设置为 nil,这反过来又将 post.topic_id 设置为 nil。\n\n我认为 PostRevisor 应该像这样获取主题:\n\n @topic = topic || Topic.with_deleted.find_by(id: post_topic_id)\n\n而不是像这样:\n\n\n @topic = topic\n\n\n\npost=Post.find_by(topic_id: 179227, post_number: 12)\npost.topic_id => 179227\npr=PostRevisor.new(post)\npost.topic_id => nil\n\n\n### 完整故事\n\n我正在编写一个脚本来修复 goo.gl 链接(该服务即将停用,因此脚本会查找 goo.gl 链接,获取它们重定向到的位置,然后用重定向到的 URL 替换 goo.gl URL。大部分功能正常。\n\n### 但是\n\n对于很多帖子,一切看起来都很好,但之后 PostRevisor 失败了,因为 post.acting_user 是 nil。然后,在我的 rescue 中,topic_id 似乎是 nil,但不是 post 是 nil,因为它仍然有一个 post_number。\n\nruby\n begin\n puts \"Revising (#{count}/#{total_posts}) https://mysite.com/t/#{post.topic_id}/#{post.post_number}\"\n puts \"missing topic_id for post #{post.id}\" if !post.topic_id\n next if !post.topic_id\n PostRevisor.new(post).revise!(system_user, raw: new_raw, **revision_options)\n rescue =\u003e e\n puts \"cannot revise (number: #{count} https://tw.forumosa.com/t/#{post.topic_id}/#{post.post_number}): #{e}\"\n end\n\n\n\nFIXING!!: https://goo.gl/maps/XaNG\nB7qaZGzhBmM78 -----\u003e https://www.google.com/maps/place/%E6%AD%A5%E9%81%93%E5%92%96%E5%95%A1%E9%A4%A8Cafe+Strada/@22.6300414,120.3153591,17z/d\nata=!3m1!4b1!4m5!3m4!1s0x346e04944a9b3471:0x520c1f01c3d62e57!8m2!3d22.6301115!4d120.3175543?shorturl=1\nRevising (680/1773) https://mysite.com/t/207069/1817\ncannot revise (number: 680 https://mysite.com/t//1817): undefined method `acting_user=' for nil\n\n\n对于绝大多数帖子,这都可以正常工作,但对于其中一部分帖子,它会像这样失败。如果我逐行手动运行代码,结果也是一样的。不过,我开始觉得,如果我执行 pr=PostRevisor.new(post),我会看到 pr 记录中的帖子没有 topic_id,然后如果我检查 post,它的 topic_id 现在被设置为 nil。”,“target_locale”:“en”}
恕我直言,你为什么不直接编辑 Post 模型呢?
因为如果你用 gsub 和一个复杂的正则表达式以及一堆边缘情况来更改 3000 篇帖子(修复:goo.gl、http://goo.gl、https://goo.gl,但不要触及 https://maps.app.goo.gl 或 https://map.goo.gl,而且你可能会被 goo.gl 限制速率,等等),你可能想在完全搞砸它之前恢复帖子!
能够查看编辑内容并看到修改前后的对比,并且能够恢复,这真的很棒!例如,一个版本会将 https://maps.app.goo.gl/abd12 变成类似 https://maps.app.https://maps.google.com/;lkajw3rpoazse;flknmase;faijserfasefklasdfa 的东西。
_那_说得通 ![]()
几年前,我曾尝试为某个客户运行一个类似的脚本,他们说(用我的话来说,不是他们的话):“哥们。你认为我们会运行你那个可以随意更改海量帖子且无法撤销的代码吗?再想想吧。”
是的,另一种方法是在受控区域的备份上运行它。但我喜欢你的解决方案。
这是初始化程序:
所以帖子不知何故到了那里,尽管 post_id 有值,但 post 没有?
我无法重现这一点。
最后一个 post.topic_id 不是 nil,并且如预期一样是先前结果的重复。
是的。它在大多数帖子中都有效。有些帖子有点奇怪。
[63] pry(main)> post.topic_id
=> 179227
[64] pry(main)> post.topic
=> nil
[65] pry(main)>
我很确定不应该是这样的。![]()
但是等等:
Topic.find(post.topic_id)
ActiveRecord::RecordNotFound: Couldn't find Topic with 'id'=179227 [WHERE "topics"."deleted_at" IS NULL]
from /var/www/discourse/vendor/bundle/ruby/3.3.0/gems/activerecord-7.2.2.1/lib/active_record/relation/finder_methods.rb:428:in `raise_record_not_found_exception!'
这个主题已被删除。
那么问题是,在已删除的主题中无法修改帖子是否是一个 bug。
我想我不会在意,让我的脚本检查 post.topic 是否为 nil,而不是 post.topic_id。
那里的外键确实存在严重的数据完整性问题!
我认为一些保护措施(咳咳)被关闭了,因为表通常太大。
也许有人执行了 topic.delete 而不是 destroy。糟糕。
我搜索帖子的方式是 Post.where("raw like '%goo.gl%'"),这会返回已删除主题中的帖子。然后这些帖子有 topic_id,但没有 topic。我认为有某种方法可以让 Topic.find 返回已删除的主题(因为管理员可以看到这些主题,这就是我感到困惑的原因。那个小垃圾桶很容易错过。)
所以就像这样:
deleted_topic = Topic.with_deleted.find_by(id: 123)
所以也许我应该那样做,然后在调用 PostRevisor 之前更新帖子?
但在用户界面中,我可以更新已删除主题中的帖子,所以我在想
def initialize(post, topic = post.topic)
@post = post
@topic = topic
# Make sure we have only one Topic instance
post.topic = topic
end
应该是
def initialize(post, topic = post.topic)
@post = post
@topic = topic || Topic.with_deleted.find_by(id: post_topic_id)
# Make sure we have only one Topic instance
post.topic = topic
end
我将把它移到 Bug,以防其他人认为这是一个 bug。
但是这个可以工作:
if !post.topic # posts in delted topics have no topic and break PostRevisor
post.topic = Topic.with_deleted.find_by(id: post.topic_id)
next if !post.topic
end
PostRevisor.new(post).revise!(system_user, raw: new_raw, **revision_options)
有 3 个主题中的帖子导致 rails 崩溃。我放弃了。 ![]()
还有一个版本,它执行 deleted_topic = Topic.with_deleted.find_by(id: 123) 来更新 post.topic,这也有效。
不过,这看起来仍然像是一个 bug。或者,也许因为 core 以其他方式来管理这个问题,所以它不是一个 bug。
或者缺少功能(因此缺少规范)?
在线上,这永远不会被调用来处理已删除主题中的帖子?
不过,我同意它应该妥善管理 ![]()
此主题在上次回复后 30 天自动关闭。不再允许回复。