Model structure in many to many relationship - ruby-on-rails

I have two models Students and Colleges for which I want to be able to list the college's a student has applied for and also the list of students applying for a college.
I have this form that creates a Student and also selects colleges through the checkbox
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h3>Add Students</h3>
<%= simple_form_for #student do |f| %>
<%= f.input :name %>
<%= f.input :sex, collection: ["Male", "Female"] %>
<%= f.input :age, collection: 15..100 %>
<%= f.input :dob %>
<%= f.input :current_school %>
<%= f.input :current_level %>
<%= f.input :country, priority: [ "Singapore" ] %>
<%= f.input :sat_score %>
<%= f.input :applied_colleges, collection: College.all,group_method: :id, as: :check_boxes %> <%= f.input :current_mentor, collection: Mentor.all, group_method: :name, as: :select %>
<%= f.button :submit %>
<% end %>
</div>
This is the schema for the app:
ActiveRecord::Schema.define(version: 20150131123428) do
create_table "colleges", force: true do |t|
t.string "name"
t.string "country"
t.integer "sat_min_score"
t.integer "sat_max_score"
t.integer "tution_fees"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "student_list"
end
create_table "mentors", force: true do |t|
t.string "name"
t.string "sex"
t.integer "age"
t.date "dob"
t.text "bio"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "students", force: true do |t|
t.string "name"
t.string "sex"
t.integer "age"
t.date "dob"
t.string "current_school"
t.string "current_level"
t.string "country"
t.integer "sat_score"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "current_mentor_id"
t.string "applied_colleges"
t.string "current_mentor"
end
add_index "students", ["current_mentor_id"], name: "index_students_on_current_mentor_id"
end
This is the student model
class Student < ActiveRecord::Base
serialize :applied_colleges, Array
has_one :mentor
validates :name, presence: true, length: { maximum:50 }
validates :age, presence: true, numericality: { lesser_than_or_equal_to: 120, only_integer: true}
validates :sat_score, presence: true, numericality: {lesser_than_or_equal_to: 1600, only_integer: true}
def self.college_id_to_college_name(applied_colleges)
colleges = []
applied_colleges.each do |var|
colleges << var.to_i
end
#college_list = []
colleges.each do |var|
#college_list << College.find_by(id:var)
end
#college_list.delete_at(#college_list.length-1)
#college_list
end
def self.save_applied_student_id_to_student_joined_column_for_college(student_id,college_list)
college_list.each do |college|
college.student_list << student_id
end
end
end
This is the College model
class College < ActiveRecord::Base
validates :name, presence:true, length: { maximum:50 }
validates :sat_min_score, presence:true, numericality: {lesser_than: 2400, only_integer: true}
validates :sat_max_score, presence:true, numericality: {lesser_than_or_equal_to: 2400, only_integer: true}
end
I am very confused with the modelling between student and college The model needs to be such that each student object has the list of colleges he has applied to and each college has the list of students who has applied.
Currently what I am doing for saving the list of colleges a student has applied to by saving the id of the colleges from the colleges chosen through the checkbox in the form and saving it in a column.
I am thinking to insert the student id of every student in the column called student_list. But this seems like a bad way.
I want to model Students and Colleges in such a way that each student object has the list of colleges he has applied to and each college object has the list of students who has applied.
How do I model this? What would be the necessary migration required?

If you want to have a good way to join students and colleges, I'd make a Model called Application [beware, this might be a reserved word, so you may need a different name for the model -- don't recall off the top of my head] (needs a student_id, and a college_id). Student has_many Applications, has_many Colleges through Applications. Reverse for Colleges, has_many applications, has_many students through applications. Application belongs_to student, and belongs_to college.
Though at this point, you're mixing the traditional definitions of students and applicants. Generally, people might be applicants, and applicants might be enrolled students.

Related

