2016-02-12 12 views
5

Tworzę aplikację biblioteki Rails, w której jeden gatunek jest gatunkami has_many, a książka belongs_to. Potrzebuję obiektów formularza, ponieważ mój model danych będzie miał wiele relacji wiele do wielu, a także dlatego, że chcę się nauczyć obsługi obiektów formularza. Oparłem obiekt formularza od Rails Cast # 416 Form Objects. Moja forma, obiekt formularza, modele i kontroler wydają się działać. Tworzone są nowe książki i są one powiązane z gatunkami, ale wszystkie są z gatunku "gatunek". Używam find_or_create_by. Myślę, że problem leży w book_form.rb, gdzie @genre ||= Genre.find_by(name: :name) nie przekazuje informacji o Gatunek z formularza. Moja odpowiedni kod jest następujący:Używanie szyn Rails find_or_create_by z obiektami formularzy

model book.rb

class Book < ActiveRecord::Base 
    belongs_to :genre 
    before_save :convert_isbn 
. 
. 
. 
end 

model genre.rb

class Genre < ActiveRecord::Base 
    has_many :books, dependent: :destroy 
    validates_associated :books 
end 

book_form.rb

class BookForm 
    include ActiveModel::Model 

    def self.model_name 
    ActiveModel::Name.new(self, nil, "Book") 
    end 
. 
. 
. 
    validates :name, presence: true 

    delegate :name, to: :genre 
    delegate :title, :year, :pages, :isbn, :summary, :volume_a, :volume_b, to: :book 

    def genre 
    @genre ||= Genre.find_by(name: :name) 
    end 

    def book 
    @book ||= genre.books.build 
    end 

    def submit(params) 
    genre.attributes = params.slice(:name).permit(:name) 
    book.attributes = params.slice(:title, :year, :pages, :isbn, :summary, :volume_a,:volume_b).permit(:title, :year, :pages, :isbn, :summary, :volume_a,:volume_b) 
    if valid? 
     genre.save! 
     book.save! 
     true 
    else 
     false 
    end 
    end 
end 

books_controller.rb

class BooksController < ApplicationController 
    before_action :admin_user, only: [:new, :edit, :destroy] 

    def new 
    @book_form = BookForm.new 
    @genres = Genre.all 
    end 

    def create 
    @book_form = BookForm.new 
    @genres = Genre.all 
    if @book_form.submit(params[:book]) 
     flash[:success] = "You've added a new book to the library!" 
     redirect_to @book_form 
    else 
     render 'new' 
    end 
    end 

Zobacz new.html.erb

<%= form_for @book_form do |f| %> 
    <%=render 'shared/error_messages', object: f.object %> 

    <%= f.label :title %> 
    <%= f.text_field :title, class: 'form-control' %> 
    . 
    . 
    . 
    <%= f.label :genre %><br> 

    <%= collection_select(:genre, :name, Genre.all, :name, :name) %> 
    <br> 
    <br> 
    <%= f.submit "Add book to library", class: "btn btn-primary" %> 
<% end %> 

Korzystanie gem pry, mam to z serwera na stworzenie książki:

Started POST "/books" for ::1 at 2016-02-22 14:19:20 -0700 
Processing by BooksController#create as HTML 
    Parameters: {"utf8"=>"✓",  "authenticity_token"=>"bPusgyl9n+p07eQsEAe9CpSsithtkg33HMifj8KTsidv3GDLuhjibOC7d2mm5boC4w7ZUne64R4n4OMQotDE4g==", 
       "book"=>{"title"=>"test", 
       "year"=>"2016", 
       "pages"=>"222", 
       "isbn"=>"9780-xx-xx-xx", 
       "summary"=>"fake summary", 
       "volume_a"=>"1", 
       "volume_b"=>"2"}, 
       "genre"=>{"name"=>"Mystery"}, 
       "commit"=>"Add book to library"} 

From: /home/nathaniel/rails_apps/allredlib/app/forms/book_form.rb @ line 38 BookForm#submit: 

    37: def submit(params) 
=> 38: binding.pry 
    39: genre.attributes = params.slice(:name).permit(:name) 
    40: book.attributes = params.slice(:title, :year, :pages, :isbn, :summary, :volume_a,:volume_b).permit(:title, :year, :pages, :isbn, :summary, :volume_a,:volume_b) 
    41: if valid? 
    42:  genre.save! 
    43:  book.save! 
    44:  true 
    45: else 
    46:  false 
    47: end 
    48: end 

Więc książka gatunek jest coraz podana w parametrach, ale Mam do niej dostęp w niewłaściwy sposób w mojej książce. Jeśli komentarz binding.pry, formularz tworzy nową książkę z gatunku "nazwa" zamiast gatunku "Tajemnica", jak chcę to zrobić.

Po wpisaniu @genre na szynach podczas korzystania binding.pry, mam

