HABTM relationship with an array - ruby-on-rails

I am a beginner with Rails 3 programming and I have one problem with creating the right model.
Let's say there is an application to manage the bibliography of a book, that is manage the mapping for each chapter of the list of referenced articles. So for the article part
I could have something like:
create_table :articles do |t|
t.string :title
t.text :content
...
On the bibliography side I would like to have a model like
create_table :bibliographies do |t|
t.string :chapter
t.text :ref
...
where ref is actually an array of references to articles, so it would be managed via serialize ActiveRecord method.
Ok, so now the issue is about how to make so that the elements of the array #bibliography.ref are references (in Ruby sense) to several article_id.
How do I model such a relationship, and what Rails 3 code should I write to express that?
The thing that confuses me is that a single field of a single instance of #bibliography would reference to many #article.id .
Thanks in advance

If you really want to store relationships like that, then I would define a method in Bibliography model, something like this
(Assuming that ref is an array of ids)
def articles
Article.where(:id => self.ref)
end
I would store the relationship differently though. Add a third table/model articles_bibliographies with article_id and bibliography_id fields. Then you can use the has_many :through association which is built into ActiveRecord.
in your Bibliography class you would then have something like:
has_many :articles_bibliographies
has_many :articles, :through => :articles_bibliographies
then you can just do #bibliography.articles
Read more here http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association

Following egze's suggestion, I found a way to solve my problem (without using arrays!).
So, I create a has_many-through relationship, but as I want to save the order for the
articles how they are mentioned in the bibliography, the table articles_bibliographies
has also a order_nr field, where I store which is the first, second, etc. article mentioned
in the bibliography:
create_table :articles_bibliographies do |t|
t.references :article
t.references :bibliography
t.integer :order_nr
This way I can retrieve and show an ordered list of elements with:
#bibliography.articles.order("order_nr ASC")

Related

Creating a one to many relation between a model and strings in rails

I want to create a join table between an ActiveRecord model and strings such that the model can have many strings. My join table is:
create_table :project_structures_sku_prefixes, id:false do |t|
t.belongs_to :project_structure, foreign_key: true
t.string :sku_prefix
end
I'm not sure how I would declare this in my model. Something like:
has_many_and_belongs_to :sku_prefixes, class_name:'String'
Can this be accomplished in Rails without having to create another simple model that just stores a string?

rails_admin: changed relationship, form not correct

I have a relationship between compositions and languages. One composition should be written in one and only one language.
My schema.rb contains the following lines:
...
create_table "compositions", force: :cascade do |t|
...
t.integer "product_language_id", null: false
end
...
add_foreign_key "compositions", "languages", column: "product_language_id"
...
I realized that the relationship was written wrong, so I changed the models to be like this:
previously there was a belongs_to :language line in composition.rb which I changed to has_one :language
previously there was a has_many :compositions line in language.rb which I changed to belongs_to :composition
Edit: FIRST QUESTION: is the procedure I made correct? I'm still a beginner at Ruby on Rails.
Now, in rails_admin, there's no possibility to select the language under the new form for composition, there is the line but no list, box nor anything, just the label name 'language', although I have an entry for it in its table.
Edit 2: Once reverting the relationship back to its initial status which I supposed it were incorrect, in rails_admin there is the possibility to add compositions from the language form, but I'd like to have also a drop down menu in the composition form to select the language, which is not appearing. Any suggestion?
Can you tell me where I'm failing? Thanks in advance
Thanks to this upwork freelancer I corrected it leaving the relationship in its original belongs_to / has_many status with the foreign key addition in composition.rb:
belongs_to :language, :foreign_key => 'product_language_id'
Rails assumes everything will be done according to convention. So foreign key of a table is expected to be tablename_id. When we break from the convention, we have to add additional options in our model to tell Rails that the foreign key is not what it expects, its something different.
We could write it this way as well:
belongs_to :product_language, class_name: 'Language'

Why is my foreign key association not yielding results?