I keep getting this error <undefined method `category_id' for #<ActionController::Parameters>>

Im currently trying to create a search form and everytime I try to send a request rails always report me the NoMethodError above. Below is my code.
controller
#search_cat = params['search_cat']
if #search_cat.present?
category_id = #search_cat['category_id']
#tasks = Task.where(user_id: current_user.id, category_id: category_id.to_i)
end
view
<%= simple_form_for :search_cat, url: tasks_index_path, method: "GET", html: { class: 'form-inline' } do |f| %>
<%= f.input :category_id, collection: current_user.categories,as: :select %>
<%= f.submit "Search", class: "btn btn-primary" %>
<% end %>
def tasks_params
params.require(:task).permit(:deadline_at,:title, :tags, :note, :is_done, :category_id, :user_id, {tag_ids: []})
end
edit:
class Task < ApplicationRecord
belongs_to :user, optional: true
belongs_to :category, optional: true
has_many :tag_associations, dependent: :destroy
has_many :tags, through: :tag_associations
end
schema
create_table "tasks", force: :cascade do |t|
t.datetime "deadline_at"
t.string "title"
t.string "note"
t.boolean "is_done"
t.integer "user_id"
t.integer "category_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["category_id"], name: "index_tasks_on_category_id"
t.index ["user_id"], name: "index_tasks_on_user_id"
I basiclly just trying send the input from the search form to query controller.
enter image description here
My guess would be that you don't have a category reference set in your task table. I would double check your schema to make sure you have all the columns within your task table match your params that you are permitting in your controller. Also make sure you have your relationships set within your models between your task model and category model.

ActiveRecord::AssociationTypeMismatch in Controller#create Error in my Project

Can I get some assistance with my code which is throwing an ActiveRecord::AssociationTypeMistach in my JobsController#create.
ActiveRecord::AssociationTypeMismatch (JobCategory(#70843392) expected, got "At Home" which is an instance of String(#20478408)):
app/controllers/jobs_controller.rb:46:in `create'
I am posting data through the form
<%= form_for #job do |f| %>
<div class="row">
<div class="col-md-4 select">
<div class="form-group">
<label>Job Category</label>
<%= f.collection_select :job_category, JobCategory.order(:job_category),
:job_category, :job_category, include_blank: false, id: "job_category",
prompt: "Select...", class: "form-control" %>
</div>
</div>
<div class="col-md-4 select">
<div class="form-group">
<label>Job Type</label>
<%= f.grouped_collection_select :job_type, JobCategory.order(:job_category),
:job_types, :job_category, :job_type_id, :job_type, id: "job_type",
prompt: "Select...", class: "form-control" %>
</div>
</div>
<div class="col-md-4 select">
<div class="form-group">
<label>Frequency</label>
<%= f.select :recurrence, [["One Off", "One Off"], ["Daily", "Daily"],
["Weekly", "Weekly"], ["Bi-Monthly", "Bi-Monthly"],
["Once-Monthly", "Once-Monthly"]],
id: "recurrence", prompt: "Select...", class: "form-control" %>
</div>
</div>
</div>
<div><%= f.submit "Post My Job", class: "btn btn-normal" %></div>
<% end %>
Schema for Job Model
create_table "jobs", primary_key: "job_id", force: :cascade do |t|
t.string "job_title"
t.text "job_description"
t.text "key_instructions"
t.integer "budget"
t.datetime "booking_date"
t.string "recurrence"
t.boolean "is_flexible"
t.string "address"
t.boolean "active"
t.integer "user_id"
t.datetime "date_posted"
t.datetime "date_ending"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "featured"
t.boolean "premium"
t.integer "job_category_id"
t.integer "job_type_id"
t.string "job_category"
t.string "job_type"
t.index ["job_category_id"], name: "index_jobs_on_job_category_id"
t.index ["job_type_id"], name: "index_jobs_on_job_type_id"
t.index ["user_id"], name: "index_jobs_on_user_id"
end
I am posting the data from my form into my job.rb model but in the process using job_category.rb models and job_type.rb associations for additional info. The three models are related as follows
class Job < ApplicationRecord
belongs_to :user
belongs_to :job_category
belongs_to :job_type
has_many_attached :images
validates :job_category, presence: true
validates :job_type, presence: true
validates :recurrence, presence: true
end
class JobType < ApplicationRecord
has_many :job_categories
has_many :jobs
#attr_accessible :job_type
#validates :job_type, :presence => true
end
class JobCategory < ApplicationRecord
has_many :job_types
has_many :jobs
#attr_accessible :job_category
#validates :job_category, :presence => true
end
Any tips on where I am going wrong would be useful. Thanks
Based on what you've got in your question, you have two choices for job categories, (which should apply also to job types):
1) You can create a table job_categories and drop job_category_id and job_category from the jobs table.
2) The simpler option, and the one I might recommend based on very little knowledge other than you seem to be relatively new to db design and Rails, is to get rid of the JobCategory class entirely, and get rid of the belongs_to: job_category. You can also drop job_category_id from the jobs table. If you do that the rest of our code looks like it should be pretty close to just working.
The associations and extra classes you've set up are really meant for cases where you've stored that additional data in separate tables, and you are telling ActiveRecord how that data is related. Here, because a job has just one category, which is really just a single string with no additional data associated, putting everything in one table is not a bad option.

How can I display the name instead of an ID in rails

