I'm trying to build a form for an "Incident" that allows users to add notes (text fields) to it dynamically with some javascript. So when they click an "Add Note" button in the form, a text field pops up and they can add a note. If they click it again, another field will show up. So far this works fine when creating a new incident, but when I edit the incident and add a new field, it doesn't pick up the relationship between incident_note and user.
For example, here is what I see when I create a new incident.
INSERT INTO "incident_notes" ("created_at", "updated_at", "user_id", "note", "incident_id") VALUES('2010-07-02 14:07:42', '2010-07-02 14:07:42', 2, 'A Note', 8)
As you can see, the user_id field has a number assigned to it. But here is what happens during the edit when I add another note:
INSERT INTO "incident_notes" ("created_at", "updated_at", "user_id", "note", "incident_id") VALUES('2010-07-02 14:09:11', '2010-07-02 14:09:11', NULL, 'Another note', 8)
The user_id is NULL. I'm not sure what I've done. The code is very similar for both "edit" and "new" in the controller.
I have the following models and relationships (only showing the relevant sections):
class Incident < ActiveRecord::Base
has_many :incident_notes
belongs_to :user
end
class IncidentNote < ActiveRecord::Base
belongs_to :incident
belongs_to :user
end
class User < ActiveRecord::Base
has_many :incidents
has_many :incident_notes
end
Here is the relevant part of the new form (the edit is essentially the same):
<% form_for([#customer,#incident]) do |f| %>
<p>
<% f.fields_for :incident_notes do |inf| %>
<%= render "incident_note_fields", :f => inf %>
<% end %>
<p><%= link_to_add_fields "Add Note", f, :incident_notes %></p>
</p>
<p>
<%= f.submit "Create" %>
</p>
<% end %>
And here are the create and update methods in the Incident controller.
def create
#incident = #customer.incidents.build(params[:incident])
#incident.capc_id = generate_capc_id
for inote in #incident.incident_notes
(inote.user = current_user) if (inote.user == nil)
end
respond_to do |format|
if #incident.save #etc
end
def update
#incident = #customer.incidents.find(params[:id])
for inote in #incident.incident_notes
(inote.user = current_user) if (inote.user == nil)
end
respond_to do |format|
if #incident.update_attributes(params[:incident])
#etc
end
There may be a better way to do this, but as you can see in the "create" method I had to manually set the incident_note user field to the current user. This works fine, but the same does not seem to work in the update method.
Any ideas, suggestions, and help will be much appeciated! I'm very stuck at the moment. :)
I suggest that you don't have incident_notes directly belonging to user. In other words a user has many incidents and an incident has many incident notes.
class Incident < ActiveRecord::Base
has_many :incident_notes
belongs_to :user
end
class IncidentNote < ActiveRecord::Base
belongs_to :incident
end
class User < ActiveRecord::Base
has_many :incidents
has_many :incident_notes, :through => :incident
end
The user's incident notes are then obtained through her incidents model
Related
I have a model named 'Assessment':
class Assessment < ApplicationRecord
has_many :assessment_students
has_many :students, through: :assessment_students
end
Join table is:
class AssessmentStudent < ApplicationRecord
belongs_to :student
belongs_to :assessment
end
There is another model:
class Classroom < ApplicationRecord
has_many :classroom_students
has_many :students, through: :classroom_students
has_many :assessments
end
In show,html.erb of classrooms, I have a dropdown which shows all assessments (generated from assessment table).
Code is:
<%= collection_select(:assessment :assessment_id, Assessment.all, :id, :assessment_name , :prompt => true) %>
Requirement of the project is: Based on the assessment chosen by the user in the show.html.erb page, we have to show all students details like name etc assigned to that particular assessment. I have stored this data in join table 'AssessmentStudent '. However, I am not sure how to pass id from the above collection_select to classroom controller. I have below code:
show.html.erb:
<%= collection_select(:assessment :assessment_id, Assessment.all, :id, :assessment_name , :prompt => true) %>
<div id="divResult">
<% #assessmentstudents1.each do |t| %>
<% t.assessment_students.each do |record| %>
<%= record.student_id %>
<% end %>
<% end %>
</div>
classroom controller:
def show
#assessmentstudents1 = Assessment.find(params[:assessment][:assessment_id]).preload(:assessment_students)
end
def classroom_params
params.require(:classroom).permit(:classroom_name, :classroom_year, :customer_id, :classroom_student, :student_ids => [])
params.require(:assessment).permit(:assessment_id)
end
I would first recommend to make a change to the controller structure of your application. Because the responsibility of the show action of your ClassroomsController should be to display the details of the classroom. When you want to show the details of an Assessment, that should be handled by an AssessmentsController.
First of all, I'm gonna assume that your Assessment model has a belongs_to :classroom association. Then I would suggest creating the following structure.
config/routes.rb
resources :classrooms
resources :assessments
app/views/classrooms/show.html.erb
<% #classroom.assessments.each do |assessment| %>
<%= link_to assessment_name, assessment_path(assessment) %>
<% end %>
app/controllers/assessments_controller.rb
class AssessmentsController < ApplicationController
def show
#assessment = Assessment.find(:id)
end
end
app/views/assessments/show.html.erb
<h1>Students for <%= #assessment.assessment_name %></h1>
<% #assessment.students.each do |student| %>
<p><%= student.student_name %></p>
<% end %>
<h2>In classroom:</h2>
<p><%= #assessment.classroom.classroom_name %></p>
So to explain what is happening here, we have configured the routes to allow the server to respond to the url /assessments/:id which will lead to the AssessmentsController#show action being called.
That action is being called when the user clicks on any of the links that we have setup in the classrooms/show template. Note that I used individual links for now instead of a select dropdown, because it is easier to setup and understand how it works. And like the previous answer suggested, using a select tag requires a little bit of JavaScript to get working.
And lastly, when the assessments/show template is being rendered, it will list out all of the students related to that particular assignment. And I also included which classroom it is assigned to (if my assumption of belongs_to was correct).
On a side note, if your question tag of ruby-on-rails-3 is correct, then the usage of params.permit and params.require is invalid, because that is something that was introduced in Rails 4 (unless I'm mistaken). And in any case, you only have to use permit when database updates take place, which means the create and update actions, not index, show, etc, because it is a way of restricting which changes are allowed.
Tag select does nothing by itself. So there are two possible options.
The first is wrapping select in form tag then submitting the form will lead to request. The second is writing a handler using JavaScript which will listen to select changes.
I want to display login student name and message.
after login student can write messages and send related to courses.the messages he send is displayed above in same page with his/her name and message
I got name, but message field fetches all messages that are in database. How to display a particular student name and message?
Here is my code
controller.erb
class CourseQueriesController <ApplicationController
def index
#course_queries = CourseQuery.all
#course_query = CourseQuery.new
end
def create
#course_query = CourseQuery.new(student_id: current_student.id, coach_id: "2", message: params[:course_query][:message])
if #course_query.save
redirect_to course_queries_path, notice: 'Query was successfully send.'
else
render :new
end
end
end
course_queries/index.html.erb
<% #course_queries.each do |queries| %>
<p><b><%= current_student.name %></b></p>
<%= queries.message %>
<% end %>
<%= simple_form_for (#course_query) do |f| %>
<%= f.input :message %>
<%= f.button :submit , "Send or press enter"%>
<% end %>
how to display a particular student name and message
You need to have the relevant associations established in your models, like what Pavan wrote.
I'll give you some more information on why this is important...
ActiveRecord
One of the main reasons Rails works so well is the way it helps you create & manage objects. In OOP, objects form everything from your init commands to your user input responses, Ruby being a prime exponent of this structure.
Rails is built on Ruby, and therefore is object orientated too. It uses ActiveRecord, the MVC structure & classes to give you a platform from which you can populate and manipulate objects:
Thus, you shouldn't be treating your application's interactions as a way to edit a database, or "display a login message" - it should be a way to invoke & manipulate objects.
Objects - in the case of Rails - are built in the models. The model data can then be used in the controllers and views.
This seems to be lacking in your code. If you can remedy it, your code will become a lot simpler and more powerful...
Associations
I'd do something like this:
#app/models/student.rb
class Student < ActiveRecord::Base
has_many :queries
has_many :coarse_queries, through: :queries
end
#app/models/course.rb
class Course < ActiveRecord::Base
has_and_belongs_to_many :coaches
has_many :queries
has_many :student_queries, through: :queries
end
#app/models/coach.rb
class Coach < ActiveRecord::Base
has_and_belongs_to_many :courses
has_many :queries
end
#app/models/query.rb
class Message < ActiveRecord::Base
belongs_to :course
belongs_to :student
belongs_to :coach (maybe)
end
This structure will allow a student to send queries to specific courses, selecting the coach as necessary. Importantly, this sets up your associations so that you don't have to invoke multiple classes each time you want to populate the various objects.
#app/controllers/course_queries_controller.rb
class CourseQueriesController <ApplicationController
def index
#queries = Query.all
#query = current_student.queries.new
end
def create
#query = current_student.queries.new query_params
if #query.save
redirect_to course_queries_path, notice: 'Query was successfully send.'
else
render :new
end
end
private
def query_params
params.require(:query).permit(:message).merge(coach_id: "2")
end
end
#app/views/queries/index.html.erb
<% #queries.each do |query| %>
<p><b><%= query.student.name %></b></p>
<%= query.message %>
<% end %>
<%= simple_form_for #query do |f| %>
<%= f.input :message %>
<%= f.button :submit , "Send or press enter"%>
<% end %>
You should add has_many :course_queries to the Student model
#student.rb
class Student < ActiveRecord::Base
has_many :course_queries
...
end
And in the controller in index method change #course_queries = CourseQuery.all to #course_queries = current_student.course_queries
Now <%= queries.message %> will only display the course_query's message of the current_student
I'm a relative novice to rails. I have a process that runs and finishes with a status (five possible different statuses). The process is run per-building. (We collect data from buildings.) I'm trying to allow users to configure themselves to receive notification email with fine-grained control: Per-building and complettion status. I'm trying to use collection_check_boxes to create a table of checkboxes for the user to select, but I'm not even sure collection_check_boxes was designed for such a case. I would be very happy to hear a yes or no on that question to start with.
I have the following modles:
class Building < ActiveRecord::Base
self.primary_key = 'building_id'
has_many :etl_status
has_many :email_notifications
end
class BuildingUserPair < ActiveRecord::Base
self.primary_key = "building_user_pairs_id"
belongs_to :building
belongs_to :user
has_many :email_notification_settings
end
class EmailNotificationSetting < ActiveRecord::Base
self.primary_key = "email_notification_settings_id"
belongs_to :building_user_pair
end
class EtlResult < ActiveRecord::Base
self.primary_key = 'etl_results_id'
# table has 5 rows, with values 1 -5 with the statuses "HUNKY DORY", "MO BETTA", "LIMPING ALONG", "DISMAIL FAILURE" and "UNEXPECTEDLY IDLE"
end
class EtlResultNotification < ActiveRecord::Base
belongs_to :building
belongs_to :user
end
class EtlStatus < ActiveRecord::Base
self.primary_key = 'etl_status_id'
belongs_to :building
has_many :users, :through => :email_notifications
end
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#buildings = Building.active
#bup = []
#email_notifications_settings = []
#buildings.each do |bldg|
bup = BuildingUserPair.where(user_id: params[:id], building_id: bldg.building_id).first_or_create
#email_notifications_settings[bldg.building_id] =
EmailNotificationSetting.where(building_user_pairs_id: bup.building_user_pairs_id).all
end
end
my users/show.html.erb contains this:
<%= form_for #user do |f| %>
<% #buildings.each do |bldg| %>
<div class="row">
<div class="col-md-4">
<%= bldg.name %>
</div>
<%= f.fields_for :email_notification_settings do |ens| %>
<%= ens.collection_check_boxes( #email_notifications_settings[bldg.building_id],
EtlResult.all, :etl_result_id, :result_name) %>
</div>
<% end %>
<% end %>
<div class="form-buttons">
<%= submit_tag("Update", data: { buildings: #buildings}) %>
</div>
The etl_result_notification table has just two columns, besides
it's primary key, a building-user-pair column and then a number,
1-5 that is a foreign key to the Etl Results table. Thus the idea is that a new line gets
created for a checkbox, and if a checkbox is newly unchecked, the row in the table is deleted.
Like I said, not even sure if form_for and collection_check_boxes was even designed to do this.
My problem is that the checkboxes are not being properly initialized. They all come up unchecked.
I'm guessing I need to pass other paraemters to collection_check_boxes, but I can't think
what they should be.
TIA
I think that you are over complicating your question,
what you want to do is save a list of id that march to the lest of emails the user want to get.
Rubyist posted https://stackoverflow.com/a/23340368/1380867 witch is a good example of what you want to do.
you should create a serialize :email_notification_ids
#MODEL
class User < ActiveRecord::Base
serialize :email_notification_ids
end
Hope this helps
Happy Codding
Ok, am still a newbie in ruby on rails trying to learn my way around. I have two models (User model and Comment model). Basically a user has a simple profile with an 'about me' section and a photo's section on the same page. Users must be signed in to comment on other users profiles.
My User Model
class User < ActiveRecord::Base
attr_accessible :email, :name, :username, :gender, :password, :password_confirmation
has_secure_password
has_many :comments
.
.
end
My Comment Model
class Comment < ActiveRecord::Base
belongs_to :user
attr_accessible :content
.
.
end
In my comments table, I have a user_id column that stores the id of the user whose profile has been commented on and a commenter_id column that stores the id of the user commenting on the profile.
Comment Form
<%= form_for([#user, #user.comments.build]) do |f| %>
<%= f.text_area :content, cols: "45", rows: "3", class: "btn-block comment-box" %>
<%= f.submit "Comment", class: "btn" %>
<% end %>
My comments Controller
class CommentsController < ApplicationController
def create
#user = User.find(params[:user_id])
#comment = #user.comments.build(params[:comment])
#comment.commenter_id = current_user.id
if #comment.save
.........
else
.........
end
end
end
This works fine storing both user_id and commenter_id in the database. My problem comes when displaying the user comments on the show page. I want to get the name of the user who commented on a specific profile.
In my user controller
def show
#user = User.find(params[:id])
#comments = #user.comments
end
I want to get the name of the user from the commenter_id but it keeps throwing errors undefined method 'commenter' for #<Comment:0x007f32b8c37430> when I try something like comment.commenter.name. However, comment.user.name works fine but it doesn't return what I want. Am guessing am not getting the associations right.
I need help getting the correct associations in the models so as to get the name from the commenter_id.
My last question, how do I catch errors in the comments form? Its not the usual form_for(#user) where you do like #user.errors.any?.
routes.rb
resources :users do
resources :comments, only: [:create, :destroy]
end
Try something like this in your models
class User < ActiveRecord::Base
has_many :received_comments, :class_name => "Comment", :foreign_key => "user_id"
has_many :given_comments, :class_name => "Comment", :foreign_key => "commenter_id"
end
class Comment < ActiveRecord::Base
belongs_to :user # comment about profile
belongs_to :commenter, :class_name => "User", :foreign_key => "commenter_id"
end
check out: http://guides.rubyonrails.org/association_basics.html
you can probably come up with better naming on the has_many collections, received and given were the best I could do on short notice :)
Note: foreign_key is option in many cases, left it in above - i think it helps with clarity
has_many fk refers to the the column in the many table (other table)
belongs_to fk refers to the column in the many table (this table)
I have a database that contains users and groups with a has_and_belongs_to_many relationship. When a new group is added, it gets created but the user's membership to the group doesn't seem to propagate until I clear the cache or login with an incognito window. I know it's getting saved correctly, it just doesn't seem to be loading until the cache is cleared. This only recently started happening and I can't figure out why! Any help would be greatly appreciated.
From the models:
class User < ActiveRecord::Base
has_many :services
has_many :recipes
has_and_belongs_to_many :groups
attr_accessible :recipes, :groups
end
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :recipes
attr_accessible :description, :title, :recipe, :picture, :featured, :user_id
end
The create group method:
def create
#user = User.find(current_user.id)
#group = Group.new(params[:group])
#group.user_id = #user.id
#user.groups << #group
redirect_to group_path(#group)
end
Displaying the group memberships of a user -- this won't update until the cache is cleared:
<% #user.groups.each do |group| %>
<% if group %>
<p class="group-title"><a href="<%= group_path(group) %>"><%= group.title %></p>
<% #latestpic = Recipe.where("group_id = ?", group).limit(1).order("created_at DESC") %>
<% if #latestpic.exists? %>
<% #latestpic.each do |pic| %>
<%= image_tag(pic.picture.url(:medium)) %>
<% end %></a>
<% else %>
<%= image_tag "http://placehold.it/300x300" %>
<% end %>
<br></br>
<% end %>
<% end %>
In your models you have a "has and belongs to many" relationship, which means your users can be in n groups and your groups contains n users.
#group.user_id
If you have created a user_id column in your "groups" table, you can drop it, because a group contains n users. You have to use a table between users and groups like this :
create_table :group_users, :id => false do |t|
t.references :group, :null => false
t.references :user, :null => false
end
Then refactor your controller as I did below :
def create
#group = current_user.groups.build(params[:group])
if #group.save
redirect_to #group, notice: 'Group was successfully created.'
else
render action: "new"
end
end
This will create a group with your current user in it. In your method, you forgot to save your modifications. Because the operator = and << does not update the database. Then I refactored a little bit, but it is the same logic.
You can also refactor a lot of things in your view, but it's not the question, we'll leave it as is.
Does it work now?
May be this answer is outdated, but might be useful for googlers who ends-up here:
When Rails(4.2 for me) updates Has-And-Belongs-To-Many association, it does not change an updated_at value for the root record. Example:
# This does not change #user.updated_at value
#user.update_attributes(group_ids: [1, 2, 3])
Every ActiveRecord objects has a special cache_key that usually built using value of updated_at and invalidation of cache is based on that. So, if we change only HABT it does not invalidates cache.
Possible solution here - manually call #user.touch if HABTM was changed.
If someone came here because data won't show after create or delete, here is what you need to do to update the cache after those actions:
# in your model file
after_save :expire_model_all_cache
after_destroy :expire_model_all_cache
def expire_model_all_cache
Rails.cache.delete("your cache name")
end