Hallo,
ich möchte die Inhaberschaft aller Eröffnungsposts in Themendiskussionen, die mit „ethan“ getaggt sind, ändern, sodass der Besitzer von „system“ auf den Benutzer „Ethan_Hoom1“ geändert wird. Ich betreibe eine selbst gehostete Instanz und habe Zugriff auf die Rails-Konsole.
Nachdem ich die Empfehlungen unter Administrative Bulk Operations und Change ownership of all posts by a specific user geprüft habe, habe ich das folgende Skript vorbereitet. Ich würde mich über jegliche Vorschläge oder Best Practices freuen, bevor ich es in der Produktion ausführe:
# Finde das Tag-Objekt mit dem Namen „ethan“
tag = Tag.find_by(name: "ethan")
# Sofort beenden, falls das Tag nicht gefunden wird
raise "Tag not found" unless tag
# Finde den Benutzer, der der neue Besitzer der Posts werden soll
# username_lower ist sicherer als username (nicht case-sensitiv)
new_owner = User.find_by(username_lower: "ethan_hoom1")
# Sofort beenden, falls der Zielbenutzer nicht existiert
raise "New owner user not found" unless new_owner
# Finde den Systembenutzer-Account
# Fallback auf Discourse.system_user, falls die Abfrage fehlschlägt
system_user = User.find_by(username_lower: "system") || Discourse.system_user
# Sofort beenden, falls der Systembenutzer nicht gefunden werden kann
raise "System user not found" unless system_user
# Wenn true, druckt das Skript nur aus, was es ändern WÜRDE
# und modifiziert nichts in der Datenbank
DRY_RUN = true
# Optionale Sicherheitsbegrenzung für den ersten Durchlauf (z. B. 10 oder 50)
# Auf nil setzen, um alle passenden Posts zu verarbeiten
LIMIT = nil
# Erstelle eine ActiveRecord-Abfrage für die Posts, die wir ändern möchten
scope = Post
# Joine Posts → Topics → Topic_Tags, um nach Tag filtern zu können
.joins(topic: :topic_tags)
# Nur den Eröffnungspost in jedem Topic anvisieren
.where(post_number: 1)
# Nur Posts anvisieren, die derzeit dem Systembenutzer gehören
.where(user_id: system_user.id)
# Nur Topics einschließen, die das „ethan“-Tag haben
.where(topic_tags: { tag_id: tag.id })
# Private Nachrichten und gelöschte Topics ausschließen
.where(topics: {
archetype: Archetype.default,
deleted_at: nil
})
# Wenn LIMIT gesetzt ist, die Anzahl der verarbeiteten Posts einschränken
scope = scope.limit(LIMIT) if LIMIT
# Durch passende Posts in Batches iterieren, um Speicherprobleme zu vermeiden
scope.find_each(batch_size: 100) do |first_post|
# Die Topic-ID für die Protokollausgabe speichern
topic_id = first_post.topic_id
# Wenn der Dry-Run-Modus aktiviert ist, nichts ändern
if DRY_RUN
puts "[DRY RUN] Would change owner for topic #{topic_id} (post #{first_post.id})"
next
end
begin
# Den offiziellen Service von Discourse zur Änderung der Inhaberschaft verwenden
PostOwnerChanger.new(
# Nur die Inhaberschaft des Eröffnungsposts ändern
post_ids: [first_post.id],
# Das Topic, das den Post enthält
topic_id: topic_id,
# Der Benutzer, der der neue Besitzer werden soll
new_owner: new_owner,
# Der handelnde Benutzer (Systembenutzer)
acting_user: system_user,
# Für diese Änderung keine Revisionsversion erstellen
skip_revision: true
).change_owner!
# Erfolg in der Konsole protokollieren
puts "Changed owner for topic #{topic_id} (post #{first_post.id})"
rescue => e
# Wenn etwas fehlschlägt, den Fehler protokollieren, aber mit der Verarbeitung anderer fortfahren
puts "FAILED topic #{topic_id} (post #{first_post.id}): #{e.class}: #{e.message}"
end
end
Fragen:
- Gibt es Einschränkungen oder Randfälle, die ich bei der Verwendung von
PostOwnerChangerfür diesen Anwendungsfall beachten sollte? - Ist es ratsam, auch das Feld
topic.userzu aktualisieren, wie in der optionalen Zeile gezeigt? - Haben Sie Empfehlungen für weitere Sicherheits- oder Leistungsverbesserungen bezüglich dieses Batch-Prozesses?
Vielen Dank für Ihre Hilfe und die großartige Dokumentation!