I don't understand the new and create associations in rails - ruby-on-rails

What I want to achieve
I have created an association between movies and schedules, but I don't know how to instantiate the association, even after reading the Rails Guide.
Code
db/schema
# movies
create_table "movies", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.string "name"
t.integer "year"
t.string "description"
t.string "image_url"
t.integer "is_showing"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
#schedules
create_table "schedules", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.bigint "movie_id", null: false
t.time "start_time", null: false
t.time "end_time", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["movie_id"], name: "index_schedules_on_movie_id"
end
model
movie.rb
class Movie < ApplicationRecord
has_many :schedules
end
schedule.rb
class Schedule < ApplicationRecord
belongs_to :movie
end
controller
schedules
class SchedulesController < ApplicationController
def index
#movies = Movie.joins(:schedules).select("movies.*", "schedules.*")
end
###############################################################################
# I don't get it.
def new
#schedule = #movie.build_schedules()
end
def create
#schedule = Schedule.new(schedule_params)
#schedule.save
redirect_to schedules_path
end
###############################################################################
private
def schedule_params
params.require(:schedule).permit(:start_time, :end_time)
end
end
new.html.erb
<!DOCTYPE html>
<html lang="en">
<head>
<%= render 'shared/head' %>
<title>schedule/new</title>
</head>
<body>
<%= form_with model: #schedule, url: movie_schedules_path do |form| %>
<div class="field">
<%= form.label :start_time %>
<%= form.date_field :start_time %>
</div>
<div class="field">
<%= form.label :end_time %>
<%= form.date_field :end_time %>
<%= form.submit %>
</div>
<% end %>
</body>
</html>
What I've tried.
・Check the Rails Guide.
I was using build instead of new, but it took an argument. I didn't know what I needed for the argument part here.
# Rails Guide
#book.author = #author
#author = #book.build_author(author_number: 123,
author_name: "John Doe")

You want to do #movie.schedules.build to instantiate a new record for the #movie.schedules association because it's a has_many.
You can also do #movie.schedules.create(schedule_params) to create the Schedule associated to the Movie instance in one step.
record.build_xxx only works for has_many/belongs_to associations (you could do some_schedule.build_movie for example).

Related

Show only the last evaluation of every category in Ruby on rails

I have many Sectors and every sector has many Evaluations (it´s just an integer of how clean is the sector), I Need to show the name of the sector and only the last Evaluation of every Sector
With this code, I have all the evaluations but I only need the last of every sector
<% #evaluations.each do |evaluation| %>
<%= evaluation.sector.name %>
<%= evaluation.note %>
<% end %>
create_table "sectors", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "evaluations", force: :cascade do |t|
t.bigint "sector_id", null: false
t.integer "note"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["sector_id"], name: "index_evaluacions_on_sector_id"
end
class Evaluation < ApplicationRecord
belongs_to :sector
end
class Sector < ApplicationRecord
end
Thanks in advance
You can add has_one association to the Sector model
class Sector < ApplicationRecord
has_one :last_evaluation, -> { order(created_at: :desc) }, class_name: 'Evaluation'
end
And then in your view:
<% #sectors.each do |sector| %>
<%= sector.last_evaluation.note %>
<% end %>
Have a missing has_many :evaluations in sectors.rb and then
<% #sectors.each do |sector| %>
<%= sector.name %>
<%= sector.evaluations.last.note %>
<% end %>

Rails 6 Complex Form with nested objects collection_select not working

