Models multipli in un form

Dunque, ho provato a seguire le guide per realizzare un form che
modifichi più models ma non ci sono riuscita.

Ho due tabelle Persone e Territori, con una relazione molti a molti, una
persona può amministrare più territori, un territorio può essere
amministrato nel tempo da più persone. Ho fatto di conseguenza la
tabella di collegamento AmministrazioneTerritori.

Ecco le tre tabelle:

class Persona < ActiveRecord::Base
has_many :amministrazioni_territori
has_many :territori, :through => :amministrazioni_territori
end

class Territorio < ActiveRecord::Base
has_many :amministrazioni_territori
has_many :persone, :through => :amministrazioni_territori
end

class AmministrazioneTerritorio < ActiveRecord::Base
belongs_to :persona
belongs_to :territorio
end


Questi i campi delle tabelle:

create_table “amministrazione_territori”, :force => true do |t|
t.integer “persona_id”, :null => false
t.integer “territorio_id”, :null => false
t.string “inizio_amministrazione”
t.string “fine_amministrazione”
t.string “carica”
t.datetime “created_at”
t.datetime “updated_at”
end

class CreatePersone < ActiveRecord::Migration
def self.up
create_table :persone do |t|
t.string :nome
t.string :famiglia
t.string :patronimico
t.string :variazione_nome
t.string :sesso
t.string :ind_temp1
t.string :int_nascita1
t.string :int_nascita2
t.string :ind_temp2
t.string :int_morte1
t.string :int_morte2
t.text :note

  t.timestamps
end

end

def self.down
drop_table :persone
end
end

class CreateTerritori < ActiveRecord::Migration
def self.up
create_table :territori do |t|
t.string :denominazione
t.string :variazione_nome
t.string :articolazione_territoriale
t.text :descrizione

  t.timestamps
end

end

def self.down
drop_table :territori
end
end

Vorrei quindi fare un form che mi permetta di:

  1. inserire tutti i dati anagrafici della persona in questione
  2. mi permetta di definire quali territori ha amministrato e in che
    periodo
  3. l’elenco dei territori amministrati venga recuperato dalla lista dei
    territori già presenti nella tabella omonima

L’ho così realizzato (in realtà ho cominciato modificando il form di
modifica, in quanto volevo prima che fosse possibile aggiungere dati ad
una persona già esistente, poi farlo anche per una persona creata ex
novo):

Modifica persona

<% form_for(@persona) do |f| %>
<%= f.error_messages %>

Dati anagrafici

<%= f.label :nome %>
<%= f.text_field :nome %>

<%= f.label :famiglia %>
<%= f.text_field :famiglia %>

<%= f.label :patronimico %>
<%= f.text_field :patronimico %>

<%= f.label :variazione_nome %>
<%= f.text_field :variazione_nome %>

<%= f.label :sesso %>
<%= f.text_field :sesso %>

Intervallo date di nascita
<%= f.select(:ind_temp1, %w[ v. p. d.], { :include_blank => true }) %> <%= f.text_field :int_nascita1, :size => 10 %> - <%= f.text_field :int_nascita2, :size => 10 %>

Intervallo date di morte
<%= f.select(:ind_temp2, %w[ v. p. d. q.], { :include_blank => true }) %><%= f.text_field :int_morte1, :size => 10 %> - <%= f.text_field :int_morte2, :size => 10 %>

Territorio di competenza <% fields_for @amministrazione_territorio do |t| %>

<%= t.label :carica %> <%= t.text_field :carica%>

<%= t.label :inizio_amministrazione %> <%= t.text_field :inizio_amministrazione%>

<%= t.label :fine_amministrazione %> <%= t.text_field :fine_amministrazione%>

<% end %>

<%= f.label :note %>
<%= f.text_area :note %>

<%= f.submit 'Update' %>

<% end %>

<%= link_to ‘Show’, @persona %> |
<%= link_to ‘Back’, persone_path %>

Le domande sono:
nella tabella AmministrazioneTerritorio ovviamente devo associare l’id
della persona e l’id del territorio? come faccio a passarglieli?
come faccio a recuperare nel menù a tendina i dati della tabella
territori?
così?

<%= t.label :territorio_id %> <%= @territori = Territorio.find(:all, :order => "name").map {|t| [t.name, t.id] } f.select(:name, @territori) %>

