programing

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

nasanasas 2020. 9. 18. 08:16
반응형

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


저는 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을 사용하고 있음)을 생성하도록하는 다른 방법이 있습니까?

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


불행히도 .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

이것은 작동합니다 :

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

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


나는 이것을 처리하는 한 가지 방법이 있습니다. 실행 된 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


I have tried it in Rails 4.2. To add your custom primary key, you can write your migration as :

# 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

While looking at the documentation of column(name, type, options = {}) and read the line :

The type parameter is normally one of the migrations native types, which is one of the following: :primary_key, :string, :text, :integer, :float, :decimal, :datetime, :time, :date, :binary, :boolean.

I got the above ides as i have shown. Here is the table meta data after running this migration :

[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=#

And from Rails console :

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

It looks like it is possible to do using this approach:

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

That will make the column widget_id the primary key for the Widget class, then it is up to you to populate the field when objects are created. You should be able to do so using the before create callback.

So something along the lines of

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

I am on Rails 2.3.5 and my following way works with SQLite3

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

  # other column definitions
end

There is no need for :id => false.


In Rails 5 you can do

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

See create_table documentation.


After nearly every solution which says "this worked for me on X database", I see a comment by the original poster to the effect of "didn't work for me on Postgres." The real issue here may in fact be the Postgres support in Rails, which is not flawless, and was probably worse back in 2009 when this question originally posted. For instance, if I remember correctly, if you're on Postgres, you basically can't get useful output from rake db:schema:dump.

I am not a Postgres ninja myself, I got this info from Xavier Shay's excellent PeepCode video on Postgres. That video actually overlooks a library by Aaron Patterson, I think Texticle but I could be remembering wrong. But other than that it's pretty great.

Anyway, if you're running into this problem on Postgres, see if the solutions work in other databases. Maybe use rails new to generate a new app as a sandbox, or just create something like

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

in config/database.yml.

And if you can verify that it is a Postgres support issue, and you figure out a fix, please contribute patches to Rails or package your fixes in a gem, because the Postgres user base within the Rails community is pretty large, mainly thanks to Heroku.


I found a solution to this that works with Rails 3:

The migration file:

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

And in the employee.rb model:

self.primary_key = :emp_id

The trick that worked for me on Rails 3 and MySQL was this:

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

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

So:

  1. use :id => false so as not to generate an integer primary key
  2. use the desired datatype, and add :null => false
  3. add a unique index on that column

Seems that MySQL converts the unique index on a non null column to a primary key!


you have to use the option :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

How about this solution,

Inside Employee model why can't we add code that will check for uniqueness in coloumn, for ex: Assume Employee is Model in that you have EmpId which is string then for that we can add ":uniqueness => true" to EmpId

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

I am not sure that this is solution but this worked for me.


I know this is an old thread I stumbled across... but I'm kind of shocked no one mentioned DataMapper.

I find if you need to stray out of the ActiveRecord convention, I've found that it is a great alternative. Also its a better approach for legacy and you can support the database "as-is".

Ruby Object Mapper (DataMapper 2) holds a lot of promise and build on AREL principles, too!


Adding index works for me, I'm using 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

참고URL : https://stackoverflow.com/questions/1200568/using-rails-how-can-i-set-my-primary-key-to-not-be-an-integer-typed-column

반응형