Rails를 사용하여 어떻게 정수형 열이 아닌 기본 키를 설정할 수 있습니까?


85

저는 Rails 마이그레이션을 사용하여 데이터베이스 스키마를 관리하고 있으며 정수가 아닌 값을 기본 키 (특히 문자열)로 사용하려는 간단한 테이블을 만들고 있습니다. 내 문제를 추상화하기 위해 employees직원이 영숫자 문자열로 식별되는 테이블이 있다고 가정 해 보겠습니다 "134SNW".

다음과 같은 마이그레이션에서 테이블을 만들려고했습니다.

create_table :employees, {:primary_key => :emp_id} do |t|
    t.string :emp_id
    t.string :first_name
    t.string :last_name
end

이것이 나에게주는 것은 라인을 완전히 무시하고 t.string :emp_id정수 열로 만든 것처럼 보입니다 . execute호출 에서 SQL을 작성하지 않고도 레일이 PRIMARY_KEY 제약 조건 (PostgreSQL을 사용하고 있음)을 생성하도록하는 다른 방법이 있습니까?

참고 : 문자열 열을 기본 키로 사용하는 것이 최선이 아니라는 것을 알고 있으므로 정수 기본 키를 추가하라는 대답은하지 마십시오. 어쨌든 하나를 추가 할 수 있지만이 질문은 여전히 ​​유효합니다.


나는 같은 문제가 있으며 이것에 대한 답을보고 싶습니다. 지금까지 어떤 제안도 효과가 없었습니다.
Sean McCleary

1
Postgres를 사용하는 경우 이들 중 어느 것도 작동하지 않습니다. Dan Chak의 "Enterprise Rails"에는 자연 / 복합 키 사용에 대한 몇 가지 팁이 있습니다.
Azeem.Butt 2010

다음과 같은 것을 사용할 때주의하십시오. <!-language : lang-rb-> "ALTER TABLE employee ADD PRIMARY KEY (emp_id);" 실행 후 테이블 제약 조건이 올바르게 설정되었지만 rake db:migrate자동 생성 된 스키마 정의에이 제약 조건이 포함되어 있지 않습니다!
pymkin

답변:


107

불행히도 .NET을 사용하지 않고는 할 수 없다고 판단했습니다 execute.

작동하지 않는 이유

ActiveRecord 소스를 조사하여 다음에 대한 코드를 찾을 수 있습니다 create_table.

에서 schema_statements.rb:

def create_table(table_name, options={})
  ...
  table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
  ...
end

따라서 create_table옵션 에서 기본 키를 지정하려고하면 지정된 이름으로 기본 키가 생성된다는 것을 알 수 있습니다 (또는 지정되지 않은 경우 id). 테이블 정의 블록 내에서 사용할 수있는 것과 동일한 메서드를 호출하여이를 수행합니다 primary_key..

에서 schema_statements.rb:

def primary_key(name)
  column(name, :primary_key)
end

이것은 지정된 유형의 이름을 가진 열을 생성합니다 :primary_key. 이는 표준 데이터베이스 어댑터에서 다음과 같이 설정됩니다.

PostgreSQL: "serial primary key"
MySQL: "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
SQLite: "INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL"

해결 방법

우리는 이것들을 기본 키 유형으로 사용 execute하기 때문에 정수가 아닌 기본 키를 만들어야합니다 (PostgreSQL serial은 시퀀스를 사용하는 정수입니다).

create_table :employees, {:id => false} do |t|
  t.string :emp_id
  t.string :first_name
  t.string :last_name
end
execute "ALTER TABLE employees ADD PRIMARY KEY (emp_id);"

로 그리고 숀 McCleary 언급 , 당신의 액티브 모델을 사용하여 기본 키를 설정해야합니다 set_primary_key:

class Employee < ActiveRecord::Base
  set_primary_key :emp_id
  ...
end

15
해결 방법은 rake db : test : clone 또는 rake db : schema : load를 만들어 emp_id를 정수 열로 만듭니다.
Donny Kurnia 2012

18
실제로 후자는 더 이상 사용되지 않으므로 self.primary_key=대신 을 사용해야합니다 set_primary_key.
Gary S. Weaver

2
나를 위해 일한 유일한 해결책 은 다음과 같습니다 . 다른 사람들에게 도움이되기를 바랍니다. stackoverflow.com/a/15297616/679628
findchris

