Rails 4 - 在不创建具有 'has_one' 关联的新嵌套模型的情况下无法保存模型
Rails 4 - Can't save model without creating new nested model with 'has_one' association
我有一个可见性模型,它有一些布尔属性和其他定义如何看到其他模型的东西。可见性是多态的,因为我希望它同时属于 User 和 Activity 模型。可见度:
class Visibility < ActiveRecord::Base
belongs_to :viewable, :polymorphic => true
end
用户(已编辑以包含全部内容):
class User < ActiveRecord::Base
# Relations
#has_many :posts
has_one :fb_connection
has_many :activities
has_many :participations
has_many :activities, :through => :participations
has_one :visibility, as: :viewable, dependent: :destroy
accepts_nested_attributes_for :visibility
has_many :friendships
has_many :friends, :through => :friendships
has_many :sent_friend_invites, :through => :friendships
has_many :received_friend_invites, :through => :friendships
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
# Callback to set a user's default visibility for their activities
before_save :default_values
# paperclip helper method
has_attached_file :profile_pic, styles: {
thumb: '100x100>',
square: '200x200#',
medium: '300x300>'
}, default_url: "https://dl.dropboxusercontent.com/u/743320/q_silhouette.gif"
# Pagination
paginates_per 100
# Validations
# :email
validates :first_name, presence: true
validates :last_name, presence: true
validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
# Paperclip validation
validates_attachment :profile_pic, :size => { :in => 0..10.megabytes }, :content_type => { :content_type => /^image\/(jpeg|png|gif|tiff)$/}
def self.paged(page_number)
order(admin: :desc, email: :asc).page page_number
end
def self.search_and_order(search, page_number)
if search
where("email LIKE ?", "%#{search.downcase}%").order(
admin: :desc, email: :asc
).page page_number
else
order(admin: :desc, email: :asc).page page_number
end
end
def self.last_signups(count)
order(created_at: :desc).limit(count).select("id","email","created_at")
end
def self.last_signins(count)
order(last_sign_in_at:
:desc).limit(count).select("id","email","last_sign_in_at")
end
def self.users_count
where("admin = ? AND locked = ?",false,false).count
end
def get_display_name
if display_name =~ /./ # Just check that the display name is not ""
#puts "display name: " + display_name
return display_name
else
#puts "name: " + [first_name, last_name].join(" ")
return [first_name, last_name].join(" ")
end
end
def get_profile_pic_thumb
return profile_pic(:thumb)
end
def measurement_labels
return I18n.t('user_label_use_metric_true') if use_metric
end
private
def default_values
viewable ||= Visibility.create(default_viewable_params)
end
def default_viewable_params
{:public => true, :app_friends => true, :facebook_friends => true, :strava_friends => true, :viewable => self }
end
end
Activity(相关部分):
class Activity < ActiveRecord::Base
...
has_one :visibility, as: :viewable, dependent: :destroy
...
end
我这辈子都做不到。任何时候我只要保存一个用户模型——即使可见性模型没有被修改——它都会为用户创建一个新的可见性模型。例如,在控制台中:
user = User.find(5)
user.foo = false
user.save
我得到这个输出:
(0.4ms) BEGIN SQL (1.0ms) INSERT INTO "visibilities" ("public",
"app_friends", "facebook_friends", "viewable_id", "viewable_type",
"created_at", "updated_at") VALUES (, , , , , , )
RETURNING "id" [["public", "t"], ["app_friends", "t"],
["facebook_friends", "t"], ["viewable_id", 5], ["viewable_type",
"User"], ["created_at", "2015-10-23 23:06:57.431922"], ["updated_at",
"2015-10-23 23:06:57.431922"]] (21.3ms) COMMIT => true
当然,尝试通过用户更新可见性模型失败了。在更新它的同时,还会在其位置创建一个新的。这是适用的 user_controller 方法和使用 simple_form_for:
的视图
#user_controller
def preferences
@user = current_user
if !@user.visibility
@user.visibility = Visibility.new
end
end
def update_preferences
user = current_user
user.update!(user_pref_params)
flash[:notice] = I18n.t('user_flash_update_success')
redirect_to profile_path(user)
end
private
def user_pref_params
params.require(:user).permit(:use_metric, visibility_attributes: [:id, :public, :app_friends, :facebook_friends])
end
<!-- preferences form -->
<%= simple_form_for @user, :url => update_preferences_path do |f| %>
<%= f.simple_fields_for :visibility do |v| %>
<%= v.input :public, label: t('user_preferences_label_public'), as: :select, required: false %>
<%= v.input :app_friends, label: t('user_preferences_label_app_friends'), as: :select, required: false %>
<%= v.input :facebook_friends, label: t('user_preferences_label_facebook_friends'), as: :select, required: false %>
<% end %>
<!-- Preference for system of measurement -->
<%= f.input :use_metric, as: :select, collection: [[t('user_preferences_use_metric_true'), true], [t('user_preferences_use_metric_false'), false]], label: t('user_preferences_label_use_metric'), include_blank: false %>
<!-- Submit button -->
<%= f.button :submit, :label => "Save", :class => "btn btn-primary" %>
<% end %>
这次批量分配的尝试给了我这样的反馈:
Started PATCH "/users/updateprefs" for 127.0.0.1 at 2015-10-23
17:02:52 -0700 Processing by UsersController#update_preferences as
HTML Parameters: {"utf8"=>"✓",
"authenticity_token"=>"FXj3kWxNDE7tl7InvBvL68UzlvmWR/d3aJ4/gTg7cCCEGEafJJZO10ud31kU2120SdKF3aNj5gy0FiizURHTeQ==",
"user"=>{"visibility_attributes"=>{"public"=>"false",
"app_friends"=>"false", "facebook_friends"=>"false", "id"=>"64"},
"use_metric"=>"true"}, "commit"=>"Update User"} User Load (0.4ms)
SELECT "users".* FROM "users" WHERE "users"."id" = ORDER BY
"users"."id" ASC LIMIT 1 [["id", 55]] (0.3ms) BEGIN Visibility
Load (0.8ms) SELECT "visibilities".* FROM "visibilities" WHERE
"visibilities"."viewable_id" = AND "visibilities"."viewable_type" =
LIMIT 1 [["viewable_id", 55], ["viewable_type", "User"]] SQL
(0.6ms) INSERT INTO "visibilities" ("public", "app_friends",
"facebook_friends", "viewable_id", "viewable_type", "created_at",
"updated_at") VALUES (, , , , , , ) RETURNING "id"
[["public", "t"], ["app_friends", "t"], ["facebook_friends", "t"],
["viewable_id", 55], ["viewable_type", "User"], ["created_at",
"2015-10-24 00:02:52.244397"], ["updated_at", "2015-10-24
00:02:52.244397"]] SQL (0.5ms) UPDATE "users" SET "use_metric" =
, "updated_at" = WHERE "users"."id" = [["use_metric", "t"],
["updated_at", "2015-10-24 00:02:52.247304"], ["id", 55]] SQL
(0.5ms) UPDATE "visibilities" SET "app_friends" = , "updated_at" =
WHERE "visibilities"."id" = [["app_friends", "f"],
["updated_at", "2015-10-24 00:02:52.250120"], ["id", 64]] (13.3ms)
COMMIT
如您所见,Rails 正在创建新的可见性并更新现有可见性!我也许可以使用一些非常丑陋的代码来临时解决这个问题,但我将不胜感激任何帮助以保持我的控制器操作干净的方法。
在您的 User
模型中,在 default_values
方法中,您正在创建新的可见性并将其分配给 viewable
。但是,这是一个范围,它只对方法可用,而不是整个 class (因此与方法变量进行比较)。
此外,viewable
是关联仅在 Visibility
模型中的正确用法。在 User
模型中,您必须将其引用为 visibility
.
需要做的是使用self
将比较和赋值应用于实例变量。
def default_values
self.visibility ||= Visibility.create(default_viewable_params)
end
我有一个可见性模型,它有一些布尔属性和其他定义如何看到其他模型的东西。可见性是多态的,因为我希望它同时属于 User 和 Activity 模型。可见度:
class Visibility < ActiveRecord::Base
belongs_to :viewable, :polymorphic => true
end
用户(已编辑以包含全部内容):
class User < ActiveRecord::Base
# Relations
#has_many :posts
has_one :fb_connection
has_many :activities
has_many :participations
has_many :activities, :through => :participations
has_one :visibility, as: :viewable, dependent: :destroy
accepts_nested_attributes_for :visibility
has_many :friendships
has_many :friends, :through => :friendships
has_many :sent_friend_invites, :through => :friendships
has_many :received_friend_invites, :through => :friendships
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
# Callback to set a user's default visibility for their activities
before_save :default_values
# paperclip helper method
has_attached_file :profile_pic, styles: {
thumb: '100x100>',
square: '200x200#',
medium: '300x300>'
}, default_url: "https://dl.dropboxusercontent.com/u/743320/q_silhouette.gif"
# Pagination
paginates_per 100
# Validations
# :email
validates :first_name, presence: true
validates :last_name, presence: true
validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
# Paperclip validation
validates_attachment :profile_pic, :size => { :in => 0..10.megabytes }, :content_type => { :content_type => /^image\/(jpeg|png|gif|tiff)$/}
def self.paged(page_number)
order(admin: :desc, email: :asc).page page_number
end
def self.search_and_order(search, page_number)
if search
where("email LIKE ?", "%#{search.downcase}%").order(
admin: :desc, email: :asc
).page page_number
else
order(admin: :desc, email: :asc).page page_number
end
end
def self.last_signups(count)
order(created_at: :desc).limit(count).select("id","email","created_at")
end
def self.last_signins(count)
order(last_sign_in_at:
:desc).limit(count).select("id","email","last_sign_in_at")
end
def self.users_count
where("admin = ? AND locked = ?",false,false).count
end
def get_display_name
if display_name =~ /./ # Just check that the display name is not ""
#puts "display name: " + display_name
return display_name
else
#puts "name: " + [first_name, last_name].join(" ")
return [first_name, last_name].join(" ")
end
end
def get_profile_pic_thumb
return profile_pic(:thumb)
end
def measurement_labels
return I18n.t('user_label_use_metric_true') if use_metric
end
private
def default_values
viewable ||= Visibility.create(default_viewable_params)
end
def default_viewable_params
{:public => true, :app_friends => true, :facebook_friends => true, :strava_friends => true, :viewable => self }
end
end
Activity(相关部分):
class Activity < ActiveRecord::Base
...
has_one :visibility, as: :viewable, dependent: :destroy
...
end
我这辈子都做不到。任何时候我只要保存一个用户模型——即使可见性模型没有被修改——它都会为用户创建一个新的可见性模型。例如,在控制台中:
user = User.find(5)
user.foo = false
user.save
我得到这个输出:
(0.4ms) BEGIN SQL (1.0ms) INSERT INTO "visibilities" ("public", "app_friends", "facebook_friends", "viewable_id", "viewable_type", "created_at", "updated_at") VALUES (, , , , , , ) RETURNING "id" [["public", "t"], ["app_friends", "t"], ["facebook_friends", "t"], ["viewable_id", 5], ["viewable_type", "User"], ["created_at", "2015-10-23 23:06:57.431922"], ["updated_at", "2015-10-23 23:06:57.431922"]] (21.3ms) COMMIT => true
当然,尝试通过用户更新可见性模型失败了。在更新它的同时,还会在其位置创建一个新的。这是适用的 user_controller 方法和使用 simple_form_for:
的视图#user_controller
def preferences
@user = current_user
if !@user.visibility
@user.visibility = Visibility.new
end
end
def update_preferences
user = current_user
user.update!(user_pref_params)
flash[:notice] = I18n.t('user_flash_update_success')
redirect_to profile_path(user)
end
private
def user_pref_params
params.require(:user).permit(:use_metric, visibility_attributes: [:id, :public, :app_friends, :facebook_friends])
end
<!-- preferences form -->
<%= simple_form_for @user, :url => update_preferences_path do |f| %>
<%= f.simple_fields_for :visibility do |v| %>
<%= v.input :public, label: t('user_preferences_label_public'), as: :select, required: false %>
<%= v.input :app_friends, label: t('user_preferences_label_app_friends'), as: :select, required: false %>
<%= v.input :facebook_friends, label: t('user_preferences_label_facebook_friends'), as: :select, required: false %>
<% end %>
<!-- Preference for system of measurement -->
<%= f.input :use_metric, as: :select, collection: [[t('user_preferences_use_metric_true'), true], [t('user_preferences_use_metric_false'), false]], label: t('user_preferences_label_use_metric'), include_blank: false %>
<!-- Submit button -->
<%= f.button :submit, :label => "Save", :class => "btn btn-primary" %>
<% end %>
这次批量分配的尝试给了我这样的反馈:
Started PATCH "/users/updateprefs" for 127.0.0.1 at 2015-10-23 17:02:52 -0700 Processing by UsersController#update_preferences as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"FXj3kWxNDE7tl7InvBvL68UzlvmWR/d3aJ4/gTg7cCCEGEafJJZO10ud31kU2120SdKF3aNj5gy0FiizURHTeQ==", "user"=>{"visibility_attributes"=>{"public"=>"false", "app_friends"=>"false", "facebook_friends"=>"false", "id"=>"64"}, "use_metric"=>"true"}, "commit"=>"Update User"} User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ORDER BY "users"."id" ASC LIMIT 1 [["id", 55]] (0.3ms) BEGIN Visibility Load (0.8ms) SELECT "visibilities".* FROM "visibilities" WHERE "visibilities"."viewable_id" = AND "visibilities"."viewable_type" = LIMIT 1 [["viewable_id", 55], ["viewable_type", "User"]] SQL (0.6ms) INSERT INTO "visibilities" ("public", "app_friends", "facebook_friends", "viewable_id", "viewable_type", "created_at", "updated_at") VALUES (, , , , , , ) RETURNING "id" [["public", "t"], ["app_friends", "t"], ["facebook_friends", "t"], ["viewable_id", 55], ["viewable_type", "User"], ["created_at", "2015-10-24 00:02:52.244397"], ["updated_at", "2015-10-24 00:02:52.244397"]] SQL (0.5ms) UPDATE "users" SET "use_metric" = , "updated_at" = WHERE "users"."id" = [["use_metric", "t"], ["updated_at", "2015-10-24 00:02:52.247304"], ["id", 55]] SQL (0.5ms) UPDATE "visibilities" SET "app_friends" = , "updated_at" = WHERE "visibilities"."id" = [["app_friends", "f"], ["updated_at", "2015-10-24 00:02:52.250120"], ["id", 64]] (13.3ms) COMMIT
如您所见,Rails 正在创建新的可见性并更新现有可见性!我也许可以使用一些非常丑陋的代码来临时解决这个问题,但我将不胜感激任何帮助以保持我的控制器操作干净的方法。
在您的 User
模型中,在 default_values
方法中,您正在创建新的可见性并将其分配给 viewable
。但是,这是一个范围,它只对方法可用,而不是整个 class (因此与方法变量进行比较)。
此外,viewable
是关联仅在 Visibility
模型中的正确用法。在 User
模型中,您必须将其引用为 visibility
.
需要做的是使用self
将比较和赋值应用于实例变量。
def default_values
self.visibility ||= Visibility.create(default_viewable_params)
end