简体   繁体   中英

Rails - How to record two reference IDs at once

Only one out of the two reference ID's is being set in one of my tables.

schema

在此处输入图片说明


Models

tech.rb

class Tech < ActiveRecord::Base
    has_many :services
end

service_menu

class ServiceMenu < ActiveRecord::Base
  has_many :services
end

service.rb

class Service < ActiveRecord::Base
  belongs_to :tech
  belongs_to :service_menu
end

Controller

services_controller.rb

def new
    @service = current_tech.services.build
end

def create
    @service = current_tech.services.build(service_params)

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

private

def service_params
  params.require(:service).permit(:name)
end

View

<%= simple_form_for @service do |f| %>
  <div class="field">
    <%= f.label "Select service category" %>
    <br>

    <%= collection_select(:service, :name, ServiceMenu.all, :name, :name, {:prompt => true }) %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Whenever I create a new service, only the tech_id is recorded but not the service_menu_id. How can I create a new service that would record both tech and service_menu ids at once to the services table?

Any help is appreciated.

EDIT 7/3 2:55 PM EST

@Yen-Ju method didn't work for me at first but now it does. At first I've failed to properly reference the service_menu_id in the services table .

My services db before:

| Field           | Type         | Null | Key | Default | Extra          |
+-----------------+--------------+------+-----+---------+----------------+
| id              | int(11)      | NO   | PRI | NULL    | auto_increment |
| name            | varchar(255) | YES  |     | NULL    |                |
| tech_id         | int(11)      | YES  | MUL | NULL    |                |
| service_menu_id | int(11)      | YES  |     | NULL    |                |
| created_at      | datetime     | NO   |     | NULL    |                |
| updated_at      | datetime     | NO   |     | NULL    |                |
+-----------------+--------------+------+-----+---------+----------------+

Reason for not working before is there was no MUL value under the KEY column.

My services db now:

| Field           | Type         | Null | Key | Default | Extra          |
+-----------------+--------------+------+-----+---------+----------------+
| id              | int(11)      | NO   | PRI | NULL    | auto_increment |
| name            | varchar(255) | YES  |     | NULL    |                |
| tech_id         | int(11)      | YES  | MUL | NULL    |                |
| service_menu_id | int(11)      | YES  | MUL | NULL    |                |
| created_at      | datetime     | NO   |     | NULL    |                |
| updated_at      | datetime     | NO   |     | NULL    |                |
+-----------------+--------------+------+-----+---------+----------------+

This also allowed me to drop the name column in the services db since it's no longer needed.

Thanks to all who responded.

You need to add has_many :through relationships to your models:

tech.rb

class Tech < ActiveRecord::Base
  has_many :services
  has_many :service_menus, through: :services
end

service_menu.rb

class ServiceMenu < ActiveRecord::Base
  has_many :services
  has_many :techs, through: :services
end

service.rb

class Service < ActiveRecord::Base
  belongs_to :tech
  belongs_to :service_menu
end

Once you do this, Active Record takes care of the join model (Service) housekeeping for you. You can demonstrate this in the Rails console:

>> tech = Tech.create(name: "bar")
   (0.2ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "techs" ("name", "created_at", "updated_at") VALUES (?, ?, ?)  [["name", "bar"], ["created_at", "2015-07-03 23:30:26.893615"], ["updated_at", "2015-07-03 23:30:26.893615"]]
   (10.7ms)  commit transaction
=> #<Tech id: 4, name: "bar", created_at: "2015-07-03 23:30:26", updated_at: "2015-07-03 23:30:26">
>> tech.service_menus << ServiceMenu.create(name: "baz")
   (0.1ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "service_menus" ("name", "created_at", "updated_at") VALUES (?, ?, ?)  [["name", "baz"], ["created_at", "2015-07-03 23:31:21.045841"], ["updated_at", "2015-07-03 23:31:21.045841"]]
   (18.9ms)  commit transaction
   (0.1ms)  begin transaction
  SQL (0.5ms)  INSERT INTO "services" ("tech_id", "service_menu_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["tech_id", 4], ["service_menu_id", 4], ["created_at", "2015-07-03 23:31:21.072932"], ["updated_at", "2015-07-03 23:31:21.072932"]]
   (33.1ms)  commit transaction
  ServiceMenu Load (0.3ms)  SELECT "service_menus".* FROM "service_menus" INNER JOIN "services" ON "service_menus"."id" = "services"."service_menu_id" WHERE "services"."tech_id" = ?  [["tech_id", 4]]
=> #<ActiveRecord::Associations::CollectionProxy [#<ServiceMenu id: 4, name: "baz", created_at: "2015-07-03 23:31:21", updated_at: "2015-07-03 23:31:21">]>
>> Service.first
  Service Load (0.4ms)  SELECT  "services".* FROM "services"  ORDER BY "services"."id" ASC LIMIT 1
=> #<Service id: 2, tech_id: 4, service_menu_id: 4, created_at: "2015-07-03 23:31:21", updated_at: "2015-07-03 23:31:21">`

I did not test the code, but this might work.

First, change your view to set service_menu_id

    <%= collection_select(:service, :service_menu_id, ServiceMenu.all, :id, :name, {:prompt => true }) %>

Second, in your controller, allow service_menu_id to pass:

def service_params
  params.require(:service).permit(:service_menu_id)
end

Then, when you build your service, the service_menu_id will be passed successfully.

You need to do the following changes.

Add service_menu_id in your servuce_params .

def service_params
  params.require(:service).permit(:name, :service_menu_id)
end

And change your view code like this

<%= simple_form_for @service do |f| %>
  <div class="field">
    <%= f.label :name %>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label "Select service category" %>
    <br>

    <%= collection_select(:service, :service_menu_id, ServiceMenu.all, :name, :name, {:prompt => true }) %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

If you use postrgesql, you could read this ActiveRecord documentation in 1.6 Composite Types and use composite types for Your task.

For example:

CREATE TYPE full_service_id AS
(
  tech_id INTEGER,
  service_menu_id_ INTEGER
);

# db/migrate/20140207133952_create_services.rb
execute <<-SQL
CREATE TYPE full_service_id AS
    (
      tech_id INTEGER,
      service_menu_id_ INTEGER
    );
SQL
create_table :services do |t|
  t.column full_service_id
end

# app/models/service.rb
class Service < ActiveRecord::Base
  belongs_to :tech, foreign_key: 'full_service_id.tech_id'
  belongs_to :service_menu, foreign_key: 'full_service_id.service_menu_id_'
end

# Usage
Service.create full_service_id: "#{tech_id},#{service_menu_id_}"
service = Service.first
service.full_service_id # => "(1,1)"
service.full_service_id = "(2,2)"
service.save!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM