Massenänderung des Besitzers von Erstbeiträgen nach Tag – Skript-Review und Vorschläge

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 PostOwnerChanger für diesen Anwendungsfall beachten sollte?
  • Ist es ratsam, auch das Feld topic.user zu 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!

1 „Gefällt mir“