8
이 접근 방식의 문제점은 데이터베이스를 설정할 때 다시 유형 열 schema.rb emp_id이된다는 것 integer입니다. 어떤 식 으로든 schema.rb저장할 수없는 것 같습니다 execute "ALTER TABLE employees ADD PRIMARY KEY (emp_id);".
Tintin81 2015 년

4
Tintin81 당신 @ @DonnyKurnia은 스키마 덤프를 전환 할 수 있습니다 schema.rbstructure.sql통해 config.active_record.schema_format이 될 수있는, 설정 :sql또는 :ruby. guides.rubyonrails.org/v3.2.13/...
Svilen 이바노프에게

21

이것은 작동합니다 :

create_table :employees, :primary_key => :emp_id do |t|
  t.string :first_name
  t.string :last_name
end
change_column :employees, :emp_id, :string

예쁘지 않을 수도 있지만 최종 결과는 정확히 원하는 것입니다.


1
드디어! 이것은 나를 위해 일한 유일한 솔루션입니다.
findchris 2014 년

마이그레이션을위한 코드도 지정해야합니까? 아니면 Rails가 아래로 마이그레이션했을 때해야 할 일을 알만큼 똑똑합니까?
amey1908

2
다운 마이그레이션의 경우 :drop_table :employees
Austin

6
안타깝게도이 메서드는 schema.rb동기화되지 않은 a 를 남겨 둡니다 . :primary_key => :emp_id선언 을 유지 하지만 rake db:schema:load테스트 시작시 호출 될 때 정수 열을 생성합니다. 그러나 structure.sql(구성 옵션) 테스트 를 사용하도록 전환 하면 설정이 유지되고 해당 파일을 사용하여 스키마를로드 할 수 있습니다.
Tom Harrison

18

나는 이것을 처리하는 한 가지 방법이 있습니다. 실행 된 SQL은 ANSI SQL이므로 대부분의 ANSI SQL 호환 관계형 데이터베이스에서 작동합니다. 이것이 MySQL에서 작동하는지 테스트했습니다.

이주:

create_table :users, :id => false do |t|
    t.string :oid, :limit => 10, :null => false
    ...
end
execute "ALTER TABLE users ADD PRIMARY KEY (oid);"

모델에서 다음을 수행하십시오.

class User < ActiveRecord::Base
    set_primary_key :oid
    ...
end


9

Rails 4.2에서 시도했습니다. 사용자 지정 기본 키를 추가하려면 마이그레이션을 다음과 같이 작성할 수 있습니다.

# tracks_ migration
class CreateTracks < ActiveRecord::Migration
  def change
    create_table :tracks, :id => false do |t|
      t.primary_key :apple_id, :string, limit: 8
      t.string :artist
      t.string :label
      t.string :isrc
      t.string :vendor_id
      t.string :vendor_offer_code

      t.timestamps null: false
    end
    add_index :tracks, :label
  end
end

문서를보고 column(name, type, options = {})다음 행을 읽으십시오.

type: primary_key와, : : 문자열, 텍스트, : 정수 : 플로트 : 진수 : 날짜 : 시간 : 날짜 : 이진 : 매개 변수는 일반적으로 마이그레이션 기본 다음 중 하나입니다 유형 중 하나입니다 부울 .

내가 보여준 것처럼 위의 ides를 얻었습니다. 이 마이그레이션을 실행 한 후의 테이블 메타 데이터는 다음과 같습니다.

[arup@music_track (master)]$ rails db
psql (9.2.7)
Type "help" for help.

music_track_development=# \d tracks
                    Table "public.tracks"
      Column       |            Type             | Modifiers
-------------------+-----------------------------+-----------
 apple_id          | character varying(8)        | not null
 artist            | character varying           |
 label             | character varying           |
 isrc              | character varying           |
 vendor_id         | character varying           |
 vendor_offer_code | character varying           |
 created_at        | timestamp without time zone | not null
 updated_at        | timestamp without time zone | not null
 title             | character varying           |
Indexes:
    "tracks_pkey" PRIMARY KEY, btree (apple_id)
    "index_tracks_on_label" btree (label)

music_track_development=#

그리고 Rails 콘솔에서 :

Loading development environment (Rails 4.2.1)
=> Unable to load pry
>> Track.primary_key
=> "apple_id"
>>

