Completely new to Rails, I read that it changes your table names because it makes a bunch of assumptions but I'm working with tables that were created pre-rails and are used in a PHP API so I can't change them.
I have a Class created in Rails that references the existing table and of course I get a sql error of table not found because it appends an s at the end of the table name, so I went ahead and put this in my Class definition:
class BookSubjects2title < ActiveRecord::Base
set_table_name "book_subjects2title"
belongs_to :bookSubjects
end
Supposedly, that should take care of the problem from what I've read. Yet it doesn't, it still keeps trying to use the name with the s at the end and I get an error from the rails console. Is there anything I need to do for Rails to read this new config? Should be dynamic no?
Not sure if this is your only issue but
belongs_to :bookSubjects
should never have an 's', use this instead.
belongs_to :book_subject
Related
I've got a rails model called Question set up as STI (ie the migration included a type attribute).
My model code for Question is in a file called base.rb in folder models/question/, and looks like this:
class Question < ActiveRecord::Base
acts_as_paranoid
belongs_to :organization
has_many :answers
end
I have a few subclasses (ie the STI types) of questions. For example, in the same folder, I have a file called text.rb:
class Text < Question
end
Here is the weird thing: it used to be:
class TextQuestion < Question
end
.. but I changed it today, and it is definitely, 100% saved as the former.
In the console when I run:
Question.subclasses.map { |c| c.name }
I expected to get an array that included Text, and no longer included TextQuestion. However, I get both!
I've reset my database locally and reloaded the console.
Any idea why I'm still seeing TextQuestion as a subclass of Question?
Remember that STI type data is stored in a column in your database called type. If you rename a subclass you'll have to create a migration to either delete all the old subclass instances that are in the database or rename them accordingly.
For example:
execute("UPDATE questions SET type='Text' WHERE type='TextQuestion'")
Of course adjust that to whatever SQL dialect you're using.
I think these phantoms you're seeing are produced by ActiveRecord since it's going to instantiate a record as whatever the type column indicates, even if that's wrong.
I have a model of say Apps and each App contains a bunch of users, and a reference to the store type, and has the properties, name, app_id, version, and price.
I want to be able to create clones of this app, which has all the same values, save for version, and store type.
I'd like to do this by adding a property/column to my app called "Is_clone", and I'd like a second table called AppToClone, which has two columns App and Clone.
In each row, I'll have say App:1 , Clone:[2,5,7,26]
This way I can edit each clone as its own app, but at the same time I can apply any changes in an app to all its clones.
I've set up the database for AppToClone but I'm stuck on how to create this association within the App Model itself. Examples I've found so far such as the gem rails_lookup_tables seems to only allow a single value rather than an array in the AppToClone table.
Currently in my App model I have the following line:
:has_many :apps, through: :appstoclones
But this doesn't feel correct to me, and I'm not sure how to build that database over time.
Feeling kinda stupid here, but I'm stuck.
So the has_many and belongs_to association stores the data in a different way than the question indicates. I would use single table inheritance, having a Clone model inherit from the App model.
class App < ActiveRecord::Base
has_many :clones
end
class Clone < App
belongs_to :app
end
You would need to have an app_id and type on your app table (strange I know), so your clone can be associated. What you would be able to do then is query your clone as an App.
Clone.last.is_a? App
-> true
What I think is closest to what you actually want though, is outlined in section 2.10 Self Joins of the Ruby on Rails Guides.
class App < ActiveRecord::Base
has_many :clones, class_name: "App",
foreign_key: "parent_app_id"
belongs_to :parent_app, class_name: "App"
end
You need to create the foreign_key in a migration.
To implement the is_clone:
def is_clone?
!!self.parent_app_id
end
To implement the AppToClone:
def clone_ids
self.clones.all.map(&id)
end
# Check it in the console:
App.last.clone_ids
-> [2,5,7,26]
The biggest concern here is thinking that a table will have a row with one column holding an array. That is not normalized! Normal associations work by having the foreign keys as seen in these examples, one per row. If you have a complex has_and_belongs_to_many relationship, the table that holds keys has two columns, each with one id.
Per the request in the comment below (self join update):
def clone_me
self.clones << self.clone
end
> new_cloned_app = App.last.clone_me
The reference for using has_many is found: In the rails guide here. Feel free to use a different helper function. Also, the returned value must be .saveed
I would say you are looking for a has-many belongs-to relationship.
For the has-many part: http://guides.rubyonrails.org/association_basics.html#the-has-many-association
For the belongs-to part: http://guides.rubyonrails.org/association_basics.html#the-belongs-to-association
One model would be the app, the other clone, where the clone table would store the the same fields an app has, as well as the additional fields needed for a clone instance. By inheriting the clone model from the app model which inherits from ActiveRecord, one can use a clone in the code where an app is used.
I'm working up an app that interfaces with a legacy database which has a type column used for single table inheritance. This database is still used by an existing PHP application so I have to work with what is there. I'm trying to set up some models for this and one of the key tables is set up with an STI scheme and a type column, however, all of the types in that column are entirely lowercase.
In my testing so far, rails works fine with the type column if I change the value to match the class name (for example, Claimant instead of claimant). However I don't want to go changing those values in the production database even though it would probably be ok, nor do I want to have to go in and modify the legacy app to save the names differently...
In order to fix this I have two questions...
1) Is there anyway I can configure the model to recognize that type = 'claimant' maps to class = 'Claimant'?
2) failing that, is there a way I can tell rails to not use STI on this table even though it has a type column?
I've done some googling and haven't come up with much yet...
I haven't tried this in an STI setting, but when using a legacy table I was able to use the method "set_table_name" on the ActiveRecord model.
class Claimant < ActiveRecord::Base
set_table_name 'claimant'
end
Or with a custom method:
def table_name
'claimant'
end
Apologies I haven't got an STI table handy to test this on, but thought it might help solve your problem.
In answer to the second part of your question, I believe you can disable Rails looking at the type column, by just specifying a non-existant column name.
class Claimant < ActiveRecord::Base
inheritance_column = :_type_column_disabled
end
im trying to reference one of my tables in my rails app but i get the following error
uninitialized constant UsersController::BadgesSashes
im in my user controller, and my table is named badges_sashes (according to my database browser)
i cant do something like...
BadgesSashes.first
in my controller? im using a gem, and i already have a
has_merit
line in my user model. do i need to do anything else? thank you
To reference a table, you need to define a model with the singular version of the name. So if your table is names badges_sashes, than you should create a file app/models/badges_sash.rb:
class BadgesSash < ActiveRecord::Base
end
Bear in mind that usually goes the other way round: You generate a model and it will create a database migration, a class and a unit test for you.
To learn more, you can do an free online course, in which all these things are explained, called Rails for Zombies, or read the book Agile Web Development with Rails or the official guide
I think there are a lot of places where my design may be screwing this up. I have very limited experience with Rails though. This is happening in Rails 2.3.2 with Postgres 8.3.
We've got two tables in our DB. One called "survey" and one called "survey_timepoint". A survey can have multiple time points so in the survey_timepoint table there is a column called "survey_id" with an fk constraint on it.
I also think I should mention that the tables were not created with a rails migration although they do follow the rails naming conventions. I suspect AR isn't anticipating a constraint on that column and it doesn't know how to handle the situation.
In my rails models I have:
has_many :survey_timepoint
and
belongs_to :survey
If I do something like:
s = Survey.new
s.survey_timepoint.push SurveyTimepoint.new
s.save!
I get:
ActiveRecord::StatementInvalid: PGError: ERROR: insert or update on table "survey_timepoints" violates foreign key constraint "survey_timepoints_fk"
DETAIL: Key (survey_id)=(59) is not present in table "surveys"
I'm assuming that if I delete that fk constraint on survey_timepoint.survey_id it'll work ok. It seems like I shouldn't have too though. Am I going to be stuck creating and saving each of the objects separately and wrapping the whole process in a transaction? It seems rather un-railsy. Apologies for any necessary information that I may have omitted.
You might want to check the SQL commands being sent. It looks like it is adding the survey_timepoint record before the survey record. Note that you are already dealing with two database changes — the survey and the survey_timepoint — so you should be using a transaction.
You can fix the immediate problem by doing s.save! before adding the timepoint (and then calling it again). My knowledge of Rails functionality is not deep enough to know if there is a more "railsy" way of doing this then wrapping it in a transaction.
I just experimented and found that this works with MySQL:
s = Survey.new()
s.survey_timepoints << SurveyTimepoint.new # Note "survey_timepoints" (plural)
s.save!
I think it would work equally well with PostgreSQL.
It does two inserts, first the Survey, then the timepoint, and wraps them in a transaction.
You can also do it all on one line:
Survey.create!({:name=>'New Survey', :survey_timepoints => [SurveyTimepoint.new]})
Incidentally, for ActiveRecord to work right you have to make sure of your singulars and plurals. (If you want to break the expected forms, you'll need to tell AR you're doing that -- a whole other topic.)
Your tables should be:
surveys
-------
# ...
survey_timepoints
-----------------
survey_id
# ...
And in your models you'd have:
class Survey < ActiveRecord::Base
has_many :survey_timepoints
# etc...
end
class SurveyTimepoint < ActiveRecord::Base
belongs_to :survey
end