Здравствуйте,
Я хочу изменить владельца всех стартовых сообщений в темах событий, помеченных тегом «ethan», так, чтобы владелец сменился с «system» на пользователя «Ethan_Hoom1». Я работаю с самохостинговой версией и имею доступ к консоли Rails.
После изучения рекомендаций в статьях Массовые административные операции и Изменение владельца всех сообщений конкретного пользователя, я подготовил следующий скрипт. Буду признателен за любые предложения или рекомендации по лучшим практикам перед запуском в производственной среде:
# Найти объект тега с именем "ethan"
tag = Tag.find_by(name: "ethan")
# Немедленно остановиться, если тег не найден
raise "Тег не найден" unless tag
# Найти пользователя, который должен стать новым владельцем сообщений
# username_lower безопаснее, чем username (регистронезависимый поиск)
new_owner = User.find_by(username_lower: "ethan_hoom1")
# Немедленно остановиться, если целевой пользователь не найден
raise "Пользователь-новый владелец не найден" unless new_owner
# Найти учетную запись пользователя системы
# В случае неудачи поиска записи использовать Discourse.system_user
system_user = User.find_by(username_lower: "system") || Discourse.system_user
# Немедленно остановиться, если пользователь системы не найден
raise "Пользователь системы не найден" unless system_user
# Если значение true, скрипт будет только выводить то, что ОН БЫ ИЗМЕНИЛ,
# но ничего не будет изменять в базе данных
DRY_RUN = true
# Опциональный предел безопасности для первого запуска (например, 10 или 50)
# Установите nil для обработки всех подходящих сообщений
LIMIT = nil
# Построить запрос ActiveRecord для сообщений, которые нужно изменить
scope = Post
# Присоединить сообщения → темы → topic_tags, чтобы можно было фильтровать по тегу
.joins(topic: :topic_tags)
# Нацелиться только на стартовое сообщение в каждой теме
.where(post_number: 1)
// Нацелиться только на сообщения, принадлежащие пользователю системы
.where(user_id: system_user.id)
// Включить только темы с тегом "ethan"
.where(topic_tags: { tag_id: tag.id })
// Исключить личные сообщения и удаленные темы
.where(topics: {
archetype: Archetype.default,
deleted_at: nil
})
# Если установлен LIMIT, ограничить количество обрабатываемых сообщений
scope = scope.limit(LIMIT) if LIMIT
# Итерироваться по подходящим сообщениям пакетами, чтобы избежать проблем с памятью
scope.find_each(batch_size: 100) do |first_post|
# Сохранить ID темы для вывода в лог
topic_id = first_post.topic_id
# Если включен режим пробного запуска, ничего не изменять
if DRY_RUN
puts "[ПРОБНЫЙ ЗАПУСК] Будет изменен владелец темы #{topic_id} (сообщение #{first_post.id})"
next
end
begin
# Использовать официальный сервис Discourse для изменения владельца
PostOwnerChanger.new(
// Изменить владельца только стартового сообщения
post_ids: [first_post.id],
// Тема, содержащая сообщение
topic_id: topic_id,
// Пользователь, который должен стать новым владельцем
new_owner: new_owner,
// Пользователь, выполняющий действие (пользователь системы)
acting_user: system_user,
// Не создавать черновик редактирования для этого изменения
skip_revision: true
).change_owner!
// Записать успех в консоль
puts "Изменен владелец темы #{topic_id} (сообщение #{first_post.id})"
rescue => e
// Если что-то пойдет не так, записать ошибку, но продолжить обработку остальных
puts "ОШИБКА тема #{topic_id} (сообщение #{first_post.id}): #{e.class}: #{e.message}"
end
end
Вопросы:
- Есть ли какие-либо нюансы или крайние случаи, о которых мне следует знать при использовании PostOwnerChanger для этого сценария?
- Целесообразно ли также обновлять поле
topic.user, как показано в опциональной строке? - Есть ли у вас рекомендации по дальнейшему повышению безопасности или производительности этого пакетного процесса?
Спасибо за вашу помощь и за отличную документацию!