Relations not working as expected - ruby-on-rails

This will be fairly quick and easy for most of you...I have a table called types, and another called projects. A project can only have one type, but a type can have many projects. For instance a community garden project and a playground project can both have the type of 'greenspace'. So I have set up a has_many association. In my types model I have this:
has_many :projects
and in my projects model I don't have anything (I previously had has_one in it but upon looking at the docs it seemed incorrect). In the projects#show view I would like the name of the type to display. The parks project's view should say 'greenspace'. but I am getting the error
undefined method `type' for #<Project:0x007ffdd14fcde8>
I am trying to access that name using:
<h3>Type: <%= #project.type.project_type %> </h3>
i have also tried:
<h3>Type: <%= #project.type_id.project_type %> </h3>
but of course type_id gives a number, and there is no project_type for a number. project_type being the name of the column which holds the string data 'greenspace'. Am I accessing it wrong? Or have I set it up incorrectly?
Also in my schema, projects looks like this:
create_table "projects", force: :cascade do |t|
t.string "type_id"
t.text "description"
t.integer "money_needed"
t.integer "money_raised"
t.float "interest_offered"
t.datetime "end_date"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
t.text "url"
end

Project can belong_to both. Like this
#app/models/project.rb
class Project < ActiveRecord::Base
belongs_to :type
belongs_to :user
#...
end
#app/models/user.rb
class User < ActiveRecord::Base
has_many :projects
#...
end
#app/models/type.rb
class Type < ActiveRecord::Base
has_many :projects
#...
end

In the Project model you should state:
belongs_to => :type
In general, for most associations there is going to be an inverse. Not always, as you might have multiple associations in Type for Project. For example as well as your current has_many :projects, you might have others to return only projects that are unfinished, and such an association would not need an inverse.
Bear in mind that when you state: #project.type Rails is going to look for a method on #project. The association is what provides this method, and effectively the result is then the Type object that is referenced by the Project. It's important to realise that #project.type only returns a Type because the association tells it to -- the magic does not extent to just inferring that that is what is wanted.

Related

has_and_belongs_to_many issue with table_name_prefix

ich would like to ask for some help with has_and_belongs_to_many association.
I have the following tables and models:
candidate_job_title_translations -> Candidate::JobTitleTranslation (in a subfolder with table_name_prefix )
create_table "candidate_job_title_translations", force: :cascade do |t|
end
profile_experiences, ProfileExperience
create_table "profile_experiences", id: :serial, force: :cascade do |t|
end
candidate_job_title_translations_profile_experiences, No model
create_table "candidate_job_title_translations_profile_experiences", id: false, force: :cascade do |t|
t.bigint "candidate_job_title_translation_id", null: false
t.bigint "profile_experience_id", null: false
end
The two models are setuped for the association:
class ProfileExperience < ApplicationRecord
has_and_belongs_to_many :candidate_job_title_translations, class_name: 'Candidate::JobTitleTranslation'
end
class Candidate::JobTitleTranslation < ApplicationRecord
has_and_belongs_to_many :profile_experiences, class_name: 'ProfileExperience'
end
My Problem now is, I get a ActiveRecord error, saying job_title_translation_id does not exist, which is correct. It should look for candidate_job_title_translation_id
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column candidate_job_title_translations_profile_experiences.job_title_translati
on_id does not exist
LINE 1: ...ces" ON "candidate_job_title_translations"."id" = "candidate...
I have the feeling I can solve it by not having the table_name_prefix and model structure but, that is not good in terms of my structure.
Maybe you have an idea.
Thanks
Thats not really a good domain model to start with.
If you want a translations table you want to do it something like:
class Position
belongs_to :title
has_many :translated_titles,
through: :title,
source: :translations
end
class Title
has_many :positions
has_many :translations,
class_name: 'Titles::Translation'
end
class Titles::Translation
belongs_to :title
end
You should be more concerned about creating meaningful relations and duplication than "I don't want to have another class, waaah" which is the most common reason for choosing HABTM.
Also when "namespacing" models in Rails the module should be plural:
Good: Titles::Translation
Bad: Title::Translation
This convention is due to the way that ActiveRecord maps tables to tables to classes and the fact that nesting your model inside another model class is not really a good idea.

rails nested resource unknown attribute error

I have a Contract and a Task_Order model. I keep getting an unknown attribute error for contract_id Each Contract has many Task Orders. I have read other nested models unknown attribute error questions but they haven't been able to help me. Please keep in mind I am pretty new to Rails and would greatly appreciate any help I can get. I am using Rails 4.0
Contract Model:
has_many :task_orders
Contract schema:
create_table "contracts", force: true do |t|
t.string "contractId"
t.string "contractName"
end
Task Order Model:
belongs_to :contracts
Task Order Schema:
create_table "task_orders", force: true do |t|
t.string "contract_Id"
t.string "task_orderId"
t.string "task_orderName"
end
When I click Show Contract, I get the error:
unknown attribute: contract_id
This is the line that gets highlighted:
<%= form_for([#contract, #contract.task_orders.new]) do |f| %>
I can tell that Rails is trying to print out contract_id, which is not in my Contract model... so how can I get it to print out contractId instead - which is in my Contract model?
Thanks!!
Task Order Model should have this line belongs_to contract
belongs_to association should be declared as a singular of corresponding model
Also there should be contract_id column within task_orders table.
Diagram below explains default behavior of belongs_to in Rails
Something you need to be aware of is the foreign_key of Rails (and relational databases in general):
Foreign Key
Rails' standard foreign_key is to use snake_case (contract_id), however, you can use non-conventional foreign_keys like this:
#app/models/order.rb
belongs_to :contract, foreign_key: "contract_Id"
#schema SHOULD be:
create_table "orders", force: true do |t|
t.integer "contract_id" #-> should
t.string "contract_Id" #-> your current
end
Primary Key
create_table "contracts", force: true do |t|
t.string "contractId" #-> don't need
t.string "contractName" #-> your current
t.string "name" #-> should be
end
Your primary_key is almost always going to be the id column. You should remove your contractId column from the contracts db!
Task Orders
You'll need to do this:
#app/models/order.rb
belongs_to :contracts
has_many :task_orders
You'll then need another model at app/models/task_order.rb
Form
Your form is showing the error. This is because you're trying to create an ActiveRecord in the view itself. You'll be much better using the standard accepts_nested_attributes_for method of passing nested model data through a form:
#app/models/contract.rb
def new
#contract = Contract.new
#contract.task_orders.build
end
#app/views/contracts/new.html.erb
<%= form_for #contract do |f| %>
Firstly,use singular names for belongs_to
Class TaskOrder < ActiveRecord::Base
belongs_to :contract
end
Secondly,try changing your contract_Id in your task_orders table to contract_id.
Rails by default look for model_name_id(in your case contract_id) foreign key unless if any of the custom foreign keys defined in your model.
And finally,specify the data type integer for default foreign key.In your case it should be t.integer contract_id
However if you want contract_Id as foreign key,you should define it as custom foreign key in the Contract model itself like this
Class Contract < ActiveRecord:Base
has_many :task_orders,:foreign_key => "contract_Id"
end

Custom foreign_key in model gives PG::Error column does not exist - Rails

I have a VideoCollection model that will contain many records from another model (called VideoWork), using the has_many relationship. The VideoCollection model inherits from the Collection model using single table inheritance, while the VideoWork model inherits from the Work model.
I'm having a problem when I try to call up the video_works that belong to a video_collection.
In my video_collection#show action, I use the following to try to display a collection's works:
def show
#video_collection = VideoCollection.find(params[:id])
#collections = #video_collection.children
#works = #video_collection.video_works
end
But when I try to use #works in the show view, I get the following:
PG::Error: ERROR: column works.video_collection_id does not exist
SELECT "works".* FROM "works" WHERE "works"."type" IN ('VideoWork') AND "works"."video_collection_id" = $1
##(Error occurs in the line that contains <% #works.each do |work| %>)
My model files:
#----app/models/video_collection.rb----
class VideoCollection < Collection
has_many :video_works
end
#----app/models/video_work.rb----
class VideoWork < Work
belongs_to :folder, class_name: "VideoCollection", foreign_key: "folder_id"
end
The "parent" models:
#----app/models/collection.rb - (VideoCollection inherits from this)
class Collection < ActiveRecord::Base
end
#----app/models/work.rb - (VideoWork inherits from this)
class Work < ActiveRecord::Base
end
The Schema file:
#----db/schema.rb----
create_table "works", force: true do |t|
t.string "header"
t.string "description"
t.string "type"
t.string "folder_id"
end
create_table "collections", force: true do |t|
t.string "type"
t.datetime "created_at"
t.datetime "updated_at"
t.text "ancestry"
t.string "name"
t.string "tile_image_link"
end
My Question
I assume that since I have a folder_id column in the works table that I should be able to set up the belongs_to relationship properly, but it seems that Rails still wants me to have a video_collection_id column instead. I would prefer not use something specific like video_collection_id as a foreign key in the works table since I need to set up other relationships (e.g.: photo_collection has_many photo_works, etc).
What am I doing wrong here?
I don't really use has_many and belongs_to with different foreign keys than the standard, but according to the docs I would do this:
class VideoCollection < Collection
has_many :video_works, foreign_key: "folder_id"
end
class VideoWork < Work
belongs_to :folder, class_name: "VideoCollection", foreign_key: "folder_id"
end
Your Pg error says that the association is looking for 'video_collection_id' instead of 'folder_id'
Guides (chapter 4.3.2.5)

polymorphic nested form active_admin rails

I've looked for many solutions on the web and I can't seem to find my answer.
I have a polymorphic association for a table links that it linked to many other tables.
Here is my models a bit simplified:
links.rb
class Links < ActiveRecord::Base
belongs_to :linkable, polymorphic: true
end
events.rb
class Event < ActiveRecord::Base
has_many :links, as: :linkable
accepts_nested_attributes_for :links
end
here is the admin form
events.rb
ActiveAdmin.register Event do
form do |f|
f.has_many :links do |link_f|
link_f.inputs "links" do
link_f.input :url
end
end
f.actions
end
end
Here's what in my schema.rb
create_table "links", force: true do |t|
t.string "url"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "linkable_id"
t.string "linkable_type"
end
It throws me that error: uninitialized constant Event::Link
I can't seem to find the problem and it is driving me nuts...
It seems like a relation is missing or something but I can't find it.
Thanks a lot for every one that can help!
I think the problem is in the way you named your models. Models are always declared as singular entities, not plural.
You should:
Rename links.rb to link.rb
Rename events.rb to event.rb
Rename class Links < ActiveRecord::Base to class Link < ActiveRecord::Base
and see if that helps.

How to create a rails habtm that deletes/destroys without error?

I created a simple example as a sanity check and still can not seem to destroy an item on either side of a has_and_belongs_to_many relationship in rails.
Whenever I try to delete an object from either table, I get the dreaded NameError / "uninitialized constant" error message.
To demonstrate, I created a sample rails app with a Boy class and Dog class. I used the basic scaffold for each and created a linking table called boys_dogs. I then added a simple before_save routine to create a new 'dog' any time a boy was created and establish a relationship, just to get things setup easily.
dog.rb
class Dog < ActiveRecord::Base
has_and_belongs_to_many :Boys
end
boy.rb
class Boy < ActiveRecord::Base
has_and_belongs_to_many :Dogs
def before_save
self.Dogs.build( :name => "Rover" )
end
end
schema.rb
ActiveRecord::Schema.define(:version => 20100118034401) do
create_table "boys", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "boys_dogs", :id => false, :force => true do |t|
t.integer "boy_id"
t.integer "dog_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "dogs", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
end
I've seen lots of posts here and elsewhere about similar problems, but the solutions are normally using belongs_to and the plural/singular class names being confused. I don't think that is the case here, but I tried switching the habtm statement to use the singular name just to see if it helped (with no luck). I seem to be missing something simple here.
The actual error message is:
NameError in BoysController#destroy
uninitialized constant Boy::Dogs
The trace looks like:
/Library/Ruby/Gems/1.8/gems/activesupport-2.3.4/lib/active_support/dependencies.rb:105:in const_missing'
(eval):3:indestroy_without_callbacks'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/callbacks.rb:337:in destroy_without_transactions'
/Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/transactions.rb:229:insend'
...
Thanks.
I don't see your destroy callback, but I do see a couple of problems. First, your associations need to be lowercase. So dog.rb should be:
class Dog < ActiveRecord::Base
has_and_belongs_to_many :boys
end
and boy.rb should be:
class Boy < ActiveRecord::Base
has_and_belongs_to_many :dogs
def before_save
self.dogs.build( :name => "Rover" )
end
end
Second, I believe you want to use self.dogs.create instead of self.dogs.build above, since build won't actually save the new dog object.
The accepted answer here solved my problem, only to create another one.
Here are my model objects:
class Complex < ActiveRecord::Base
set_table_name "Complexes"
set_primary_key "ComplexID"
has_and_belongs_to_many :amenities
end
class Amenity < ActiveRecord::Base
set_table_name "Amenities"
set_primary_key "AmenityID"
end
Rails uses the name of the association as the table name when creating the select query. My application runs on Unix against a legacy MySQL database and my table names are case-sensitive and don't conform to Rails conventions. Whenever my app actually tried to load the association, I would get an exception that MySQL couldn't find table amenities:
SELECT * FROM `amenities`
INNER JOIN `ComplexAmenities` ON `amenities`.AmenityID = `ComplexAmenities`.AmenityID
WHERE (`ComplexAmenities`.ComplexID = 147 )
I searched and searched and could not find a way to tell Rails to use the correct case for the table name. Out of desperation, I tried passing a :table_name option to habtm and it worked. My new Complex model looks like this:
class Complex < ActiveRecord::Base
set_table_name "Complexes"
set_primary_key "ComplexID"
has_and_belongs_to_many :amenities, :table_name => 'Amenities'
end
This works under Rails 2.3.5.
This option is not mentioned in the Ruby on Rails docs.

Resources