Forum: Italian Ruby user group le foreign keys.

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
B0f6d8efcf671ea3163449e231264cc4?d=identicon&s=25 Mauro (Guest)
on 2008-12-15 18:17
(Received via mailing list)
Per gestire per foreign keys so che e' possibile fare una cosa del
genere
con le migrations.
Tabelle: settori, categorie.
script/generate scaffold sector name:string;
script/generate scaffold category codice:string descrizione:text
esempi:text
sector:references.
Come database uso postgres e lavoro su linux debian sid.
Quando vado  a vedere le proprieta' della colonna sector_id, che sarebbe
la
foreign_key, con lo strumento pgadmin, mi aspetto di vedere un "si" alla
voce "chiave esterna".
Invece c'e' un bel "no" secco.
Eppure http://dev.rubyonrails.org/changeset/7973 sembrerebbe che quel
"references" serva proprio a creare le chiavi esterne.
Non e' cosi'?
3e9939f082cf2f71083f784c78755feb?d=identicon&s=25 Salvatore (Guest)
on 2008-12-16 11:20
(Received via mailing list)
Io ho risolto con un plugin molto carino (foreign_key_migrations) però
sono nuovo di questo mondo
magari c'è un metodo migliore, la parola agli esperti :)

ciao ciao.
B0f6d8efcf671ea3163449e231264cc4?d=identicon&s=25 Mauro (Guest)
on 2008-12-16 13:17
(Received via mailing list)
2008/12/16 Salvatore <safeforge@gmail.com>

> Io ho risolto con un plugin molto carino (foreign_key_migrations) però
> sono nuovo di questo mondo
> magari c'è un metodo migliore, la parola agli esperti :)


allora esperti fatevi sentire dai.....
7de465f222e6a9c7fe658e370d0bfe05?d=identicon&s=25 Paolo Montrasio (pmontrasio)
on 2008-12-16 21:58
A quanto ho capito, ma potrei sbagliarmi, il metodo references crea
nella tabella i campi con i nomi giusti per poi essere usati
dall'accoppiata belongs_to e has_one/many, però non crea la foreign key
nello schema del db.

Mauro wrote:
> 2008/12/16 Salvatore <safeforge@gmail.com>
>
>> Io ho risolto con un plugin molto carino (foreign_key_migrations) per�
> sono nuovo di questo mondo
>> magari c'� un metodo migliore, la parola agli esperti :)
>
>
> allora esperti fatevi sentire dai.....

Non conoscevo quel plugin ma da quel che ho visto nella sua
documentazione pare risolvere il problema molto bene. Finora, proprio
con PostgreSQL, includevo questo modulo nelle migration però adesso
forse cambierò abitudini.

