If I have a user and article model with an association has_many :articles and belongs_to :user, I would write user.articles.new to create a new article object with the correct user_id .
So my question is about a model with many belongs_to relations:
class Ownership < ActiveRecord::Base
attr_accessible :right_read, :right_create, :right_update, :right_delete
belongs_to :element
belongs_to :user
belongs_to :ownership_type
end
Is there a solution to create an object Ownership with the 3 IDs completed (element_id, user_id, ownership_type_id) ?
And is it dangerous to write this IDs in the "attr_accessible" ?
Thank you.
The new method accepts a hash where the keys match the attributes in the model. This should work just fine:
Ownership.new(:element_id => element_id, :user_id => user_id, :ownership_type_id => ownership_type_id)
Reference: http://apidock.com/rails/ActiveRecord/Base/new/class
Also, no, it's not dangerous to include those attributes under attr_accessible -- actually, that's the only way you'll be able to directly write to them using new or update_attributes.
Related
I have a question about Rails Nested Attributes.
I'm using Rails 4 and have this model:
model Location
has_one parking_photo
has_many cod_photos
accepts_nested_attributes_for :parking_photo
accepts_nested_attributes_for :cod_photos
end
When I use for example:
Location.find(100).update(cod_photo_ids: [1,2,3]) it works.
But Location.find(100).update(parking_photo_id: 1) doesn't works.
I don't know what difference between nested attributes has_one and has_many.
Or do we have any solution for my case, when I already have child object and want to link the parent to the child and don't want to use child update.
Thank you.
The problem has nothing to do with nested attributes. In fact you're not even using nested attributes at all in these examples.
In this example:
Location.find(100).update(cod_photo_ids: [1,2,3])
This will work even if you comment out accepts_nested_attributes_for :cod_photos as the cod_photo_ids= setter is created by has_many :cod_photos.
In the other example you're using has_one where you should be using belongs_to or are just generally confused about how you should be modeling the association. has_one places the foreign key on the parking_photos table.
If you want to place the parking_photo_id on the locations table you would use belongs_to:
class Location < ActiveRecord::Base
belongs_to :parking_photo
# ...
end
class ParkingPhoto < ActiveRecord::Base
has_one :location # references locations.parking_photo_id
end
Of course you also need a migration to actually add the locations.parking_photo_id column. I would really suggest you forget about nested attributes for the moment and just figure out the basics of how assocations work in Rails.
If you really want to have the inverse relationship and put location_id on parking_photos you would set it up like so:
class Location < ActiveRecord::Base
has_one :parking_photo
# ...
end
class ParkingPhoto < ActiveRecord::Base
belongs_to :location
validates_uniqueness_of :location_id
end
And you could reassign a photo by:
Location.find(100).parking_photo.update(location_id: 1)
I have a complex nested attributes system that I'll slim down to two models:
I have 2 models:
class Product < ActiveRecord::Base
has_many :products_colors
accepts_nested_attributes_for :products_colors, :reject_if => :all_blank, :allow_destroy => true
class ProductsColor < ActiveRecord::Base
belongs_to :product
belongs_to :color
What method would I have to overwrite so that when I'm on the products edit form with a nested products_colors form and I create a product + products_colors it first looks to see if a products_color with a certain color_id and product_id exists and returns that instead of a new ProductsColor instance?
The reason is that if there is a ProductsColor of Product A and Color A that already exists, I want to just update that one instead of creating a new ProductsColor.
I'm thinking of something like this:
class ProductsColor < ActiveRecord::Base
...
def self.new(args)
pc = ProductsColor.where(color_id: args[:color_id], product_id: args[:product_id]).first_or_initialize
pc.assign_attributes(args)
end
end
Here's an example params submitted through the edit products form:
{"product"=>{"name"=>"zzzz", "products_colors_attributes"=>{"0"=>{"color_id"=>"1"}}, "commit"=>"Update Product", "id"=>"24"}
I know it's a bit confusing... let me know if you need any more info.
You must create a new method in Product something like update_or_create_colors that updates or creates new ProductColor, and in your controller just call that method, I don't thing there is a method in rails for your especific case.
This is a situation where you could submit a "color_ids" attribute (this is a dynamic attribute created by has_many
class Product < ActiveRecord::Base
has_many :products_colors
has_many :colors, :through => :products_colors
This will build the join relationships for product and colors.
I have the following associations and then action in my Observer:
class Product < ActiveRecord::Base
attr_accessible :price, :name, :watch_price
belongs_to :user
belongs_to :store
has_many :product_subscriptions, :dependent => :destroy
has_many :product_subscribers, :through => :product_subscriptions, :class_name => 'User'
end
class ProductSubscription < ActiveRecord::Base
belongs_to :product
belongs_to :product_subscriber, :class_name => 'User'
attr_accessible :watched_price, :watched_name
end
class ProductObserver < ActiveRecord::Observer
def after_create(product)
ProductSubscription.new(product.attributes.merge({
:watched_name => name,
:watched_price => price,
:store_id => :store_id,
}))
end
end
The code above, successfully creates the ProductSubscription with the user_id and product_id but :watched_name and :watched_price aren't filled with the original Product :price and :name.
I noticed the issue lies in this. Which doesn't make any sense because when I look in the database, it is assigned as I mentioned above:
WARNING: Can't mass-assign protected attributes: product_id
Now I do have other fields that are apart of the Product model that aren't apart of the ProductSubscription model so maybe its screwing up because of that?
I don't want the product_id to be mass assignable. How could I correct this?
Your hash values must reference the attribute methods, not some symbols. That way, the method returning the respective attribute value gets called and the value gets inserted into the hash. The symbols you used have no meaning whatsoever.
ProductSubscription.new(product.attributes.merge({
:watched_name => name,
:watched_price => price,
:store_id => store_id,
}))
end
Also, you don't seem to save your new ProductSubscription. Just calling new won't persist the object to the database. Use something like create instead.
And finally, as Andrew Marshall said, your database design is not really optimal. Copying whole table rows around is not going to offer great performance. Instead you will soon suffer from inconsistencies and the hassles of keeping all the copied data up-to-date. You really should learn about joins and the concepts of Database normalization
I am creating an app for uploading and sharing files between users.
I have User and Files models and have created a third File_Sharing_Relationships model which contains a sharer_id, file_id and shared_with_id columns. I want to be able to create the following methods:
#upload.file_sharing_relationships - lists users that the file is shared with
#user.files_shared_with - lists files that are shared with the user.
#user.files_shared - lists files that the user is sharing with others
#user.share_file_with - creates a sharing relationship
Are there any rails associations, such as 'polymorphic' that I could be using to make these relationships?
Any suggestions appreciated. Thanks.
All you need to do is to read Rails Guides and apply all what you learn.
Basically you need to store info about:
user who created a "sharing"
user or group or whatever is a target of a sharing action
resource that is being shared
So:
class SharedItem < ActiveRecord::Base
belongs_to :sharable, :polymorphic => true #this is user, please think of better name than "sharable"...
belongs_to :resource, :polymorphic => true #can be your file
belongs_to :user
end
You need SharedItem to have:
user_id: integer, sharable_id: integer, sharable_type: string, resource_id: integer, resource_type: string
Then you can get "methods" you specified by writing named scopes like:
named_scope :for_user, lambda {|user| {:conditions => {:user_id => user.id} }}
or by specifying proper associations:
class File < ActiveRecord::Base
has_many :shared_items, :as => :resource, :dependent => :destroy
end
I think you should create relationships something like this:
class User
has_many :files
has_many :user_sharings
has_many :sharings, :through => :user_sharings
end
class File
belongs_to :user
end
class Sharing
has_many :user_sharings
has_many :users, :through => :user_sharings
end
class UserSharing
belongs_to :user
belongs_to :sharing
end
.. this is very basic model of relations(It is just my point of view :)). User can have many sharings and also belongs to sharings. You can set file id to UserSharing table when you create user and it's share. And then you can create methods, you listed above, as scopes in proper models. I hope I helped you a little.
Both Attendment & Vouching:
belongs_to :event
belongs_to :account
Therefore: 1 to 1 relationship between attendments and vouchings.
Is there a way to do this without my thinking too much?
# attendment
has_one :vouching :through => [:event, :account]
Note: I don't mind thinking too much, actually.
Yeah i don't think you can use a has_one for this. Assuming I'm reading this correctly, you have two models:
Attendment
Vouching
They both store an event_id and account_id. You want to know from the attendment model, what vouching shares the same event and account as the attendment. I think the easiest solution for this is to write a method inside your attendment.rb file.
class Attendment < ActiveRecord::Base
# belong to statements go here
def voucher
Voucher.where(:event_id => self.event_id, :account_id => self.account_id).first
end
end