many to many association - ruby-on-rails

So I have a model below that represents the situation:
Students participate in Contests.
Contests measure certain skills.
For each contest, each student gets a score for each skill measured.
Here are my models:
class Student < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
attr_accessible :name
has_and_belongs_to_many :skills
has_many :contest_participations
has_many :contests, :through => :contest_participations
has_many :contest_scores, :through => :contests
end
class Skill < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :students
end
class Contest < ActiveRecord::Base
attr_accessible :name, :contest_participations_attributes, :contest_scores_attributes
has_many :contest_participations
has_many :students, :through => :contest_participations
has_many :contest_skills
has_many :skills, :through => :contest_skills
has_many :contest_scores
accepts_nested_attributes_for :contest_participations, :contest_scores
end
class ContestParticipation < ActiveRecord::Base
attr_accessible :contest_id, :student_id
belongs_to :student
belongs_to :contest
end
class ContestScore < ActiveRecord::Base
attr_accessible :contest_id, :score, :skill_id, :student_id
has_and_belongs_to_many :contests
has_and_belongs_to_many :skills
has_and_belongs_to_many :student
end
In my contests edit view, I'm trying to create a form, with formtastic, that will show all contest participants, and allow the user to add a score for each skill in the contest as follows.
But I get an error (student_id invalid symbol). How can I update the student scores?
<%= semantic_form_for #contest do |f| %>
<%= f.input :name %>
<%= #contest.students.each do |student| %>
<%= #contest.skills.each do |skill| %>
<%= f.inputs :name => "Scores", :for => :contest_scores do |scores_form| %>
<%= scores_form.input :student_id => "student.id" %>
<%= scores_form.input :skill_id => "skill.id" %>
<%= scores_form.input :score, :label => "Score" %>
<% end %>
<br />
<% end %>
<% end %>
<%= f.actions do %>
<%= f.action :submit, :as => :button %>
<% end %>
<% end %>

Try this. Added hidden because e think you don't want ids to show up
<%= scores_form.input :student_id, :as => :hidden, :value => student.id %>
<%= scores_form.input :skill_id, :as => :hidden, :value => skill.id %>

Related

has_one belongs_to association attribute validation