3
문제 schema.rb는 생성 되는 파일이 string기본 키 의 유형을 반영하지 않으므로를 사용 하여 데이터베이스를 생성 할 때 load기본 키가 정수로 생성된다는 것입니다.
Peter Alfvin 2015

9

Rails 5에서 할 수있는 일은

create_table :employees, id: :string do |t|
  t.string :first_name
  t.string :last_name
end

create_table 문서를 참조하십시오 .


before_create :set_id기본 키의 값을 할당하기 위해 모델에 일종의 방법 을 추가해야한다는 점은 주목할 가치가 있습니다.
FloatingRock

8

이 방법을 사용하여 수행 할 수있는 것 같습니다.

create_table :widgets, :id => false do |t|
  t.string :widget_id, :limit => 20, :primary => true

  # other column definitions
end

class Widget < ActiveRecord::Base
  set_primary_key "widget_id"
end

그러면 widget_id 열이 Widget 클래스의 기본 키가되고 개체가 생성 될 때 필드를 채우는 것은 사용자의 몫입니다. before create 콜백을 사용하면 가능합니다.

그래서 라인을 따라 뭔가

class Widget < ActiveRecord::Base
  set_primary_key "widget_id"

  before_create :init_widget_id

  private
  def init_widget_id
    self.widget_id = generate_widget_id
    # generate_widget_id represents whatever logic you are using to generate a unique id
  end
end

이것은 적어도 PostgreSQL에서는 작동하지 않습니다. 기본 키가 전혀 지정되지 않았습니다.
Rudd Zwolinski

흠 흥미롭게도 MySQL on Rails 2.3.2로 테스트했습니다. 레일 버전과도 관련이있을 수 있습니까?
paulthenerd 2009-07-29

잘 모르겠습니다. Rails 버전이 아닌 것 같습니다. Rails 2.3.3을 사용하고 있습니다.
Rudd Zwolinski

안타깝게도이 접근 방식을 사용하는 테이블에 대해 실제 기본 키가 설정되지 않았습니다.
Sean McCleary

2
이 접근 방식은 적어도 Rails 4.2 및 Postgresql에서 작동합니다. 나는 테스트를했지만 대답에 주어진 primary_key: true대신 사용해야 합니다primary
Anwar

8

저는 Rails 2.3.5를 사용하고 있으며 다음 방법은 SQLite3에서 작동합니다.

create_table :widgets, { :primary_key => :widget_id } do |t|
  t.string :widget_id

  # other column definitions
end

: id => false는 필요하지 않습니다.


죄송합니다.하지만 귀하의 기술을 적용 할 때 widget_id가 정수로 변경되었습니다. ... eny 솔루션이 있습니까?
kikicarbonell 2013-06-18

1
이것은 적어도 ActiveRecord 4.1.4에서는 작동하지 않습니다. 그것은 다음과 같은 오류를 제공합니다 :you can't redefine the primary key column 'widgets'. To define a custom primary key, pass { id: false } to create_table.
테이머 Shlash에게

4

"이것이 X 데이터베이스에서 나를 위해 일했습니다"라고 말하는 거의 모든 솔루션 후에 "Postgres에서 나를 위해 작동하지 않았습니다"라는 효과에 대한 원래 포스터의 댓글을 봅니다. 여기서 진짜 문제는 실제로 Rails의 Postgres 지원 일 수 있습니다. 이는 완벽하지 않으며이 질문이 처음 게시되었을 때 2009 년에 더 나빴을 것입니다. 예를 들어, 내가 정확하게 기억한다면 Postgres를 사용한다면 기본적으로 rake db:schema:dump.

나는 Postgres 닌자가 아닙니다. Postgres에 대한 Xavier Shay의 훌륭한 PeepCode 비디오에서이 정보를 얻었습니다. 이 비디오는 실제로 Aaron Patterson의 라이브러리를 간과합니다. Texticle이라고 생각하지만 잘못 기억할 수 있습니다. 하지만 그 외에는 꽤 좋습니다.

어쨌든 Postgres 에서이 문제가 발생하면 솔루션이 다른 데이터베이스에서 작동하는지 확인하십시오. rails new새 앱을 샌드 박스로 생성하는 데 사용 하거나

sandbox:
  adapter: sqlite3
  database: db/sandbox.sqlite3
  pool: 5
  timeout: 5000

에서 config/database.yml.

