Model creation depends on other model creation - ruby-on-rails

I have an Item model:
class Item < ActiveRecord::Base
attr_accessible :author, :title
end
And a Book model:
class Book < ActiveRecord::Base
attr_accessible :item_id, :plot
belongs_to_ :item
end
I want to be able to create a book by using
Book.new(:title=>"Title", :author=>"Author", :plot=>"BlaBla")
Book.save
And it will create an Item with the title and author, and also create a Book with the created Item ID.
How is it possible?

You need to use :after_create callback and virtual_attributes as follows.
In you book model write this
attr_accessor :title, :author
attribute_accessible :title, :author, :plot
after_create :create_item
def create_item
item = self.build_item(:title => self.title, :author => self.author)
item.save
end

Using before_save or before_create
class Book
attr_accessor :title, :author
before_save :create_item
#before_create :create_item
def create_item
if self.title.present? && self.autor.present?
item = Item.new(:title => self.title, :author => self.author)
item.save(:validate => false)
self.item = item # or self.item_id = item.id
end
end
end

Related

Rails Model doesn't save data with polymorphic

I have a model job and a model user, the both can choose contracts types (that's why i use polymorphic).
I created a model contract for each contracts and i create an attached_contract model too.
Job model
class Job < ApplicationRecord
has_many :contracts, through: :attached_contracts
has_many :attached_contracts, as: :contractable, dependent: :destroy
accepts_nested_attributes_for :attached_contracts, reject_if: :all_blank, allow_destroy: true
end
AttachedContract model
class AttachedContract < ApplicationRecord
belongs_to :contract
belongs_to :contractable, polymorphic: true
validates :contract, uniqueness: { scope: [:contractable_type,:contractable_id] }
end
Contract model
class Contract < ApplicationRecord
validates :name, presence: true, allow_blank: false
has_many :attached_contracts
end
Jobs_controller
def new
#job = Job.new
#job.attached_contracts.build
end
def create
#job = current_company.jobs.build(set_params)
if #job.save
redirect_to job_path(#job)
end
else
render :new
end
end
def set_params
params.require(:job).permit(:title, :description, :address, attached_contracts_attributes: [:id, :contract_id, :_destroy]
end
In my view :
<%= simple_form_for([:company, #job]) do |f| %>
<div class="nested-fields">
<%= f.association :contracts, as: :check_boxes %>
</div>
<% end %>
When I submit my form my model AttachedContract still empty, and the data are lost.
I try tu put a "raise" in my controller after #job = current_company.jobs.build(set_params)
and I have a empty array if I call #job.attached_contracts
I don't understand beause in the "Request parameters" (rails debug console) I have the values : "contract_ids"=>["", "1", "3"]
Any idea ? May be the problem is in the polymorphic implantation ?
Finally, I changed the requested parameters by "contract_ids: [ ]" and that's work perfectly !

rails_admin - has_many association - default value

My Page model look like this:
class Page < ActiveRecord::Base
has_many :blocks
accepts_nested_attributes_for :blocks, allow_destroy: true
rails_admin do
edit do
fields :title, :slug, :blocks
end
end
end
My Block model look like this:
class Block < ActiveRecord::Base
belongs_to :page
rails_admin do
edit do
field :title
field :body, :ck_editor
end
end
end
I needed workflow like this:
As an admin I click create page and I should see opened new block section with prefield title.
How can I create this scenario?
My own answer is realy dearty, but it works for me:
class Page < ActiveRecord::Base
has_many :blocks
accepts_nested_attributes_for :blocks, allow_destroy: true
rails_admin do
edit do
fields :title, :slug
field :blocks do
# It is needed to show nested form
active true
end
end
end
# It is needed to create default block with title "main"
after_initialize do
if self.blocks.empty? && self.new_record?
self.blocks << Block.new(title: 'main')
end
end
# It is needed to prevent create default block when form has errors
after_validation do
return if(self.persisted? || self.blocks.empty?)
destroy_array = []
self.blocks.each do |block|
destroy_array << block if block.title == 'main' && block.body.nil?
end
self.blocks.destroy(destroy_array)
end
end

Rails 4 / Ruby 2: Saving nested attributes does not work as expected

I am trying to save the attributes of an assignment-model through a form for a person-model:
class Person < ActiveRecord::Base
has_one :assignment, dependent: :destroy
accepts_nested_attributes_for :assignment, allow_destroy: true
end
class Assignment < ActiveRecord::Base
belongs_to :person
belongs_to :project
end
class Project < ActiveRecord::Base
has_many :reverse_assignments, class_name: 'Assignment'
end
class PersonsController < ApplicationController
def new
#person = Person.new
end
def create
#person = Person.build(person_params)
#person.build_assignment(assignment_params) # Shouldn't this be obsolete?
redirect_to root_url
end
private
def person_params
params.require(:person).permit(:name, assignment_attributes: [:id, :project_id])
end
def assignment_params
params.require(:assignment).permit(:person_id, :project_id) # Only needed because of the "obsolete" line
end
end
class AssignmentsController < ApplicationController
end
This is the form (slim-html):
= form_for(#person) do |f|
= f.text_field :name
= fields_for :assignment do |r|
= r.collection_select :project_id, Project.order(:name), :id, :name
= f.submit 'Save'
Creating the assignment through the project-form works, but only by including a second line in the PersonsController's create action. However, shouldn't the first line suffice, because I already included the assignment_params in the person_params? I am asking, because I have issues updating the assignment through an edit-person-form which uses very similar code.
= form_for(#person) do |f|
= f.text_field :name
= f.fields_for :assignment do |r|
= r.collection_select :project_id, Project.order(:name), :id, :name
= f.submit 'Save'
Try adding the f.fields_for
You may also want to add this to your "new" action:
def new
#person = Person.new
#person.build_assignment
end
This builds the ActiveRecord object for assignment, which is then passed through the nested attributes to the other model :)

Writing a method to not show private resource unless it's current users?

I have my Product model that has the attribute private. I want to make a method that allows the current user to be able to select their private products and others that are private = false only.
class Product < ActiveRecord::Base
attr_accessible :name, :private
belongs_to :user
def permission
unless self.current_user
unless self.private
end
end
end
end
Does it start out like this? How would I write this method? The goal is to put this inside of a select menu.
I would create a class method for this which you would pass something like current_user to
class Product < ActiveRecord::Base
attr_accessible :name, :private
belongs_to :user
def self.for_user_or_public(user)
where("user_id = ? or private = ?", user.id, false)
end
end
Usage:
products = Product.for_user_or_public(current_user)
As for the select, something like the following should work
<%= f.collection_select :owner_id, Product.for_user_or_public(current_user), :user_id, :name, { :include_blank => true } %>

Has "def validate" been taken out in Rails 3.1?

Has "def validate" been taken out in Rails 3.1? I'm on Rails 3.1 pre and it doesn't seem to be working
class Category < ActiveRecord::Base
validates_presence_of :title
private
def validate
errors.add(:description, "is too short") if (description.size < 200)
end
end
The "title" validation works but the "description" validation doesn't.
Does something like this work for you?
class Category < ActiveRecord::Base
validates_presence_of :title
validate :description_length
def description_length
errors.add(:description, "is too short") if (description.size < 200)
end
end
class Category < ActiveRecord::Base
validates_presence_of :title
private
validate do
errors.add(:description, "is too short") if (description.size < 200)
end
end
For other types of validations, you can also add 'Validators' like the one listed here:
http://edgeguides.rubyonrails.org/3_0_release_notes.html#validations
class TitleValidator < ActiveModel::EachValidator
Titles = ['Mr.', 'Mrs.', 'Dr.']
def validate_each(record, attribute, value)
unless Titles.include?(value)
record.errors[attribute] << 'must be a valid title'
end
end
end
class Person
include ActiveModel::Validations
attr_accessor :title
validates :title, :presence => true, :title => true
end
# Or for Active Record
class Person < ActiveRecord::Base
validates :title, :presence => true, :title => true
end

Resources