Get record from url

I know this is a really simple question but I guess my brain and google-fu isn't working so well today.

Let's say I have an Event, with Registrants, and they can pay for the event using one or more payments.

I'm trying to create a payment linked to a registrant (who is linked to an event). So my payment should have both registrant_id and event_id.

My URL looks something like this: (nested routes)

http://mysite.com/events/1/registrants/1/payments/new

My controller looks something like:

def create
  @event = Event.find(params[:event_id])
  @registrant = Registrant.find(:first, conditions: {id: params[:registrant_id], event_id: params[:event_id]} )

  @payment = Payment.new params[:payment]
end

I know there is a much better way to do it, but I'm having trouble with the wording to properly google it :)

What syntax should I be using to make the .new automatically aware of the event_id and registrant_id?

Answers


Based on the discussion in the comments, there are several ways that the question can be addressed: the direct way and the Rails way.

The direct approach to creating objects that are related is to create the object using new_object = ClassName.new as suggested in the question. Then take the id of the created object and set that on an existing object (directly with existing_object.id = new_object.id or through some other method if additional logic is required). Or set the id on a new object by defining a custom initializer, such as:

class Payment
  def initializer id_of_registrant
    @registrant_id = id_of_registrant
  end 
...
end

The advantage of this approach is that it allows you to assign registrant IDs that may come from a range of objects with different classes, without having to deal with unnecessary or perhaps incorrect (for your solution) inheritance and polymorphism.

The Rails way, if you always have a direct relationship (1 to 1) between a Registrant and a 'mandatory' Payment is to use a has_many or belongs_to association, as described in the Rails guide: http://guides.rubyonrails.org/association_basics.html

For the example classes from the question:

class Registrant < ActiveRecord::Base
  has_one :payment
end

class Payment < ActiveRecord::Base
  belongs_to :registrant
end

You will want to use the appropriate migration to create the database tables and foreign keys that go with this. For example:

class CreateRegistrants < ActiveRecord::Migration
  def change
    create_table :registrants do |t|
      t.string  :name
      t.timestamps
    end

    create_table :payments do |t|
      t.integer :registrant_id
      t.string  :account_number
      t.timestamps
    end
  end
end

Of course, if you registrants only optionally make a payment, or make multiple payments, then you will need to look at using the has_many association.

With the has and belongs associations, you can then do nice things like:

 @payment.registrant = @registrant

if you have instantiated the objects by hand, or

 @payment.new(payment_amount)
 @registrant = @payment.build_registrant(:registrant_number => 123,
   :registrant_name => "John Doe")

if you would like the associations populated automatically.

The Rails Guide has plenty of examples, though in my experience only trying the most appropriate one for your actual use case will show if there are restrictions that could not be anticipated. The Rails approach will make future queries and object building much easier, but if you have a very loose relationship model for your objects you may find it becomes restrictive or unnatural and the equivalent associations are better coded by hand with your additional business rules.


It's not great practice to set id attributes directly, as the id might not refer to an actual database row. The normal thing to do here would be to use CanCan (https://github.com/ryanb/cancan), which seems like it would solve all your problems.

EDIT:

If you're not using authentication of any kind then I'd either put the load methods in before_filters to keep things clean:

before_filter :load_event

def load_event
  @event = Event.find params[:event_id]
end

or define some funky generic loader (unnecessarily meta and complex and not recommended):

_load_resource :event

def self._load_resource resource_type
   before_filter do
     resource = resource_type.constantize.find params[:"#{ resource_type }_id]
     instance_variable_set :"@#{ resource_type }", resource
   end
end

Need Your Help

Making an animating main menu

iphone objective-c xcode

What would be the best way to make a main menu for a game? I would prefer it to be UIButtons. More specifically, what would be the best way of animating the buttons? For example, an animation on th...

Generated Sqlite db is not found on server but file exists

c# sqlite iis iis-7 download

I'm using c# to generate a sqlite db with some tables and records in it. then I pass the database url to the client so they can download it. when creating the sqlite file the name goes like this:

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.