Pytanie Alternatywa dla accepts_nested_attributes_for - być może virtus


Jestem stosunkowo nowy na szynach iw końcu znalazłem właściwy sposób użycia accepts_nested_attributes_for.

Istnieją jednak poważne zasoby w sieci, które mówią, że za pomocą accepts_nested_attributes_for jest ogólnie złą praktyką (taką jak ta jeden).

Jakie zmiany są konieczne, aby tego uniknąć accepts_nested_attributes_for i w którym folderze wstawilibyśmy dodatkowy plik klasy (chyba potrzeba dodatkowej klasy).

Przeczytałem to virtus jest odpowiedni do tego. Czy to prawda?

Oto bardzo prosty przykład wciąż używany accepts_nested_attributes_for (znajdź pełny przykład tutaj):

Modele

class Person < ActiveRecord::Base

    has_many :phones
    accepts_nested_attributes_for :phones

end

class Phone < ActiveRecord::Base

    belongs_to :person

end

Kontroler

class PeopleController < ApplicationController

    def new

        @person = Person.new
        @person.phones.new

    end

    def create

        @person = Person.new(person_params)
        @person.save

        redirect_to people_path

    end

    def index

        @people = Person.all

    end

private

    def person_params

        params.require(:person).permit(:name, phones_attributes: [ :id, :number ])

    end

end

Zobacz (people / new.html.erb)

<%= form_for @person, do |f| %>
    <p>
        <%= f.label :name %><br />
        <%= f.text_field :name %>
    </p>
    <%= f.fields_for :phones do |builder| %>
    <p>
            <%= builder.label :number %><br />
            <%= builder.text_field :number %>
    </p>
    <% end %>
    <%= f.submit %>
<% end %>

[edytować]
Czy byłoby dobrym pomysłem skorzystanie z obiektu usługi?


18
2017-07-13 22:10


pochodzenie




Odpowiedzi:


Twoje pytanie sugeruje, że uważasz, że funkcja accepts_nested_attributes jest złą rzeczą, co nie jest prawdą i działa doskonale.

Zacznę od stwierdzenia, że ​​nie potrzebujesz alternatywy dla accepts_nested_attributes_for, ale omówię to na końcu tego postu.

W nawiązaniu do podanego linku, nie powołuje się na to, dlaczego plakat uważa, że ​​accepts_nested_attributes_for powinien być w ogóle przestarzały i po prostu stany

moim skromnym zdaniem, powinien być przestarzały

Zagnieżdżone atrybuty są niezwykle ważną koncepcją przy rozważaniu przechwytywania wielu rekordów związanych z rodzicem w jednej postaci, która nie jest tylko funkcją Ruby on Rails, ale jest używana w większości złożonych aplikacji internetowych do wysyłania danych z powrotem do serwera z przeglądarki, niezależnie od tego, języków używanych do rozwijania witryny.

Nie krytykuję artykułu, o którym w ogóle mówisz. Dla mnie jest to po prostu wskazanie oczywistych alternatyw dla wypełnienia modelu opartego na bazach danych z dużą ilością kodu, który niekoniecznie jest powiązany z logiką biznesową. Konkretny przykład jest jedynie alternatywą dla stylu kodowania.

Kiedy czas to pieniądz, a presja jest włączona i jedna linia kodu wykona zadanie w porównaniu z 22 liniami kodu pokazanymi w przykładzie, w większości przypadków moje preferencje (nie wszystkie przypadki) polegają na używaniu jednej linii kodu w modelu (accepts_nested_attributes_for ), aby zaakceptować zagnieżdżone atrybuty odesłane z formularza.

Odpowiednia odpowiedź na twoje pytanie jest niemożliwa, ponieważ nie powiedziałeś właściwie, dlaczego uważasz, że accepts_nested_attributes_for nie jest dobrą praktyką, jednak najprostszą alternatywą jest wyodrębnienie atrybutów mieszania params w akcji kontrolera i obsługiwanie każdego rekordu pojedynczo w ramach transakcji.

Aktualizacja - kontynuuj w komentarzu

Myślę, że autor połączonego artykułu twierdzi, że po   oop-paradygmaty, każdy obiekt powinien tylko czytać i zapisywać własne dane.   Dzięki accepts_nested_attributes_for jeden obiekt zmienia niektóre   dane innych obiektów.

OK. Pozwala to wyjaśnić. Po pierwsze, paradygmaty OO nie sugerują takiej rzeczy. Zajęcia powinny być dyskretne, ale mogą wchodzić w interakcje z innymi klasami. W rzeczywistości nie byłoby sensu podejście OO w Ruby, gdyby tak było, ponieważ WSZYSTKO w rubinach jest klasą, dlatego nic nie będzie w stanie porozmawiać z niczym innym. Po prostu wyobraź sobie, co by się stało, gdyby obiekt, który akurat jest przypadkiem kontrolera, nie był w stanie wchodzić w interakcje z modelami lub innymi kontrolerami?

Dzięki accepts_nested_attributes_for jeden obiekt zmienia niektóre   dane innych obiektów.