[1] pry(#<BookForm>)> @genre 
=> #<Genre:0x007f19554e1220 
id: 20, 
name: "name", 
book_id: nil, 
created_at: Thu, 25 Feb 2016 20:28:45 UTC +00:00, 
updated_at: Thu, 25 Feb 2016 20:28:45 UTC +00:00> 
wyniki

najnowszy binding.pry 27: def gatunek => 28: binding.pry 29: @genre || = Genre.find_or_initialize_by (name:: name) 30: # @ gatunek || = Genre.find_or_initialize_by (name: params [: gatunek] [: name]) 31: koniec

+1

Myślę, że możesz tego szukać? http://stackoverflow.com/questions/7537180/how-to-pass-argument-to-delegate-method-in-rails – lacostenycoder

+0

Tak, tak myślę. Jestem całkiem nowy dla Rails i nie wiem, jak wdrożyć swoje rozwiązanie we właściwy sposób. Jakieś wskazówki? – neallred

Odpowiedz

3

Jeśli Będziesz szukał gatunku według jego nazwy, a następnie musisz podać atrybut nazwy w metodzie hash opcji.

def genre 
    @genre ||= Genre.find_or_create_by(name: :name) 
end 

UPDATE

zmiana

genre.attributes = params.slice(:name).permit(:name) 

do

genre.attributes = { :name => params[:genre][:name] } 

rzucać także binding.pry przed i po genre.save! i zobacz, co otrzymasz, jeśli wpiszesz @genre, ponieważ twoja metoda gatunku powinna utworzyć tę zmienną instancji, do której właśnie wysyłane są parametry z genre.attributes, która akceptuje skrót.

Rozważałbym to samo w przypadku parów książkowych, ale jeśli plaster działa, niż w porządku.

Ponadto, ponieważ dzwonisz Genre.all w widoku formularza, to prawdopodobnie nie trzeba robić tego w działaniach kontrolerów, ale jeśli nie, to za pomocą obiektu @genres wewnątrz formularza widzenia collection_select bo zamiast ciebie "Wykonuję wszystkie żądania dwa razy na modelu.

@genres = Genre.all 
+0

Jest to bardzo pomocne. Dodałem dane wyjściowe z Pry na dole mojej odpowiedzi. Prawy gatunek jest przynajmniej przekazywany do mieszania, więc wygląda na to, że 'name:: name' z' @genre || = Genre.find_or_create_by (name:: name) 'jest niewłaściwym sposobem uzyskania dostępu do nazwy gatunku. Nie mogę uzyskać do niego dostępu za pomocą @ genre.name lub genre.name, co powoduje błąd lub powoduje awarię serwera. Zgadzam się, że potrzebuję testów, ale nie wiem wystarczająco dużo o testach, aby napisać pomocne testy. – neallred

+0

Popatrzę na to. – lacostenycoder

+0

Jest 'Parametry: { "utf8"=> "✓", "authenticity_token"=> "bPusgyl9n + p07eQsEAe9CpSsithtkg33HMifj8KTsidv3GDLuhjibOC7d2mm5boC4w7ZUne64R4n4OMQotDE4g ==", "książka"=> { "title"=> "test", "rok" = > "2016", "strony" => "222", "isbn" => "9780-xx-xx-xx", "summary" => "fałszywe podsumowanie", "volume_a" => "1 ", " volume_b "=>" 2 "}, " gatunek "=> {" name "=>" Mystery "}, " commit "=>" Dodaj książkę do biblioteki "}' – neallred

0

Tworzycie gatunku z gatunku ustawione na gatunek za każdym razem. Nie chcesz, aby create gatunek, prawdopodobnie chcesz tylko utworzyć instancję gatunku (z new).

Spróbuj zmianę tego

@genre ||= Genre.find_or_create_by(genre: :genre) 

aby coś takiego

@genre ||= Genre.new 

Również chciałbym zaproponować zmianę nazwy pola „gatunek” na modelu gatunek do czegoś innego jak „nazwa”. W przeciwnym razie sprawy stają się naprawdę mylące.

myślę tej linii:

collection_select(:genre, :id, Genre.all, :genre, :genre) 

powinno być więcej takich jak:

collection_select(:genre, :genre, Genre.all, :genre, :genre) 

a jeśli były, aby zmienić na "name":

collection_select(:genre, :name, Genre.all, :name, :name) 
+0

To prawda. Problem z @genre || = Genre.new polega na tym, że nie znajdzie lub nie stworzy mojego gatunku. Tworzy tylko nowy gatunek z atrybutem gatunek jako zero i atrybut genre_id jako następny numer id. – neallred

+0

Dodałem kilka komentarzy. Problem z find_or_create w sposób, w jaki go używasz, polega na tym, że za pierwszym razem stworzy gatunek "gatunek", a następnie odnajdzie ten sam gatunek za każdym następnym razem i nie będziesz tworzyć nowych. Lub po prostu zmienisz nazwę starych. – msergeant

+0

Dziękuję za pomoc. Zgadzam się, że gatunek jest mylącą nazwą kolumny i zmieniłem ją. To, co naprawdę chcę zrobić, to wybrać już istniejący gatunek i powiązać z nim nową książkę. Mogę to zrobić statycznie za pomocą "def genre @genre || = Genre.find_by (name:: History) koniec", ale muszę zastąpić: Historia z czymś, co odczytuje wartość wejściową formularza. Jakieś pomysły? – neallred

Powiązane problemy