2

I've read this question : has_and_belongs_to_many, avoiding dupes in the join table but I wasn't able to fix my issue;

I've got a class TypeProduit which has many ExtensionFichier :

class TypeProduit < ActiveRecord::Base
    has_and_belongs_to_many :extension_fichiers
end

and:

class ExtensionFichier < ActiveRecord::Base
  has_and_belongs_to_many :type_produits
end

They are related thanks to

class LinkExtensionFichiers < ActiveRecord::Migration
  def change
     create_join_table :extension_fichiers, :type_produits
  end
end

Edit:

Here's the [correct] Controler:

class TypeProduitsController < ApplicationController
before_action :set_type_produit, only: [:show, :edit, :update, :destroy]

# GET /type_produits
# GET /type_produits.json
def index
  @type_produits = TypeProduit.all
end

# GET /type_produits/1
# GET /type_produits/1.json
def show
end

# GET /type_produits/new
def new
  @type_produit = TypeProduit.new
end

# GET /type_produits/1/edit
def edit
end

# POST /type_produits
# POST /type_produits.json
def create
  @type_produit = TypeProduit.new(type_produit_params)

  puts "------extension_fichier_params------"
  puts extension_fichier_params

  #@type_produit.extension_fichier_ids = extension_fichier_params

  @type_produit.extension_fichiers << ExtensionFichier.find_by(:id => extension_fichier_params)

  respond_to do |format|
    if @type_produit.save
      format.html { redirect_to @type_produit, notice: 'Type produit was successfully created.' }
      format.json { render :show, status: :created, location: @type_produit }
    else
      format.html { render :new }
      format.json { render json: @type_produit.errors, status: :unprocessable_entity }
    end
  end
end

# PATCH/PUT /type_produits/1
# PATCH/PUT /type_produits/1.json
def update
  respond_to do |format|
    if @type_produit.update(type_produit_params)
      format.html { redirect_to @type_produit, notice: 'Type produit was successfully updated.' }
      format.json { render :show, status: :ok, location: @type_produit }
    else
      format.html { render :edit }
      format.json { render json: @type_produit.errors, status: :unprocessable_entity }
    end
  end
end

# DELETE /type_produits/1
# DELETE /type_produits/1.json
def destroy
  @type_produit.destroy
  respond_to do |format|
    format.html { redirect_to type_produits_url, notice: 'Type produit was successfully destroyed.' }
    format.json { head :no_content }
  end
end

private
  # Use callbacks to share common setup or constraints between actions.
  def set_type_produit
    @type_produit = TypeProduit.find(params[:id])
  end

  def extension_fichier_params
    @type_produit.extension_fichier_ids
  end

  # Never trust parameters from the scary internet, only allow the white list through.
  def type_produit_params
    params.require(:type_produit).permit(:nom, :extension_fichier_ids => [])
  end
end

And here's the Rails server output:

Started POST "/type_produits" for 127.0.0.1 at 2015-01-27 17:55:15 +0100
Processing by TypeProduitsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"4LBhwTvGjXOq+3qzO+loGUV/oBCYjW66aVDeE1Zj/AM=", "type_produit"=>{"nom"=>"qsdfqsdf", "extension_fichier_ids"=>["1", "2", ""]}, "commit"=>"Create Type produit"}
  ExtensionFichier Load (0.4ms)  SELECT "extension_fichiers".* FROM "extension_fichiers"  WHERE "extension_fichiers"."id" IN (1, 2)
------extension_fichier_params------
1
2
  ExtensionFichier Load (0.2ms)  SELECT  "extension_fichiers".* FROM "extension_fichiers"  WHERE "extension_fichiers"."id" IN (1, 2) LIMIT 1
   (0.1ms)  begin transaction
  TypeProduit Exists (0.1ms)  SELECT  1 AS one FROM "type_produits"  WHERE "type_produits"."nom" = 'qsdfqsdf' LIMIT 1
  SQL (0.4ms)  INSERT INTO "type_produits" ("created_at", "nom", "updated_at") VALUES (?, ?, ?)  [["created_at", "2015-01-27 16:55:15.923132"], ["nom", "qsdfqsdf"], ["updated_at", "2015-01-27 16:55:15.923132"]]
  SQL (0.1ms)  INSERT INTO "extension_fichiers_type_produits" ("extension_fichier_id", "type_produit_id") VALUES (?, ?)  [["extension_fichier_id", 1], ["type_produit_id", 8]]
  SQL (0.1ms)  INSERT INTO "extension_fichiers_type_produits" ("extension_fichier_id", "type_produit_id") VALUES (?, ?)  [["extension_fichier_id", 2], ["type_produit_id", 8]]
  SQL (0.0ms)  INSERT INTO "extension_fichiers_type_produits" ("extension_fichier_id", "type_produit_id") VALUES (?, ?)  [["extension_fichier_id", 1], ["type_produit_id", 8]]
   (0.5ms)  commit transaction

Why is rails making 3 insert while it just has 2 objects

Community
  • 1
  • 1
Thomas Ayoub
  • 29,063
  • 15
  • 95
  • 142
  • You're showing the models and logs for the creation of a `TypeProduit` object, but your controller is the code for creating `Produit` objects. To diagnose this properly we need to see the `TypeProduitsController` – smathy Jan 27 '15 at 17:29
  • @smathy you're right, stupid copy/paste. It's fixed now – Thomas Ayoub Jan 28 '15 at 08:38
  • Your code looks right, you should check that there is no duplicate record for id 1 in your extension_fichiers table: `ExtensionFichier.find_by(:id => [1,2])` That seems improbable though. – Thomas Guillory Jan 28 '15 at 09:12
  • @ThomasGuillory no dupes in the DB :/ – Thomas Ayoub Jan 28 '15 at 09:36

2 Answers2

2

There's a very obvious error in your controller code. Let's go step by step through your create action.

You create a new TypeProduit from type_produit_params which includes :nom and :extension_fichier_ids. You then output the value of extension_fichier_params which itself is @type_produit.extension_fichier_ids and you get "1\n2\n" so your @type_produit already has both of those extension_fichiers attached to it.

You then append ExtensionFichier.find_by(:id => extension_fichier_params) to @type_produit.extension_fichier_ids. Now a find_by is basically a where().first so it gets only the first one, ie. id=1. So now in your @type_produit.extension_fichiers array you have ids 1, 2 and 1 again. Ie. three objects.

Then you save the TypeProduit and all three of those associations are saved with the three INSERTs.

And yeah, the overall solution to this (other than fixing your unnecessary << code or your model-or-view that's adding 1 and 2) is to add the -> { uniq } scope to your habtm association(s) as per https://stackoverflow.com/a/20226630/152786

Community
  • 1
  • 1
smathy
  • 26,283
  • 5
  • 48
  • 68
0

Have you tried the solution suggested by using-uniq-in-a-has-and-belongs-to-many-relationship-in-rails-4 (and maybe also adding an index as suggested in has-and-belongs-to-many-avoiding-dupes-in-the-join-table ?)

Community
  • 1
  • 1
farialima
  • 217
  • 2
  • 6