accepts_nested_attributes_for的替代方法-也许是virtus [英] Alternative for accepts_nested_attributes_for - maybe virtus

查看:77
本文介绍了accepts_nested_attributes_for的替代方法-也许是virtus的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Rails的新手,终于找到了使用accepts_nested_attributes_for的正确方法.

但是,网络上有一些严肃的资源说使用accepts_nested_attributes_for通常是不好的做法(例如虚拟机适合于此.是吗?

这是一个仍在使用accepts_nested_attributes_for的非常基本的示例(在此处找到完整的示例):

模型

class Person < ActiveRecord::Base

    has_many :phones
    accepts_nested_attributes_for :phones

end

class Phone < ActiveRecord::Base

    belongs_to :person

end

控制器

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

查看(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 %>


使用服务对象是个好主意吗?

解决方案

您的问题暗示您认为accepts_nested_attributes功能是一件坏事,事实并非如此,而且效果很好.

我首先要说,您不需要accepts_nested_attributes_for的替代方法,但是我将在本文结尾处进行介绍.

关于您提供的链接,它没有提及发布者为何认为应完全弃用accepts_nested_attributes_for而仅声明

的原因.

在我看来,应该弃用

在考虑如何以单一形式捕获与父项相关的多个记录时,嵌套属性是一个非常重要的概念,这不仅是Ruby on Rails的事情,而且在大多数复杂的Web应用程序中用于从服务器将数据发送回服务器的信息.浏览器,而与开发网站所使用的语言无关.

我完全不批评您所指向的文章.对我来说,它只是指出了一些显而易见的替代方案,可以用很多不一定与业务逻辑相关的代码来填充数据库支持的模型.使用的特定示例仅仅是编码样式首选项的替代方案.

在时间充裕且压力很大的情况下,一行代码即可完成工作,而示例中显示的22行代码在大多数情况下(并非所有情况下),我倾向于在一行代码中使用一行代码模型(accepts_nested_attributes_for)接受从表单发回的嵌套属性.

要正确回答您的问题是不可能的,因为您尚未真正说明为什么您认为accepts_nested_attributes_for不是一个好习惯,但是最简单的选择是仅提取控制器操作中的params哈希属性并在事务中单独处理每个记录. /p>

更新-跟进评论

我认为链接文章的作者认为 oop-paradigms,每个对象应该只读取和写入自己的数据. 但是使用accepts_nested_attributes_for可以更改一个对象 其他对象数据.

好让我们澄清一下. 首先,OO范例表明没有这种事情.班级应谨慎,但允许它们与其他班级进行交互.实际上,如果是这种情况,那么在Ruby中就没有指向OO的方法,因为在ruby中的一切都是一类,因此没有其他任何人可以进行交谈.试想一下,如果恰好是控制器实例的对象无法与模型或其他控制器进行交互,会发生什么?

使用accepts_nested_attributes_for,可更改一个对象 其他对象数据.

该声明中的观点很复杂,我将尝试使其尽可能简短.

1)模型实例保护数据.在涉及任何/大多数其他语言(包括C,Delphi,VB等)的数百个表的非常复杂的场景中,三层解决方案中的中间层就可以做到这一点.用Rails术语,模型是业务逻辑的地方,并在3层解决方案中完成中间层的工作,该解决方案通常由RDBMS中的存储过程和视图来备份.模型很正确,应该能够互相交谈.

2)accepts_nested_attributes_for完全没有违反任何OO原则.它只是简化了该方法不存在时(您所发现的)所需编写的代码量.如果您接受嵌套在子模型的params哈希内的属性,则您所要做的就是允许子模型以与控制器操作相同的方式处理该数据.不会绕过任何业务逻辑,您会获得更多好处.

最后

我有能力关心代码的优美性(不仅仅是时间)

我可以向您保证,编写20多行代码比您需要的多,并且从gem中添加几百行代码(其中一行代码将为您完成工作)并没有什么优雅之处.正如其他人(包括我)所说的那样,accepts_nested_attributes_for并不总是适合使用的ActiveRecord方法,这是一件好事,您可以通过查看不同的方法来做,因为最终您将能够就何时使用build做出更明智的判断.在方法中以及何时编写自己的方法.但是,我建议您完全了解正在发生的事情(如您所陈述的那样),最好编写自己的代码来处理表单对象并接受嵌套属性的替代方法.这样,您会发现自己了解得更多.

希望您的学习有意义并带来好运.

更新2

最终弄清楚您的观点并参考您自己的答案,并考虑其他人对您自己的答案所做的出色评论,这些表格由virtus gem支持,是一个非常合理的解决方案,尤其是在处理数据方式时必须收集.这种组合有助于将用户界面逻辑与业务逻辑分开,并且只要您最终将数据传递给模型,从而不绕过业务逻辑(如您所展示的,您正是在这样做),那么您就有了不错的解决方案