I am trying to have a drop down from the other model and display its name in the index page
I have a 3 tables "students", "classrooms", and "classroom_students"
what I am trying is when a student is created he should be able to add the classroom from a dropdown which is populated from classroom table, at the moment the drop down is working however it is getting the id from the dropdown
How to get the classroom name to display in index page
classroom model
class Classroom < ApplicationRecord
belongs_to :user
has_many :classroom_students
has_many :students, through: :classroom_students
end
student model
class Student < ApplicationRecord
has_many :classroom_students
has_many :classrooms, through: :classroom_students
validates :student_fname, presence: true, length: { minimum: 3, maximum: 50 }
end
classroom_student model
class ClassroomStudent < ApplicationRecord
belongs_to :classroom
belongs_to :student
end
students controller
def student_params
params.require(:student).permit(:student_fname, :student_lname, :gender, :dob, :aboriginal, :esl, :special_provisions, :user_id, :classroom_id, :group_id, :active)
end
classrooms controller
def classroom_params
params.require(:classroom).permit(:classroom_name, :classroom_year, :classroom_student)
end
students views form
<%= form.select :classroom_id, Classroom.where(:user_id => current_user.id).map {|r| [r.classroom_name, r.id]} %>
student index
at the moment it is id but I want it to be classroom name
<td><%= student.classroom_id %>
Schema file
create_table "classroom_students", force: :cascade do |t|
t.integer "classroom_id"
t.integer "student_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "classrooms", force: :cascade do |t|
t.string "classroom_name"
t.date "year"
t.string "classroom_student"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "users_id"
t.integer "user_id"
t.string "classroom_year"
t.index ["users_id"], name: "index_classrooms_on_users_id"
end
create_table "students", force: :cascade do |t|
t.string "student_fname"
t.string "student_lname"
t.boolean "gender"
t.string "dob"
t.boolean "aboriginal"
t.boolean "esl"
t.text "special_provisions"
t.integer "user_id"
t.integer "classroom_id"
t.integer "group_id"
t.boolean "active"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "classroom_name"
end
Your association says that student has many classrooms but your database schema says you have a clasroom_id too in the students table. Remove that classroom_id first if you need to associate a student to multiple classrooms. Then your select says:
<%= form.select :classroom_id, Classroom.where(:user_id => current_user.id).map {|r| [r.classroom_name, r.id]} %>
Now as you have multiple classrooms associated to one student so this will not work because classroom_id is not valid. Instead it should be like this:
<%= f.select :classroom_ids, Classroom.where(user_id: current_user.id).map { |r| [r.classroom_name, r.id] }, {}, multiple: true %>
This will associate multiple classrooms with the student.
And donot forget to add classroom_ids in strong params:
def student_params
params.require(:student).permit(:student_fname, :student_lname, :gender, :dob, :aboriginal, :esl, :special_provisions, :user_id, :classroom_id, :group_id, :active, :classroom_ids => [])
end
Hope this helps.
First of all, you have an association User has_many :classrooms, so you can't think that you only have 1 classroom.
You can get all the classes associated to an user with something like this:
<%= f.form_for #student do |f| %>
<%= f.collection_select(:classrrom_id, Classroom.all, :id, :classroom_name,
{:prompt => "Select your classrooms"}, {:multiple => true}) %>
<% end %>
And at your students_controller.rb you should add classroom_ids to your params
def student_params
params.require(:student).permit(:student_fname, :student_lname, :gender,
:dob, :aboriginal, :esl, :special_provisions, :user_id, :classroom_id,
:group_id, :active, :classroom_ids => [])
end

How to send mail related to a category

