accessing attribute from other model in my search form - ruby-on-rails

I'm a newbie and struggling a little with this:
I have two models: User & Job, the relationship is as follows:
class User < ApplicationRecord
has_many :jobs, dependent: :destroy
end
class Job < ApplicationRecord
belongs_to :user
end
In my jobs index view I have a search form where I want to locate jobs by address (ie: look for the User's address),
<%= form_tag(jobs_path, method: :get) do %>
<%= text_field_tag :address, params[:address] %>
<%= submit_tag 'Search', class:'btn btn-primary' %>
<% end %>
which is one of the user's attribute:
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.string "activation_digest"
t.boolean "activated", default: false
t.datetime "activated_at"
t.string "reset_digest"
t.datetime "reset_sent_at"
t.float "latitude"
t.float "longitude"
***t.string "address"***
t.string "phone"
t.index ["email"], name: "index_users_on_email", unique: true
end
In my JobsController, how do I point to the User's attributes? ie: User's address ? This is the index function I have for now:
def index
#jobs = if params[:address]
Job.where('address LIKE ?', "%#{params[:address]}%").paginate(page: params[:page], per_page: 20)
else
#jobs = Job.paginate(:page => params[:page], per_page: 4)
end
end
But obviously I'm not getting any thing when doing a search.
Thank you for your guiding advice in advance. Rodolphe

You should do something like:
Job.joins(:user).where(users: { address: address }) }

Related

How to use a form to add to both joined tables in Rails

I have a joined table but looking for a way to input the information from a form into both tables, or have it work in general:
My schema:
create_table "categories", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "categories_listings", id: false, force: :cascade do |t|
t.integer "category_id", null: false
t.integer "listing_id", null: false
end
create_table "listings", force: :cascade do |t|
t.string "name"
t.text "description"
t.decimal "price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "image"
t.integer "user_id"
end
Models:
class Category < ApplicationRecord
has_and_belongs_to_many :listings
end
Listing < ApplicationRecord
belongs_to :category, required: false
belongs_to :categories_listings, required: false
end
Views
<%= form_with(model: listing, local: true) do |form| %>
...
<div class="space">
<%= form.select :category_ids, options_from_collection_for_select(Category.all, :id, :name), :prompt => "Select a Category", :multiple => true %>
</div>
...
Before i joined the tables, I had it working with a categories element (i believe thats the right term) within the listing tables that was attached to a categories table... You can see my previous post on SO where I was suggested to do this: Allowing multiple records in category to submit to listing
When i click submit, nothing enters into the categories_listings tables. Suggestions on how I make this happen?
The associations in your Listing model are wrong. It should be just
Listing < ApplicationRecord
has_and_belongs_to_many :categories
end
I suggest you to read has_and_belongs_to_many

Rails: show name instead of Id in a show page

