https://speakerdeck.com/u/bostonrb/p/what-to-expect-in-rails-4-dot-0
大概看下来,大的亮点是 Russian Doll Caching 和 ActiveSupport::Queue。也许是因为早就知道这两个会有,所以没觉得怎么激动,作为一个主要版本变化不算太大。有些惊讶 pjax 没有被包括
现在 Django 分得很坑爹,搞了个半调子的 Form,弄得你用也不是不用也不是。
比如就像 GitHub 那样,每个用户的自己的仓库是不能重名的。
Django 里面你在 model 里定义unique_together = [('user', 'repository_name')]
接着,因为 user 是从 session 那里拿到的,你用 Django 自带的 ModelForm 生成 Form 的时候,会去 exclude user 这个 column。(这个是文档里提到的做法,https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#using-a-subset-of-fields-on-the-form)
问题就来了,Django 的validate_unique
的时候,因为 user 被 exclude 了,unique_together 里面,('user', 'repository_name')
这条就直接就无视了。
据我所知 Django 到现在也没一个标准做法来处理这个问题。这个问题也不是一天两天了,是 4 年多了,这个问题一直没解决。
https://code.djangoproject.com/ticket/8795#comment:11 https://code.djangoproject.com/ticket/12028 https://code.djangoproject.com/ticket/12521 https://code.djangoproject.com/ticket/12901 https://code.djangoproject.com/ticket/13091 https://code.djangoproject.com/ticket/13249 https://code.djangoproject.com/ticket/15326
当然了,我承认在 Ruby 社区吐槽 Django 是很不好的行为。只是看到这个话题,联想到 Django 有个坑爹的问题,就顺便吐槽一下了。
Active Record 是一种 ORM 的模式,简单地说就是每一个业务模型的 object 对应一个数据库表的一个纪录。这在小的应用里面是很方便的。但一旦业务规则复杂了,很多业务模型并不是都对应数据库表的。比如在你的应用里需要有“VIP 用户特权“这样一个概念,包含比如”免运费“, ”促销商品再打九折“, ”自动短信提醒“等等。这东西并不对应数据库,因为是一些”规则“而不是具体的数据。那很多就会把这些散落在各种不同的 Model 比如"Shipping", "Promotion", "Reminder" 里面,或者做成 Call Back, 或者每个地方加上“if current_user.type == 'vip' "这样的判断。这样当你的业务规则增多的时候,你的代码会是一团乱麻,最明显的就是遍地像这样的条件判断(顺手写的)。。
if current_user.type == 'vip'
cart.shipping_cost = 0
if current_user.cart.amount > 50
if current_user.gender == 'female'
Gift.create(user: current_user, type: 'flower')
else
Gift.create(user: current_user, type: 'chocolate')
end
else
....
end
else
if current_user.benefits.include?("free shipping")
cart.shipping_cost = 0
else
...
end
end
这样的逻辑散落在你系统各处。。能不能可持续的开发是一目了然的。。如果一旦上面穿西装的人说,业务规则全变了!想想后果把
#10 楼 @knwang 看那样子就变成 data mapper 了。但我没发现你举的这个例子,在两个 pattern 里有啥区别
http://martinfowler.com/eaaCatalog/activeRecord.html http://martinfowler.com/eaaCatalog/dataMapper.html
#1 楼 @hhuaitest 这俩东西有啥关系吗?
#14 楼 @yggg 参照 java,加个 service 层,也未尝不可。业务逻辑全放到 model 里很容易一团糟。
@knwang 感觉 strong parameters
是个好东西呀,这些验证就应该放到最开始处。否则前面处理了一堆东西,后来保存时发现某个字段非法,前面做的处理就白费了。
#9 楼 @knwang #5 楼 @fresh_fish #6 楼 @fredwu #8 楼 @bhuztez
@knwang的代码例子可以改一下,我也随手写的
class User
def free_shipping?
vip? or benefits.include?("free shipping")
end
end
class Cart
def initialize(user)
@user = user
end
def with_gift?
amount > 50
end
def shipping_cost
actual_cost = 5
@user.free_shipping? ? 0 : actual_cost
end
def add
...
end
def checkout
...
send_gift if with_gift?
end
def send_gift
add Gift.create_by_user(@user)
end
end
class Gift
class << self
def create_by_user(user)
gift = Gift.new(user: user)
gift.type = user.female? ? 'flower' : 'chocolate'
gift.save
end
end
end
这个改写后的例子其实证明了把业务逻辑放入 ActiveRecord 的好处,ActiveRecord 里边可以有相关业务逻辑,但如果太复杂,一定是架构设计的不良,当然,完全可以建立跟持久化无关的 Model 来解决问题,比如 Cart 在任何教程里都是经典的非持久化 model,但是,一定不要在框架层面上鼓励将业务逻辑从 ActiveRecord 剥离出去
我赞成现有的方案,更简单清晰,对于 rails 这种约定大于配置的框架来说,很重要的一个原则,其实就是每次的约定都要谨慎