views:

72

answers:

2

Hi there, I'm undertaking a rather large conversion from a legacy database-driven Windows app to a Rails app. Because of the large number of forms and database tables involved, I want to make sure I've got the right methodology before getting too far.

My chief concern is minimizing the amount of code I have to write. There are many models that interact together, and I want to make sure I'm using them correctly. Here's a simplified set of models:

class Patient < ActiveRecord::Base has_many :PatientAddresses has_many :PatientFileStatuses end

class PatientAddress < ActiveRecord::Base belongs_to :Patient end

class PatientFileStatus < ActiveRecord::Base belongs_to :Patient end

The controller determines if there's a Patient selected; everything else is based on that.

In the view, I will be needing data from each of these models. But it seems like I have to write an instance variable in my controller for every attribute that I want to use. So I start writing code like this:

@patient = Patient.find(session[:patient]) @patient_addresses = @patient.PatientAddresses @patient_file_statuses = @patient.PatientFileStatuses

@enrollment_received_when = @patient_file_statuses[0].EnrollmentReceivedWhen @consent_received = @patient_file_statuses[0].ConsentReceived @consent_received_when = @patient_file_statuses[0].ConsentReceivedWhen

The first three lines grab the Patient model and its relations. The next three lines are examples of my providing values to the view from one of those relations.

The view has a combination of text fields and select fields to show the data above. For example:

<%= select("patientfilestatus", "ConsentReceived", {"val1"=>"val1", "val2"=>"val2", "Written"=>"Written"}, :include_blank=>true )%> <%= calendar_date_select_tag "patient_file_statuses[EnrollmentReceivedWhen]", @enrollment_complete_when, :popup=>:force %>

(BTW, the select tag isn't really working; I think I have to use collection_select?)

My questions are:

  1. Do I have to manually declare the value of every instance variable in the controller, or can/should I do it within the view?
  2. What is the proper technique for displaying a select tag for data that's not the primary model?
  3. When I go to save changes to this form, will I have to manually pick out the attributes for each model and save them individually? Or is there a way to name the fields such that ActiveRecord does the right thing?

Thanks in advance, Aaron.

A: 

1) There is nothing stopping you from writing arbitrary Ruby code in the view, so you can just write @patient.PatientAddresses, instead of defining @patient_addresses in the controller. That is just boiler-plate anyway and very easy to refactor with search/replace, if you wish to change it later. OTOH I would put the line

@patient = Patient.find(session[:patient])

in the controller, since you only want to perform the look-up once and the query might also change.

3) The save method in ActiveRecord is a bit subtle. In general, it will save only the object on which you invoke the method as well as any dependencies. It may not be possible to save just one row, if that row has a foreign key relation with another table. This means that for the code

patient = Patient.new; file_status.patient = patient

patient would be saved automatically when calling file_status.save, since the id for patient must be generated for the foreign key relation. Calling patient.save would not save file_status however.

If you want the associated objects to be saved along with the Patient you can specify :autosave => true when defining the has_many relation.

The details are in the docs.

Jørgen Fogh
A: 

Do I have to manually declare the value of every instance variable in the controller, or can/should I do it within the view?

As a general rule of thumb, you should avoid defining instance variables in your views; especially if it's call to a model. It can be confusing to maintain, more difficult to test and cannot be reused.

What is the proper technique for displaying a select tag for data that's not the primary model?

Provided that the "secondary" models are associated with the "primary" model, you can chain methods together. When you define an association in your model, you'll get these methods for free.

# Where attr is an attr belonging to the patient_address which is 
# associated with the patient
<%= @patient.patient_address.attr %>

When I go to save changes to this form, will I have to manually pick out the attributes for each model and save them individually? Or is there a way to name the fields such that ActiveRecord does the right thing?

You will be interested in the fields_for method for nesting associated models in a single form. Railscasts also has some great examples of working with associated models in forms.

Tate Johnson