Tworzenie nowego rekordu wykorzystującego tablice słownikowe

Witam… znowu.
Nie wiem czy określenie tablica słownikowa jest ogólnie przyjmowane, ale mam nadzieję, że jest przynajmniej zrozumiałe :slight_smile:

Próbuję stworzyć widok, który pozwalałby dodać nowy rekord do bazy danych, ale część jego pól jest wybierana (najlepiej z droplisty) bazując na dostępnych wierszach tablicy słownikowej. Bawię się w stworzenie bazy danych pojazdów kosmicznych (taa, wiem :)), w której każdy obiekt posiada pole klasy statku i może przynależeć tylko do jednej z wcześniej zdefiniowanych klas, takich jak niszczyciel, czy krążownik.

Nie wiem jak zmapować tablicę słownikową i jej wartości (id i name), żeby wyświetlały się w dropliście i można je było tam wybrać.

Póki co natkałem się w Agile Web Development(…) na wybieranie jednego z elementów zdefiniowanych wcześniej w zwykłej tablicy.

EDIT: aha, przydałaby się jeszcze jakaś metoda walidacji, sprawdzająca, czy pole rekordu zawiera się w zbiorze elementów tablicy słownikowej.

Proszę o pomoc.

Helpery select i select_tag łykają tablicę – albo pojedynczych elementów (stringów) które są wtedy etykietą ORAZ wartością (przesyłaną do serwera przy wysłaniu formularza), albo dwuelementowych tablic etykieta-wartość.

Jeśli masz hasha, musisz go najpierw do takiej tablicy skonwertować. Jak – to już sam wybierz :wink:

np.:

[code=ruby]class Spaceship < ActiveRecord::Base

default_scope :order => ‘name’

def self.find_all_for_select
self.all.map {|x| [x.name, x.id]}
end

end

kontroler:

@spaceships = Spaceship.find_all_for_select

w widoku (zakładając, że klucz to spaceship_id):

<%= f.select :spaceship_id, @spaceships -%>[/code]
Jeszcze tylko małe sprostowanie do tego co napisał Tomash: helper select_tag nie przyjmuje tablicy etykieta-wartość tylko gotowe option tagi (Jeden) jako html.

Do przygotowywania option tagów z tablicy służy helper options_for_select. Czytajcie dokładnie dokumentację Rails - tam są wszystkie odpowiedzi na takie pytania :slight_smile:

Dzięki wielke za odpowiedź,
Miałem małą przerwę od programowania, więc to co opisaliście sprawdzam dopiero dziś. Mam jednak problem, moja lista próbuje wysłać do bazy danych nil:

[code] Showing app/views/vessels/new.html.erb where line #12 raised:

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.inject

Extracted source (around line #12):

9:


10:


11: <%= f.label :classtype %>

12: <%= f.select :classtype, @classtypes -%>
13:


14:


15: <%= f.label :classsubtype %>
[/code]
Na pewno po prostu porąbałem coś z nazwami, które (jakoś tak się złożyło) są dość niekonsekwentne: w bazie danych przetrzymuję pole classtype, które wybierane jest z tablicy vesseltype, ale żeby od razu nil ? Tak, jakby select wyświetlał tylko dostępne nazwy, ale przy wyborze konkretnej pozycji wcale nie przesyłał jej wartości do bazy.

Wygląda na to, że nie przypisałeś nic do @classtypes w kontrolerze. Czyli nie masz linijki:

@spaceships = Spaceship.find_all_for_select

z przykładu Howsiaka.

Ależ mam:

[code=ruby]class VesselsController < ApplicationController
(…)

def new
@vessel = Vessel.new
@companies = Company.find(:all)
@classtypes = VesselType.find_all_for_select
@classsubtypes = VesselSubtype.find_all_for_select
end

(…)
end[/code]
Gdybym w ogóle tej linijki nie miał, moim zdaniem nie widziałbym zawartości drop-listy.

Ok nie przeczytałem. :wink:

Nie powinno być, przypadkiem?:

<%= f.select :classtype_id, @classtypes -%>

[quote=tjeden]Nie powinno być, przypadkiem?:

<%= f.select :classtype_id, @classtypes -%>

[/quote]
Wtedy wywala, że nie zna metody classtype_id, pewnie dlatego, że takie pole nie istnieje w tabeli w bazie. Być może rails absolutnie wymaga, żeby pola będące w relacji z innymi tablicami miały końcówki _id, choć mam nadzieję, że nie.

No absolutnie nie wymaga, ale taka jest konwencja i pasuje się jej trzymać.

Ale jeśli nie chcesz:

class Order < ActiveRecord::Base
  belongs_to :customer, :foreign_key => "klucz_w_bazie_danych_bez_koncowki_id"
end

Czyli działać powinno, a ciągle nic z tego.
W ogóle zaskakuje mnie, że widok new próbuje wstrzyknąć obiekt nil do bazy danych, mimo, że są odpowiednie walidacje, upewniające się, że tak nie będzie.

Przykład omawiany w “Agile (…)” z wykorzystaniem tablicy tablic (do wybierania jednej z trzech metod płatności przy zamówieniu - jeśli ktoś przerabiał i wie o czym mówię) jest bardzo podobny. Ja natomiast mapuję tablicę z bazy na tablicę w aplikacji, sprawdzałem jak dokładnie działa to mapowanie, czy wszystko gra, więc poszedłem do konsoli projektu i tam wywołałem:

VesselType.all.map {|x| [x.name, x.id]}

i dostałem:

[["fighter", 1], ["bomber", 2], ... ["battleship", 7]]

Wszystko wydaje się być w porządku, dlatego chyba nie rozumiem dokładnie skąd w selekcie bierze się nil

Ciekawe, że nikomu nie wpadło do głowy, że trzeba użyć w modelach odpowiednich dowiązań relacyjnych:

w modelu reprezentującym konkretny egzemplarz statku:

has_many

w modelu reprezentującym klasy pojazdów:

belongs_to

Zrobiłem wszystko od nowa i teraz śmiga :slight_smile:

Wydawało mi się, że te asocjacje są oczywiste, ale fajnie że już działa. :slight_smile: