开发者

Associating multiple ids on creation of Item

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:

  1. ActiveRecord::Associations: defines has_many, belongs_to, their options, and the convenience methods they add.
  2. Understanding MVC: Provides a better understanding of the flow of information as it relates to Rails
  3. RESTful resources: Understanding resources.
  4. Form Helpers: In depth description of form_for and why the above code works.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