J’essaie de créer une relation de clé étrangère avec la table Topics.
Le problème est que cela échoue dans l’environnement de test du workflow GitHub lors des tests pour la raison la plus étrange : il essaie d’accéder à un champ de la table parente qui n’existe plus et qui a été supprimé dans une migration principale il y a des années !
L’erreur est PG::UndefinedColumn: ERROR: column topics.off_topic_count does not exist
Je ne fais en aucun cas référence explicitement à ce vieux champ de table parente… il semble qu’il génère le SQL lui-même… mais de manière inappropriée pour la définition actuelle des choses.
== 20231119010101 CreateLocationsTopicTable: migrating ========================rake aborted!
[12035](https://github.com/paviliondev/discourse-locations/actions/runs/7039607316/job/19158951878?pr=103#step:19:12036)StandardError: An error has occurred, this and all later migrations canceled: (StandardError)
[12036](https://github.com/paviliondev/discourse-locations/actions/runs/7039607316/job/19158951878?pr=103#step:19:12037)
[12037](https://github.com/paviliondev/discourse-locations/actions/runs/7039607316/job/19158951878?pr=103#step:19:12038)PG::UndefinedColumn: ERROR: column topics.off_topic_count does not exist
[12038](https://github.com/paviliondev/discourse-locations/actions/runs/7039607316/job/19158951878?pr=103#step:19:12039)LINE 1: ...cs"."deleted_at", "topics"."highest_post_number", "topics".\"...
La définition de la table est vraiment simple :
class CreateLocationsTopicTable < ActiveRecord::Migration[7.0]
def change
create_table :locations_topic do |t|
t.references :topic, foreign_key: true
t.float :latitude, null: false
SNIP
La partie encore plus étrange est que cette migration fonctionne en production !
Infractions :
db/migrate/20231119010101_create_locations_topic_table.rb:6:7: C: Discourse/NoAddReferenceOrAliasesActiveRecordMigration : Les méthodes AR add_reference, add_belongs_to, t.references et t.belongs_to présentent un risque élevé pour les grandes tables et comportent trop d'opérations magiques en arrière-plan.
Au lieu de cela, écrivez une migration disable_ddl_transactions! et écrivez du SQL personnalisé pour ajouter la nouvelle colonne et CREATE INDEX CONCURRENTLY. Utilisez la clause IF NOT EXISTS pour rendre la migration réexécutable si elle échoue à mi-chemin.
t.references :topic, foreign_key: true
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Maintenant, cela pourrait faire partie du problème.
Le conseil de Rubocop est bon, mais je ne pense pas que ce soit la cause de cette erreur. (le « risque » qu’il mentionne fait référence aux verrous sur les tables à fort trafic qui affecteraient le trafic de production. Ce n’est donc pas quelque chose qui causerait des erreurs dans un environnement de test)
Vous pouvez probablement reproduire la même erreur localement en faisant RAILS_ENV=test bin/rake db:drop db:create db:migrate (c’est-à-dire en migrant une base de données totalement à partir de zéro, avec le plugin locations activé)
Je soupçonne que le problème est que vous invoquez des tâches Rake dans une migration. Dans le cœur, nous évitons généralement d’exécuter tout type de code d’application dans les migrations en raison des effets secondaires étranges qui peuvent se produire. Il est préférable de s’en tenir au SQL brut.
Dans ce cas, mon hypothèse est que le cache de schéma ActiveRecord est rempli tôt dans les migrations (lorsque topics.off_topic_count existe toujours). Ensuite, lorsque votre tâche Rake s’exécute, elle s’exécute avec un ancien cache de schéma et ActiveRecord essaie donc de charger des colonnes qui n’existent plus.
Vous pouvez probablement atténuer le problème en ajoutant ActiveRecord::Base.clear_cache! avant d’invoquer les tâches Rake… mais ne le prenez pas comme une recommandation . La meilleure chose à faire serait d’éviter d’invoquer les tâches Rake complètement. Si vous avez besoin de manipuler quelque chose dans la base de données, utilisez du SQL brut dans la migration.