G'day guys,
I'm currently flitting through building a test "Auction" website to learn rails. I've set up my Auction and User models and have it so that only authenticated users can edit or delete auctions that are associated with them.
What I'm having difficulty doing is associating bid items with the Auction.
My models are as follows:
class Auction < ActiveRecord::Base
belongs_to :creator, :class_name => "User"
has_many :bids
validates_presence_of :title
validates_presence_of :description
validates_presence_of :curprice
validates_presence_of :finish_time
attr_reader :bids
def initialize
@bids = []
end
def add_bid(bid)
@bids << bid
end
end
class Bid < ActiveRecord::Base
belongs_to :auction, :class_name => "Auction", :foreign_key => "auction_id"
belongs_to :bidder, :class_name => "User", :foreign_key => "bidder_id"
validates_presence_of :amount
validates_numericality_of :amount
@retracted = false
end
class User < ActiveRecord::Base
has_many :auctions, :foreign_key => "owner_id"
has_many :bids, :foreign_key => "owner_id"
#auth stuff here
end
I'm attempting to add a bid record to an auction, but the auction_id simply will not add to the record.
I create a bid with a value from within a view of the auction, having the @auction as the local variable.
<% form_for :bid, :url => {:controller => "auction", :action => "add_bids"} do |f|%>
<p>Bid Amount <%= f.text_field :amount %></p>
<%= submit_tag "Add Bid", :auction_id => @auction %>
<% end %>
This is connected to the following code:
def add_bids
@bid = current_user.bids.create(params[:bid])
if @bid.save
flash[:notice] = "New Bid Added"
redirect_to :action => "view_auction", :id => @bid.auction_id
end
end
The problem I am getting is that the auction_id is not put into the bid element. I've tried setting it in the form HTML, but I think I'm missing something very simple.
My Data model, to recap is
Users have both bids and auctions
Auctions have a user and have many bids
Bids have a user and have a auction
I've been struggling with trying to fix this for the past 4 hours and I'm 开发者_运维技巧starting to get really downhearted about it all.
Any help would be really appreciated!
You're not quite doing things the Rails way, and that's causing you a bit of confusion. Successful coding in Rails is all about convention over configuration. Meaning, Rails will guess at what you mean unless you tell it otherwise. There's usually a couple of things it will try if it guesses wrong. But in general stick to the deterministic names and you'll be fine.
There are so many errors in your code, so I'm going to clean it up and put comments every way to let you know what's wrong.
app/models/auction.rb
class Auction < ActiveRecord::Base
belongs_to :creator, :class_name => "User"
has_many :bids
# Given the nature of your relationships, you're going to want to add this
# to quickly find out who bid on an object.
has_many :bidders, :through => :bids
validates_presence_of :title
validates_presence_of :description
validates_presence_of :curprice
validates_presence_of :finish_time attr_reader :bids
#These two methods are unnecessary.
# Also don't override initialize in ActiveRecord. Instead use after_initialize
#def initialize Supplied by rails when you do has_many :bids
# @bids = [] @bids will be populated by what is picked up from
#end the database based on the has_many relationship
#def add_bid(bid) Supplied by rails when you do has_many :bids
# @bids << bid auction.bids << is a public method after has_many :bids
#end
end
app/models/bid.rb
class Bid < ActiveRecord::Base
# :class_name and :foreign_key are ony necessary when rails cannot guess from a
# association name. :class_name default is the association singularized and
# capitalized. :foreign_key default is association_id
belongs_to :auction #, :class_name => "Auction", :foreign_key => "auction_id"
# here we need :class_name because Rails is looking for a Bidder class.
# also there's an inconsistency. Later user refers to has_many bids with
# a foreign_key of owner_id, which one is it? bidder_id or owner_id?
# if it's owner_id? you will need the :foreign_key option.
belongs_to :bidder, :class_name => "User" #, :foreign_key => "bidder_id"
validates_presence_of :amount
validates_numericality_of :amount
# This will never get called in a useful way.
# It really should be done in the migration, setting default
# value for bids.retracted to false
# @retracted = false
end
app/models/user.rb
class User < ActiveRecord::Base
# This makes sense, because an auction can have many bidders, who are also users.
has_many :auctions, :foreign_key => "owner_id"
# This doesn't. A bid belongs to a user, there's no need to change the name.
# See above note re: owner_id vs. bidder_id
has_many :bids, :foreign_key => "owner_id"
# You could also use this to quickly get a list of auctions a user has bid on
has_many :bid_on_auctions, :through => :bids, :source => :auction
... auth stuff ...
end
So far so good, right?
The view isn't bad but it's missing the form parts for the bid amount. This code assumes that you store the value of the bid in an amount column. I also arbitrarily named it auctions/bid
app/views/auctions/bid.html.erb
<% form_for :bid, @auction.bids.new do |f|%>
<%= f.label_for :amount %>
<%= f.text_field :amount%>
<!-- Don't need to supply @auction.id, because form_for does it for you. -->
<%= submit_tag "Add Bid" %>
params hash generated by the form: that is passed to the controller:
params =
{
:bid =>
{
:auction_id => @auction.id
:amount => value of text_field
}
}
params hash generated by the from as you wrote it (note: I'm guessing at names because they were left out of the posted code):
params =
{
:id => @auction_id ,
:bid => { :amount => value of text_field }
}
However, your controller code is where all your problems are coming from this is almost entirely wrong. I'm guessing this is in the auction controller, which seems wrong because you're trying to create a bid. Lets see why:
app/controllers/auctions_controller.rb
...
def add_bids
# not bad, but... @bid will only fill in the owner_id/bidder_id. and bid amount.
@bid = current_user.bids.create(params[:bid])
# create calls save, so this next line is redundant. It still works though.
# because nothing's happening between them to alter the outcome of save.
if @bid.save
flash[:notice] = "New Bid Added"
# you should be using restful routes, this almost works, but is ugly and deprecated.
# it doesn't work becasue @bid.auction_id is never set. In fact you never use
# the auction_id any where, which was in your params_hash as params[:id]
redirect_to :action => "view_auction", :id => @bid.auction_id
end
end
...
Here's how your controller should work. First of all, this should be in the bids_controller, not auctions_controller
app/controllers/bids_controller.rb
...
def create
@bid = Bid.new(params[:bid]) # absorb values from form via params
@bid.bidder = current_user # link bid to current_user.
@auction = bid.auction based on.
# @auction is set, set because we added it to the @bid object the form was based on.
if @bid.save
flash[:notice] = "New Bid Added"
redirect_to @auction #assumes there is a show method in auctions_controller
else
render "auctions/show" # or what ever you called the above view
end
end
...
You'll also need to make sure the following is in your routes.rb (in addition to what may already be there. These few lines will set you up with RESTful routes.
config/routes.rb
ActionController::Routing::Routes.draw do |map|
...
map.resources :auctions
map.resources :bids
...
end
In any case you weren't far off. It seems you're off to a decent start, and could probably benefit from reading a book about rails. Just blindly following tutorials doesn't do you much good if you don't understand the underlying framework. It doesn't help that 90% of the tutorials out there are for older versions of rails and way out of date.
A lot of your code is the old way of doing things. Particularly redirect_to :action => "view_auction", :id => @bid.auction_id
and <% form_for :bid, :url => {:controller => "auction", :action => "add_bids"} do |f|%>
. With RESTful routing, they become redirect_to @auction
and <% form_for @auction.bid.new do |f| %>`
Here's something resources you should read up on:
- ActiveRecord::Associations: defines has_many, belongs_to, their options, and the convenience methods they add.
- Understanding MVC: Provides a better understanding of the flow of information as it relates to Rails
- RESTful resources: Understanding resources.
- Form Helpers: In depth description of form_for and why the above code works.
精彩评论