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
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 !
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
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 :)
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? 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