रेल का उपयोग करते हुए, मैं पूर्णांक-टाइप किए गए कॉलम नहीं होने के लिए अपनी प्राथमिक कुंजी कैसे सेट कर सकता हूं?


85

मैं डेटाबेस स्कीमा का प्रबंधन करने के लिए रेल माइग्रेशन का उपयोग कर रहा हूं, और मैं एक साधारण तालिका बना रहा हूं जहां मैं प्राथमिक कुंजी के रूप में एक गैर-पूर्णांक मान का उपयोग करना चाहता हूं (विशेष रूप से, एक स्ट्रिंग)। मेरी समस्या को दूर करने के लिए, मान लीजिए कि एक तालिका है 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और आगे बढ़कर इसे पूर्णांक स्तंभ बना दिया। वहाँ एक और रास्ता है कि मेरे लिए PRIMARY_KEY बाधा (मैं PostgreSQL का उपयोग कर रहा हूँ) उत्पन्न कर रहा हूँ, एक executeकॉल में SQL लिखने के बिना ?

नोट : मुझे पता है कि प्राथमिक कुंजी के रूप में स्ट्रिंग कॉलम का उपयोग करना सबसे अच्छा नहीं है, इसलिए कृपया पूर्णांक प्राथमिक कुंजी जोड़ने के लिए कोई जवाब नहीं दें। मैं वैसे भी एक जोड़ सकता हूं, लेकिन यह प्रश्न अभी भी मान्य है।


मुझे भी यही समस्या है और मैं इसके लिए एक उत्तर देखना पसंद करूंगा। अब तक कोई भी सुझाव काम नहीं आया है।
शॉन मैकक्लेरी

1
यदि आप Postgres का उपयोग कर रहे हैं तो उनमें से कोई भी काम नहीं करेगा। डैन चाक के "एंटरप्राइज रेल्स" में प्राकृतिक / समग्र कुंजियों का उपयोग करने के कुछ सुझाव हैं।
अज़ीम.बट्ट

सावधान रहें कि जब आप कुछ इस तरह का उपयोग करते हैं: <! यद्यपि तालिका बाधा को सही ढंग से चलाने के बाद सेट किया गया है rake db:migrateऑटो-जनरेट स्कीमा परिभाषा में यह बाधा नहीं है!
झांकना

जवाबों:


107

दुर्भाग्य से, मैंने निर्धारित किया है कि इसका उपयोग किए बिना ऐसा करना संभव नहीं है 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);"

और जैसा कि शॉन मैक्लेरी ने उल्लेख किया है , आपके ActiveRecord मॉडल को प्राथमिक कुंजी का उपयोग करना चाहिए set_primary_key:

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

15
वर्कअराउंड रेक db: टेस्ट: क्लोन या रेक db: स्कीमा: लोड करेगा जो पूर्णांक स्तंभ के रूप में emp_id बनाने के लिए।
डॉनी कुर्निया

18
वास्तव में, अब आप के self.primary_key=बजाय का उपयोग किया जाना चाहिए set_primary_key, क्योंकि बाद में पदावनत किया जाता है।
गैरी एस बुनकर

2
यहाँ एकमात्र समाधान है जो मेरे लिए काम करता है। मुझे आशा है कि यह दूसरों की मदद करता है: stackoverflow.com/a/15297616/679628
3

8
इस दृष्टिकोण के साथ समस्या यह है कि जब आप अपने डेटाबेस से सेटअप करते schema.rb emp_idहैं तो एक integerप्रकार का कॉलम फिर से होगा । ऐसा लगता है कि किसी भी तरह से schema.rbस्टोर नहीं किया जा सकता है execute "ALTER TABLE employees ADD PRIMARY KEY (emp_id);"
तिनतिन T T

4
@DonnyKurnia @ Tintin81 आप से स्कीमा डंप स्विच कर सकते हैं schema.rbकरने के लिए structure.sqlके माध्यम से config.active_record.schema_formatसेटिंग है, जो हो सकता है या तो :sqlया :rubyguide.rubyonrails.org/v3.2.13/…
Svilen Ivanov

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
आखिरकार! यह एकमात्र समाधान है जिसने मेरे लिए काम किया।
खोजक

क्या हमें नीचे माइग्रेशन के लिए भी कोड निर्दिष्ट करना होगा? या रेल काफी स्मार्ट है यह जानने के लिए कि नीचे माइग्रेट होने पर क्या करना है?
amey1908

2
डाउन माइग्रेशन के लिए:drop_table :employees
ऑस्टिन

6
दुर्भाग्य से, यह विधि भी हमें एक साथ छोड़ती है schema.rbजो सिंक में नहीं है ... यह :primary_key => :emp_idघोषणा को बनाए रखेगा , लेकिन जब rake db:schema:loadइसे बुलाया जाएगा, जैसा कि परीक्षणों की शुरुआत में है, यह एक पूर्णांक कॉलम का उत्पादन करेगा। हालाँकि, यह ऐसा हो सकता है कि यदि आप structure.sql(config विकल्प) परीक्षणों का उपयोग करने के लिए स्विच करते हैं , तो सेटिंग्स को बनाए रखेंगे और स्कीमा को लोड करने के लिए उस फ़ाइल का उपयोग करेंगे।
टॉम हैरिसन

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

