Update multiple records using one form - ruby

I have a following situation: user has multiple assets and each asset has one asset_detail record.
Models:
class User < ActiveRecord::Base
has_many :assets
end
class Asset < ActiveRecord::Base
has_one :asset_detail
belongs_to :user
accepts_nested_attributes_for :asset_detail,
:allow_destroy => false
attr_accessible # ...
end
class AssetDetail < ActiveRecord::Base
belongs_to :asset
attr_accessible # ...
end
Controller actions:
def edit
#user = current_user
#assets = Asset.all
end
def update
#user = current_user
#user.update_attributes(params["user"])
end
View:
= form_for #user, url: 'update action url' do |f|
= f.fields_for :assets do |ff|
= ff.text_field :title
= ff.fields_for :asset_detail do |fff|
= fff.text_field :value
The problem is that all the form fields are populated properly but i'm not able to save them. The form is sent without any error but the data is not updated.

I think your models should look like this:
class User < ActiveRecord::Base
attr_accessible :assets_attributes #...
has_many :assets
accepts_nested_attributes_for :assets_attributes
end
class Asset < ActiveRecord::Base
attr_accessible :asset_detail_attrbutes # ...
has_one :asset_detail
belongs_to :user
accepts_nested_attributes_for :asset_detail_attributes,
:allow_destroy => false
end
the reason being, you need to be able to set the attributes via the attributes hash passed to each model. HTH!

Related

Rails 4 dynamic form nested attributes without hidden input