Would be super grateful if anyone had any insights into what I should be doing that I'm not already doing.
db/schema.rb
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.string "email"
t.string "password_digest"
t.bigint "cell_number"
t.boolean "sms_daily"
t.string "initials"
t.string "remember_token"
t.boolean "admin", default: false
t.boolean "vacation_mode", default: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "remember_digest"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["remember_token"], name: "index_users_on_remember_token"
end
create_table "schedules", force: :cascade do |t|
t.text "comments"
t.integer "author_id", null: false
t.boolean "draft", default: true
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["author_id"], name: "index_schedules_on_author_id"
end
create_table "rooms", force: :cascade do |t|
t.integer "schedule_id", null: false
t.integer "order"
t.string "site"
t.string "note"
t.string "name"
t.integer "start_hour"
t.integer "start_minute"
t.string "user_initials"
t.boolean "block"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["schedule_id"], name: "index_rooms_on_schedule_id"
end
app/model/schedule.rb
class Schedule < ApplicationRecord
belongs_to :author, class_name: "User"
has_many :rooms
accepts_nested_attributes_for :rooms
...
end
app/model/room.rb
class Room < ApplicationRecord
belongs_to :schedule
...
end
app/views/schedule/new.hthl.erb
<% provide(:title, 'Create Schedule') %>
<h1>Create Schedule</h1>
<div class="row">
<aside class="span4">
<section>
<%= form_with(model: #schedule, local: true) do |f| %>
<div class="field">
<%= f.fields_for :rooms, html: {class: 'form-inline' } do |r| %>
<div class="room-title" id="<%="#{r.object[:site]}-#{r.object[:name].delete(' ')}" unless r.object[:name] == nil %>">
<%= "#{r.object[:site]} #{r.object[:name]}"%>
<%= r.hidden_field :site, value: r.object[:site] %>
<%= r.hidden_field :name, value: r.object[:name] %>
<% unless (r.object[:name] == "CHARGE" || r.object[:name] == "CO-CHARGE" || r.object[:site] == "ON_CALL" || r.object[:site] == "On-Call TODAY (Sunday or Holiday)") %>
<br/>
<%= r.label :start_hour %>
<%= r.number_field :start_hour, in: 0..24, step: 1 %>
<br/>
<%= r.label :start_minute %>
<%= r.number_field :start_minute, in: 0..60, step: 1 %>
<% end %>
<%= r.collection_select(:user_initials,User.all.map(&:initials).sort.unshift("-- late start").collect,:initials,:initials, include_blank: true) %>
</div>
<% end %>
<%= f.submit "Save Schedule and Review", class: "btn btn-large btn-primary", input_html: { :tabindex => autotab } %>
</div>
<% end %>
</section>
</aside>
</div>
I'd like for the form to present a choice of all the User's initials to be assigned to the room's user_initials, also offer the choice of not designating anyone's initials with "-- late start" as well as leaving the initials blank for an empty room.
When I deploy my code to heroku the schedule/new page generates a "We're sorry, something went wrong" error and the logs say:
ActionView::Template::Error (undefined method `initials' for "-- late start":String):
I got this all working in Rails 4 using simple_form but heroku will no longer support the stack that I deployed the site to starting in November, 2020, and heroku's new stack's won't use the old ruby version from the original site. I'm rewriting the code in Rails 6 which simple_form doesn't work for.
Thanks in advance for your consideration!
collection_select works on a collection of objects and will look for the initials method on each object.
when you do this...
<%= r.collection_select(:user_initials,User.all.map(&:initials).sort.unshift("-- late start").collect,:initials,:initials, include_blank: true) %>
You're just creating an array of strings, and you're prepending another string to the array, and the strings don't have an initials method.
Better might be just to use select
<%= r.select(:user_initials, options_for_select(User.all.pluck(:initials).sort.unshift("-- late start"), r.object.user_initials), include_blank: true) %>
You can use collection_select, it must be ALL user objects, so you could temporarily create a user with initials '--late start', something like...
<%= r.collection_select(:user_initials, User.order(:initials).to_a.prepend(User.new(initials: "-- late start")), :initials, :initials, include_blank: true) %>

How can I save the datas checked with simple_form checkbox?

I am beginner with Ruby on Rails, and I am trying to build a little app that permit people to order a pastrie from a cooker, for a day chosen.
When they select the pastrie using a checkbox, I would like that the selected pastrie was saved as pastrie_id in the table fight but instead, I have an issue :
Validation failed: Pastrie must exist.
UPDATE this is working :
<%= f.association :pastrie, as: :check_boxes, label: 'Pastrie' %>
I still have an issue, the saving params are not good I have :
"fight"=>{"pastrie_id"=>["", "1"]},
I tried a lot of solutions found on stackoverflow but nothing seems to work.
I am using Rails 5.2.3
So, this is my schema :
ActiveRecord::Schema.define(version: 2019_06_06_094318) do
create_table "cookers", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "pastrie"
end
create_table "events", force: :cascade do |t|
t.datetime "date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "fights", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "pastrie_id"
t.integer "event_id"
t.integer "cooker_id"
t.index ["cooker_id"], name: "index_fights_on_cooker_id"
t.index ["event_id"], name: "index_fights_on_event_id"
t.index ["pastrie_id"], name: "index_fights_on_pastrie_id"
end
create_table "pastries", force: :cascade do |t|
t.string "pastrie_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
Here is my models :
class Event < ApplicationRecord
has_many :pastries, through: :fights
has_many :cookers, through: :fights
has_many :fights
end
class Fight < ApplicationRecord
belongs_to :pastrie
belongs_to :event
belongs_to :cooker, optional: true
end
class Pastrie < ApplicationRecord
has_many :fights
has_many :events, through: :fights
end
This is my controller. What I understand is : in order to create a Fight, I need an event_id and a pastrie_id (cooker_id is optional). So first, I create a new event (and so I have an event_id), and next, I need to connect a pastrie_id (existing in my seed) to my fight. But this is not working if I am doing that :
class FightsController < ApplicationController
def new
#fight = Fight.new
#event = Event.find(params[:event_id])
#pastries = Pastrie.all
end
def create
#fight = Fight.new(fight_params)
#event = Event.find(params[:event_id])
#fight.event = Event.find(params[:event_id])
#fight.pastrie_id = params[:fight][:pastrie_id]
#fight.save!
redirect_to root_path
end
def show
#events = Event.all
end
def index
#fights = Fight.all
fights_by_event = []
end
private
def fight_params
params.require(:fight).permit(:event_id, :pastrie_id, :cooker_id)
end
end
And my view when I am creating my "fight" :
<div class=margin-bottom>
<h2 class=text-center> Bonjour Linguini, quelles patisseries veux-tu choisir ?</h2>
</div>
<div class="container margin-bottom">
<%= simple_form_for [#event, #fight] do |f| %>
<% #pastries.each do |pastrie| %>
<%= f.label :pastrie_id do %>
<%= f.check_box :pastrie_id, as: :boolean, checked_value: true, unchecked_value: false %> <span><%= pastrie.pastrie_name%></span>
<% end %></br>
<% end %>
<%= f.submit "Valider", class: "btn btn-primary" %>
<% end %>
</div>
And this is my routes if you need this :
Rails.application.routes.draw do
root to: 'pages#home'
resources :events do
resources :fights, only: [:new, :show, :create]
end
resources :fights, only: [:index]
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
In your form change
<%= f.check_box :pastrie_id, as: :boolean, checked_value: true, unchecked_value: false %> <span><%= pastrie.pastrie_name%></span>
To
<%= f.check_box :pastrie_id, as: :boolean, checked_value: pastrie.id, unchecked_value: nil %> <span><%= pastrie.pastrie_name%></span>
In the first version you submit a param of {pastrie_id: true}, which obviously doesn't relate to a Pastry. The second version should submit the ID of the checked box (although if it belongs to only 1 pastry it might make more sense to make these radio buttons)

Rails searching a has_many relationship

Heyo. Been trying to figure this out but I've been stuck too long and it's just getting painful!
I'm trying to do an Advanced Search form allowing you to search for Users based off settings that are in other models. e.g. Search for a User named Jim, who does Running, and has weight loss as his goal.
I have three models:
User (using Devise)
Sport (many-to-many with user)
Goals (user has_many goals, goal belongs_to user)
So far I have managed to get it working so I can search for things in the User model (such as name) and also for Users Sports through a select box. What I haven't been able to get working is searching for the Users goals and I don't get why.
What I get is "Nobody seems to have these preferences" when searching ONLY for Goals and no other fields.
I have tried using the same code as my Sports but that didn't work (guessing because of the different relationships?)
# searches/show.html.erb
<% if #search.search_users.empty? %>
<p>Nobody seems to have these preferences</p>
<% else %>
<% #search.search_users.each do |u| %>
<tr>
<td><%= u.name %></td>
<% u.sports.each do |s| %>
<td><%= s.name %></td>
<% end %>
<% u.goals.each do |g| %>
<td><%= g.name %></td>
<% end %>
</tr>
<% end %>
I've done associations in the console and when I type for example u.goals I get this (and the opposite when I query what users are associated with a goal):
irb(main):015:0> u.goals
=> #<ActiveRecord::Associations::CollectionProxy [#<Goal id: 1, name: "Weight Loss", user_id: 1>, #<Goal id: 3, name: "Strength", user_id: 1>]>
Here's my current code:
# user.rb
class User < ApplicationRecord
has_and_belongs_to_many :sports
has_many :goals, :foreign_key => :goal_id
end
# sport.rb
class Sport < ApplicationRecord
has_and_belongs_to_many :users
end
# goal.rb
class Goal < ApplicationRecord
belongs_to :user, :foreign_key => :goal_id
end
And my searches stuff:
# search.rb
def search_users
users = User.all
users = users.where("users.name ILIKE ?", "%#{keywords}%") if keywords.present?
users = users.joins(:sports).where("sports.name ILIKE ?", "%#{name}%") if name.present?
users = users.where(goal_id: goal_id) if goal_id.present?
return users
end
# searches/new.html.erb
<%= form_for #search do |s| %>
<div class="form-group">
<%= s.label :keywords %>
<%= s.text_field :keywords %>
</div>
<div class="form-group">
<%= s.label :exercise %>
<%= s.select :name, options_for_select(#s_names), include_blank: true %>
</div>
<div class="form-group">
<%= s.label :goals %>
<%= s.collection_select :goal_id, Goal.order(:name), :id, :name, include_blank: true %>
</div>
<%= s.submit "Search", class: "btn btn-primary" %>
<% end %>
# searches_controller.rb
class SearchesController < ApplicationController
def new
#search = Search.new
#s_names = Sport.uniq.pluck(:name)
#users = User.uniq.pluck(:name)
end
def create
#search = Search.create(search_params)
redirect_to #search
end
def show
#search = Search.find(params[:id])
end
private
def search_params
params.require(:search).permit(:keywords, :name, :goal_id)
end
end
and then my schema for reference:
create_table "goals", force: :cascade do |t|
t.string "name"
t.integer "user_id"
t.index ["user_id"], name: "index_goals_on_user_id", using: :btree
end
create_table "searches", force: :cascade do |t|
t.string "keywords"
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "goal_id"
t.index ["goal_id"], name: "index_searches_on_goal_id", using: :btree
end
create_table "sports", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "sports_users", id: false, force: :cascade do |t|
t.integer "user_id", null: false
t.integer "sport_id", null: false
t.index ["user_id", "sport_id"], name: "index_sports_users_on_user_id_and_sport_id", using: :btree
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
t.integer "movement_id"
t.integer "goal_id"
t.index ["email"], name: "index_users_on_email", unique: true, using: :btree
t.index ["goal_id"], name: "index_users_on_goal_id", using: :btree
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
end
add_foreign_key "goals", "users"
end
Extremely sorry for the huge amount of messy code but I'm just tripping over myself at this point and getting confused.
Thank you greatly in advance.
I would change
users = users.where(goal_id: goal_id) if goal_id.present?
To
users = users.joins(:goals).where(goals: {id: goal_id})
For advanced searching I've recently used approach presented in this article: http://www.justinweiss.com/articles/search-and-filter-rails-models-without-bloating-your-controller/ and I think it's worth reading if you think about expanding search options.
EDIT: full response in comments below
:foreign_key => :goal_id needed to be removed

Getting no method error in Customers#index while trying to access attribute of Order model

How to access customer's name from Customer table onto my Order's action view index. And vice versa.
I am getting this error:
NoMethodError in Customers#index.
Undefined method `order' for #Customer:0x24f4...
class Customer < ActiveRecord::Base
has_many :orders, foreign_key: "customer_id"
end
class Order < ActiveRecord::Base
belongs_to :customer
end
In my migration:
create_table "customers", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "orders", force: :cascade do |t|
t.integer "customer_id"
t.datetime "orderdate"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "orders", ["customer_id"], name: "index_orders_on_customer_id", using: :btree
end
In customer's index.html.erb
<% #customers.each do |customer| %>
<%= customer.name %>
<%= customer.order.orderdate %>
<% end %>
In orders's index.html.erb
<% #orders.each do |order| %>
<%= order.orderdate %>
<%= order.customer.name %>
<% end %>
you have has many relation with customer and order so
In customer's index.html.erb
<% #customers.each do |customer| %>
<%= customer.name %>
<!-- this will display customers all order and fetch first and then show that order's orderdate -->
<%= customer.orders.first.orderdate %>
<% end %>
Try a different approach to setting the relationships between your models in the migration:
t.belongs_to :customer, index: true
See http://guides.rubyonrails.org/association_basics.html#the-belongs-to-association

Resources