मैंने इसे रेल्स 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,:: स्ट्रिंग,: पाठ,: पूर्णांक,: नाव,: दशमलव,: दिनांक, समय,: दिनांक,: द्विआधारी,: पैरामीटर सामान्य रूप से माइग्रेशन देशी प्रकार, जो निम्न में से एक है में से एक है बूलियन ।

जैसा कि मैंने दिखाया है मुझे उपरोक्त आईडी मिली हैं। इस माइग्रेशन को चलाने के बाद तालिका मेटा डेटा इस प्रकार है:

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

और रेल कंसोल से:

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

3
हालाँकि, समस्या यह है schema.rbकि उत्पन्न होने वाली फ़ाइल stringप्राथमिक कुंजी के प्रकार को प्रतिबिंबित नहीं करती है , इसलिए जब डेटाबेस डेटाबेस को जनरेट करता है load, तो प्राथमिक कुंजी पूर्णांक के रूप में बन जाती है।
पीटर अल्फविन

9

रेल 5 में आप कर सकते हैं

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

Create_table प्रलेखन देखें ।


वर्थ नोटिंग कि आपको before_create :set_idप्राथमिक कुंजी के मान को निर्दिष्ट करने के लिए मॉडल में किसी प्रकार की विधि जोड़ने की आवश्यकता होगी
फ्लोटिंगरॉक

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

स्तंभ विजेट_id को विजेट श्रेणी के लिए प्राथमिक कुंजी बना देगा, फिर ऑब्जेक्ट्स बनाए जाने पर फ़ील्ड को पॉप्युलेट करना आपके ऊपर है। कॉलबैक बनाने से पहले आपको ऐसा करने में सक्षम होना चाहिए।

तो कुछ की तर्ज पर

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 में नहीं। कोई भी प्राथमिक कुंजी निर्दिष्ट नहीं है।
रुड ज़्वोलिंस्की

हम्म दिलचस्प है कि मैंने इसे MySQL के साथ रेल 2.3.2 पर परीक्षण किया है - शायद रेल के संस्करण से संबंधित हो सकता है?
पौलथेनरड

मुझे यकीन नहीं है - मुझे नहीं लगता कि यह रेल का संस्करण है। मैं रेल्स का उपयोग कर रहा हूँ।
रूड ज़्वोलिंस्की

दुर्भाग्य से इस दृष्टिकोण का उपयोग करते हुए तालिका के लिए एक वास्तविक प्राथमिक कुंजी सेट नहीं है।
शॉन मैक्लेरी

2
यह दृष्टिकोण कम से कम रेल्स 4.2 और पोस्टग्रैक्स्ल के साथ काम करता है। मैंने परीक्षण किया है, लेकिन आपको primary_key: trueकेवल primaryउत्तर में दिए गए जवाब के बजाय उपयोग करने की आवश्यकता है
अनवर

8

मैं रेल 2.3.5 पर हूं और मेरा निम्नलिखित तरीका SQLite3 के साथ काम करता है

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

  # other column definitions
end

इसके लिए कोई आवश्यकता नहीं है: आईडी => असत्य।


क्षमा करें, लेकिन जब मैं आपकी टेक्निन लागू करता हूं, तो विजेट_डियर पूर्णांक में बदल गया है ... क्या आपके पास एनी समाधान है?
किकाइकार्बोनेल

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.
ताम्र शालाश

4

लगभग हर समाधान के बाद जो कहता है "यह मेरे लिए एक्स डेटाबेस पर काम करता है", मैं मूल पोस्टर द्वारा "पोस्टग्रेस पर मेरे लिए काम नहीं किया" के प्रभाव के लिए एक टिप्पणी देखता हूं। यहाँ वास्तविक मुद्दा वास्तव में रेल्स में पोस्टग्रैज सपोर्ट हो सकता है, जो निर्दोष नहीं है, और संभवतः 2009 में वापस बदतर हो गया था जब यह प्रश्न मूल रूप से पोस्ट किया गया था। उदाहरण के लिए, यदि मुझे सही याद है, यदि आप पोस्टग्रेज पर हैं, तो आप मूल रूप से उपयोगी आउटपुट प्राप्त नहीं कर सकते rake db:schema:dump

मैं खुद पोस्टग्रेज निंजा नहीं हूं, मुझे यह जानकारी जेवियर शाय के पोस्टग्रेज पर उत्कृष्ट पीपकोड वीडियो से मिली। वह वीडियो वास्तव में हारून पैटरसन द्वारा एक पुस्तकालय को नजरअंदाज करता है, मुझे लगता है कि टेक्स्टिकल लेकिन मुझे गलत याद हो सकता है। लेकिन इसके अलावा यह बहुत अच्छा है।