So, I have 3 models Quotes, Categories, and Subscribers.
Essentially, it's a newsletter application. Subscribers can select a category and then enter their email and they will receive quotes related to that category via email.
There's two issues here,
1. The category's are stored in a constant in the category model.
so how do I display the category type that the subscriber chose
in text format? I'd like to show in the email
something like "here's your email on <%= category.name %>" which would translate to
"here's your email on Food."
2. I'd like to ONLY send emails about the category that the subscriber subscribed to.
How might I accomplish this? Please provide examples.
This is what I have so far:
Category.rb
class Category < ActiveRecord::Base
belongs_to :quote
belongs_to :subscriber
CATEGORY_TYPE = {
1 => "Food",
2 => "Fitness",
3 => 'Cats',
}
end
Quote.rb
class Quote < ActiveRecord::Base
has_many :categories
belongs_to :category
validates :title, presence: true
end
Subscriber.rb
class Subscriber < ActiveRecord::Base
has_one :category
validates :email, presence: true
end
schema
create_table "categories", force: true do |t|
t.string "name"
t.integer "quote_id"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "subscriber_id"
t.integer "category_type"
end
create_table "quotes", force: true do |t|
t.string "title"
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "subscribers", force: true do |t|
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
end
Update subscribers_mailer.rb
def SendMyEmail(email, category, quote, subscribers)
#category = category
#quote = quote
#subscribers = subscribers
#email = email
mail to: email, subject: 'New Quotes'
end
end
and then of course:
Category.all.each do |category|
SubscriptionMailer.SendMyEmail("email#test.com", category, category.quotes.first, category.subscribers).deliver
end
Update:
There's two remaining issues here,
1. Categories aren't syncing to subscribers. For example when I run category.subscribers.last
it's nil. and category.subscribers throws an empty array. How can I sync these? I think it has
to do with the fact that subscribers are selecting a category from the Category::CATEGORY_TYPES
constant as seen in the view code below.
2. I'd like to automate it so that these emails are sent to subscribers once a day.
How might I do this?
view code (subscribers/new.html.erb:
<div class="styled email-input2">
<%= form_for #subscriber do |f| %>
<% if #subscriber.errors.any? %>
<div class="errorExplanation" style="color: white;">
<h2><%= pluralize(#subscriber.errors.count, 'error') %> encountered:</h2>
<ul>
<% #subscriber.errors.full_messages.each do |m| %>
<li><%= m %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.fields_for :subscriber, #subscriber.build_category do |cat| %>
<%= cat.select(:category_type, Category::CATEGORY_TYPE.map{|p| [p[1], p[0]]}, {prompt: 'Please select'}, {class: 'styled email-input2'}) %>
<% end %>
</div>
Ok first lets modify the migrations:
create_table "categories", force: true do |t|
#DELETE QUOTE_ID AND SUBSCRIBER ID
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "category_type"
end
create_table "quotes", force: true do |t|
t.string "title"
t.text "body"
t.datetime "created_at"
t.datetime "updated_at"
t.references :category #this will add integer category_id
end
create_table "subscribers", force: true do |t|
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
t.references :category #this will add integer category_id
end
Next we will modify the models to reflet the changes in the migration:
class Category < ActiveRecord::Base
has_many :quotes
has_many :subscribers
CATEGORY_TYPE = {
1 => "Food",
2 => "Fitness",
3 => 'Cats',
}
end
class Quote < ActiveRecord::Base
belongs_to :category
validates :title, presence: true
end
class Subscriber < ActiveRecord::Base
belongs_to :category
validates :email, presence: true
end
Now you can get the subscribers for a cateogyr using the following:
category = Category.find(1) #use whatever id you want
category.subscribers #list of all subscribers for a category
Getting the quotes for a category is similarly straight forward:
category = Category.find(1)
category.quotes
So assuming your mailer takes a category, quote and a list of subscribers for the email to send
Category.all.each do |category|
Mailer.SendMyEmail(category, category.quotes.first, category.subscribers).deliver
end
In the "SendMyEmail" function in your mailer you will have
def SendMyEmail(category, quote, subscribers)
#YOUR CODE FOR TYHE VIEW HERE
end

Rails associations in a form

I am trying to figure out how to register certain values to their respective table using the form, check the order form to better understand which values need to be registering to which table.
I have also displayed the table entries, models and controller related to this question. If someone can guide me to where I can obtain further understanding on coding the associations in forms that would be great.
order form
<%= simple_form_for(#order) do |f| %>
<%= f.association :items, collection: Item.all, label_method: :name, value_method: :id %>
<%= [need to display the price of the item selected] %>
<%= f.input :quantity ???? [need to register in the order_items table] %>
<%= [register sub total to orders table] %>
<%= f.submit %>
<% end %>
tables
create_table "order_items", force: true do |t|
t.integer "item_id"
t.integer "order_id"
t.integer "quantity"
end
create_table "orders", force: true do |t|
t.integer "user_id"
t.integer "client_id"
t.boolean "status"
t.decimal "sub_total"
end
create_table "items", force: true do |t|
t.string "name"
t.decimal "price"
t.integer "stock"
end
models
class Order < ActiveRecord::Base
...
has_many :order_items
has_many :items, :through => :order_items
end
class Item < ActiveRecord::Base
...
has_many :order_items
has_many :orders, :through => :order_items
end
class OrderItem < ActiveRecord::Base
belongs_to :item
belongs_to :order
end
orders controller
def create
#order = Order.new(order_params)
#order.user_id = current_user.id
#order.status = TRUE
end
def order_params
params.require(:order).permit(:code, :client_id, :user_id, :memo, :status, item_ids: [])
end

Resources