Previously I asked a question about building an attendance list getting the students and building a check list to mark attendance and came up with this.
def new
#attendance_list = #classroom.attendance_lists.new
#attendance_list.attendances = #classroom.student_ids.map do |student_id|
#attendance_list.attendances.build(student_id: student_id)
end
end
def create
#attendance_list = #classroom.attendance_lists.new(attendance_list_params)
#attendance_list.classroom_id = params[:classroom_id]
respond_to do |format|
end
params
params.require(:attendance_list)
.permit(:post_date, :remark,
attendances_attributes: [:student_id, :attended, :remarks ])
with simple fields
= simple_form_for [#school, #classroom, #attendance_list] do |f|
= f.input :post_date
= f.input :remark
= f.simple_fields_for :attendances do |g|
** you see i needed a hidden student_id **
= g.input :student_id, as: :hidden
......
model
class AttendanceList < ActiveRecord::Base
belongs_to :classroom
has_many :attendances
has_many :students, :through => :attendances
accepts_nested_attributes_for :attendances
end
class Attendance < ActiveRecord::Base
belongs_to :student
belongs_to :attendance_list
end
class Classroom < ActiveRecord::Base
belongs_to :school
has_and_belongs_to_many :students
has_many :attendance_lists
validates :class_name, presence: true
end
how do I do without the hidden input because this line doesn't seem to work.
build(student_id: student_id)
class AttendanceList < ActiveRecord::Base
belongs_to :classroom
has_many :attendances
has_many :students, :through => :attendances
accepts_nested_attributes_for :attendances
end
class Attendance < ActiveRecord::Base
belongs_to :student
belongs_to :attendance_list
end
class Classroom < ActiveRecord::Base
belongs_to :school
has_and_belongs_to_many :students
has_many :attendance_lists
validates :class_name, presence: true
end
class Student < ActiveRecord::Base
has_many :attendances
has_many :attendance_lists, :through => :attendances
end
view file
= simple_form_for #student do |f|
= f.simple_fields_for :attendances do |g|
** your code **

Papertrail undefined method `klass' for nil:NilClass

I have this revert method in my controller
def revert
version = PaperTrail::Version.find(params[:id])
if version.reify
version.reify(:has_many=>true).save!
else
version.item.destroy
end
share_type = ShareType.find(params[:share_type_id])
redirect_to :back
end
I am getting error of undefined method `klass' for nil:NilClass on
version.reify(:has_many=>true).save!
but when I try
version.reify.save!
everything become fine what am I doing wrong I am using revert method for another resource with
def revert
version = PaperTrail::Version.find(params[:id])
if version.reify
version.reify(has_many: true).save!
else
version.item.destroy
end
company = Company.find_by_slug(params[:company_id])
redirect_to :back
end
This works fine with out any error.
here are my models for both resources
class ShareType < ActiveRecord::Base
has_paper_trail
belongs_to :company
has_many :issue_shares, dependent: :destroy
has_many :users, through: :shares
validates :company, :code, :name, :reason, :effective_date, presence: true
before_save :convert_new_line_to_html
def convert_new_line_to_html
self.description.to_s.gsub!(/\r\n?/, "<br>")
end
end
and for company
class Company < ActiveRecord::Base
extend FriendlyId
friendly_id :slug_candidates, use: :slugged
has_many :user_companies, dependent: :destroy
has_many :users, through: :user_companies
has_many :advisor_invitations
has_many :company_advisors
has_many :advisors, through: :company_advisors
belongs_to :created_by, class_name: "User", foreign_key: "user_id"
has_many :share_types, dependent: :destroy
validates :user_id, :name,:company_no,:company_type,:registered_office, :principle_place, presence: true
validates_uniqueness_of :name, :company_no
validates_presence_of :register_address_occupier, :if => lambda { |o| !o.occupied_by_company }
has_paper_trail
def slug_candidates
[
:name,
[:name, :company_type],
[:name, :company_type, :company_no],
]
end
def should_generate_new_friendly_id?
slug.blank? || name_changed?
end
def is_admin?(user)
!user_companies.where("user_id=? AND role=?",user.id, 0).empty?
end
def share_holder?(user)
shares = Share.includes(issue_share:[:share_type]).where("share_types.company_id=? AND shares.user_id=?", id, user.id).references(:share_types)
!shares.empty?
end
def is_director?(user)
!user_companies.where("user_id=? AND role=?",user.id, 1).empty?
end
def is_advisor?(user)
!advisors.where("user_id=?",user.id).empty?
end
end
I solved the problem there is an association
has_many :users, through: :shares
in share_types while I do not have
has_many :shares
so I need to remove this line
has_many :users, through: :shares
which I do not need this solved my problem.

How can two user instances have the same account

In my rails app I want to create a shared account (later with different access levels). I want two instances of the User to have the same account.
My User Model has_many :accounts and my Account belongs_to :user
In the rails console when I do, e.g:
account = Account.first
account.user_id = [1, 2]
account.save
it returns true but when I check it again account.user_id = nil
How should I go on about it, then?
EDIT:
The Account Model:
class Account < ActiveRecord::Base
belongs_to :user
has_many :products
validates_presence_of :title
end
and the User model:
class User < ActiveRecord::Base
has_many :accounts
has_many :products, through: :accounts
accepts_nested_attributes_for :accounts
validates_presence_of :name
before_save do
self.email = email.downcase unless :guest?
end
validates :name, presence: true, length: { maximum: 50 }, unless: :guest?
end
EDIT 2:
I've updated the models relationship so the new Account model is:
class Account < ActiveRecord::Base
has_and_belongs_to_many :users
validates_presence_of :title
end
and the new User model is:
class User < ActiveRecord::Base
has_and_belongs_to_many :accounts
belongs_to :account
has_many :products, through: :accounts
accepts_nested_attributes_for :accounts
validates_presence_of :name
before_save do
self.email = email.downcase unless :guest?
end
validates :name, presence: true, length: { maximum: 50 }, unless: :guest?
end
I also created a new migration, a accounts_users table
class CreateAccountsUsersJoinTable < ActiveRecord::Migration
def change
create_join_table :users, :accounts do |t|
## Not sure if this is necessary
##t.index [:user_id, :account_id]
##t.index [:account_id, :user_id]
t.belongs_to :user
t.belongs_to :account
end
end
end
Still I can't have a account with multiple users

How to pass user_id to nested_resource model?

I have a small project setup with devise and cancan. There are User, Project, Responsible and Task Models. Project has nested Tasks. Each Project is assigned to one or multiple users. The task model has a name, user_id and project_id. Authentication and Authorization is working like expected.
When adding a new Task (only an input for name) the project_id gets automatically passed to model/table (i think this is because of routing) but not the user_id.
Do i have to pass the user_id in a hidden_field or is it somehow possible to set this in a before filter?
Can somebody give a hint on howto set user_id in taskcontroller?
Thanks
# Routes
resources :projects do
resources :tasks
end
#Models
class User < ActiveRecord::Base
has_many :responsibilities, :dependent => :destroy
has_many :projects, :through => :responsibilities
has_many :tasks, :dependent => :destroy
...
class Project < ActiveRecord::Base
has_many :tasks, :dependent => :destroy
...
class Task < ActiveRecord::Base
belongs_to :project
belongs_to :user
...
# Tasks Controller with all Task.find/new/update/...
# methods removed like explained in cancan manual
class TasksController < ApplicationController
load_and_authorize_resource :project
load_and_authorize_resource :task, :through => :project
...
def create
respond_to do |format|
if #task.save
format.html { redirect_to [#project, #task], notice: 'created.' }
else
format.html { render action: "new" }
end
end
# Task Form View
<%= semantic_form_for [#project, #task] do |f| %>
<%= f.inputs do %>
<%= f.input :name %>
<% end %>
<%= f.actions %>
<% end %>
Update
It seems to work with a before filter. Is this the right way?
class TasksController < ApplicationController
before_filter :set_user, :only => [:create]
load_and_authorize_resource :client
load_and_authorize_resource :task, :through => :client, :shallow => true
...
def set_user()
params[:task][:user_id] = current_user.id.to_s
end
...
if you are using devise you can just pass in the create action you user_id
def create
#task.user = current_user
respond_to do |format|
if #task.save
format.html { redirect_to [#project, #task], notice: 'created.' }
else
format.html { render action: "new" }
end
end
not quiet sure if this is what you mean

has_many :through child and parent validations

I have a rails 3.0
has_many :X, :through => :something set up and i have a custom validator that does some custom validation on some complicated logic. I want to when you add anything to this many to many relationship both models are valid?
Project Class:
class Project < ActiveRecord::Base
has_many :assignments
has_many :users, :through => :assignments
validates :name, :presence => true
end
Assignment:
class Assignment < ActiveRecord::Base
belongs_to :project
belongs_to :user
end
User class with custom validator:
class MyCustomValidator < ActiveModel::Validator
def validate( record )
if record.projects.length > 3
record.errors[:over_worked] = "you have to many projects!"
end
end
end
class User < ActiveRecord::Base
has_many :assignments
has_many :projects, :through => :assignments
validates :name, :presence => true
validates_with MyCustomValidator
end
What i really want to do is prevent each model from invalidating the other so to say
prevent
my_user.projects << fourth_project
and
my_project.users << user_with_four_projects_already
from happening. Right now it allows the assignment and just the user becomes invalid.
class Project < ActiveRecord::Base
has_many :assignments
has_many :users, :through => :assignments
validates :name, :presence => true
validates_associated :users
end
According to the docs, users must already be assigned to Projects in order to be validated. So:
my_user.projects << fourth_project
would occur, then projects would validate the user and see that it is indeed invalid, making the project invalid as well as in the inverse example.

Resources