Help with DRY: too much code in my view

I’m a newbie to rails, and really to the whole DRY and MVC thing as
well, except where I accidentally fell into things doing it my own way
in the past.

I’ve got a fairly complex DB structure I want to create an interface for
using Rails. Getting simple tables to display, edit, save, not too much
problem there. But where I’m having trouble is figuring out where to
place some code I have had to write to get info from multiple tables
into one view. For example, for a list view of “projects” (main table),
I have this code in the list.rhtml:

<%
odd_or_even = 0
for project in @projects
	@project_translations = ProjectTranslation.find(project.id)
 	@business_unit = BusinessUnit.find(project.business_unit_id)
	@requestor = User.find(project.requester_user_id)
	@vendor_projects = 

VendorProject.find_by_project_translation_id(@project_translations.id)
@vendor = Vendor.find(@vendor_projects.vendor_id)
odd_or_even = 1 - odd_or_even
%>

This code I would probably need in other views as well. For the list
that customers see, for individual record views, etc. The question is:
where should I be putting that code? In the controller? In the model?
and how, exactly?

I have another question semi-related to this about how ids work and why
I have to do find all the time, but I’ll post that in a different
thread.

Micah

On Feb 25, 2006, at 3:11 PM, Micah B. wrote:

<%

This code I would probably need in other views as well. For the list
that customers see, for individual record views, etc. The question is:
where should I be putting that code? In the controller? In the
model?
and how, exactly?

You’re not using associations, and you’re not following naming
conventions.

Once you get everything together, you’ll be able to say things like:

project.project_tranlations
project.business_unit
project.requestor
project.project_tranlations.vendor_projects
project.project_tranlations.vendor_projects.vendor


– Tom M.

Micah,

1/ replace “odd_or_even” by TextHelper.cycle

2/ It looks like your model could be enriched, to have Rails do the hard
work or finding related records. In other words, remove all the "find"s
in your views by adding “has_one”, “has_many” and “belongs_to” in your
models.
(Tip: when you see “belongs_to :x”, think “references :x”, or “points_to
:x”)

Example: if you add

class Project < ActiveRecord::Base
    has_one :translation
end
class Translation < ActiveRecord::Base
    belongs_to :project
end

this code
@project_translation = ProjectTranslation.find(project.id)

would become
@project_translation = project.translation

3/ I’m puzzled by code like this
@project_translations = ProjectTranslation.find(project.id)

  • Why the plural on the left side, whey you’re obviously fetching 1
    record?
  • How come the project and the translation share the same id? Normally,
    you don’t handle ids explicitely (? is this a legacy project/db)?

Alain

Alain R. wrote:

Micah,

1/ replace “odd_or_even” by TextHelper.cycle

Sadly, I don’t even know what that is. I’ll do some digging, thanks for
the hint!

2/ It looks like your model could be enriched, to have Rails do the hard
work or finding related records. In other words, remove all the "find"s
in your views by adding “has_one”, “has_many” and “belongs_to” in your
models.
(Tip: when you see “belongs_to :x”, think “references :x”, or “points_to
:x”)

Example: if you add

class Project < ActiveRecord::Base
    has_one :translation
end
class Translation < ActiveRecord::Base
    belongs_to :project
end

this code
@project_translation = ProjectTranslation.find(project.id)

would become
@project_translation = project.translation

hmm. That’s what I thought it should be doing too… (see above
reply).

3/ I’m puzzled by code like this
@project_translations = ProjectTranslation.find(project.id)

  • Why the plural on the left side, whey you’re obviously fetching 1
    record?

Well, this isn’t complete yet. The reason is that there can be 1+
project_translations for any project. Or if you named it differently, a
“project” has one source language, and n number of target languages,
each of which could be done by a separate vendor, have separate
leveraging, cost, deadlines, etc.

  • How come the project and the translation share the same id? Normally,
    you don’t handle ids explicitely (? is this a legacy project/db)?