Grazie in anticipo a chiunque abbia voglia di aiutarmi! Sarei persa
senza di voi!
paola

Hai letto questa?
http://it.asciicasts.com/episodes/196-nested-model-form-parte-1

Nel controller:
@territori = Territorio.find(:all, :order => “name”)

Nella view dentro fields for:
f.collection_select :territorio_id, @territori

Non ho provato, prendilo come spunto

Grazie mille per il link, l’ho trovato molto utile, per poterlo
“provare” ho dovuto fare l’update a rails 2.3.5 e installare
esplicitamente la gem rack 1.0.1 (altrimenti dava errore).
Sto finendo di seguire il tutorial, poi provo ad applicare qualcosa e ti
dico cosa ne viene fuori!
Grazie ancora!!!

Riprovo:

Vorrei che il form che ho fatto riempisse sia i campi della tabella
persone che quelli della tabella amministrazione_territori.
La prima parte è facile, la seconda l’ho fatta così:

partial: _form_html.erb

<% f.fields_for :amministrazione_territori do |builder| %>
<%= render ‘amministrazione’, :f => builder %>
<% end %>

_amministrazione.html.erb

<%= f.label :territorio_id %>
<%= @territori = Territorio.find(:all, :order => "denominazione") f.collection_select(:territorio_id, @territori)%>

<%= f.label :carica %>
<%= f.text_field :carica %>

<%= f.label :inizio_amministrazione %>
<%= f.text_field :inizio_amministrazione %>

<%= f.label :fine_amministrazione %>
<%= f.text_field :fine_amministrazione %>

Gli ultimi 3 campi sono relativamente facili, sono della tabella
amministrazione_territori e si riempiono normalmente. Il primo invece
vorrei riempirlo con l’id del territorio recuperando il valore dalla
tabella territori, con una selection list. Ma così non va bene. E poi
un’altra domanda, la tabella amministrazione_territori è una tabella di
associazione di id, ossia associa ad ogni persona un territorio, l’id
della persona lo prende in automatico o devo passargli anche quello?
grazie come al solito!
p

class Persona < ActiveRecord::Base
has_many :amministrazione_territori
has_many :territori, :through => :amministrazione_territori
accepts_nested_attributes_for :territori
accepts_nested_attributes_for :amministrazione_territori
end

class Territorio < ActiveRecord::Base
has_many :amministrazione_territori
has_many :persone, :through => :amministrazione_territori
accepts_nested_attributes_for :persone
accepts_nested_attributes_for :amministrazione_territori
end

non avevo aggiunto gli accepts nested attributes for, ora l’ho fatto e
mi da un altro errore “uninitialized constant
Persona::AmministrazioneTerritori”.

Ho provato così:

persone_controller.rb

class PersoneController < ApplicationController

GET /persone

GET /persone.xml

def index
@persone = Persona.all :order => :nome
@territori = Territorio.all :order => :denominazione

_amministrazione.html.erb

<%= f.label :territorio_id %>
<%= f.collection_select(:amministrazione_territorio, :territorio_id, @territori, :id, {:prompt => "select territorio"}) %>

