I'm trying to pull up the names, contact types, and companies associated with a listing, but I'm struggling with rendering then in ActiveRecord. The tables (Contacts, Company, ContactType, join table ListingContacts and Listing) are as follows:
Contacts.rb
belongs_to :company #trying to access `name` attribute in view
belongs_to :contact_type #trying to access `label` attribute in view
Company.rb
belongs_to :account
has_many :contacts
Accessed through Listing.rb using a joined table, Listing_contacts.rb:
has_many :listing_contacts
has_many :contacts, through: :listing_contacts
As accessed through listings_controller:
def show
#listing = Listing.includes(*LISTING_EAGER_LOADED_ASSOCIATIONS).find(params[:id])
end
private
def listing_params
params.require(:listing).permit(:name, :address, :description, images_attributes: [:file, :unprocessed_image_url], listing_contacts_attributes: [:contact_id])
end
with LISTING_EAGER_LOADED_ASSOCIATIONS as follows (it's a common format in this app, as it gets pretty extensive - I've been avoiding messing with it, and it accesses other models that the ones in the question don't touch:
LISTING_EAGER_LOADED_ASSOCIATIONS = [
rfid_tags: :trackable,
selected_selections: :rfid_tag,
staging_selections: :rfid_tag,
staged_selections: :rfid_tag,
destaged_selections: :rfid_tag,
unstaged_selections: :rfid_tag,
]
As mentioned earlier, I'm struggling with conveying these associations in the view - here's what I'm working with, based off of the template from the rest of the page - accessing listing_contacts is working through the console, but I stall after that:
<%= #listing.listing_contacts.each do |contact| -%>
<div class="row">
<div class="col-sm-6">
<h5 class="subtle-header">Name:</h5>
<h3 class="inline"><%= show(contact.name) %></h3> #working!
</div>
</div>
<div class="col-sm-6">
<h5 class="subtle-header">Address</h5>
<h3 class="inline"><%= show(contact.address) %></h3> #working!
</div>
</div>
<div class="row">
<div class="col-sm-6">
<h5 class="subtle-header">Contact Type:</h5>
<h3 class="inline"><%= show(#reaches through contacts to contact_type.label ) %></h3> #not working
</div>
<div class="col-sm-6">
<h5 class="subtle-header">Company:</h5>
<h3 class="inline"><%= show(#reaches through contacts to company.name) %></h3> #not working
</div>
</div>
<% end %>
structure.sql for reference - uses SQL views rather than schema;
CREATE TABLE public.listing_contacts (
id integer NOT NULL,
listing_id integer,
contact_id integer,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
);
CREATE TABLE public.contacts (
id integer NOT NULL,
company_id integer,
contact_type_id integer,
name character varying,
phone character varying,
email character varying,
address text,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
);
CREATE TABLE public.companies (
id integer NOT NULL,
account_id integer,
name character varying,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
);
CREATE TABLE public.contact_types (
id integer NOT NULL,
label character varying,
show_name boolean DEFAULT false,
show_phone boolean DEFAULT false,
show_address boolean DEFAULT false,
show_email boolean DEFAULT false,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
);
You can do <%= #listing.contacts.each do |contact| -%>
And then in your loop, you should be able to do <%= show(contact.name) %>
Have you tried <%= lc.contacts.name %> instead of <%= show(lc.contacts.name) %> ?
Related
I'm learning to use Rails has_many :through association. Most of what I'm reading provides only how to set up the models, but not how to set up the controller actions. My app is very basic for the purposes of learning this topic. I have a form that lists some vertical industries. When a "vertical" is created, there is an option to select from a list of apps that apply to that vertical (check boxes). When the vertical gets created, the associations between the vertical and the selected apps should be established.
I have 3 models:
class App < ActiveRecord::Base
has_many :solutions
has_many :verticals, through: :solutions
end
class Vertical < ActiveRecord::Base
has_many :solutions
has_many :apps, through: :solutions
end
class Solution < ActiveRecord::Base
belongs_to :app
belongs_to :vertical
end
Here is my form:
<%= simple_form_for(#vertical) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :name %>
<%= f.input :description %>
<%= f.association :apps, as: :check_boxes %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
Here is my verticals_controller create action:
def create
#vertical = Vertical.new(vertical_params)
#solutions = #vertical.apps.build(params[:app])
<respond_to code omitted for brevity>
end
def vertical_params
params.require(:vertical).permit(:name, :description, apps_attributes: [ :name, :description, :developer, :mpp, :partner, :website, :app_id[] ])
end
I am able to create the associations from the rails console this way:
vertical = Vertical.first
app = App.first
vertical.apps << app
But I don't think this is the right way to do it in the controller, nor do I understand how to get the app params that were selected in the form. I'm looking for some basic, clean code examples to follow that adhere to Rails best practices. Also, if you can point me to any recent tutorials that address controller code would be great. Thx.
I was able to get the associations to be created by making the following changes:
In my Create action in my controller:
def create
#vertical = Vertical.new(vertical_params)
#solutions = #vertical.apps.build
<respond_to code omitted for brevity>
end
In my secure parameters I have the following:
def vertical_params
params.require(:vertical).permit(:name, :description, app_ids:[])
end
I am not certain this is done to Rails best practices or not.
I have seen the other links to this similar question but none of them seem to work in my case.
My update action is creating new records in mysql database
My app consists of 3 models
FashionModel
ModelProfile and
Measurement
They are defined as follows:
class FashionModel < ActiveRecord::Base
has_secure_password
has_one :model_profile
has_one :measurement
accepts_nested_attributes_for :model_profile
accepts_nested_attributes_for :measurement
end
class ModelProfile < ActiveRecord::Base
belongs_to :fashion_model
end
class Measurement < ActiveRecord::Base
belongs_to :fashion_model
end
The fashion_model_controller.rb is as follows:
class FashionModelsController < ApplicationController
def new
#fashion_model = FashionModel.new
end
def create
#fashion_model = FashionModel.new(fashion_models_sign_up_params)
if #fashion_model.save
flash[:success] = "Welcome to Meriad"
session[:fashion_model_id] = #fashion_model.id
redirect_to edit_fashion_model_path(#fashion_model)
else
render 'new'
end
end
def edit
#fashion_model = FashionModel.find(params[:id])
#fashion_model.build_model_profile
#fashion_model.build_measurement
end
def update
#fashion_model = FashionModel.find(params[:id])
if #fashion_model.update(fashion_models_edit_params)
flash[:success] = "Saved!"
redirect_to fashion_model_path(#fashion_model)
else
render 'edit'
end
end
def show
#fashion_model = FashionModel.find(params[:id])
end
end
The fashion_model_edit_params are
def fashion_models_edit_params
params.require(:fashion_model).permit(:first_name,
:last_name,
:email,
:password,
model_profile_attributes: [:id,
:location,
:bio, :gender,
:phone_number,
:rate,
:profile_image,
:birthdate],
measurement_attributes: [:id,
:feet,
:inches,
:bust,
:waist,
:hips,
:dress,
:shoes,
:hair,
:eyes])
end
I want something on these lines:
The fashion model signs up to the app through new.html.erb (which is stored in the fashion_models table)
The index.html.erb contains a list of all fashion_models with an option to edit information (update their profile)
The edit.html.erb contains fields from the model_profiles table as well as the measurements table, both of which have the foreign keys to the fashion_models table.
My fashion_models/new.html.erb template is pretty straightforward containing first_name, last_name, email, password.
My fashion_models/edit.html.erb template is something like:
<%= form_for #fashion_model do |f| %>
# fashion_model fields here
<%= f.fields_for :model_profile do |t| %>
# model_profile fields here
<% end %>
<%= f.fields_for :measurement do |t| %>
# measurement fields here
<% end %>
<% end %>
Now, whenever I edit a fashion_model/:id, the model_profile and measurement create a new record in the database rather than updating the existing record. Also, when I am on the Edit Profile page, none of the fields pre populate with the existing data. I have to manually enter all the data again.
First, I thought it was because of the build methods, but when I remove them, the fields_for do not display.
Appreciate any help!
You're probably making a mess with their routes.
It works like this:
form_for calls #fashion_model.new_record?
IF True: It will submit to POST /fashion_model, and create an object.
IF False: It will submit to PUT /fashion_model/:id, and update an object.
As you can see, it just depends if the object already exists in the database or not. So, check again, where you are using the form_for and which object you are passing it.
For questions with the routes, see: http://guides.rubyonrails.org/routing.html
So I finally figured out a way to do this. Hope it helps someone.
My problem was that I had 2 fields_for inside one form_for and I needed to update 2 models with to these forms.
I tired using callbacks like after_action :create_model_profile and after_action :create_measurement. And in both of these methods I tried to save fashion_model_id as the :id of the newly created fashion_model.
But since callbacks are considered as code smells, I wanted a better solution.
Instead of creating a form_for #fashion_models, I wrote a helper method as follows
# app/helpers/form_helper.rb
module FormHelper
def setup_fashion_model(fashion_model)
fashion_model.model_profile ||= ModelProfile.new
fashion_model.measurement ||= Measurement.new
fashion_model
end
end
and in my form_for I used something like
form_for(setup_fashion_model(#fashion_model)) do |f|
...
end
This created a new instance of ModelProfile and Measurement if it did not persist in the database.
It then of course solved the problem of pre populating the fields which contained data.
This was it. Simple solution, but I'd been there scratching my head.
Hope it helps.
I would like with Rails 4 to do a simple association beetween 3 models but i can't succeed to do that.
First model:Reader,Second model:Comments Third model : Books
The relation beetween the 3 models are:
class Book < ActiveRecord::Base
has_many :comments
class Comment < ActiveRecord::Base
belongs_to :book
belongs_to :reader
class Reader < ActiveRecord::Base
has_many :comments
has_many :books, through: :comments
I have no problem for the relation Book has_many :comments but i have some problem with the relation Reader has_many :comments.
Field from CommentsTable : username, message and reader_id (for the has_many association).
Field from ReaderTable : name , email.
I have a file _form.html.erb in view:
<%= form_for [#book, #comment] do |f| %>
<%= f.hidden_field :book_id %>
<%= f.text_field :reader_id, :placeholder => "reader id" %>
<br />
<%= f.text_area :message %>
<br />
<%= f.submit %>
<% end %>
And an other file _comment.html.erb:
<% if comment.reader_id != nil %>
<li><strong><%= link_to comment.reader.name, comment.reader %></strong></li>
<p><%= comment.message %></p>
<% end %>
Like you see i have a form with reader_id and message. When the user will enter this 2 informations, it will appears the reader.name (thanks to reader_id) and the comment.message.
But i don't want that the user enter the integer "reader_id" (it has no sense) but directly the string "reader.name".
How can i do that, knowing that after i will do a link_to on the reader.name to comment.reader ?
Please help me.
Thank you.
I'm guessing the reader is the user. If it is, then you'll probably want to have a whole authentication system (maybe think about Devise) and just assign the comment to the current user within the controller. You'll also want to assign the book in the controller.
EDIT:
Devise will make available a current_user method in your controller. So your create method in your comments controller should look like this:
def create
comment = current_user.comments.new(comment_params)
end
You don't need to assign the reader.name to comments. That's what the association is for. You can do comment.reader.name, or better yet, use delegate (look that up) and do comment.reader_name.
I have two models - book.rb -
class Book < ActiveRecord::Base
belongs_to :user
end
and user.rb -
class User < ActiveRecord::Base
has_many :books
end
books_controller.rb includes
def index
#books = Book.all
end
And in /books/index.html.erb I have
<%= #books.each do |books| %>
<%= book.title %>
<%= User.find(book.user_id).name %>
.....
This all works fine - but should I have more of that logic in the controller? And if so, how do I pass the book id from the block into the controller in order to pass back the user name?
There's no need to do so. Since you already have the belongs_to relation you can write your view as:
<%= #books.each do |books %>
<%= book.title %>
<%= book.user.name %>
.....
An even faster way to do this is:
def index
#books = Book.includes(:user)
end
This will execute 2 SQL queries, one to get all the Books and one to get all associated Users. This is much cheaper in terms of number of queries, because if you have 10 Books you'd have to do 11 queries if you just use Book.all (one to get all the Books and one for each Book to get its User).
The view can stay the same:
<%= #books.each do |book| %>
<%= book.title %>
<%= book.user.name %>
.....
I am trying to make a booking system for restaurants in Ruby on Rails. I have three tables: users, tables and reservations. 'users' has the columns 'id', 'name', 'email', 'phone' and 'totalPersons'. 'table' has the columns 'id' and 'seats'. 'reservations' has the columns 'id', 'user_id', 'table_id' and 'begin_datetime'.
The models look like this:
class User < ActiveRecord::Base
has_many :reservations
has_many :tables, :through => :reservations
end
class Table < ActiveRecord::Base
has_many :reservations
has_many :users, :through => :reservations
end
class Reservation < ActiveRecord::Base
belongs_to :table
belongs_to :user
end
I was able to join the tables and reservations tables but I was unable to join the three together.
I am trying to show a full reservation with name and at what table the user is.
<table>
<thead>
<tr>
<th>Reserverings ID</th>
<th>Tafel nummer</th>
<th>Seats</th>
<th>User</th>
<th>Begint om</th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<% #tables.each do |table| %>
<tr>
<td><%= reservation.id %></td>
<td><%= reservation.table_id %></td>
<td><%= table.seats %></td>
<td><%= user.name %></td>
<td><%= reservation.begin_datetime %></td>
<td><%= link_to 'Show', table %></td>
<td><%= link_to 'Edit', edit_table_path(table) %></td>
<td><%= link_to 'Destroy', table, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
In the controller my join looks like
#reservations = reservation.joins(:tables, :users)
Can you help me?
Rails version 4.0
EDIT:
On http://guides.rubyonrails.org/ I saw
Post.joins(:comments => :guest)
The SQL that is produced is:
SELECT posts.* FROM posts
INNER JOIN comments ON comments.post_id = posts.id
INNER JOIN guests ON guests.comment_id = comments.id
I guess my SQL code for the all reservations with users and the table they booked would look like
SELECT * FROM reservations, users, tables
INNER JOIN reservations ON reservations.user_id = users.id
INNER JOIN reservations ON reservations.table_id = tables.id
Maybe that will clarify things for you. So now I need to know how that is produced in ruby on rails.
Rails allows You to write custom join conditions inside a string, like this:
Reservation.joins('INNER JOIN "tables" ON "tables"."id" = "reservations"."table_id" INNER JOIN "users" ON "users"."id" = "reservations"."user_id"')
or much cleaner just specify several join clauses:
Reservation.joins(:user).joins(:table)
To extract data you need just apply a few custom select clauses, like this:
Reservation.joins(:user).joins(:table).select("tables.seats AS table_seats")
Same as with join, you can apply several of those. Each of the returned objects in collection will have a same name method as You defined the column name after "AS" (in this case it will be . There is a catch though - all these will be returned as strings because AR can't really guess what those are.
You should use "includes" instead of "joins". Also when you're looping through #tables to print the reservations, reservation and user aren't defined. You need to at least say:
#tables.each do |table|
reservation = table.reservation
user = reservation.user
...
end