view one has_many record in rails - ruby-on-rails

Restaurant Load (1.5ms) SELECT * FROM "restaurants" INNER JOIN "restaurant_branches" ON "restaurant_branches"."restaurant_id" = "restaurants"."restaurant_id"
+----------+---------+---------+---------+----------+---------+----------+---------+---------+---------+---------+---------+---------+---------+---------+-------+
| resta... | res_... | res_... | crea... | updat... | user_id | resta... | addr... | addr... | addr... | addr... | addr... | addr... | numb... | numb... | email |
+----------+---------+---------+---------+----------+---------+----------+---------+---------+---------+---------+---------+---------+---------+---------+-------+
| 27 | DOGG... | WE S... | 2014... | 2014-... | 4 | 28 | 405 ... | | CHICAGO | IL | 60666 | USA | | | |
| 27 | DOGG... | WE S... | 2014... | 2014-... | 4 | 29 | 111 ... | | CHICAGO | IL | 60661 | USA | | | |
+----------+---------+---------+---------+----------+---------+----------+---------+---------+---------+---------+---------+---------+---------+---------+-------+
As you can see I have a model restaurants and restaurant_branches. How would I approach if I want to open a restaurant record and ONLY ONE branch? Because right now, my form displays all the branches. How will I open this on my index.html.erb file so that my show.html.erb file will only be one restaurant_branches? Thank you for any help.
<h1>
<strong>Restaurant Name:</strong>
<%= #restaurant.res_name %>
</h1>
<p>
<strong>Restaurant Description:</strong>
<%= #restaurant.res_description %>
</p>
<ol class="restaurant_branch_fields">
<% #restaurant.restaurant_branches.each do |f| %>
<li>
<p><strong>Address</strong><%= f.set_address %></p>
<p><strong>Contact Info</strong><%= f.set_contact_info %></p>
</li>
<% end %>
</ol>
<p>
<%= link_to "Edit", edit_restaurant_path(#restaurant) %>
<%= link_to "Destroy", #restaurant, :confirm => 'Are you sure?', :method => :delete %>
<%= link_to "View All", restaurants_path %>
</p>
class RestaurantsController < ApplicationController
before_filter :authenticate_user!, except: [:index, :show]
def index
#restaurant = Restaurant.get_list
end
def show
##restaurant = Restaurant.select('*').joins(:restaurant_branches).find_by(:restaurant_id => params[:id], :restaurant_branches => {:restaurant_branch_id => params[:id]})
##restaurant = Restaurant.joins(:restaurant_branches).find_by(:restaurant_id => params[:id], :restaurant_branches => {:restaurant_branch_id => params[:restaurant_branch_id]})
#restaurant = Restaurant.find(params[:id])
end
def new
#restaurant = Restaurant.new
end
def create
if user_signed_in?
#restaurant = Restaurant.new(restaurant_params)
if #restaurant.save
redirect_to #restaurant
else
render 'new'
end
else
redirect_to new_owner_session_path
end
end
def edit
#restaurant = Restaurant.find(params[:id])
end
def update
#restaurant = Restaurant.find(params[:id])
if #restaurant.update_attributes(restaurant_params)
flash.now[:notice] = "You have successfully updated #{#restaurant.res_name.titleize}."
redirect_to #restaurant
else
render 'edit'
end
end
def destroy
#restaurant = Restaurant.find(params[:id])
#restaurant.destroy
flash.now[:notice] = "#{#restaurant.res_name.titleize} has been deleted!"
redirect_to restaurants_url
end
private
def restaurant_params
params.require(:restaurant).permit(:res_name, :res_description, restaurant_branches_attributes: [ :id, :address_line1, :address_line2, :address_line3, :address_line4, :address_line5, :address_line6, :number_phone, :number_fax, :email, :_destroy, pictures_attributes: [ :id, :name, :image, :_destroy] ] ).merge(user_id: current_user.id)
end
end
Rails.application.routes.draw do
devise_for :users, path_names: {sign_in: "login", sign_out: "logout"}
resources :restaurants do
resources :restaurant_branches, shallow: true
end
root to: 'restaurants#index'
end
Any help is appreciated.

Your page displays all the branches because of this iteration:
<% #restaurant.restaurant_branches.each do |f| %>
#restaurant.restaurant_branches contains all the branches, and .each will loop through them.
If you need to access only the first of those branches you can use this code in your controller's view action
#branch = #restaurant.restaurant_branches.first
Then in your view:
<p><strong>Address</strong><%= #branch.address %></p>
<p><strong>Contact Info</strong><%= #branch.contact_info %></p>
If you need the last branch you could have use in your controller #branch = #restaurant.restaurant_branches.last
If you need a specific id of a branch you could have use in your controller #branch = #restaurant.restaurant_branches.find(123)

If you really want that each restaurant can have only one branch then I suggest you go with has_one relationship instead of has_many.
class Restaurant < ActiveRecord::Base
has_one :restaurant_branch
end
class RestaurantBranch < ActiveRecord::Base
belongs_to :restaurant
end
Of course, you have to update your controller and views a bit as you will reference the restaurant_branch like this
#restaurant.restaurant_branch #without 'es'
More information: http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_one
Regards

Related

nested attributes with has many through

I am getting unpermitted params when I am passing values from UI. The association is many to many between models.
class User < ApplicationRecord
has_many :user_posts
has_many :posts, through: :user_posts
end
class Post < ApplicationRecord
has_many :user_posts
has_many :users, through: :user_posts
accepts_nested_attributes_for :user_posts
end
class UserPost < ApplicationRecord
belongs_to :user
belongs_to :post
accepts_nested_attributes_for :user
end
posts/_form.html.erb
<%= form_with(model: post) do |form| %>
<% if post.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% post.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%#= form.label :name %>
<%#= form.text_field :name %>
</div>
<div class="field">
<%= form.fields_for :user_posts do |f| %>
<%= f.collection_select :user_id, User.all, :id, :username, {include_blank: false, include_hidden: false }, {:multiple => true, :class=>""} %>
<% end %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
class PostsController < ApplicationController
before_action :set_post, only: %i[ show edit update destroy ]
# GET /posts or /posts.json
def index
#posts = Post.all
end
# GET /posts/1 or /posts/1.json
def show
end
# GET /posts/new
def new
#post = Post.new
#post.user_posts.build
end
# GET /posts/1/edit
def edit
end
# POST /posts or /posts.json
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: "Post was successfully created." }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1 or /posts/1.json
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: "Post was successfully updated." }
format.json { render :show, status: :ok, location: #post }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1 or /posts/1.json
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: "Post was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
# Only allow a list of trusted parameters through.
def post_params
params.require(:post).permit(:username, user_posts_attributes: [:user_id])
end
end
When I run the below line it gives unpermitted params
#post = Post.new(post_params)
Unpermitted parameter: :user_id
Schema
\d users
Table "public.users"
Column | Type | Collation | Nullable | Default
------------+--------------------------------+-----------+----------+-----------------------------------
id | bigint | | not null | nextval('users_id_seq'::regclass)
username | character varying | | |
created_at | timestamp(6) without time zone | | not null |
updated_at | timestamp(6) without time zone | | not null |
\d posts
Table "public.posts"
Column | Type | Collation | Nullable | Default
------------+--------------------------------+-----------+----------+-----------------------------------
id | bigint | | not null | nextval('posts_id_seq'::regclass)
name | character varying | | |
created_at | timestamp(6) without time zone | | not null |
updated_at | timestamp(6) without time zone | | not null |
\d user_posts
Table "public.user_posts"
Column | Type | Collation | Nullable | Default
------------+--------------------------------+-----------+----------+----------------------------------------
id | bigint | | not null | nextval('user_posts_id_seq'::regclass)
user_id | bigint | | not null |
post_id | bigint | | not null |
created_at | timestamp(6) without time zone | | not null |
updated_at | timestamp(6) without time zone | | not null |
Am I missing something crucial? Any help is appreciated
rails - 6.1
ruby - 3.0
You do not need or even want to use nested attributes here.
If you just want to associate existing records you just need a select for the user_ids attribute:
<%= form_with(model: post) do |form| %>
...
<div class="field">
<%= form.label :user_ids, 'Select the users' %>
<%= f.collection_select :user_ids, User.all, :id, :username, { include_blank: false, include_hidden: false }, { multiple: true, class: ""} %>
</div>
...
<% end %>
These setters and getters are created by has_many :users, through: :user_posts.
And to whitelist the post[user_ids] parameter as an array:
def post_params
params.require(:post).permit(
:foo,
:bar,
:baz,
user_ids: []
)
end
As you can see you don't need to explitly deal with the join table either as user_ids= will do all the work for you.
Creating join table entities with nested attributes is only necissary if the join table actually contains additional data that must be input by the user.

How can i make this specific relationship?

School has one or many Teachers and Teacher has one or many Subjects
The same Teacher can teach in different schools and different subjects,eg:
Dan teaches English and Math in the School A and Physics in the School B
I tried use a has many through between these 3 model, but i don't know how can i add many schools and many subjects in a specific Teacher
Here what i tried
class Teacher < ApplicationRecord
has_many :school_teacher_subjects
has_many :schools, through: :school_teacher_subjects
has_many :subjects, through: :school_teacher_subjects
end
class School < ApplicationRecord
has_many :school_teacher_subjects
has_many :teachers, through: :school_teacher_subjects
has_many :subjects, through: :school_teacher_subjects
end
class Subject < ApplicationRecord
has_many :school_teacher_subjects
has_many :teachers, through: :school_teacher_subjects
has_many :schools, through: :school_teacher_subjects
end
class SchoolTeacherSubject < ApplicationRecord
belongs_to :teacher
belongs_to :school
belongs_to :subject
end
What i want is that inside the Teacher New/Edit form, i can save one or many schools and one or many subjects in the database at same time in this way:
+----+------------+-----------+------------+
| id | teacher_id | school_id | subject_id |
+----+------------+-----------+------------+
| 1 | 2 | 2 | 4 |
| 2 | 2 | 2 | 1 |
| 3 | 1 | 3 | 2 |
| 4 | 1 | 3 | 6 |
+----+------------+-----------+------------+
But all I can do is:
+----+------------+-----------+------------+
| id | teacher_id | school_id | subject_id |
+----+------------+-----------+------------+
| 72 | 8 | 2 | 2 |
| 74 | 2 | | 2 |
| 75 | 2 | | 6 |
| 76 | 1 | 3 | |
| 77 | 1 | | 2 |
| 78 | 1 | | 6 |
+----+------------+-----------+------------+
Here what i'm doing:
my controller and form
def new
#teacher = Teacher.new
#schools = School.all.order(name: :asc)
#subjects = Subject.all.order(name: :asc)
end
def edit
#teacher = Teacher.find(params[:id])
#schools = School.all.order(name: :asc)
#subjects = Subject.all.order(name: :asc)
end
def create
#teacher = Teacher.new(teacher_params)
respond_to do |format|
if #teacher.save
format.html { redirect_to admin_teacher_index_path, notice: 'Escola criada com sucesso.' }
else
format.html { render :new }
end
end
end
def update
#teacher = Teacher.find(params[:id])
respond_to do |format|
if #teacher.update(teacher_params)
format.html { redirect_to admin_teacher_index_path, notice: 'Escola editada com sucesso.' }
else
format.html { render :edit }
end
end
end
private
def teacher_params
params.require(:teacher).permit(:full_name, :genre, :status, school_ids: [], subject_ids: [])
end
FORM.HTML.ERB
<div class="row mb-3">
<div class="col">
<%= f.label :school_ids, 'Escolas(s)' %>
<%= f.collection_select(:school_ids, #schools, :id, :name, {:include_blank => "Selecione uma ou mais"}, {:class => "multiple-select2 custom-select", multiple: true}) %>
</div>
</div>
<div class="row mb-3">
<div class="col">
<%= f.label :subject_ids, 'Disciplina(s)' %>
<%= f.collection_select(:subject_ids, #subjects, :id, :name, {:include_blank => "Selecione uma ou mais"}, {:class => "multiple-select2 custom-select", multiple: true}) %>
</div>
</div>
One of the problems you have is that a Teacher can teach many subjects at many schools, but in your form you are selecting schools and subjects independently. Schools and Subjects must be selected together. I don't think you can do it with two multiple selects and passing two arrays (school_ids and subject_ids). In fact, a teacher could teach a subject in two schools and this cannot be implemented with your form. You need a more complex form. I would do it in a form where you could dynamically add new lines (subjects and schools related) using cocoon gem.
Models
class Teacher < ApplicationRecord
has_many :school_teacher_subjects
has_many :schools, through: :school_teacher_subjects
has_many :subjects, through: :school_teacher_subjects
# NEW
accept_nested_attributes_for :school_teacher_subjects,
reject_if: :all_blank, allow_destroy: true
end
class School < ApplicationRecord
has_many :school_teacher_subjects
has_many :teachers, through: :school_teacher_subjects
has_many :subjects, through: :school_teacher_subjects
end
class Subject < ApplicationRecord
has_many :school_teacher_subjects
has_many :teachers, through: :school_teacher_subjects
has_many :schools, through: :school_teacher_subjects
end
class SchoolTeacherSubject < ApplicationRecord
belongs_to :teacher
belongs_to :school
belongs_to :subject
end
Controller
private
def teacher_params
# CHANGED
params.require(:teacher).permit(:full_name, :genre, :status,
:school_teacher_subjects_attributes => [ :school_id, :subject_id, :id, :_destroy ])
end
View (main form):
<Teacher fields (fullname, genre, status, etc)>
<.............................................>
<div class="row mb-3">
<div class="col">
<%= f.simple_fields_for :school_teacher_subjects do |sts| %>
<%= render 'sts_fields', f: sts %>
<% end %>
<%= link_to_add_association 'Add new class', f,
:school_teacher_subjects,
:partial => 'sts_fields',
:force_non_association_create => true,
:data => {"association-insertion-method" => "before", "association-insertion-node" => 'this'}
%>
</div>
</div>
View (partial form for subject and school) sts_fields.html.erb :
<div class="nested-fields">
<div class="row">
<div class="col-xs-2">
<%= link_to_remove_association 'Remove', f %>
</div>
<div class="col-xs-5">
<%= f.collection_select :subject_id, #subjects, :id, :name %>
</div>
<div class="col-xs-5">
<%= f.collection_select :school_id, #schools, :id, :name %>
</div>
</div>
</div>

Rails: Update data via link_to (without view)

I'd like to update the data without form.
Although there are similar questions, they don't work for me.
Update field through link_to in Rails
link_to update (without form)
What I'd like to do is to delete data as followings;
For example, delete name and address when delete link is clicked.
id | name | address | ...
12 | aaa | bbb | ...
to
id | name | address | ...
12 | | | ...
Although I tried some, error was displayed.(such as ActionController::RoutingError)
schema
create_table "rooms", force: :cascade do |t|
t.string "name"
t.text "address"
...
model
schedule.rb
class Schedule < ActiveRecord::Base
belongs_to :user
has_many :rooms, inverse_of: :schedule, dependent: :destroy
accepts_nested_attributes_for :rooms, allow_destroy: true
...
room.rb
class Room < ActiveRecord::Base
belongs_to :schedule, inverse_of: :rooms
default_scope -> { order(day: :asc) }
...
view
I'd like to add the link in schedules/_schedule.html.erb
It has the followings;
...
<% schedule.rooms.each do |room| %>
...
<% if room.name.present? %>
<%= link_to "Delete", rooms_path(room, room:{address: nil}) , method: :put, data: { confirm: "You sure?"} %>
...
I also tried another code as below, but they don't work.
<%= link_to "Delete", rooms_path(room:{address: nil}) , method: :put, data: { confirm: "You sure?"} %>
<%= link_to "Delete", rooms_path(room) , method: :put, params: {address: nil}, data: { confirm: "You sure?"} %>
and so on.
routes.rb
...
resources :schedules do
resources :events
end
resources :schedules do
resources :rooms
end
resources :rooms do
resources :events
end
...
rooms_controller.rb
...
def edit
#nothing
end
def update
#room = Room.find(params[:id])
if #room.update(room_params)
flash[:success] = "Room updated!"
redirect_to itinerary_path(#room.itinerary) || current_user
else
render 'edit'
end
end
def destroy
#room = Room.find(params[:id])
#room.destroy
flash[:success] = "Room deleted"
redirect_to schedule_path(#room.schedule) || current_user
end
private
def room_params
params.require(:room).permit(
:id, :_destroy, :name, :address, :schedule_id, :day)
end
...
It would be appreciated if you could give me any suggestion.
You need to change rooms_path to room_path.
Without params:
<%= link_to "Delete", room_path(room) , method: :put, data: { confirm: "You sure?"} %>
With params:
<%= link_to "Delete", room_path(room, room: {name: nil, address: nil}) , method: :put, data: { confirm: "You sure?"} %>

Rails conditional statement

I don't know my heading title good or bed because I'm new in ruby on rails, I'm troubling one conditional issue like below example.
I have three table like user, post & saved_post
user table
user_id | user_name |
---------------------
1 | ABC |
---------------------
2 | efg |
post table
post_id | title |
--------------------
1 | XYZ |
--------------------
2 | xyz |
saved_post table
id | user_id | post_id |
-----------------------------
1 | 1 | 2 |
View
<% #post.each do |p| %>
<%= p.post_title %>
<%= form_for :create, url: home_path(#save), action: :create, method: :post do |f| %>
<%= f.hidden_field :post_id, :value => p.post_id %>
<button class="btn btn-default btn-save" type="submit">Save</button>
<% end %>
<% end %>
If user_id 1 save post_id 2 in the saved_post table then show this post
saved only for user_id 1, otherwise show save.
How can I reach this solution?
You can try has_many :through Association which Active Record have.
Refer this link.
class Users < ActiveRecord::Base
has_many :posts
has_many :saved_posts, through: :posts
end
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :saved_post
end
class SavedPost < ActiveRecord::Base
has_many :posts
has_many :users, through: :posts
end
More details regarding this you will find in above link.
Rails is all about convention over configuration. Meaning if you write your Rails applications following Rails conventions, you'll have huge benefit from it.
Your tables should be named in plural form. Tables and Posts. What I understood from your comments is that user has many posts and posts can belong to many users. This is typical has_and_belongs_to_many association.
If you rename your saved_post table as posts_users (both table names in alphabetical order) Rails will know how to deal with these tables automatically.
Database should look like this:
I made a sample application with your two user and methods to add posts to either of those users.
# routes.rb
root 'posts#index'
resources :posts
# posts_controller.rb
class PostsController < ApplicationController
before_action :set_user
def index
#posts = #current_user.posts
end
def new
#post = #current_user.posts.build(title: 'totally new title')
end
def edit
#post = Post.find(params[:id])
end
def create
#post = Post.new(post_params)
#post.save
redirect_to posts_path
end
def update
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to posts_path
else
render 'edit'
end
end
# --------------------------------------------------------------------------
private
# --------------------------------------------------------------------------
# Sets the user based on the params value
def set_user
if params[:user_id]
#current_user = User.find(params[:user_id])
else
# If not found, sets the first from the Users table
#current_user = User.first
end
end
def post_params
params.require(:post).permit(:title, {user_ids: []})
end
end
index
# index.html.erb
<h3>Users</h3>
<% User.all.each do |user| %>
<%= link_to user.username, posts_path(user_id: user.id) %> <br />
<% end %>
<h3>Posts for the user: <%= #current_user.username %></h3>
<p>
<% #posts.each do |post| %>
<%= link_to post.title, edit_post_path(post) %> <br />
<% end %>
</p>
<p>
<%= link_to 'Create new post', new_post_path %>
</p>
In edit you can choose which users are attached to this post:
# edit.html.erb
<%= render :partial => "post_form", locals: {button_name: 'update'} %>
# _post_form.html.erb
<h3>Post <%= #post.title %> for the user <%= #current_user.username %></h3>
<%= form_for #post do |f| %>
<%= f.text_field :title %> <br />
<p>
<label>
<%= f.collection_check_boxes(:user_ids, User.all, :id, :username) %>
<label><br />
</p>
<p>
<%= f.submit button_name, class: "btn btn-default btn-save" %>
</p>
<% end %>
In post.rb and user.rb files you need to specify association between these two classes. And when you have posts_users table named correctly, Rails finds it automatically.
# post.rb
class Post < ActiveRecord::Base
has_and_belongs_to_many :users
end
# user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :posts
end
And if you create a new title, if will use the same form:
# new.html.erb
<%= render :partial => "post_form", locals: {button_name: 'create'} %>

Ruby on Rails, devise: How can i add the id of users table to posts.user_id column?

new.html.erb
<h3>Add post</h3>
<%= form_tag :controller=>'posts', :action=>'create' do %>
<%= label :q, :Title %>
<%= text_field :data, :title, :class => :addtextsize %><br/>
<%= label :q, :Content %>
<%= text_area :data, :content, :rows=>10 , :class => :addtextarea %><br/>
<%= label :q, :Category %>
<%= select :data, :category_id, #categories_select %><br/>
<%= label :q, :Tags %>
<%= text_field :data, :tags, :class => :addtextsize %><br/>
<%= label :q, :Submit %>
<%= submit_tag "Add Post" %>
<% end %>
create action of PostController.rb
def create
#categories_select = Category.all.collect {|c| [ c.category_name, c.id ] }
#addpost = Post.new params[:data]
if #addpost.save
flash[:notice] = "Post has been saved successfully."
redirect_to posts_path
else
flash[:notice] = "Post can not be saved, please enter information."
render :new
#redirect_to new_post_path
end
end
Getting users.id i need to insert it into posts table. How can i do it ?
posts table
Table "public.posts"
Column | Type | Modifiers
-------------+------------------------+----------------------------------------------------
id | integer | not null default nextval('posts_id_seq'::regclass)
title | character varying(100) | not null
content | character varying(500) | not null
created_at | date |
updated_at | date |
tags | character varying(55) | not null default '50'::character varying
category_id | integer | not null default 1
user_id | integer |
Indexes:
"posts_pkey" PRIMARY KEY, btree (id)
Devise provides a helper method called current_user which you can use to get the authenticated user. So, you could put a hidden text input in your form, like this:
<%= hidden_field_tag :user_id, current_user.id %>
This will allow it to be passed into your collection of params.
The other option would be to manually add it in the create method:
def create
#categories_select = Category.all.collect {|c| [ c.category_name, c.id ] }
#addpost = Post.new params[:data]
#addpost.user_id = current_user.id
if #addpost.save
flash[:notice] = "Post has been saved successfully."
redirect_to posts_path
else
flash[:notice] = "Post can not be saved, please enter information."
render :new
#redirect_to new_post_path
end
end

Resources