请不要排除出accepts_nested_attributes失控的情况.

通过观看Ryan Bates在表单对象上的 railscasts ,您还可能会获得一些好处.

I'm relatively new to rails and finally found the right way to use accepts_nested_attributes_for.

However, there are some serious resources on the web who say that using accepts_nested_attributes_for is generally a bad practice (like this one).

What changes are necessary to avoid accepts_nested_attributes_for and in which folder would you put the additional class-file (I guess one needs an additional class).

I read that virtus is appropriate for that. Is that right?

Here is a very basic example still using accepts_nested_attributes_for (find the full example here):

Models

class Person < ActiveRecord::Base

    has_many :phones
    accepts_nested_attributes_for :phones

end

class Phone < ActiveRecord::Base

    belongs_to :person

end

Controller

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

View (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 %>

[edit]
Would it be a good idea to use a service object?

解决方案

Your question implies that you believe accepts_nested_attributes functionality to be a bad thing which is totally not the case and works perfectly well.

I'll start by saying that you don't need an alternative to accepts_nested_attributes_for but I'll cover that at the end of this post.

With reference to the link you provide, it cites nothing about why the poster believes accepts_nested_attributes_for should be deprecated at all and just merely states

in my humble opinion, should be deprecated

Nested attributes are an extremely important concept when considering how to capture multiple records related to a parent in a single form which is not just a Ruby on Rails thing but used in most complex web applications for sending data back to the server from the browser regardless of of the languages used to develop the site.

I'm not criticising the article you point to at all. To me it's just pointing out obvious alternatives to filling up a database backed model with lots of code that is not necessarily related to business logic. The specific example used is merely a coding style preference alternative.

When time is money and the pressure is on and one line of code will do the job versus the 22 lines of code shown in the example my preference in most cases (not all cases) is to use one line of code in a model (accepts_nested_attributes_for) to accept nested attributes posted back from a form.

To answer your question properly is impossible as you have not actually stated why YOU think accepts_nested_attributes_for is not good practice however the simplest alternative is to just extract the params hash attributes in your controller action and handle each record individually inside a transaction.

Update - follow up on comment

I think the author of the linked article argues that, following oop-paradigms, every object should only read and write its own data. With accepts_nested_attributes_for, one object however changes some other objects data.

O.K. Lets clear that up. Firstly OO paradigms suggest no such thing. Classes should be discreet but they are allowed to interact with other classes. In fact there would be no point to an OO approach in Ruby if this was the case as EVERYTHING in ruby is a class therefore nothing would be able to talk to anything else. Just imagine what would happen if an object that just happens to be an instance of your controller were not able to interact with models or other controllers?

With accepts_nested_attributes_for, one object however changes some other objects data.

Couple of points on that statement as it's a complex one I'll try to be as brief as possible.

1) Model instances guard the data. In very complex scenarios involving hundreds of tables in any/most other languages (C, Delphi, VB to name a few) a middle tier in a 3 tier solution does just that. In Rails terms a model is a place for business logic and does the job of the middle tier in a 3 tier solution that is normally backed up by stored procedures and views in the RDBMS. Models quite rightly should be able to talk to each other.

2) accepts_nested_attributes_for does not break any OO principles at all. it merely simplifies the amount of code that you would need to write if the method did not exist (as you are finding out). If you accept attributes that are nested inside a params hash for child models all you are doing is allowing the child models to handle that data in the same way that your controller's action would have to do. No business logic is bypassed and you get added benefits.

Lastly

I can afford to care about elegance of code (more than about time)

I can assure you that there is nothing elegant about writing 20 + more lines of code than you need to and adding hundreds of lines more code from a gem where one line of code will do the work for you. As others have stated (including me) accepts_nested_attributes_for is not always an appropriate ActiveRecord method to use and it is a good thing that you are doing by looking at different approaches as ultimately you will be able to make better informed judgements as to when to use built in methods and when to write your own. However I would suggest that to fully understand what is going on (as you state you have the time) you would be better writing your own code to handle form objects and accepts nested attributes alternatives. That way you would find yourself understanding so much more.

Hope that makes sense and good luck with your learning.

UPDATE 2

To finally get to your point and in reference to your own answer plus taking into account of the excellent comments others have made on your own answer form objects backed by the virtus gem is a perfectly reasonable solution especially when dealing with the way data has to be collected. The combination helps to separate the user interface logic from the business logic and so long as you are ultimately passing off the data to the models so that business logic is not bypassed (as you show you are doing exactly this) then you have a great solution.

Just don't rule out accepts_nested_attributes out of hand.

You might also gain some benefit from watching the railscasts by Ryan Bates on form objects.

这篇关于accepts_nested_attributes_for的替代方法-也许是virtus的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