ma mi da errore “undefined method `map’ for :territorio_id:Symbol”

e poi solo così:

<%= f.label :territorio_id %>
<%= f.select("amministrazione_territorio", :territorio_id, Territorio.all.collect, {|t| [ t.denominazione, t.id ] }, {:prompt => "select territorio"}) %>

ma anche questo mi da errore.
Non so più cosa fare…

Pietro G. wrote:

Il 02 marzo 2010 10.48, Paola A. [email protected] ha scritto:

class Persona < ActiveRecord::Base
 has_many :amministrazione_territori
non avevo aggiunto gli accepts nested attributes for, ora l’ho fatto e
mi da un altro errore “uninitialized constant
Persona::AmministrazioneTerritori”.
Ma tu gliel’hai spiegato che il plurale di Territorio è Territori e
non Territorios?

Da quello che ho capito (ma non ho mai provato, finora ho sempre
seguito le convenzioni rails), dovresti inserire in
config/environments.rb qualcosa tipo:

Inflector.inflections do |inflect|
inflect.irregular ‘territorio’, ‘territori’
end

Poi non so se è abbastanza furbo da farsi bastare questo o se invece
richiede anche

inflect.irregular ‘amministrazione_territorio’,
‘amministrazione_territori’

o qualcosa del genere.

pietro

Si si certo, ho usato le espressioni regolari per fare tutta la
grammatica che mi serviva…so che potevo lasciare tutto in inglese ma
volevo provare.

ActiveSupport::Inflector.inflections do |inflect|

inflect.plural(/a$/, “e”)
inflect.plural(/o$/, “i”)
inflect.plural(/e$/, “i”)
inflect.plural(/io$/, “i”)
inflect.plural(/(z)io$/, ‘\1ii’)
inflect.plural(/([cg])a$/, ‘\1he’)

inflect.singular(/e$/, “a”)
inflect.singular(/(font|attestazion|coniug|genitor)i$/, ‘\1e’)
inflect.singular(/([cg])he$/, ‘\1a’)
inflect.singular(/(territor)i$/, ‘\1io’)

end

“amministrazione_territori”.singularize
=> “amministrazione_territorio”
“amministrazione_territorio”.pluralize
=> “amministrazione_territori”

dici che è un problema di questo tipo?

Il 02 marzo 2010 10.48, Paola A. [email protected] ha scritto:

class Persona < ActiveRecord::Base
 has_many :amministrazione_territori
non avevo aggiunto gli accepts nested attributes for, ora l’ho fatto e
mi da un altro errore “uninitialized constant
Persona::AmministrazioneTerritori”.
Ma tu gliel’hai spiegato che il plurale di Territorio è Territori e
non Territorios?

Da quello che ho capito (ma non ho mai provato, finora ho sempre
seguito le convenzioni rails), dovresti inserire in
config/environments.rb qualcosa tipo:

Inflector.inflections do |inflect|
inflect.irregular ‘territorio’, ‘territori’
end

Poi non so se è abbastanza furbo da farsi bastare questo o se invece
richiede anche

inflect.irregular ‘amministrazione_territorio’,
‘amministrazione_territori’

o qualcosa del genere.

pietro

Il 02 marzo 2010 11.11, Paola A. [email protected] ha scritto:

“amministrazione_territori”.singularize
=> “amministrazione_territorio”
“amministrazione_territorio”.pluralize
=> “amministrazione_territori”

dici che è un problema di questo tipo?
Non saprei, bisogna fare delle prove. Da console,
Persona.first.amministrazione_territori funziona?

pietro

Pietro G. wrote:

Il 02 marzo 2010 11.11, Paola A. [email protected] ha scritto:

“amministrazione_territori”.singularize
=> “amministrazione_territorio”
“amministrazione_territorio”.pluralize
=> “amministrazione_territori”

dici che � un problema di questo tipo?
Non saprei, bisogna fare delle prove. Da console,
Persona.first.amministrazione_territori funziona?

pietro

no mi da lo stesso errore.

Pietro G. wrote:

Il 02 marzo 2010 11.19, Paola A. [email protected] ha scritto:

Persona.first.amministrazione_territori funziona?
no mi da lo stesso errore.

Ok, allora: se, da console, AmministrazioneTerritorio funziona, �
quasi certamente questione di inflessione, e in quel caso temo di non
poterti essere di aiuto, avendo sempre e solo usato l’inglese. Se
invece AmministrazioneTerritorio non funziona, bisogner� capire il
perch�.

pietro

Non ho capito se metto nella console

Persona.first.amministrazione_territori

l’errore che mi da è questo, ossia lo stesso che a video:

NameError: uninitialized constant Persona::AmministrazioneTerritori
from
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/dependencies.rb:105:in
const_missing' from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2199:in compute_type’
from
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/core_ext/kernel/reporting.rb:11:in
silence_warnings' from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2195:in compute_type’
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/reflection.rb:156:in
send' from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/reflection.rb:156:in klass’
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/reflection.rb:187:in
quoted_table_name' from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations/has_many_association.rb:97:in construct_sql’
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations/association_collection.rb:21:in
initialize' from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations.rb:1300:in new’
from
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.5/lib/active_record/associations.rb:1300:in
`amministrazione_territori’
from (irb):5

