[RUBY] Jusqu'à ce que vous récupériez plusieurs données dans la zone de sélection

É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.

information sur la version

ruby 2.6.4p104 RubyGems 3.0.3 Rails 5.2.3

Chose que tu veux faire

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".

Relation plusieurs à plusieurs

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>

Erreur HasManyThroughOrder

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

Erreur AssociationType Missmatch

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>

Erreur AssociationTypeMissmatch à nouveau

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.

à partir de maintenant

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.

Recommended Posts

Jusqu'à ce que vous récupériez plusieurs données dans la zone de sélection
Jusqu'à ce que vous exécutiez l'exemple de code Apache Velocity
Utilisons le menu déroulant (boîte de sélection) -Sélectionnez la destination de publication-