This is a test set of data (a migration from existing data source), and
each project in this test set only has one target language, so when the
insert happened, the projects and project_translations ids ended up
being 1:1. That won’t be the case in production.

I think I’ll revert a few versions back and see what error messages I
was getting and try to sort out what’s going wrong with my models. I
was getting frustrated and wanted to make it show me SOMETHING.

Thanks help,

Micah

Tom M. wrote:

You’re not using associations, and you’re not following naming
conventions.

Once you get everything together, you’ll be able to say things like:

project.project_tranlations
project.business_unit
project.requestor
project.project_tranlations.vendor_projects
project.project_tranlations.vendor_projects.vendor

Well, that’s what I thought I should be able to, it’s what I was going
to make the other post about. I think I’ve followed the db naming
conventions pretty closely… Everything has an ‘id’ field that’s an
int, foreign key fields are named with the table name + “_id”. I also
set up in the models, all the has_one, has_many, belongs_to, etc.
things.

I should have said that I did try the above reference style, but got
error messages. I’ll revert a few versions and check out exactly what
those errors were.

Micah

On 2/25/06, Micah B. [email protected] wrote:

Alain R. wrote:

Micah,

1/ replace “odd_or_even” by TextHelper.cycle

Sadly, I don’t even know what that is. I’ll do some digging, thanks for
the hint!

hint #2 :slight_smile:
http://api.rubyonrails.com/classes/ActionView/Helpers/TextHelper.html#M000430

Pat M. wrote:

hint #2 :slight_smile:
Peak Obsession

Alain and Pat,

Thanks for that one, it works like a charm!


Ok, I did some reverting, and these are the types of errors I get:

undefined method `vendor_projects’ for ProjectTranslation:Class
32: vendor_projects = project.project_translations.vendor_projects

and if I go back to manually finding for vendor_projects there, just to
get past that, I get this nil error from the vendor_projects.vendor_id:

You have a nil object when you didn’t expect it!
The error occured while evaluating nil.vendor_id
35: vendor = Vendor.find(vendor_projects.vendor_id)

If I did everything the way I think I should be doing it, I’d have
something like this:
for project in @projects
project_translations = project.project_translations
bu_name = project.business_unit.name
requester = User.find(project.requester_user_id)
# ^ still haven’t solved this one because i don’t use “user_id” as the
field name here (I have 3 users referenced in this table)
vendor_projects = project.project_translations.vendor_projects
vendor = vendor_projects.vendor

But that doesn’t work for me yet. If you’re still reading, here’s how I
have the Classes set up:

class Project < ActiveRecord::Base
has_many :project_translations
has_many :users
has_one :language

class ProjectTranslation < ActiveRecord::Base
belongs_to :project
has_one :language
has_many :vendor_projects

class User < ActiveRecord::Base
belongs_to :project
belongs_to :project_translation

class Vendor < ActiveRecord::Base
belongs_to :vendor_projects

class VendorProject < ActiveRecord::Base
belongs_to :project_translations
has_many :vendor_invoices
has_many :vendor_scope_changes
has_one :vendor

I’m not sure I have the Project>has_many :users thing right… it’s
really 3 relationships, but all are has_one relationships. (3 fields,
can only be 1 users for each of them).

Just wanted to thank everyone for the suggestions. I did get the above
mess cleaned up and working in what I hope is a more Railed manner. In
my view, I now have just this:
for project in @projects
project_hash = project.list_hash

list_hash is a method in the Project class that gets the data I need
from the various tables, does some calculations, sum(), avg(), etc, and
returns it in a hash that the view can use, as here:

<%= number_to_currency(project_hash["vendor_estimate"]) %>

I made an effort to keep anything that formatted a number, out of the
Project class, and into the view helpers. I believe that’s the right
Rails thing to do.

Except for 3 connection.select_value() calls to get avg and sum, there’s
no more sql in the method at all, no find_by…

Micah