Ora ho riprovato a fare solo quel pezzettino di progetto ossia le sole
tre tabelle people, lands, administrations in inglese appunto, ma sono
arrivata allo stesso errore di prima ossia “undefined method `map’ for
:land_id:Symbol” ma questa volta i nested attributes li ho già messi.
:(((((

Il 02 marzo 2010 11.19, Paola A. [email protected] ha scritto:

Persona.first.amministrazione_territori funziona?
no mi da lo stesso errore.

Ok, allora: se, da console, AmministrazioneTerritorio funziona, è
quasi certamente questione di inflessione, e in quel caso temo di non
poterti essere di aiuto, avendo sempre e solo usato l’inglese. Se
invece AmministrazioneTerritorio non funziona, bisognerà capire il
perché.

pietro

Riprovo a rifare tutto il percorso:

Allora ho tre tabelle People, Lands e Administrations (in realtà ho
sbagliato a scrivere e l’ho chiamata Amministrations, ma pazienza). Le
prime due hanno una relazione molti a molti, una
persona può amministrare più territori, un territorio può essere
amministrato nel tempo da più persone. Ed ecco quindi la ragione di
esistere della tabella Administrations.

Ed ecco le 3 tabelle con i riferimenti alle chiavi esterne.
class CreatePeople < ActiveRecord::Migration
def self.up
create_table :people do |t|
t.string :nome
t.string :famiglia
t.string :patronimico
t.string :variazione_nome
t.string :sesso

  t.timestamps
end

end

def self.down
drop_table :people
end
end

class CreateLands < ActiveRecord::Migration
def self.up
create_table :lands do |t|
t.string :denominazione
t.string :variazione_nome
t.string :articolazione_territoriale
t.text :descrizione

  t.timestamps
end

end

def self.down
drop_table :lands
end
end

class CreateAmministrations < ActiveRecord::Migration
def self.up
create_table :amministrations do |t|
t.integer :person_id, :null => false, :options =>
“CONSTRAINT fk_amministration_people REFERENCES people(id)”
t.integer :land_id, :null => false, :options =>
“CONSTRAINT fk_amministration_lands REFERENCES lands(id)”
t.string :inizio_amministrazione
t.string :fine_amministrazione
t.string :carica

  t.timestamps
end

end

def self.down
drop_table :amministrations
end
end

Ho impostato le relazioni has_many e belongs_to

class Amministration < ActiveRecord::Base
belongs_to :person
belongs_to :land
end

class Land < ActiveRecord::Base
has_many :amministrations
has_many :people, :through => :amministrations
accepts_nested_attributes_for :people
accepts_nested_attributes_for :amministrations

end

class Person < ActiveRecord::Base
has_many :amministrations
has_many :lands, :through => :amministrations
accepts_nested_attributes_for :lands
accepts_nested_attributes_for :amministrations
end

Dopodichè vorrei in un unico form impostare una nuova persona e dire
anche che carica ha in un determinato territorio, quindi il form deve
completare la tabella People, recuperare l’elenco e gli id dei territori
già inseriti e completare la tabella Administrations con una data di
inizio amm, una data di fine amm, una carica (conte marchese o
quant’altro) e l’accoppiata id del territorio e id della persona.

Il form l’ho fatto così:

New person

<% form_for(@person) do |f| %>
<%= f.error_messages %>

Dati anagrafici

<%= f.label :nome %>
<%= f.text_field :nome %>

<%= f.label :famiglia %>
<%= f.text_field :famiglia %>

<%= f.label :patronimico %>
<%= f.text_field :patronimico %>

<%= f.label :variazione_nome %>
<%= f.text_field :variazione_nome %>

<%= f.label :sesso %>
<%= f.text_field :sesso %>

Territorio di competenza

<% f.fields_for :amministration do |builder| %>

<%= builder.label :land_id %>
<%= builder.collection_select(:amministration, :land_id, @lands, :id, {:prompt => "select territorio"}) %>

<%= builder.label :carica %>
<%= builder.text_field :carica %>

<%= builder.label :carica %>
<%= builder.text_field :carica %>

<%= builder.label :inizio_amministrazione %>
<%= builder.text_field :inizio_amministrazione %>

<%= builder.label :fine_amministrazione %>
<%= builder.text_field :fine_amministrazione %>

<% end %>

<%= f.submit 'Create' %>

<% end %>

<%= link_to ‘Back’, people_path %>

In tutto questo non ho ancora capito come passargli l’id della persone o
se gli viene passata in automatico.

Per @lands in questa parte del form
<%= builder.label :land_id %>

<%= builder.collection_select(:amministration, :land_id, @lands, :id,
{:prompt => “select territorio”}) %>

ho messo nel people_controller

def index
@people = Person.all
@lands = Land.all :order => :denominazione

ma non so se vada bene o meno.
Inoltre mi sembra di aver capito di dover aggiungere un

def new
@person = Person.new
@amministration = Amministration.new

def edit
@person = Person.find(params[:id])
@amministration = Amministration.new(params[:amministration])

idem al def create, al def update e non so al def destroy.

Fatto tutto ciò mi da errore “undefined method `map’ for
:land_id:Symbol”
Come ne esco? Dove ho sbagliato?

