Étant donné que le travail d'extraction de plusieurs données de la boîte de sélection dans Ruby on Rails est étonnamment compliqué, le processus d'essais et d'erreurs jusqu'à présent est organisé ici. Il n'a pas encore été résolu, nous le mettrons donc à jour au fur et à mesure de son évolution.
ruby 2.6.4p104 RubyGems 3.0.3 Rails 5.2.3
Actuellement, je crée un site SNS d'apprentissage des langues. Cette fois, je voudrais créer une boîte de sélection qui permet à l'utilisateur de sélectionner plusieurs langues sur l'écran d'édition du profil. Enfin, j'aimerais utiliser un joyau qui couvre toutes les langues du monde, mais tout d'abord, comme étape de test, "japonais, anglais, chinois Nous allons créer une boîte de sélection contenant 4 langues d '"espagnol".
Je pense que «utilisateur» et «langue» ont une relation plusieurs-à-plusieurs, j'ai donc utilisé une table intermédiaire appelée «user_language» pour connecter «utilisateur» et «langue» dans une relation plusieurs-à-plusieurs.
** Modèle utilisateur **
models/user.rb
class User < ApplicationRecord
has_secure_password
has_many :languages, through: :user_language
has_many :user_language
def language_name
::LanguageSelect::LANGUAGES[language]
end
end
** Modèle de langue **
models/language.rb
class Language < ApplicationRecord
has_many :users, through: :user_language
has_many :user_language
end
** Tableau intermédiaire entre le modèle utilisateur et le modèle de langage **
models/user_language.rb
class Language < ApplicationRecord
belongs_to :user
belongs_to :language
end
schema
db/schema.rb
ActiveRecord::Schema.define(version: 2020_05_21_034306) do
create_table "languages", force: :cascade do |t|
t.string "description"
t.boolean "done"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_languages_on_user_id"
end
create_table "user_languages", force: :cascade do |t|
t.integer "user_id"
t.integer "language_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["language_id"], name: "index_user_languages_on_language_id"
t.index ["user_id"], name: "index_user_languages_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "image_name"
t.string "password_digest"
t.string "cover_image_name"
t.string "sex"
t.string "country"
t.string "language"
t.text "introduction"
end
La page de modification du profil est décrite comme suit.
views/users/edit.html.erb
<div class="col-1">Spoken language:</div>
<div class="col-2">
<select name="language[]" multiple="multiple" class="form-control">
<option>Japonais</option>
<option>Anglais</option>
<option>chinois</option>
<option>Espagnol</option>
<input type="button" value="+" class="add pluralBtn">
<input type="button" value="-" class="del pluralBtn">
</div>
Le contrôleur est décrit comme suit.
controllers/users_controller.rb
def update
@user = User.find_by(id: params[:id])
@user.languages = params[:language]
if @user.save
flash[:notice] = "Informations utilisateur modifiées"
redirect_to("/users/#{@user.id}")
else
render("users/edit")
end
end
Enfin, la page de profil est décrite comme suit.
views/users/show.html.erb
<div class="language">
<% @user.languages.each do |language| %>
<%= @user.language %>
<% end %>
</div>
Maintenant que je suis prêt, lorsque j'essaye de sélectionner et d'enregistrer plusieurs langues, j'obtiens l'erreur suivante:
ActiveRecord::HasManyThroughOrderError in UsersController#update
Cannot have a has_many :through association 'User#languages' which goes through 'User#user_language' before the through association is defined.
Extracted source (around line #125):
@user.languages = params[:language]
Lors de la description d'une relation plusieurs-à-plusieurs, l'erreur ci-dessus semble se produire à moins que le code lié à la table intermédiaire (user_language dans ce cas) ne soit écrit en premier. En référence à cet article, j'ai réécrit la relation comme suit.
** Modèle utilisateur **
models/user.rb
class User < ApplicationRecord
has_secure_password
has_many :user_language
has_many :languages, through: :user_language
def language_name
::LanguageSelect::LANGUAGES[language]
end
end
** Modèle de langue **
models/language.rb
class Language < ApplicationRecord
has_many :user_language
has_many :users, through: :user_language
end
En réécrivant, l'erreur HasManyThroughOrder a disparu, mais cette fois l'erreur suivante a été affichée.
ActiveRecord::AssociationTypeMismatch in UsersController#update
Language(#135869460) expected, got "Anglais" which is an instance of String(#21954020)
@user.languages = params[:language]
Il semble que j'avais l'intention de recevoir une instance de Language, mais que j'ai reçu la chaîne "English". Donc, pour éviter cela, j'ai décidé d'ajouter un attribut de valeur à chacune des langues sélectionnées sur la page d'édition de profil.
views/users/edit.html.erb
<div class="col-1">Spoken language:</div>
<div class="col-2">
<select name="language[]" multiple="multiple" class="form-control">
<option>Japonais</option>
<option>Anglais</option>
<option>chinois</option>
<option>Espagnol</option>
<input type="button" value="+" class="add pluralBtn">
<input type="button" value="-" class="del pluralBtn">
</div>
La chose qui était comme ça a été modifiée comme suit.
views/users/edit.html.erb
<div class="col-1">Spoken language:</div>
<div class="col-2">
<select name="language[]" multiple="multiple" class="form-control">
<option value="1">Japonais</option>
<option value="2">Anglais</option>
<option value="3">chinois</option>
<option value="4">Espagnol</option>
<input type="button" value="+" class="add pluralBtn">
<input type="button" value="-" class="del pluralBtn">
</div>
Je pensais que cela le résolvait, mais la même déclaration d'erreur à nouveau.
ActiveRecord::AssociationTypeMismatch in UsersController#update
Language(#128573540) expected, got "2" which is an instance of String(#21954020)
@user.languages = params[:language]
J'avais l'intention de passer language_id
cette fois, mais il semble qu'il soit également passé comme une" chaîne de caractères "appelée" 2 "
avec guillemets doubles.
En fait, cela est probablement dû au fait que Les données qui peuvent être envoyées avec la valeur Rails sont limitées aux chaînes de caractères. était.
Si les choses ne se passent pas bien, j'ai l'impression d'en avoir fini avec les boutons radio, mais à la fin je veux sélectionner plus de 100 langues, alors je vais m'en tenir à la boîte de sélection et faire de mon mieux.
Pour le moment, je trébuche ici. Si vous l'organisez en détail, si les informations de la langue que l'utilisateur apprend dans le contrôleur sont définies comme "@ user.languages" comme indiqué ci-dessous, l'erreur de ʻAssociationTypeMismatch` sera affichée.
controllers/users_controller.rb
def update
@user = User.find_by(id: params[:id])
@user.languages = params[:language]
if @user.save
flash[:notice] = "Informations utilisateur modifiées"
redirect_to("/users/#{@user.id}")
else
render("users/edit")
end
end
De plus, si vous définissez "@ user.language" au singulier comme indiqué ci-dessous, aucune erreur ne sera affichée, mais elle sera sortie sous forme de tableau (sous forme de chaîne de caractères) comme [" 1 "," 2 "]
. Sera fait.
controllers/users_controller.rb
def update
@user = User.find_by(id: params[:id])
@user.language = params[:language]
if @user.save
flash[:notice] = "Informations utilisateur modifiées"
redirect_to("/users/#{@user.id}")
else
render("users/edit")
end
end
Comme le chinois japonais, nous envisagerons une solution pour que les données puissent être individuellement extraites et affichées sans inclure [] et "". Je pense à deux façons:
** Méthode 1 **
Après avoir reçu la chaîne de caractères telle que [" 1 "," 2 "]
, elle est convertie en type numérique.
** Méthode 2 ** Si vous pouvez spécifier le type numérique avant de recevoir les données, spécifiez-le.
Je ne sais pas comment les décrire tous les deux, et je suis actuellement dans une situation d'essais et d'erreurs. Je suis désolé pour ceux qui l'ont lu en prévision d'une solution, mais je le mettrai à jour au fur et à mesure de son évolution.