I have two tables, accounts and items and I would like to show the buisness_name instead of the idfrom the accounts table on the view/items/show.html.erb page.
Currently I have no associations between the models, but I have the account_id column in the items table.
create_table "accounts", force: :cascade do |t|
t.string "buisness_name"
t.string "web_site"
t.string "phone_number"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "items", force: :cascade do |t|
t.string "title"
t.text "description"
t.string "image"
t.decimal "price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "category_id"
t.json "attachments"
t.integer "account_id"
end
I'm getting the account_id via this: <%= #item.account_id %>, but I would like to show the buisness_name instead.
Try something like this
class Item < ActiveRecord::Base
belongs_to :account
end
Into the view
<% if #item.account %>
<%= #item.account.buisness_name %>
<% end %>
Should be business_name, as #Sergio Tulentsev already told you
I updated my answer because I noticed from the table that the account_id has not a not null constraint
If you have
<%= #item.account_id %>
The horrible way to get the account would be
<%= Account.find_by(id: #item.account_id).try(:buisness_name) %>
Much smarter would be
class Item
belongs_to :account
delegate :buisness_name, to: :account, prefix: true, allow_nil: true
And then in the view...
<%= #item.account_buisness_name %>

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

Ruby on Rails Params set recipient and sender with users_id

Have a recipient and sender, both of the same class(Message) for a messaging system in rails. Want to set the params for both i.e. if user creates a message sender by default is the user_id and recipient will be the contact selected from the users contact list.
Currently the database is only receiving a user_id to the recipient_id column which is wrong and should be to sender_id column. Sender_id receives nothing.
After reading, some say not to amend the params as this is bad practice. So set a hidden field in the message view (like the body and title) yet this isn't pushing in to the database.
Two questions, is this process an appropriate rails practice? (ask this as new to rails) If not: can you advise another path or direction? If so: any ideas/thoughts why this isn't saving in to the database?
user model
class User < ActiveRecord::Base
has_many :messages, class_name: "Message", foreign_key: "recipient_id"
has_many :sent_messages, class_name: "Message", foreign_key: "sender_id"
has_many :contacts, dependent: :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates_presence_of :firstname, allow_blank: false
validates_presence_of :surname, allow_blank: false
end
message model
class Message < ActiveRecord::Base
belongs_to :sender, class_name: "User", foreign_key: "sender_id"
belongs_to :recipient, class_name: "User", foreign_key: "recipient_id"
validates_presence_of :body, :title
end
Messages controller
class MessagesController < ApplicationController
before_action :message, only: [:show]
before_action :authenticate_user!
def index
#messages = current_user.messages
end
def new
#message = Message.new
end
def create
current_user.messages.create(message_params)
redirect_to '/messages'
end
def show
end
private
def message_params
params.require(:message).permit(:title, :body, :sender_id, :recipient_id)
end
def message
#message = Message.find(params[:id])
end
end
message/new view
<%= form_for #message do |f| %>
<%= hidden_field_tag :sender_id, current_user.id %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<%= f.submit %>
<% end %>
schema
ActiveRecord::Schema.define(version: 20160517131719) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "contacts", force: :cascade do |t|
t.string "firstname"
t.string "surname"
t.string "email"
t.integer "phone"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.integer "user_id"
end
add_index "contacts", ["user_id"], name: "index_contacts_on_user_id", using: :btree
create_table "messages", force: :cascade do |t|
t.string "title"
t.text "body"
t.integer "sender_id"
t.integer "recipient_id"
t.datetime "created_at"
t.datetime "updated_at"
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 "firstname"
t.string "surname"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
add_foreign_key "contacts", "users"
end
Try changing your form to this:
<%= form_for #message do |f| %>
<%= f.hidden_field :sender_id, value: current_user.id %>
<%= f.text_field :title %>
<%= f.text_field :body %>
<%= f.submit %>
<% end %>
Currently the database is only receiving a user_id to the recipient_id
column which is wrong and should be to sender_id column.
In your create action, you have current_user.messages.create(message_params). This creates a message record in the DB with the foreign key's(i.e, recipient_id in your case) value with the parent's(user) id. This is the reason, the recipient_id gets the value of user's id.
Sender_id receives nothing.
This is because the hidden_field set for sender_id is not wrapped with the form builder instance. You need to change
<%= hidden_field_tag :sender_id, current_user.id %>
to
<%= f.hidden_field :sender_id, current_user.id %>

rails - form_for - how to have my 'Property' form update the 'Ticket' model?

I have 3 tables- OwnerofProperty , Property and Ticket. I want to make a form using form_for to represent property booking; can I make a form to retrieve data from Property where the submit button saves the data in the Ticket table? I am asking because I have no idea if that can be possible or how to make it.
Note: I have only created the relations :
OwnerofProperty one-to-many Property
Property one-to-one Ticket
I need this form just to make a user able to see the avaliable properties and can book only one , how to make this form ?
Schema.rb for the three models :
create_table "owners", :force => true do |t|
t.string "f_name"
t.string "l_name"
t.string "address"
t.string "tel_no"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "properties", :force => true do |t|
t.string "p_street"
t.string "p_city"
t.string "postcode"
t.string "property_type"
t.integer "rooms"
t.integer "rent"
t.integer "owner_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "properties", ["owner_id"], :name => "index_properties_on_owner_id"
create_table "tickets", :force => true do |t|
t.string "city"
t.string "street"
t.string "ticket_type"
t.integer "rooms"
t.integer "rent"
t.integer "property_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "tickets", ["property_id"], :name => "index_tickets_on_property_id"
Yes, it is possible.
let's see ticket_controller.rb
def new
#property = Property.find 20 #20 is property id
#properties = Property.all
##ticket = Ticket.new
end
now in view (where you want to create form):
<%= form_for #ticket do |f| %>
<%= f.select :property_id, #properties.collect {|p| [ p.name, p.id ] }%> <!-- just an example, Ticket model has a field named "property_id" -->
<%= f.submit %>
<%= end %>
this form submits to create action of ticket_controller. And you are able to get all data as params and save it to table.
def create
#ticket = Ticket.new(params[:ticket])
#ticket.save
respond_to do |format|
format.html{redirect_to( your_desired_path)}
end
end

Resources