Il 02 marzo 2010 12.16, Paola A. [email protected] ha scritto:

<%= builder.collection_select(:amministration, :land_id, @lands, :id, {:prompt => “select territorio”}) %>

Fatto tutto ciò mi da errore “undefined method `map’ for
:land_id:Symbol”
Come ne esco? Dove ho sbagliato?

Parto dalla fine, scusa ma ora non posso ricontrollare tutto.

Qui l’errore è che stai passando un parametro di troppo,
cioè :amministration.

I form helper, infatti, possono essere usati o “da soli”, ad esempio
<%= text_field :object, :method %>

oppure associati all’oggetto form_for o fields_for:
<%= f.text_field :method %>

Nel secondo caso non è necessario specificare l’oggetto.

Quel messaggio di errore, undefined method map for :land_id:Symbol,
significa che ha provato a trattare l’oggetto :land_id (che è un
Symbol) come un array (il metodo map è un metodo di array).

In questo caso dovresti usare qualcosa tipo:
<%= builder.collection_select(:land_id, @lands, :id, :denominazione,
{:prompt => “select territorio”}) %>

Ovvero:
:land_id è il nome del campo;
@lands è la collezione di oggetti da usare per riempire la select;
:id è il metodo di Land da usare come valore della select (e in
effetti quello che ti serve nel form è l’id);
:denominazione è il metodo di Land da usare per mostrare ciò che deve
apparire all’utente per ogni scelta.

pietro

2010/3/2 Paola A. [email protected]:

You have a nil object when you didn’t expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.map

Certo, perché

@lands = Land.all :order => :denominazione

l’hai messo nel metodo index, ma non nel metodo new, dove dovrebbe
essere, per cui nel form @lands non è definita e quindi è nil.

Sposta quella riga nel metodo new.

pietro

Grazie per la pazienza, intanto!!
Ho sostituito <%= builder.collection_select(:amministration, :land_id,
@lands, :id,
{:prompt => “select territorio”}) %>

con <%= builder.collection_select(:land_id, @lands, :id, :denominazione,
{:prompt => “select territorio”}) %>

e adesso mi da

You have a nil object when you didn’t expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.map

Ok!!
Adesso lo visualizza!! Grazie!
Peccato che mi dia un altro errore nel momento che provo a
salvarlo…ossia
“unknown attribute: amministration”

si riferisce all’amministration qui
<% f.fields_for :amministration do |builder| %>
o qualcos’altro nel controller?

Il 02 marzo 2010 13.01, Paola A. [email protected] ha scritto:

Ok!!
Adesso lo visualizza!! Grazie!
Peccato che mi dia un altro errore nel momento che provo a
salvarlo…ossia
“unknown attribute: amministration”

<% f.fields_for :amministration do |builder| %>

prova :amministrations

pietro

Pietro G. wrote:

Il 02 marzo 2010 13.01, Paola A. [email protected] ha scritto:

Ok!!
Adesso lo visualizza!! Grazie!
Peccato che mi dia un altro errore nel momento che provo a
salvarlo…ossia
“unknown attribute: amministration”

<% f.fields_for :amministration do |builder| %>

prova :amministrations

pietro

ehm se metto :amministrations visualizza solo la prima parte del form,
la seconda non la fa vedere!

ehm se metto :amministrations visualizza solo la prima parte del form,
la seconda non la fa vedere!

non mi da nessun tipo di errore, semplicemente non mi visualizza la
parte del forum da fields_for in poi. Poi visualizza regolarmente il
tasto Submit e se salvo la persona la salva.
Ho ricontrollato la documentazione ma non capisco cosa non vada (come al
solito).
Poi ancora non ho capito se devo passargli l’id della persona da
associare nella tabella Amministrations oppure no.