RailsAdmin has_many direct creation - ruby-on-rails-3

I have a simple has_many attachments situation:
class Project < ActiveRecord::Base
has_many :images, :class_name => 'ProjectImage', :dependent => :destroy
class ProjectImage < ActiveRecord::Base
has_attached_file :image
belongs_to :project
Is it possible (via Rails Admin) to directly add images when creating/editting project?
Now there are two ways (both suck!):
1) Create/Edit a ProjectImage instance and add it to the project (you have to search for it).
2) Add a new Project image which creates a modal and is afterwards same as 1)

The key is: nested attributes using accepts_nested_attributes_for.
eg:
has_many :images, :class_name => 'ProjectImage', :dependent => :destroy, :inverse_of => :project
accepts_nested_attributes_for :images, :allow_destroy => true

Related

ActiveRecord::HasManyThroughNestedAssociationsAreReadonly Error in Rails Admin

I just upgraded to Rails 3.2.10 and am getting an error message that I never used to get when updating a record via RailsAdmin.
ActiveRecord::HasManyThroughNestedAssociationsAreReadonly at /admin/vendor/12/edit
Message Cannot modify association 'Vendor#categories' because it goes through more than one other association.
This is my Vendor model:
class Vendor < ActiveRecord::Base
attr_accessible :name, :description, :banner_image, :logo_image, :intro_text, :thumb_image, :category_ids, :product_ids, :user_id, :remove_banner_image, :banner_image_cache, :remove_logo_image, :logo_image_cache
mount_uploader :banner_image, ImageUploader
mount_uploader :logo_image, ImageUploader
mount_uploader :thumb_image, ImageUploader
has_many :products, :dependent => :destroy
has_many :categories, :through => :products
belongs_to :owner, :class_name => "User",
:foreign_key => "user_id"
end
This is my Category model:
class Category < ActiveRecord::Base
attr_accessible :name, :product_ids, :category_ids
has_many :category_products do
def with_products
includes(:product)
end
end
has_many :products, :through => :category_products
end
This is my Product model:
class Product < ActiveRecord::Base
attr_accessible :name, :description, :price, :vendor_id, :image, :category_ids, :sku, :remove_image, :image_cache
mount_uploader :image, ImageUploader
belongs_to :vendor
has_many :category_products do
def with_categories
includes(:category)
end
end
has_many :categories, :through => :category_products
end
This is my CategoryProduct model:
class CategoryProduct < ActiveRecord::Base
attr_accessible :product_id, :category_id, :purchases_count
belongs_to :product
belongs_to :category
validates_uniqueness_of :product_id, :scope => :category_id
end
This happens because your association is nested, meaning (from rails source) :
A through association is nested if there would be more than one join table... which is your case here.
Apparently a workaround (I didn't test) is telling Vendor it doesn’t need to autosave the association.
has_many :categories, :through => :products, :autosave => false
You can mark the association as readonly and rails_admin will then not generate the category fields in the form for vendor:
has_many :categories, -> { readonly }, through: :products

Rails 3 - WARNING: Can't mass-assign protected attributes: user_ids

I have a has_many through relationship between Course and User.
class Course < ActiveRecord::Base
belongs_to :user
has_many :enrollments, :dependent => :delete_all
has_many :users, :through => :enrollments
attr_accessible :description, :duration, :name, :prerequisites, :short_name, :start_date, :user_id
accepts_nested_attributes_for :users, :allow_destroy => true
attr_accessible :users_attributes
and User:
class User < ActiveRecord::Base
has_many :subjects, :class_name => "Course" # to get this call user.subjects
has_many :enrollments, :dependent => :delete_all
has_many :courses, :through => :enrollments
and Enrollment:
class Enrollment < ActiveRecord::Base
belongs_to :course
belongs_to :user
attr_accessible :course_id, :user_id
end
Now I'm trying to set user_ids from inside Course, using a nested form. It keeps giving me that Mass Assignment warning, and nothing is saved. I read I was supposed to add attr_accessible user_id but it still doesn't work.
Even if I do something like this from the rails console:
#c.update_attributes({:user_ids => [7,8]})
with #c being the course
Any help would be greatly appreciated,
Thank you.
It's user_ids, not user_id.
You need to add user_ids to your attr_accessible.

Rails self-referential has_many through with custom naming of join table

I have some troubles wrapping my head around the following situation..
I am trying to create a tree structure, where I will be able to give custom names to connections between nodes..
So I want to have Node and Relation models. Each
Node
has_many :relations
Each
Relation
has_many :nodes
Node can be either a parent or a child.. So far everything was easy and there are tons of examples that show how to make a self-referential has_many table... The problem is that I want to be able to give names to relations, so that I can do something like:
relation1 = node1.relations.create(:name => "relation_name", :child => node2)
and in result get something like:
relation1.name == "relation_name"
relation1.parent == node1
relation1.child == node2
All the creations are happening within the model, this activity is not really exposed to user, if that matters.
Thanks!
EDIT2:
Here is how it works now:
class Node < ActiveRecord::Base
belongs_to :sentence
has_one :parent_relation, :foreign_key => "child_id", :class_name => "Relation"
has_many :child_relations, :foreign_key => "parent_id", :class_name => "Relation"
has_one :parent, :through => :parent_relation
has_many :children, :through => :child_relations, :source => :child
has_many :relations, :foreign_key => "child_id"
has_many :relations, :foreign_key => "parent_id"
class Relation < ActiveRecord::Base
has_many :videos, :as => :videoable, :dependent => :destroy
has_many :phrases, :through => :videos
belongs_to :parent, :class_name => "Node"#, :inverse_of => :parent_relation
belongs_to :child, :class_name => "Node"#, :inverse_of => :child_relation
So what you're talking about is more like a Joins Model than a Self-Reference.
Note: I changed your relation association 'labels' because I was having a hard time with your naming, so you don't have to change your 'labels' that was just for me.
So for your Node class you could do something like this
class Node < ActiveRecord::Base
has_one :parent_relation, :foreign_key => "child_id",
:class_name => "Relation"
has_many :child_relations, :foreign_key => "parent_id",
:class_name => "Relation"
has_one :parent, :through => :parent_relation
has_many :children, :through => :child_relations, :source => :child
end
Then for your Relation class you could something like
class Relation < ActiveRecord::Base
belongs_to :parent, :class_name => "Node", :inverse_of => :parent_relation
belongs_to :child, :class_name => "Node", :inverse_of => :child_relations
end
The :inverse_of option should let you build let you build a Node based off the parent and children associations from your Node instances, this is just a caveat from the magic with :through relationships. (Documentation for this is at the bottom of the Joins Model section.)
I don't fully understand your association structure, but I think this should model it correctly. Lemme know if there are any problems though.
Side Note: Since Relation is a constant set in the ActiveRecord module you might consider changing it to something like NodeRelationship. I don't think it will interfere with your program, but it definitively caused some trouble for my thought process.

Rails 3: How can I find an object with no children in it

I have a 3 models
class Task < ActiveRecord::Base
belongs_to :user, :dependent => :destroy
has_many :clock_ins
accepts_nested_attributes_for :clock_ins, :allow_destroy => true
end
class ClockIn < ActiveRecord::Base
belongs_to :task, :dependent => :destroy
has_one :clock_out
end
class ClockOut < ActiveRecord::Base
belongs_to :clock_in, :dependent => :destroy
end
Currently I can create a ClockIn for each Task.
When I start a new ClockIn, I want to create a ClockOut for whichever Task is currently open.
How do I search my tasks for one with a ClockIn that does not have a ClockOut?
Solution
Combine Models
Fix destroys
Iterate all tasks then update task.clocks.where(:clock_out => nil).first.update_attribute :clock_out, Time.now
As you have a one-to-one relation between clock_in and clock_out, switching has_one and belongs_to shouldn't make much difference. What is the data stored in clock_in and clock_out? if is is just a datetime you might want to consider merging the two models and using a single table. If you do not want to change any of the modeling LEFT OUTER JOIN is the way to go. So you have three options:
Merge the models:
class Task < ActiveRecord::Base
belongs_to :user
has_many :working_hours
accepts_nested_attributes_for :working_hours, :allow_destroy => true
end
class WorkingHour < ActiveRecord::Base
belongs_to :task
# has two columns clock_in_time and clock_out_time
end
task.working_hours.where(:clock_out_time => nil).first.update_attribute(:clock_out_time => Time.now)
Switch has_one and belongs_to
class ClockIn < ActiveRecord::Base
belongs_to :task
belongs_to :clock_out
# now clock_ins will have column check_out_id
end
class ClockOut < ActiveRecord::Base
has_one :clock_in
# doen't have any check_in_id
end
task.clock_ins.where(:clock_out_id => nil).first.create_clock_out(:time => Time.now)
Go with the outer join
class ClockIn < ActiveRecord::Base
belongs_to :task
has_one :clock_out
scope :has_no_check_out, {
:joins => "LEFT OUTER JOIN clock_outs ON clock_ins.id = clock_outs.clock_in_id"
:conditions => "clock_outs.clock_in_id IS NULL"
}
end
task.clock_ins.has_no_check_out.first.create_clock_out(:time => Time.now)
Please note that your :dependent => :destroy definitions don't look very good. Currently if you destroy a clock_out, corresponding clock_in will be destroyed, resulting in corresponding task being destroyed and leaving other clock_ins related with the task orphaned. Also when the task is destroyed, it will result in user being destroyed. This seems to be very odd chain of events. Destroying a clock_out, results in destroying a user, Ouch!
You should use :dependent => :destroy like following:
# user.rb
has_many :tasks, :dependent => :destroy
# task.rb
has_many :clock_ins, :dependent => :destroy
# clock_in.rb
has_one :clock_out, :dependent => :destroy

How do I delete a record from all tables it is referred to?

Good morning fellow Overflowers,
Small problem with model associations. I have these model associations:
class Categorization < ActiveRecord::Base
belongs_to :exhibit
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :categorizations
has_many :exhibits, :through => :categorizations
acts_as_indexed :fields => [:title]
validates :title, :presence => true, :uniqueness => true
end
class Exhibit < ActiveRecord::Base
has_many :categorizations
has_many :categories, :through => :categorizations, :source => :category
acts_as_indexed :fields => [:title, :bulb]
validates :title, :presence => true, :uniqueness => true
belongs_to :foto, :class_name => 'Image'
end
So, essentially Categorization ends up with these columns (date/time stamps omitted):
categorization_id, exhibit_id and category_id.
My problem is that when I delete an Exhibit, its reference on the Categorization table is not deleted thus getting a DB error on my view. I have to first unassign the Exhibit from any Category and then delete it safely. Or (given for example that the Exhibit I delete has :exhibit_id=>'1') when I give in the rails console: Categorization.find_by_exhibit_id(1).destroy
Thanks for any help!!
You can set the :dependent options on associations that you want Rails to follow when you delete their parents:
class Exhibit < ActiveRecord::Base
has_many :categorizations, :dependent => :destroy
...
end