그리고 그것이 Postgres 지원 문제임을 확인할 수 있고 수정 사항을 알아낼 수 있다면 Rails 커뮤니티 내 Postgres 사용자 기반이 상당히 큽니다. .


2
FUD 및 부정확성에 대한 반대 투표. 저는 2007 년부터 대부분의 Rails 프로젝트에서 Postgres를 사용했습니다. Rails의 Postgres 지원은 훌륭하고 스키마 덤퍼에 문제가 없습니다.
Marnen Laibow-Koser

2
한편으로는 Postgres가 여기서 문제가 아니었다는 것은 사실입니다. 다른 한편으로는 2009 년의 pg 전용 스키마 덤퍼 버그가 있습니다. rails.lighthouseapp.com/projects/8994/tickets/2418 그리고 다른 하나는 rails.lighthouseapp.com/projects/8994/tickets/2514
Giles Bowkett

Postgres 8.3 업데이트를 둘러싼 Rails 및 Postgres와 관련하여 결정된 문제가 있었는데, 여기서 (정확히 IMO) 문자열 리터럴 및 인용에 대한 SQL 표준으로 변경하기로 결정했습니다. Rails 어댑터는 Postgres 버전을 확인하지 않았고 한동안 몽키 패치를해야했습니다.
Judson

@Judson 흥미 롭군요. 나는 그것을 만난 적이 없습니다.
Marnen Laibow-Koser 2012-06-14

4

Rails 3에서 작동하는 해결책을 찾았습니다.

마이그레이션 파일 :

create_table :employees, {:primary_key => :emp_id} do |t|
  t.string :emp_id
  t.string :first_name
  t.string :last_name
end

그리고 employee.rb 모델에서 :

self.primary_key = :emp_id

3

Rails 3과 MySQL에서 나를 위해 일한 트릭은 다음과 같습니다.

create_table :events, {:id => false} do |t|
  t.string :id, :null => false
end

add_index :events, :id, :unique => true

그래서:

  1. 정수 기본 키를 생성하지 않도록 : id => false를 사용하십시오.
  2. 원하는 데이터 유형을 사용하고 다음을 추가하십시오. : null => false
  3. 해당 열에 고유 인덱스 추가

MySQL이 null이 아닌 열의 고유 인덱스를 기본 키로 변환하는 것 같습니다!


MySQL 5.5.15가 설치된 Rails 3.22에서는 고유 키만 생성하고 기본 키는 생성하지 않습니다.
lulalala

2

당신은 옵션을 사용해야합니다 : id => false

create_table :employees, :id => false, :primary_key => :emp_id do |t|
    t.string :emp_id
    t.string :first_name
    t.string :last_name
end

이것은 적어도 PostgreSQL에서는 작동하지 않습니다. 기본 키가 전혀 지정되지 않았습니다.
Rudd Zwolinski

1
이 접근 방식은 id 열을 생략하지만 기본 키는 실제로 테이블에 설정되지 않습니다.
Sean McCleary

1

이 솔루션은 어떻습니까?

Employee 모델 내부에 왜 우리는 coloumn에서 고유성을 확인하는 코드를 추가 할 수 없습니다. 예 : Employee가 문자열 인 EmpId가 있다는 점에서 Employee가 Model이라고 가정하면 EmpId에 ": uniqueness => true" 를 추가 할 수 있습니다 .

    class Employee < ActiveRecord::Base
      validates :EmpId , :uniqueness => true
    end

이것이 해결책인지 확실하지 않지만 이것은 나를 위해 일했습니다.


1

나는 이것이 내가 우연히 만난 오래된 스레드라는 것을 알고 있지만 아무도 DataMapper를 언급하지 않았다는 사실에 충격을 받았습니다.

ActiveRecord 규칙에서 벗어나야한다면 이것이 훌륭한 대안이라는 것을 알게되었습니다. 또한 레거시에 대한 더 나은 접근 방식이며 데이터베이스를 "있는 그대로"지원할 수 있습니다.

Ruby Object Mapper (DataMapper 2)는 많은 약속을 가지고 있으며 AREL 원칙을 기반으로합니다!


1

색인 추가는 저에게 효과적이며 MySql btw를 사용하고 있습니다.

create_table :cards, {:id => false} do |t|
    t.string :id, :limit => 36
    t.string :name
    t.string :details
    t.datetime :created_date
    t.datetime :modified_date
end
add_index :cards, :id, :unique => true
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.