What would be the most rails friendly way to create a new object strictly from landing on a URL, bypassing the need for a form?
Suppose I have 3 models:
A business has many customers, and a customer has one rating.
Example: How would you rate your meal at XYZ Restaurant? 1, 2, or 3? (Each of those being separate links that would save a score of 1/2/3)
What will my rating#new and rating#create methods & routes need to look like to accomplish this?
I was able to get it working by creating a totally new method which you can see below:
Example URL: abc.com/ratings/:customer_token/:score
def rate
@customer = Customer.find_by_token(params[:customer_token])
@business = @customer.business
unless @customer.ratings.exists?
@rating = @customer.ratings.new
@rating.customer_id = @customer.id
@rating.business_id = @business.id
@rating.score = params[:score].to_i
@rating.save
if @rating.save
redirect_to :action => 'thanks'
end
end
end
While the above works, I know it isn't a good way to do it. Would love some advice!
Edit: I want to make it clear that the links I'd like to generate will be from an email client, so I can't use ruby logic there or any sort of javascript.
答案 0 :(得分:0)
The reason your proposed method is risky is it nests all your parameters in the URL get stream without verifying via the post stream. Someone could type in something rediculous in your URL and add something odd, like abc.com/ratings/[legit customer token]/55
and your code would put a rating of 55 out of 3 in your database.
You could still use a form, with hidden variables, to create the rating. You can still have 1, 2, and 3, be the content of links (like buttons) to submit that form.
#in your view
<% 3.times do |i| %>
<%= form_for @rating, action: :rate do |f| %>
<%= f.hidden_field :score, value: i %>
<%= f.hidden_field :customer_id, value: @customer.id %>
<%= f.hidden_field :business_token, value: @business.id %>
<%= f.submit i %>
<% end %>
<% end %>
and
#in your controller
def rate
@rating = Rating.new(rating_params)
if @rating.save
#handle success
else
#handle error
end
end
private
def rating_params
params.require(:rating).permit(:rating, :customer_id, :business_id)
end
答案 1 :(得分:0)
I give you credit for coming up with a solution that on the surface works, but I can see a problem... if it's actually a link_to
that you've created that creates a GET
action, then you could end up with unexpected postings from web crawlers!
Imagine Google's spiders following every link on your site... they will find links to a customer, they will find links to rating, and by following the rating link, they'll create a rating! This is why actions that create, change, or delete data should never be behind GET
actions.
Consider using button_to
helpers which create buttons that essentially do a POST
(and modify your routes to accept POST
to your show url)
button_to('Rate 1', customer_token: @customer.token, rating: 1)
I guess a customer belongs to a business? @business = @customer.business
. If a customer rates two businesses, there are two customer records?
Also, you don't need the first @rating.save
... the second one is a repeat of the save. But that's a minor issue.
答案 2 :(得分:0)
The only option (aside from an AJAX call) would be to use the link_to helper, with a method POST
:
link_to 'Rate', ratings_path(score: 1, customer_token: 123), method: :post
Although this will not look like a form in your codebase, Rails will utilise jquery-ujs and will generate a form using JavaScript.
Your controllers will just have to use the params
to find the appropriate Customer
and add a rating
with the score to it.
Hope that helps!