[英]Rails 4 - Can't save model without creating new nested model with 'has_one' association
I have a Visibility model, which has some boolean attributes and other things defining how other models can be seen. 我有一个可见性模型,该模型具有一些布尔属性和定义其他模型如何显示的其他内容。 Visibility is polymorphic, because I want it to belong to both the User and Activity models. 可见性是多态的,因为我希望它既属于User模型又属于Activity模型。 Visibility: 能见度:
class Visibility < ActiveRecord::Base
belongs_to :viewable, :polymorphic => true
end
User (edited to include totality): 用户(已编辑为包括总数):
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 (relevant parts): 活动(相关部分):
class Activity < ActiveRecord::Base
...
has_one :visibility, as: :viewable, dependent: :destroy
...
end
I can't for the life of me get this to work. 我一辈子都做不到。 Any time I just save a user model -- even if the Visibility model is not modified -- it creates a NEW Visibility model for the user. 任何时候只要保存用户模型(即使未修改可见性模型),它都会为用户创建一个新的可见性模型。 Eg, in the console: 例如,在控制台中:
user = User.find(5)
user.foo = false
user.save
I get this output: 我得到以下输出:
(0.4ms) BEGIN SQL (1.0ms) INSERT INTO "visibilities" ("public", "app_friends", "facebook_friends", "viewable_id", "viewable_type", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7) 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 (0.4ms)BEGIN SQL(1.0ms)插入“可见性”(“ public”,“ app_friends”,“ facebook_friends”,“ viewable_id”,“ viewable_type”,“ created_at”,“ updated_at”)值($ 1,$ 2, $ 3,$ 4,$ 5,$ 6,$ 7)返回“ id” [[“ public”,“ t”],[“ app_friends”,“ t”],[“ facebook_friends”,“ t”],[“ viewable_id”, 5],[“ viewable_type”,“用户”],[“ created_at”,“ 2015-10-23 23:06:57.431922”],[“ updated_at”,“ 2015-10-23 23:06:57.431922”] ](21.3ms)COMMIT => true
And naturally, trying to update the Visibility model through User fails. 当然,尝试通过用户更新可见性模型失败。 While it is updated, a new one is also created in its place. 在更新时,也会在其位置创建一个新的。 Here is the applicable user_controller methods and view using simple_form_for: 这是适用的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 %>
This attempt at mass assignment gives me this feedback: 这种大规模分配的尝试给了我以下反馈:
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" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 55]] (0.3ms) BEGIN Visibility Load (0.8ms) SELECT "visibilities".* FROM "visibilities" WHERE "visibilities"."viewable_id" = $1 AND "visibilities"."viewable_type" = $2 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 ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["public", "t"], ["a 从2015年10月23日17:02:52 -0700开始为127.0.0.1的PATCH“ / users / updateprefs”,由UsersController#update_preferences作为HTML参数进行处理:{“ utf8” =>“✓”,“ authenticity_token” =>“ FXj3kWxNDE7tl7InvBvL68UzlvmWR / d3aJ4 / gTg7cCCEGEafJJZO10ud31kU2120SdKF3aNj5gy0FiizURHTeQ =“ =” “” =>“ 64”},“ use_metric” =>“ true”},“提交” =>“更新用户”}用户负载(0.4毫秒)选择“用户”。*来自“用户”,而“用户”。 id“ = $ 1 ORDER BY” users“。” id“ ASC LIMIT 1 [[” id“,55]](0.3ms)开始可见性负载(0.8ms)选择“ visibilities”。*从“ visibilities”中选择“ visibilities” 。“ viewable_id” = $ 1和“ visibilities”。“ viewable_type” = $ 2 LIMIT 1 [[“” viewable_id“,55],[” viewable_type“,” User“]] SQL(0.6ms)插入“ visibilities”(“ public “,” app_friends“,” facebook_friends“,” viewable_id“,” viewable_type“,” created_at“,” updated_at“)值($ 1,$ 2,$ 3,$ 4,$ 5,$ 6,$ 7)返回“ id” [[[” public “,” t“],[” a pp_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" = $1, "updated_at" = $2 WHERE "users"."id" = $3 [["use_metric", "t"], ["updated_at", "2015-10-24 00:02:52.247304"], ["id", 55]] SQL (0.5ms) UPDATE "visibilities" SET "app_friends" = $1, "updated_at" = $2 WHERE "visibilities"."id" = $3 [["app_friends", "f"], ["updated_at", "2015-10-24 00:02:52.250120"], ["id", 64]] (13.3ms) COMMIT pp_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“ = $ 1,” updated_at“ = $ 2 WHERE” users“。 “ id” = $ 3 [[“” use_metric“,” t“],[” updated_at“,” 2015-10-24 00:02:52.247304“],[” id“,55]] SQL(0.5ms)UPDATE”可见性“ SET” app_friends“ = $ 1,” updated_at“ = $ 2,” visibilities“。” id“ = $ 3 [[”“ app_friends”,“ f”],[“ updated_at”,“ 2015-10-24 00:02: 52.250120“],[” id“,64]](13.3ms)提交
As you can see, Rails is both creating a new visibility and updating the existing one! 如您所见,Rails既创建了新的可见性,又更新了现有的可见性! I can maybe jerry-rig this to work around it with some really ugly code, but I would appreciate any help to keep my controller actions clean. 也许我可以用杰瑞(Jerry-rig)操纵它,用一些非常丑陋的代码来解决它,但是我希望能为保持控制器动作整洁提供任何帮助。
In your User
model, in the default_values
method, you're creating a new visibility and assigning it to viewable
. 在User
模型中,使用default_values
方法创建新的可见性并将其分配给viewable
。 However, this is scoped such that it is only available to the method, not the class as a whole (and thus does comparison against the method variable). 但是,此方法的作用域仅适用于方法,而不适用于整个类(因此可以与方法变量进行比较)。
Also, viewable
is the correct usage of the association only in the Visibility
model. 另外, viewable
是仅在Visibility
模型中的关联的正确用法。 In the User
model, you will have to reference it as visibility
. 在User
模型中,您必须将其作为visibility
引用。
What needs to be done is to use self
to apply the comparison and assignment to the instance variable instead. 需要做的是使用self
来将比较和赋值应用于实例变量。
def default_values
self.visibility ||= Visibility.create(default_viewable_params)
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.