I have two models in my rails app, Appointment and Policy. My Appointment model has_many :policies, class_name: "Policy", foreign_key: 'writing_code' and my Policy model belongs_to :appointment. The writing_code column in each table is a String.
While it would appear that the association has been made (my app runs), #appointment.policies yields no results. Can someone shed some light on where I've gone wrong here?
Also, to preemptively answer the obvious question, I cannot simply use appointment_id because I will be uploading policy data that has an appointment's (or user's) "writing code" associated to each record. The data will not include the appointment id since it comes from a separate, third party system.
Thanks in advance for all your help!
EDIT:
schema:
create_table "policies", :force => true do |t|
t.integer "product_id"
t.decimal "premium"
t.string "writing_code"
t.datetime "created_at", :null => false t.datetime "updated_at", :null => false end`
I think here is your problem:
You have:
class Appointment
has_many :policies, class_name: "Policy", foreign_key: 'writing_code'
end
Here is what the guides says
"By convention, Rails assumes that the column used to hold the foreign key on this model is the name of the association with the suffix _id added."
Here is an example from the guides:
class Order < ActiveRecord::Base
belongs_to :customer, class_name: "Patron",
foreign_key: "patron_id"
end
In your case, your class name is Policy but your foreign_key name is writing_code. which is not conventional.
Rails is very opinionated. Stay within the way rails likes to work and life gets much easier.
I Agree completely with #WaliAli said also.
"#appointment.policies yields no results"
For appointment to have many policies, each policy needs to be linked with an appointment, in the Model AND in the the table schema.
What this means is that policies should have a field 'appointment_id' which is an integer.
Add an appointment_id field to the policies table.
$ rails generate migration AddAppointmentIdToPolicies appointment_id:integer
$ rake db:migrate
Then you do the following:
class Policy
has_many :appointments # this lets you do #policy.appointments
end
class Appointment
belongs_to :policy # this lets you do #appointment.policy
end
For 99% of rails apps doing it another way, whereby has_many & belongs_to includes more optional paramaters is a code smell.
[Update 1:]
"I have policy data that will be uploaded from a legacy system via .csv
file. This data will have no knowledge of the appointment_id, which is
why I need to use the writing_code as the foreign key value, as it's
the only common data that can associate the two models."
I would suggest resolving this as part of your CSV importer.
As you import each record, do a find for an appointment record that has a matching 'writing code' and then save the record to include the appointment id.
Something like this:
# Inside your CSV importer script
csv_rows.each do |row|
policy = Policy.new
policy.appointment_id = Appointment.find(writing_code: row.writing_code).id
# more code here..
policy.save
end

What is the best way to convert a has_many relation to has_and_belongs_to_many?

I have a has_many relation in my app. e.g. department has many users.
I want to covert it to a has_and_belongs_to_many relation.
As part of the migration I need to preserve the current relation between users and departments, meaning I have to move all the data to the new connecting table.
This is the migration I created:
class CreateUserDepartment < ActiveRecord::Migration
def change
create_table :users_departments do |t|
t.belongs_to :user
t.belongs_to :department
end
###############################################
# need to move the data to the new table here #
###############################################
remove_column :users, :sub_department_id
end
end
what is the best way to write the missing line?
If you must, you can use execute "your SQL". See this question:
How do I add some inserts in rails migration?
The main value of the "Don't" answer to that question is in explaining why you would not want to use your models to do this. Also, I'd be surprised if you can or would want to do this using change, you would probably need to use self.up.

Which fields I should index in a Rails application?

I've been reading about the add_index method in Rails.
In a tutorial, I found this example:
class AddTitleToMicroposts < ActiveRecord::Migration
def change
create_table :microposts do |t|
t.string :title
t.string :content
t.integer :user_id
t.timestamps
end
add_index :microposts, [:user_id, :created_at]
end
end
I have no idea why [:user_id, :created_at] are indexed and not the others.
How do I know which fields to index in a Rails application?
Well it depends on your application, you mostly index foreign keys to improve Database performance. For instance next time you are going to search all the micropost that belong to a user, it will use the user_id index, and whenever you are searching all post created between certain times i'll use the created_at index. Indexes basically speed queries, you index based on what queries you are going to be doing constantly.
I would say that everywhere there is a belongs_to relationship, including join tables, there should be a corresponding index. Some might call this overboard, but the performance difference between an indexed relationship and a non-indexed relationship is profound.
So, if a User has_many Books, then your migration should have a corresponding line add_index: :books, :user_id. If a Book has_many Authors, and an Author also has_many Books through a join table BookAuthorings, then there should be two indexes on BookAuthoring - one that covers author_id, and one that covers book_id.
I'd say that a good, solid 90% of Rails performance problems that I've run into stem from a missing index somewhere.
I mostly index (as others said) the foreign keys and all other fields I intend to use for searching or sorting. So in you case I would add a key to at least the title field if you want to show the microposts in alphabetical order.
In case of the content field it may depend. If it contains a lot of text, you will most likely not do a simple sort or search but would want to do fulltext search. In this case a normal database index would not be of great help and you would have to use a fulltext search engine instead.

Resources