18

Desidero avere un modello "Customer" con una normale chiave primaria e un'altra colonna per memorizzare un "numero cliente" personalizzato. Inoltre, voglio che il db gestisca i numeri cliente predefiniti. Penso che definire una sequenza sia il modo migliore per farlo. Io uso PostgreSQL. Dai un'occhiata alla mia migrazione:Come aggiungere sequenze a una migrazione e utilizzarle in un modello?

class CreateAccountsCustomers < ActiveRecord::Migration 
    def up 

    say "Creating sequenze for customer number starting at 1002" 
    execute 'CREATE SEQUENCE customer_no_seq START 1002;' 

    create_table :accounts_customers do |t| 
     t.string :type 
     t.integer :customer_no, :unique => true 
     t.integer :salutation, :limit => 1 
     t.string :cp_name_1 
     t.string :cp_name_2 
     t.string :cp_name_3 
     t.string :cp_name_4 
     t.string :name_first, :limit => 55 
     t.string :name_last, :limit => 55 
     t.timestamps 
    end 

    say "Adding NEXTVAL('customer_no_seq') to column cust_id" 
    execute "ALTER TABLE accounts_customers ALTER COLUMN customer_no SET DEFAULT NEXTVAL('customer_no_seq');" 

    end 

    def down 
    drop_table :accounts_customers 
    execute 'DROP SEQUENCE IF EXISTS customer_no_seq;' 
    end 

end 

Se conosci una migliore "binari-like" approccio per aggiungere sequenze, sarebbe fantastico per farmi sapere.

Ora, se faccio qualcosa di simile

cust = Accounts::Customer.new 
cust.save 

il campo customer_no non è pre riempita con il valore successivo della sequenza (dovrebbe essere 1002).

Conosci un buon metodo per integrare le sequenze? O c'è un buon plugin? Saluti a tutte le risposte!

risposta

11

Non ho suggerimenti per un modo più 'rotaie' di gestire sequenze personalizzate, ma posso dirti perché il campo customer_no sembra non essere popolato dopo un salvataggio.

Quando ActiveRecord salva un nuovo record, l'istruzione SQL restituirà solo l'ID del nuovo record, non tutti i suoi campi, si può vedere dove questo avviene nella sorgente rotaie corrente qui https://github.com/rails/rails/blob/cf013a62686b5156336d57d57cb12e9e17b5d462/activerecord/lib/active_record/persistence.rb#L313

Al fine di vedere il valore è necessario ricaricare l'oggetto ...

cust = Accounts::Customer.new 
cust.save 
cust.reload 

Se hai sempre voglia di fare questo, prendere in considerazione l'aggiunta di un gancio after_create al tuo classe del modello ...

class Accounts::Customer < ActiveRecord::Base 
    after_create :reload 
end 
+0

Fantastico! Ha funzionato per me – Hiasinho

3

Credo che la risposta dei roboli non sia corretta.

Ho provato a implementarlo sulla mia applicazione (esattamente lo stesso ambiente: RoR + PostgreSQL), e ho scoperto che quando viene emesso save su RoR con l'oggetto con attributi vuoti, tenta di eseguire un INSERT sul database ricordando che tutti i VALORI devono essere impostati su NULL. Il problema è il modo in cui PostgreSQL gestisce i NULL: in questo caso, verrà creata la nuova riga ma con tutti i valori vuoti, vale a dire che il DEFAULT verrà ignorato. Se save ha scritto solo sugli attributi dell'istruzione INSERT riempiti su RoR, ciò funzionerebbe correttamente.

In altre parole, e concentrandosi solo sull'attributo type e customer_no menzionato sopra, questo è il modo PostgreSQL comporta:

SITUAZIONE 1:

INSERT INTO accounts_customers (type, customer_no) VALUES (NULL, NULL);

(questo è il modo Rotaie 'save funziona)

Risultato: una nuova riga con vuoto.210 e vuota customer_no

SITUAZIONE 2:

INSERT INTO accounts_customers (type) VALUES (NULL);

risultati: una nuova riga con vuota type e customer_no riempito con del sequenza NEXTVAL

ho un thread corso su questo, date un'occhiata a:

Ruby on Rails+PostgreSQL: usage of custom sequences

+0

Hmm, hai ragione su questo. Colpa mia! Ho inserito il tuo thread tra i preferiti. Vediamo, cosa succede. – Hiasinho

+0

Credo che questo fosse un bug di Rails, e non è più il caso di Rails 4+ (https://github.com/rails/rails/issues/5529) – nfm

2

Ho affrontato un problema simile, ma ho anche inserito :null => false sul campo saltando che verrà popolato automaticamente con nextval.

Bene, nel mio caso AR stava ancora cercando di inserire NULL se non è stato fornito alcun attributo nella richiesta e ciò ha provocato un'eccezione per la violazione del vincolo non nullo.

Ecco la mia soluzione. Ho appena cancellato questa chiave di attributo da @attributes e @changed_attributes e in questo caso postgres inserisce correttamente la sequenza prevista nextval.

ho messo questo nel modello:

before_save do 
    if (@attributes["customer_no"].nil? || @attributes["customer_no"].to_i == 0) 
    @attributes.delete("customer_no") 
    @changed_attributes.delete("customer_no") 
    end 
end 

Rails 3.2/Postgres 9.1

+0

Hack sporco ma funziona. – freemanoid

2

Se stai usando PostgreSQL, controllare la gemma che ho scritto, pg_sequencer:

https://github.com/code42/pg_sequencer

Fornisce un DSL per creare, rilasciare e modificare sequenze nelle migrazioni di ActiveRecord.

+0

Bel gioiello ma non una risposta. – freemanoid

Problemi correlati