-------------
module MigrationHelpers
  def foreign_key(from_table, from_column, to_table)
    constraint_name = "fk_#{from_table}_#{from_column}"

    execute %{alter table #{from_table}
              add constraint #{constraint_name}
              foreign key (#{from_column})
              references #{to_table}(id)}
  end

  def remove_foreign_key(from_table, from_column)
    constraint_name = "fk_#{from_table}_#{from_column}"

    execute %{alter table #{from_table}
              drop constraint #{constraint_name}}
  end
end
-------------

Paolo
B0f6d8efcf671ea3163449e231264cc4?d=identicon&s=25 Mauro (Guest)
on 2008-12-16 23:04
(Received via mailing list)
2008/12/16 Paolo Montrasio <paolo@paolomontrasio.com>

> >> magari c'� un metodo migliore, la parola agli esperti :)
> module MigrationHelpers
>    constraint_name = "fk_#{from_table}_#{from_column}"
>
>    execute %{alter table #{from_table}
>              drop constraint #{constraint_name}}
>  end
> end
> -------------
>

Nel manuale Agile development with rails, nell'applicazione di esempio,
per
creare le foreign keys fa una cosa del genere:

class CreateLineItems < ActiveRecord::Migration
  def self.up
    create_table :line_items do |t|
      t.integer :product_id,  :null => false, :options => "CONSTRAINT
fk_line_item_products REFERENCES products(id)"
      t.integer :order_id,    :null => false, :options => "CONSTRAINT
fk_line_item_orders REFERENCES orders(id)"
      t.integer :quantity,    :null => false
      t.decimal :total_price,  :null => false, :precision => 8,  :scale
=>
2

      t.timestamps
    end
  end

  def self.down
    drop_table :line_items
  end
end
7de465f222e6a9c7fe658e370d0bfe05?d=identicon&s=25 Paolo Montrasio (pmontrasio)
on 2008-12-17 09:19
Mauro wrote:
>     create_table :line_items do |t|
>       t.integer :product_id,  :null => false, :options => "CONSTRAINT
> fk_line_item_products REFERENCES products(id)"

Anche questa è una possibilità, ma il libro prosegue avvertendo che
"Rails migrations doesn’t provide a database-independent way to specify
these foreign key constraints, so we had to resort to adding native DDL
clauses". Quel codice in particolare è per SQLlite3.

Quando ritorna sull'esempio un centinaio di pagine dopo infatti crea
anche lui un modulo MigrationHelper che definisce un metodo foreign_key.
Il codice è totalmente diverso da quello che ho presentato io ma l'idea
è la stessa.

L'ideale sarebbe avere un plugin con driver per i vari db, esattamente
come fa ActiveRecord. Anzi, l'ideale sarebbe che
ActiveRecord::ConnectionAdapters::TableDefinition finalmente supportasse
nativamente le foreign key. Cito Agile Web Development with Rails:

"Many Rails developers don’t bother specifying database-level
constraints such as foreign keys, relying instead on the application
code to make sure that everything knits together correctly. That’s
probably why Rails migrations don’t let you specify constraints.
However, when it comes to database integrity, many (including Dave and
Sam) think an ounce of extra checking can save pounds of latenight
production system debugging."

Una cosa infatti è certa: prima o poi l'applicazione sbaglierà e lascerà
dati inconsistenti nel database. Sta al db difendersi tramite
l'impostazione degli opportuni constraint.

Paolo
B0f6d8efcf671ea3163449e231264cc4?d=identicon&s=25 Mauro (Guest)
on 2008-12-17 10:14
(Received via mailing list)
2008/12/17 Paolo Montrasio <paolo@paolomontrasio.com>

> Mauro wrote:
> >     create_table :line_items do |t|
> >       t.integer :product_id,  :null => false, :options => "CONSTRAINT
> > fk_line_item_products REFERENCES products(id)"
>
> Anche questa è una possibilità, ma il libro prosegue avvertendo che
> "Rails migrations doesn't provide a database-independent way to specify
> these foreign key constraints, so we had to resort to adding native DDL
> clauses". Quel codice in particolare è per SQLlite3.


Esatto, infatti non funziona su postgres.
Usero' allora lo stesso metodo ma col codice per postgres.
B0f6d8efcf671ea3163449e231264cc4?d=identicon&s=25 Mauro (Guest)
on 2008-12-17 14:42
(Received via mailing list)
2008/12/16 Paolo Montrasio <paolo@paolomontrasio.com>

> >> magari c'� un metodo migliore, la parola agli esperti :)
> module MigrationHelpers
>    constraint_name = "fk_#{from_table}_#{from_column}"
>
>    execute %{alter table #{from_table}
>              drop constraint #{constraint_name}}
>  end
> end


Solo per creare delle foreign key non c'e' necessita' di installarsi un
plugin, questo modulo basta e avanza, posso copiarmelo pari pari? :-)
Hai preso lo spunto dal manuale agile development?
Ho visto il codice in quel manuale, aggiunge anche dei trigger.
Forse pero' manca del codice, ne riporto uno stralcio:

module MigrationHelpers

  def foreign_key(from_table, from_column, to_table)
    constraint_name = "#{from_table}_#{to_table}_fkey"
    execute %{
      CREATE TRIGGER #{constraint_name}_insert
      BEFORE INSERT ON #{from_table}
      FOR EACH ROW BEGIN
        SELECT
          RAISE(ABORT, "constraint violation: #{constraint_name}" )
        WHERE
          (SELECT id FROM #{to_table} WHERE id = NEW.#{from_column}) IS
NULL;
      END;
    }

ci sono altri due trigger, uno per l'update e uno per il delete, non
vedo
pero' dove crea le references.
7de465f222e6a9c7fe658e370d0bfe05?d=identicon&s=25 Paolo Montrasio (pmontrasio)
on 2008-12-18 11:17
Mauro wrote:
> 2008/12/16 Paolo Montrasio <paolo@paolomontrasio.com>
>
>> >> magari c'� un metodo migliore, la parola agli esperti :)
>> module MigrationHelpers
>>    constraint_name = "fk_#{from_table}_#{from_column}"
>>
>>    execute %{alter table #{from_table}
>>              drop constraint #{constraint_name}}
>>  end
>> end
>
>
> Solo per creare delle foreign key non c'e' necessita' di installarsi un
> plugin, questo modulo basta e avanza, posso copiarmelo pari pari? :-)

Usalo pure, più si riusa il codice e meglio è. Se fai dei miglioramenti
però postali così ne approfittiamo tutti :-)

> Hai preso lo spunto dal manuale agile development?

Non credo. Ho dei vaghi ricordi a proposito di un post su un blog. Da lì
ho preso del codice che poi ho modificato un po' (forse era per mysql?
forse mancava la remove?)

> Ho visto il codice in quel manuale, aggiunge anche dei trigger.
> Forse pero' manca del codice, ne riporto uno stralcio:
>
> module MigrationHelpers
>
>   def foreign_key(from_table, from_column, to_table)
>     constraint_name = "#{from_table}_#{to_table}_fkey"
>     execute %{
>       CREATE TRIGGER #{constraint_name}_insert
>       BEFORE INSERT ON #{from_table}
>       FOR EACH ROW BEGIN
>         SELECT
>           RAISE(ABORT, "constraint violation: #{constraint_name}" )
>         WHERE
>           (SELECT id FROM #{to_table} WHERE id = NEW.#{from_column}) IS
> NULL;
>       END;
>     }
>
> ci sono altri due trigger, uno per l'update e uno per il delete, non
> vedo pero' dove crea le references.

Credo che faccia proprio a meno delle reference e gestisca tutto con i
trigger. Dovrebbe avere circa lo stesso effetto ma mi chiedo se sia una
buona soluzione: se oltre ai trigger ci sono anche le foreign key forse
una ragione ci sarà...  Ci vorrebbe però un esperto DBA per disquisire
di queste cose sapendo veramente quel che si dice. Ce n'è qualcuno che
ci legge?

Paolo
05720447a341aaffb8827039df3931df?d=identicon&s=25 Luca Mearelli (Guest)
on 2008-12-18 15:11
(Received via mailing list)
2008/12/18 Paolo Montrasio <paolo@paolomontrasio.com>:
> Credo che faccia proprio a meno delle reference e gestisca tutto con i
> trigger. Dovrebbe avere circa lo stesso effetto ma mi chiedo se sia una
> buona soluzione: se oltre ai trigger ci sono anche le foreign key forse
> una ragione ci sarà...  Ci vorrebbe però un esperto DBA per disquisire
> di queste cose sapendo veramente quel che si dice. Ce n'è qualcuno che
> ci legge?

non sono un dba, ma provo ad aggiungere qualcosa alla discussione:

Idealmente i trigger sono da preferire (per lo scopo in oggetto)
perche' permettono di definire le regole di consistenza dei dati in
maniera "dichiarativa", ovvero come proprieta' del modello dei dati.
Insomma sono una soluzione piu' "pulita" al problema di mantenere la
consistenza della base dati, e la manutenzione e' piu' semplici
rispetto ai trigger (e.g. non c'e' bisogno di tenere allineate tre
diverse procedure per delete, insert ed update)

Ma in realta' *dipende* dal database che si usa perche' trigger e
constraints sono implementati in maniera diversa sui diversi database.
Addirittura in MySQL non tutti gli storage engine mettono a
disposizione le foreign key: le tabelle MyISAM, pur accettando la
definizione di una FK senza errori poi NON la usano :)

per esempio:

CREATE TABLE parent_innodb (id INT NOT NULL,
                     PRIMARY KEY (id)
) ENGINE=INNODB;
CREATE TABLE child_innodb (id INT, parent_id INT,
                    INDEX par_ind (parent_id),
                    FOREIGN KEY (parent_id) REFERENCES parent_innodb(id)
) ENGINE=INNODB;

insert into parent_innodb values (1);
insert into child_innodb values (1, 1);
commit;
select * from child_innodb;
insert into child_innodb values (1, 2);
select * from child_innodb;
delete from parent_innodb where id=1;


CREATE TABLE parent_myisam (id INT NOT NULL,
                     PRIMARY KEY (id)
) ENGINE=MyISAM;
CREATE TABLE child_myisam (id INT, parent_id INT,
                    INDEX par_ind (parent_id),
                    FOREIGN KEY (parent_id) REFERENCES parent_myisam(id)
) ENGINE=MyISAM;

insert into parent_myisam values (1);
insert into child_myisam values (1, 1);
commit;
select * from child_myisam;
insert into child_myisam values (1, 2);
select * from child_myisam;
delete from parent_myisam where id=1;

In quei casi usare un trigger puo' essere l'unico modo (a meno di
cambiare il db).

un paio di link (su MySQL o Oracle)

- http://www.oracle.com/technology/oramag/oracle/08-...
-
http://stackoverflow.com/questions/229765/triggers...
- http://forge.mysql.com/wiki/Triggers#Emulating_Che...

ciao!
Luca
This topic is locked and can not be replied to.