वैसे भी, यदि आप Postgres पर इस समस्या में चल रहे हैं, तो देखें कि क्या समाधान अन्य डेटाबेस में काम करते हैं। शायद rails newसैंडबॉक्स के रूप में एक नया ऐप बनाने के लिए उपयोग करें, या बस कुछ ऐसा बनाएं

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

में है config/database.yml

और अगर आप यह सत्यापित कर सकते हैं कि यह एक पोस्टग्रैज सपोर्ट इश्यू है, और आप एक फिक्स का पता लगाते हैं, तो कृपया रैल में पैच का योगदान दें या अपने फिक्स को एक रत्न में पैकेज करें, क्योंकि पोस्ट्स का यूजर बेस रेल्स समुदाय के भीतर बहुत बड़ा है, मुख्य रूप से हरकू के लिए धन्यवाद ।


2
FUD और अशुद्धि के लिए डाउनवोटिंग। मैंने 2007 के बाद से अपने अधिकांश रेल परियोजनाओं पर पोस्टग्रेज का उपयोग किया है। रेल में पोस्टग्रेट्स का समर्थन उत्कृष्ट है, और स्कीमा डम्पर के साथ कोई समस्या नहीं है।
मार्नेन लाइबो-कोसर

2
एक ओर, यह सच है कि Postgres यहाँ समस्या नहीं थी। दूसरी ओर, यहां 2009 से बग डम्पर को पीजी केवल स्कीमा है rails.lighthouseapp.com/projects/8994/tickets/2418 और यहाँ एक और एक है rails.lighthouseapp.com/projects/8994/tickets/2514
जाइल्स Bowkett

पोस्टग्रेज 8.3 अपडेट के आसपास रेल और पोस्टग्रेज के साथ एक निश्चित मुद्दा था, जहां उन्होंने (सही, आईएमओ) स्ट्रिंग मानकों और उद्धृत के लिए एसक्यूएल मानक को बदलने का फैसला किया। रेल एडाप्टर ने पोस्टग्रेज संस्करणों की जांच नहीं की और उसे थोड़ी देर के लिए बंद करना पड़ा।
जुडसन

@ जूडसन दिलचस्प। मैं उसमें कभी नहीं भागा।
१६:५५ पर मार्नेन लाईबो-कोसर

4

मुझे इसका एक हल मिला जो रेल 3 के साथ काम करता है:

माइग्रेशन फ़ाइल:

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

और कर्मचारी.आरबी मॉडल में:

self.primary_key = :emp_id

3

रेल 3 और MySQL पर मेरे लिए काम करने वाली चाल यह थी:

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

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

इसलिए:

  1. उपयोग: आईडी => झूठी तो एक पूर्णांक प्राथमिक कुंजी उत्पन्न करने के लिए नहीं
  2. वांछित डेटाटाइप का उपयोग करें, और जोड़ें: null => गलत
  3. उस कॉलम पर एक अद्वितीय सूचकांक जोड़ें

ऐसा लगता है कि MySQL एक गैर कुंजी कॉलम पर एक प्राथमिक कुंजी के लिए अद्वितीय सूचकांक को परिवर्तित करता है!


MySQL 5.5.15 के साथ रेल 3.22 में यह केवल एक अद्वितीय कुंजी बनाता है, लेकिन प्राथमिक कुंजी नहीं।
लूलालाल

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 में नहीं। कोई भी प्राथमिक कुंजी निर्दिष्ट नहीं है।
रुड ज़्वोलिंस्की

1
यह दृष्टिकोण आईडी कॉलम को छोड़ देगा लेकिन प्राथमिक कुंजी वास्तव में टेबल पर सेट नहीं होगी।
शॉन मैकक्लेरी

1

इस समाधान के बारे में कैसे,

कर्मचारी मॉडल के अंदर हम उस कोड को क्यों नहीं जोड़ सकते हैं जो कोलम में अद्वितीयता के लिए जाँच करेगा, उदाहरण के लिए: मान लें कि कर्मचारी में मॉडल है कि आपके पास EmpId है जो स्ट्रिंग है तो इसके लिए हम जोड़ सकते हैं ": विशिष्टता =" EmpId के लिए "सही"

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

मुझे यकीन नहीं है कि यह समाधान है लेकिन इसने मेरे लिए काम किया।


1

मुझे पता है कि यह एक पुराना धागा है जिसे मैंने ठोकर खाया था ... लेकिन मैं हैरान हूं कि किसी ने उल्लेखित DataMapper नहीं है।

मुझे लगता है कि अगर आपको ActiveRecord सम्मेलन से बाहर निकलने की आवश्यकता है, तो मैंने पाया है कि यह एक बढ़िया विकल्प है। इसके अलावा यह विरासत के लिए एक बेहतर दृष्टिकोण है और आप डेटाबेस का समर्थन कर सकते हैं "जैसा है"।

रूबी ऑब्जेक्ट मैपर (डेटामैपर 2) बहुत सारे वादे रखता है और एआरईएल सिद्धांतों पर भी निर्माण करता है!


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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.