In my app I have an Athlete which has many sports. I am trying to build out the edit account section where the user can edit the sports they play, but am stuck.
I have a Join table UserSport which has the fields of athlete_id, sport_id, primary. Here is my view:
<% #athlete.user_sports.each do |user_sport| %>
<% if user_sport.primary? %>
<div class="row">
<label>Primary Sport:</label>
<%= f.semantic_fields_for :user_sports do |builder| %>
<%= builder.select :sport_id, Sport.all.collect { |sp| [sp.name, sp.id] }, { include_blank: "Choose One", selected: user_sport.sport_id }, { class: "chosen field autosave primary-sport-select", :"data-form-selector" => "#athlete-form", id: "primary-sport-select" } %>
<% end %>
<div class="cl"> </div>
</div>
<% else %>
<div class="row">
<% if #athlete.user_sports.many? %>
<label>More Sports</label>
<% else %>
<% end %>
</div>
<% end %>
<% end %>
I am not sure how to make it so that the user can change their primary sport
list the #athlete rows into your table.
give each row an edit link that links to the edit_athlete_path(#athlete)
make your Athlete form include the field(s) for what sports they play
if you're writing any data into the UserSport table from the Athlete form, you'll need to give it permission to do so via the attr_accessible command in your models. and in that case, you'll want to go watch the railscast video about nested models in forms
Related
In my index view, I'm iterating over a list of bookings.
Also, I added a dropdown menu with the option to sort by created_at: asc and created_at: desc.
index.html.erb
<div class="dropdown">
<button class="btn" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Sort by
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<p> <%= link_to "ASC", sort: :asc %> </p>
<p> <%= link_to "DESC", sort: :desc %> </p>
</div>
</div>
<% #bookings.each do |booking| %>
<%= booking.address %>
<%= booking.created_at %>
<% end %>
This is the way I am sorting the #bookings in the controller:
booking_controller.rb
class Users::BookingsController < ApplicationController
def index
#bookings = current_user.bookings.order(created_at: params[:sort])
end
end
I'm not really sure if this is the best solution and if it has some vulnerability in terms of SQL injections...
Generally using params for order is unsafe, see https://rails-sqli.org/#order
You can use sanitize_sql_for_order to sanitaze input for ActiveRecord#order
Passing key/value pairs to order(created_at: params[:sort]) is safe. Rails validates the direction. If you give it an invalid direction it will raise ArgumentError: Direction "..." is invalid. It's been this way since the syntax was introduced in Rails 4.
Passing a string to order as in order("created_at #{params[:sort]}") could be exploited in Rails 5 and earlier. See Rails SQL Injection for details. Rails 6 now sanitizes order arguments and will raise an exception if it detects funny business.
Rails 6, in general, is more robust against SQL injection. But it's up to you to sanitize your inputs before passing them to anything which accepts raw SQL.
Your view is not turning the bookings into a drop down menu. Instead, it's just a bunch of text. As lurker suggested, use a function like collection_select to generate the select and option tags for you.
<%= form_for #user do |f| %>
<%= f.collection_select :booking_id, #bookings, :id, proc { |b| "#{b.address} #{b.created_at}" , prompt: true %>
<%= f.submit %>
<% end %>
To tidy that up a bit, you can add a method to Booking to produce the label you want and replace the proc.
class Booking
def dropdown_value
"#{address} #{created_at}"
end
end
<%= form_for #user do |f| %>
<%= f.collection_select :booking_id, #bookings, :id, :dropdown_value, prompt: true %>
<%= f.submit %>
<% end %>
New to Rails and SQL and I'm trying to access associations in my app.
I have an itinerary web app where users can come and download itineraries. As part of the process they can leave reviews with their thoughts. I want the owner of the review to be able to edit or delete her review after the thought if need be, but the following Active Record Association is returning an error for #itinerary.reviews.user_id:
undefined method `user_id' for #<Review::ActiveRecord_Associations_CollectionProxy:0x007fb969392978>
Inside my itineraries_helper:
def user_is_reviewer?
#itinerary = Itinerary.find(params[:id])
!!current_user.id == #itinerary.reviews.user_id
end
In my view:
<div class = "review-container">
<h2>What Travelers Thought</h2>
<% if #itinerary.reviews.count > 0 %>
<% #itinerary.reviews.each do |review| %>
<div class = "reviews">
<div class = "star-rating" data-score= <%= review.rating %> ></div>
<p>
<%= review.comment %>
</p>
<% if user_is_reviewer? %>
<% link_to "Edit", edit_itinerary_review_path(#itinerary, #review) %>
<% end %>
<% end %>
</div>
Questions:
Why does Review.first.user_id work but not itineraries.reviews.user_id
What SQL command will achieve what I am looking for?
If I can use SQL, what SQL resource can I pick up to learn more? If not, whats a better way?
Review.first will return your a Review Object and you are accessing the user_id associated with it by Review.first.user_id
while
itinerary.reviews
is returning a collection of all the reviews that belongs to a particular itinerary and it is collection of active record objects and you were trying to access the user_id from active record collection which is not going to work.
You can solve it by passing review id to your helper method
def user_is_reviewer?(review_id)
review = Review.find(review_id)
!!current_user.id == review.user_id
end
In your view updating the helper method to have review id passed
<% if user_is_reviewer?(review) %>
<% link_to "Edit", edit_itinerary_review_path(#itinerary, #review) %>
<% end %>
OR else you can simply go without a helper method as you already have a review object like following in your view
<% if current_user.id == review.user_id %>
<% link_to "Edit", edit_itinerary_review_path(#itinerary, #review) %>
<% end %>
I have a page in my rails application that shows a list of listings (paginated using will_paginate). I am trying to find a good gem to implement searching and sorting with the list on this page. The Ransack gem looked like what i needed but i'm running into an issue with it. This is what my code looks like right now with the Ransack gem.
My model (didn't include the whole model, didn't want the validations and extra code to get in the way):
UNRANSACKABLE_ATTRIBUTES = ["id", "state", "show_email", "email", "show_phones", "primary_phone", "secondary_phone", "updated_at", "user_id"]
def self.ransackable_attributes auth_object = nil
(column_names - UNRANSACKABLE_ATTRIBUTES) + _ransackers.keys
end
My controller action:
def index
#search = Listing.search(params[:q])
#listings = #search.result.paginate(page: params[:page], per_page: 20, joins: "LEFT OUTER JOIN attachments ON listings.id = attachments.attachable_id", select: "listings.*, COUNT(attachments.id) AS photos", group: "listings.id", order: "listings.created_at DESC, listings.title ASC, listings.id DESC")
#search.build_sort if #search.sorts.empty?
end
My search form on the page:
<div class="advanced-search">
<%= search_form_for #search do |f| %>
<div class="search-by">
<div class="search-field">
<%= f.label :title_cont, "Title Contains" %>
<%= f.text_field :title_cont %>
</div>
<div class="search-field">
<%= f.label :description_cont, "Description Contains" %>
<%= f.text_field :description_cont %>
</div>
<div class="search-field">
<%= f.label :price_gteq, "Price Between" %>
<%= f.text_field :price_gteq %>
<%= f.label :price_lteq, "and" %>
<%= f.text_field :price_lteq %>
</div>
</div>
<div class="sort-by">
<%= f.sort_fields do |s| %>
<%= s.sort_select %>
<% end %>
</div>
<div class="search-actions">
<%= f.submit "Search" %>
</div>
<% end %>
</div>
My route:
resources :listings do
collection do
match 'search' => 'listings#index', via: [:get], as: :search
end
end
With everything i have in place now i have been able to get the application to successfully search and sort the listings.
My question: The sort_select field is showing everything option that is "ransackable". Is there a way to make one attribute available for the searching, but not the sorting? For example i want the ability to search on the description attribute but it doesn't make any sense to include that attribute in the sorting. How can i use an attribute in one and not the other? Thanks for any help and let me know if my question is not clear enough.
Couldn't figure out how to get the values i wanted into the dropdown so i just used links instead. Here's what i ended up with for the sorting.
Sort by <%= sort_link #search, :title %> | <%= sort_link #search, :price %> | <%= sort_link #search, :created_at, "Date Listed" %>
I have seen a few posts here about searching across multiple models (here and here, in particular). However, I'm wondering if I can adapt Ryan's Railscast #37 to do this for three or four models without having to figure out Thinking Sphinx as I'm a beginner to all this.
As I said I have a few models, although I only reference two for brevity below, and I made a searches_controller with index and show actions.
On the models I included Ryan's model code:
def self.search(search)
if search
find(:all, :conditions => ['name LIKE ?', "%#{search}%"])
else
find(:all)
end
end
And in the searches_controller I have the following:
def index
#profiles = Profile.search(params[:search])
#employers = Employer.search(params[:search])
end
def show
#profiles = Profile.search(params[:search])
#employers = Employer.search(params[:search])
end
In my searches/show.html.erb I have the following:
<%= #profiles.each do |profile| %>
<div class="question">
<div class="questionHeader">
<h5 class="questionTitle"><%= link_to profile.first_name, profile %></h5>
</div>
</div><!-- end question -->
<% end %>
<%= #employers.each do |employer| %>
<div class="question">
<div class="questionHeader">
<h5 class="questionTitle"><%= link_to employer.name, employer %></h5>
</div>
</div><!-- end question -->
<% end %>
When I perform the search, my show renders what looks like three empty arrays [][][].
Without ditching this and moving to Thinking Sphinx is there anything I can do to get this working using the simple form below?
<% form_tag searches_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "#", :name => nil %>
</p>
<% end %>
I'm trying to build a form that will list all users and allow you to check the ones that you want to add to a team. Here's my first cut at the form:
<div id="add_team_mates">
<%= form_tag do %>
<%= will_paginate #users %>
<ul class="users">
<% #users.each do |user| %>
<li>
<%= gravatar_for user, :size => 30 %>
<%= link_to user.name, user %>
<%= check_box_tag("add", user.id) %>
</li>
<% end %>
</ul>
<%= submit_tag "Add Team Mates", :action => "add_team_mates" %>
<% end %>
</div>
And, right now this is all that I have in the controller:
def add_team_mates
end
The problem is that if I check multiple users, I only get the last user.id rather than multiple is as I'd expect. Here's some example from the log:
Started POST "/teams/5" for 127.0.0.1 at 2011-04-14 15:28:13 -0700
Processing by TeamsController#add_team_mates as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"IHFDevfKES8NibbCMlRa1t9qHn4/ZMKalK1Kjczh2gM=", "add"=>"3", "commit"=>"Add Team Mates", "id"=>"5"}
Completed in 12ms
Any help on this would be greatly appreciated. Thanks!
All your checkboxes have the same name, change the line to
check_box_tag("add[]",user.id)
In the controller your parameters will be like so:
params[:add] = ['foo','bar','baz']