I use devise for user authentication. Have two models User and Profile which are associated to each other like this
#app/model/user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile, dependent: :destroy
enum role: [:admin, :seller, :buyer]
end
#app/model/profile.rb
class Profile < ApplicationRecord
belongs_to :user
end
As you can see I have also role for users.
Here is forms for registrations of both models.
#user registration form
<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
...
<%= f.select :role, collection: User.roles.keys.last(2) %>
<%= f.input :mobile_number, required: true, autofocus: true %>
...
<% end %>
#profile registration form
<%= simple_form_for(#profile) do |f| %>
...
<% if current_user.seller? %>
<%= f.input :location, required: true %>
<% else %>
<%= f.input :company_code, required: true %>
<% end %>
...
<% end %>
Problem
When current user is seller I need to validate presence of :location , when user is buyer then validate presence of :company_code.
How can I achieve that? I appreciate any helps , Thanks!
validates method accepts if option:
# app/model/profile.rb
class Profile < ApplicationRecord
belongs_to :user
validates :location, presence: true, if: "user.seller?"
validates :company_code, presence: true, if: "user.buyer?"
end
Also, current_user.role == "seller" is obsolete. Rails automatically sets up additional methods in model for enum option values, therefore you may just write: current_user.seller?, etc.

How to show name of user who wrote comment (Rails)?

In my rails app I have users(authors),posts(articles),comments. If registered user write comment to article, I want to show his name beside his comment, if he isn't registered user I want to show "Anonymous" beside his comment. How can I do this?
comment model:
class Comment < ActiveRecord::Base
attr_accessible :post_id, :text
belongs_to :post
belongs_to :user
end
user model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
has_many :posts, :dependent => :destroy
has_many :comments, :dependent => :destroy
validates :fullname, :presence => true, :uniqueness => true
validates :password, :presence => true
validates :email, :presence => true, :uniqueness => true
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :fullname
end
post model:
class Post < ActiveRecord::Base
attr_accessible :text, :title, :tag_list
acts_as_taggable
validates :user_id, :presence => true
validates :title, :presence => true
validates :text, :presence => true
belongs_to :user
has_many :comments
end
view file (show.html.erb)
<h1><%= #post.title %></h1>
<p>
Created: <%= #post.created_at.strftime("%Y/%m/%d")%> by
<%= link_to #post.user.fullname, user_posts_path(#post.user) %>
</p>
<p><%=simple_format #post.text %></p>
<p>
Tags: <%= raw #post.tag_list.map { |t| link_to t, tag_path(t) }.join(', ') %>
</p>
<h2>Comments</h2>
<% #post.comments.each do |comment| %>
<p><%= comment.created_at.strftime("%Y/%m/%d") %>
by <%= HERE I NEED ADD SOMETHING%></p>
<p><%= comment.text %></p>
<p><%= link_to "Delete comment", [#post, comment], :method => :delete,
:confirm =>"Are you sure?"%></p>
<% end %>
<%= form_for [#post, #post.comments.build] do |f| %>
<p><%= f.text_area :text %></p>
<p><%= f.submit "Post comment" %></p>
<% end %>
<% if user_signed_in?%>
<p>
<%= link_to "Back", posts_path %>
<%= link_to "Edit", edit_post_path(#post) %>
<%= link_to "Delete", #post, :method => :delete, :confirm => "Are you sure?"%>
</p>
<% end%>
You can do this by calling the user method on the comment, and then name on that:
<%= comment.user.name %>
You can also define a to_s method in the User model:
def to_s
name
end
Which would mean you could get away with doing just this in the view:
<%= comment.user %>
If you're loading a whole bunch of comments, then I would recommend loading them this way:
#comments = Comment.includes(:user)
If you don't have that includes(:user) there, then Rails will issue a new query for every single comment to find that comment's user. Doing it this way makes Rails load all the users for all the comments upfront in just one query.
I think you want to create two assocations pointing to the same table:
class CreatePostss < ActiveRecord::Migration
def change
create_table :posts do |t|
t.text :text
t.references :user, index: true, foreign_key: true
t.references :author, index: true, foreign_key: { to_table: :users }
t.timestamps null: false
end
end
end
Here user_id references the target user of the post and author_id references the user writing the post. Both reference users.id.
Then create two belongs_to associations in your post model:
class Post < ApplicationRecord
belongs_to :user
belongs_to :author,
class_name: 'User',
inverse_of: :authored_posts
end
And two has_many associations in your User model:
class User < ApplicationRecord
# posts about this user
has_many :posts
# postss written by this user
has_many :authored_posts,
class_name: 'Post',
foreign_key: :author_id,
inverse_of: :author
end
Here is the controller
class PostsController < ApplicationController
before_action :set_user, only: [:new, :create]
# ...
def new
#post = #user.posts.new
end
def create
#post = #user.posts.new(post_params) do |c|
c.author = current_user
end
if #post.save
redirect_to doctor_path(id: #user.id)
else
render :new
end
end
private
def set_user
#user = User.find(params[:id])
end
# ...
end
To display posts you would do:
<% #user.posts.each do |post| %>
<div class="post">
<div class="body">
<%= post.body %>
</div>
<p class="author"><%= post.author.email %></p>
</div>
<% end %>

'merge' error with check_box field in rails

In my Rails app, I need to show user email id's with checkboxes in a form to allocate users to a particular project. I have an array object #programmers and each row in the object contains email id's which I need to show inside the form with checkboxes.
My partial view containing form is:
_allocate_programmer.html.erb
<h1> Allocate programmers </h1>(Please check the programmers that you want to add)<br />
<%= form_for(#project) do |f| %>
<% if #project.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#project.errors.count, "error") %> prohibited this project from being saved:</h2>
<ul>
<% #project.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% unless #programmers.nil? %>
<% #programmers.each do |programmer| %>
<%= f.check_box :programmer, programmer.email %>
<% end %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
My routes.rb has:
match 'projects/:id/allocate_programmers' => 'projects#allocate'
My projects_controller.rb has the following code:
def allocate
#project = Project.find(params[:id])
#programmers = User.where(:role => 'programmer')
render "_allocate_programmer"
end
I am getting following error in the view
NoMethodError in Projects#allocate
Showing /home/local/Rajesh/ticket_system/app/views/projects/_allocate_programmer.html.erb where line #18 raised:
undefined method 'merge' for "test#gmail.com":String
I think its an issue with checkbox hash. Please help.
User.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :token_authenticatable,
:rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :role
# attr_accessible :title, :body
has_many :assignments
has_many :projects, :through => :assignments
has_many :tickets
ROLES = ['admin', 'network/system admin', 'manager', 'programmer']
def role?(base_role)
ROLES.index(base_role.to_s) <= ROLES.index(role)
end
end
Project.rb
class Project < ActiveRecord::Base
attr_accessible :project_name, :description, :duration_from, :duration_upto, :user_id
has_many :assignments
has_many :users, :through => :assignments
validates :project_name, :presence => true
validates :description, :presence => true
validates :duration_from, :presence => true
validates :duration_upto, :presence => true
#validates :user_id, :presence => true //this gives error
end
Assignment.rb
class Assignment < ActiveRecord::Base
attr_accessible :user_id, :project_id
belongs_to :user
belongs_to :project
end
Please check. I have updated the question with 3 models.
Supposing that you have set up your associations correctly.
<% #programmers.each do |programmer| %>
<%= check_box_tag "project[programmer_ids][]", programmer.id, #project.programmers.include?(programmer) %>
<%= programmer.email %>
<% end %>
ref this
check_box does not work when the check box goes within an array-like parameter
Following should work for you
<% #programmers.each do |programmer| %>
<%= check_box_tag "project[programmer]", programmer.email %>
<% end %>

How to filter or scope physician in a form only to list physician that belongs to an organization?

When creating an appointment, for let say Organization 'ABC', I can also see physicians that belongs to other organization. It suppose to only list physician from 'ABC' and not others. How should I go about this.
Thank you.
My Appointment form:
<%= simple_form_for(#appointment, :html => { :class => 'form-horizontal' }) do |f| %>
<div class="form-inputs">
<%= f.hidden_field :patient_id %>
<%= f.association :physician, :label_method => :first_name, :include_blank => false, :as => :radio_buttons, :required => true %>
<%= f.hidden_field :appointment_date, :value => DateTime.now %>
<%= f.hidden_field :organization_id, :value => current_user.organization_id%>
</div>
<div class="form-actions">
<%= f.button :submit, "Create Appointment" %>
</div>
<% end %>
My models:
/app/models/physician.rb
class Physician < ActiveRecord::Base
has_many :appointments
has_many :patients, :through => :appointments
belongs_to :organization
attr_accessible :physician_name, :organization_id
end
/app/models/appointment.rb
class Appointment < ActiveRecord::Base
belongs_to :physician
belongs_to :patient
belongs_to :organization
attr_accessible :physician_id, :patient_id, :appointment_date, :state, :organization_id
end
/app/models/patient.rb
class Patient < ActiveRecord::Base
has_many :appointments
has_many :physicians, :through => :appointments
belongs_to :organization
attr_accessible :patient_name, :organization_id
end
My Controllers:
/app/controllers/appointment_controller.rb
class AppointmentsController < ApplicationController
def new
#appointment = Appointment.new
#appointment.patient_id = params[:patient_id]
end
def create
#appointment = Appointment.new(params[:appointment])
if #appointment.save
flash[:notice] = "New appointment record created"
redirect_to dashboards_path
else
render 'new'
end
end
end
This is because simple_form does not know about your scopes. If you tell it:
<%= f.association :phyisician %>
it will simply list all available physicians in the database.
The solution is to give it the collection of physicians you want to show, for example you could write:
<%= f.association :physician,
:collection => #appointment.patient.organization.physicians,
:label_method => :first_name,
:include_blank => false,
:as => :radio_buttons,
:required => true %>

mass assignment selecting childs of user, using Devise

I have a very simple issue:
User model:
class User < ActiveRecord::Base
devise :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :id, :email, :password, :password_confirmation, :remember_me,
:firstname, :lastname, :mobile_phone, :user_type, :department_id, :department_attributes
belongs_to :department
accepts_nested_attributes_for :department, :allow_destroy => false
Departments model:
class Department < ActiveRecord::Base
has_many :users
accepts_nested_attributes_for :users, :allow_destroy => true
I created a form to be able to select my departments member from my existing users using simple_form:
<%= simple_form_for #department, :validate => true do |form| %>
<%= form.error_messages %>
<%= form.association :users, :prompt => 'assign a user', :label => 'User'%>
<%= form.button :submit %>
<% end %>
Then I (try to) update my users via the department controller:
def update
#department = Department.find(params[:id])
respond_to do |format|
if #department.update_attributes(params[:department])
...
This generates following error:
WARNING: Can't mass-assign protected attributes: user_ids
My guess is that some devise settings generate this error but I don't know which ones.
Can you help? Thanks!
Add attr_accessible :user_ids to your Department model.

Resources