Kilka punktów w tym stwierdzeniu, ponieważ jest to skomplikowane, postaram się być jak najkrótszy.

1) Instancje modelu chronią dane. W bardzo złożonych scenariuszach obejmujących setki tabel w dowolnym / większości innych języków (C, Delphi, VB, aby wymienić tylko kilka), warstwa pośrednia w rozwiązaniu trójwarstwowym właśnie to robi. W kategoriach Railsowych model jest miejscem dla logiki biznesowej i spełnia zadanie warstwy środkowej w rozwiązaniu trójwarstwowym, które zwykle jest wspierane przez procedury składowane i widoki w RDBMS. Modele całkiem słusznie powinny być w stanie rozmawiać ze sobą.

2) accepts_nested_attributes_for nie łamie żadnych zasad OO. po prostu upraszcza ilość kodu, który musiałbyś napisać, gdyby ta metoda nie istniała (jak się dowiadujesz). Jeśli przyjmiesz atrybuty zagnieżdżone w haszu params dla modeli potomnych, wszystko, co robisz, pozwala modelom podrzędnym obsługiwać te dane w taki sam sposób, jak robiłby to kontroler. Żadna logika biznesowa nie jest pomijana i masz dodatkowe korzyści.

W końcu

Mogę sobie pozwolić na dbałość o elegancję kodu (więcej niż o czas)

Mogę was zapewnić, że nie ma nic eleganckiego w pisaniu 20 + więcej linii kodu, niż potrzeba, i dodaniu setek linii więcej kodu z klejnotu, gdzie jedna linia kodu wykona pracę za ciebie. Jak stwierdzili inni (w tym ja) accepts_nested_attributes_for nie zawsze jest odpowiednią metodą ActiveRecord do użycia i dobrze, że robisz to patrząc na różne podejścia, ponieważ ostatecznie będziesz w stanie dokonać bardziej świadomego osądu, kiedy użyć wbudowanego w metodach i kiedy pisać własne. Sugerowałbym jednak, że aby w pełni zrozumieć, co się dzieje (jak twierdzisz, że masz czas), lepiej byłoby napisać własny kod do obsługi obiektów formularza i zaakceptować zagnieżdżone atrybuty alternatywne. W ten sposób zrozumiałbyś znacznie więcej.

Mam nadzieję, że ma sens i powodzenia w nauce.

AKTUALIZACJA 2

Aby w końcu dojść do punktu, w odniesieniu do własnej odpowiedzi, a także biorąc pod uwagę doskonałe komentarze, które inni poczynili na twojej własnej formie odpowiedzi, przedmioty poparte klejnotem virtus są doskonałym rozwiązaniem, szczególnie gdy mamy do czynienia z tym, jak dane muszą być Zebrane. Ta kombinacja pomaga oddzielić logikę interfejsu użytkownika od logiki biznesowej i tak długo, jak ostatecznie przenosisz dane do modeli, dzięki czemu logika biznesowa nie jest ominięta (jak pokazujesz, że robisz dokładnie to), masz świetne rozwiązanie .

Po prostu nie wykluczaj akceptowanych_zatrzymanych_pisów z ręki.

Możesz także zyskać na oglądaniu filmu railscasts Ryana Batesa na obiektach form.


16
2017-07-14 11:37



Rozumiem twoją argumentację bardzo dobrze i popieram ją w okolicznościach, które opisujesz. Ponieważ nie jestem profesjonalnym programistą, ale hobbiestem, mogę sobie pozwolić na dbałość o elegancję kodu (więcej niż o czas). Sądzę, że autor połączonego artykułu twierdzi, że każdy kolejny obiekt powinien tylko czytać i zapisywać własne dane, stosując się do paradygmatów oop. Z accepts_nested_attributes_for, jeden obiekt zmienia jednak niektóre inne dane obiektów. - speendo
Wybór najlepszego wzoru zakończy się zaoszczędzeniem więcej czasu na dłuższą metę. Zanieczyszczanie modeli wieloma różnymi problemami i obowiązkami tylko później wywoła ból głowy. Używanie obiektu formularza w razie potrzeby jest znacznie lepszą praktyką niż używanie zagnieżdżonych atrybutów. Klucz jest "tam, gdzie jest to właściwe" - istnieją sytuacje, w których używanie ataków zagnieżdżonych jest lepszym rozwiązaniem. - Logan Serman
Ponadto - dlaczego deprecjacja jest może nieco ekstremalna, nadal uważam, że lepiej pozwolić nowym deweloperom (speendo) eksperymentować z alternatywami do "Railsowej drogi". Ostatnio wydaje się, że wiele osób zaczęło używać rzeczy takich jak Obiekty Form po tym, jak Rails nie skaluje się (kod, a nie sama aplikacja) z ogromnymi aplikacjami. Zniechęcanie ludzi do robienia tego tylko boli, a Railsy będą miały trudniejsze czasy do przodu i dojrzewają jako ramy. - Logan Serman
@LoganSerman można określić, w którym jest właściwe stosowanie zagnieżdżonych atrybutów, z paradygmatycznego punktu widzenia? - speendo
W każdym przypadku będzie inaczej. Zagnieżdżone atrybuty są dobre dla bardzo prostych modeli zagnieżdżonych imo. Ale gdy masz dużą formę, z dużymi modelami zagnieżdżonymi (lub wieloma modelami zagnieżdżonymi), właściwszy jest obiekt formularza. Musisz po prostu eksperymentować z nimi, dopóki nie poczujesz, kiedy użyć tego narzędzia. - Logan Serman


Znacznie łatwiej używać virtus zamiast accepts_nested_attributes_for wówczas pomyślałem. Najważniejszym wymogiem było odważenie się na robienie rzeczy, których nie uwzględniono w żadnych tutorialach, które jeszcze czytałem.

Krok po kroku:

  1. dodałem gem 'virtus' do Gemfile i uciekł bundle install.
  2. Napisałem plik models / contact.rb i napisał następujący kod:

    class Contact
      include Virtus
    
      extend ActiveModel::Naming
      include ActiveModel::Conversion
      include ActiveModel::Validations
    
      attr_reader :name
      attr_reader :number
    
    
      attribute :name, String
      attribute :number, Integer
    
      def persisted?
        false
      end
    
      def save
        if valid?
          persist!
          true
        else
          false
        end
      end
    
    private
    
      def persist!
        @person = Person.create!(name: name)
        @phones = @person.phones.create!(number: number)
      end
    end
    
  3. Potem pobiegłem rails generate controller contacts i wypełnione * modele / contacts_controller.rb * z

    class ContactsController < ApplicationController
    
      def new
    
        @contact = Contact.new
    
      end
    
      def create
    
        @contact = Contact.new(contact_params)
        @contact.save
        redirect_to people_path
    
      end
    
      def contact_params
    
        params.require(:contact).permit(:name, :number)
    
      end
    
    end
    
  4. Następnym krokiem był widok. ja stworzyłem views / contacts / new.html.erb i napisał tę podstawową formę

    <%= form_for @contact do |f| %>
      <p>
        <%= f.label :name %><br />
        <%= f.text_field :name %>
      </p>
    
      <p>
        <%= f.label :number %><br />
        <%= f.text_field :number %>
      </p>
    
      <%= f.submit %>
    <% end %>
    
  5. Oczywiście musiałem również dodać trasę resources :contacts

to jest to! Może uda się to zrobić bardziej elegancko. Być może opłaciłoby się używać tylko klasy Kontakty, także dla innych działań CRUD. Nie próbowałem tego jeszcze ...

Tutaj znajdziesz wszystkie zmiany: https://github.com/speendo/PhoneBook/tree/virtus/app/models


5
2017-07-14 20:36



To nie Virtus robi tutaj ciężki uniesień. Używasz tylko obiektu formularza (zgodnie z opisem w blogu, który podałeś). Virtus to po prostu biblioteka pomagająca w tworzeniu obiektów formularzy (między innymi). - Logan Serman
@LoganSerman oczywiście, ale bez niego byłoby trudniej stworzyć widok, prawda? - speendo
Pozwala tworzyć modele z hashiem atrybutów, dzięki czemu Contact.new(contact_params) jest możliwe. Zasadniczo tworzy normalne obiekty (twoje Contact obiekt formularza) działają bardziej jak ActiveRecord. - Logan Serman
Uważam, że ten przykład został wykonany zgodnie z tym z tego filmu wideo: youtube.com/watch?v=5yX6ADjyqyE#t=1136 - NoDisplayName
Poleciłbym umieszczenie trwałości obiektów zależnych w transakcji - obecnej persist! wygląda jak bardzo zła rada. - user3467349


Tak więc zaakceptowana odpowiedź mówi tylko dlaczego accepts_nested_attributes_for jest często dobrym rozwiązaniem, ale nigdy nie oferuje rozwiązania, jak to zrobić. I przykład w połączonym artykule napotka problemy, jeśli chcesz, aby twój formularz akceptował dynamiczną liczbę zagnieżdżonych obiektów. To jedyne rozwiązanie, które znalazłem

https://coderwall.com/p/kvsbfa/nested-forms-with-activemodel-model-objects

Dla potomności to podstawa, ale jest jeszcze trochę na stronie:

class ContactListForm
include ActiveModel::Model

attr_accessor :contacts

def contacts_attributes=(attributes)
  @contacts ||= []
  attributes.each do |i, contact_params|
    @contacts.push(Contact.new(contact_params))
  end
end
end

class ContactsController < ApplicationController
   def new
      @contact_list = ContactListForm.new(contacts: [Contact.new])
    end
  end

i f.fields_for :contacts powinien zachowywać się jak has_many związek i łatwo obsługiwane przez obiekt formularza.

Gdyby Contact nie jest AR model, który musisz sfałszować persisted? także.


4
2018-05-08 03:49





Railscasts ma również odcinek w modelach zagnieżdżonych: http://railscasts.com/episodes/196-nested-model-form-revised?view=asciicast

Jak pokazano w komentarzach, klejnot kokonu znacznie upraszcza: https://github.com/nathanvda/cocoon


-2
2017-07-14 10:32