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'
Related
I want to add to the table Orders two fields: seller_id and buyer_id that will reference to the Users Table.
I am not sure what is the best way to do this?
rails g migration AddUsersToOrder seller_id:integer buyer:integer
I can run this command, but as I see it this is not a good solution, because this command doesn't 'connect' the two tables, it just creates two integer fields without a FOREIGN KEY constraint. In other words the reference wouldn't be created, but definitely will work.
There is a pretty good article (but quite old from 2008) with the same example but it doesn't generate migration and it focuses on Model.
Also #Joe Kennedy wrote a very good solution for a similar question. But as I said, I think this will not create the reference.
rails g migration AddUserRefToOrder seller_id:references
This is the best way to do it, but the problem is that we can't create another migration for buyer this way.
I am wondering what should I do am I missing something?
Depends on how you want to interact with the things you can choose from 2 versions.
For tasks it was important to see who was the assigner/executor so I was able to sort the different (incoming/outgoing) tasks. So I could call user.assigned_tasks or user.executed_tasks.
For conversations I was not interested in interacting with that model. It does not matter who created it. I don't need conversation.sender or conversation.recipient. I need the messages (nested under conversation) to be identified based on the sender/recipient.
Version A
class Task < ActiveRecord::Base
belongs_to :assigner, class_name: "User"
belongs_to :executor, class_name: "User"
end
class User < ActiveRecord::Base
has_many :assigned_tasks, class_name: "Task", foreign_key: "assigner_id", dependent: :destroy
has_many :executed_tasks, class_name: "Task", foreign_key: "executor_id", dependent: :destroy
end
create_table "tasks", force: :cascade do |t|
t.integer "assigner_id"
t.integer "executor_id"
end
add_index "tasks", ["assigner_id"], name: "index_tasks_on_assigner_id", using: :btree
add_index "tasks", ["executor_id"], name: "index_tasks_on_executor_id", using: :btree
Version B
class User < ActiveRecord::Base
has_many :conversations, foreign_key: "sender_id", dependent: :destroy
end
class Conversation < ActiveRecord::Base
belongs_to :sender, class_name: "User", foreign_key: "sender_id"
belongs_to :recipient, class_name: "User", foreign_key: "recipient_id"
end
create_table "conversations", force: :cascade do |t|
t.integer "sender_id"
t.integer "recipient_id"
end
add_index "conversations", ["recipient_id"], name: "index_conversations_on_recipient_id", using: :btree
add_index "conversations", ["sender_id"], name: "index_conversations_on_sender_id", using: :btree
UPDATE:
If you run rails g model User or rails g model Order, migration will also be generated along test files, model file etc. This is the preferred way if the table does not exist yet. If table already exists and you wanna change it then you create only the migration which can be done 2 ways. The first is what you are talking about where u also pass the arguments, so when u open up the migration file the columns will already be there. But that is the hard way. You can just simply run rails g migration AddSomethingToThis. Here the migration name does not matter, you should just choose something descriptive so you can easily recognize the migration file later on. Then you open up the migration file and you put there the columns you want. In my code u see the necessary foreign keys. Besides that you can create price column etc., what you business logic needs. Then just run rake db:migrate, which will change your db schema. You can always check out your current schema in schema.rb. Never change the schema manually only by migrations.
Summing up:
Create migration (with model if there is no table yet)
Edit the migration file
Run rake db:migrate so schema will be updated.
Update 2:
You can generate indexes easily by adding t.integer "recipient_id", index :true in the migration file and then the the line that you highlighted will appear in schema.rb (where I copied that part of my code from) after running rake db:migrate . Check out some migration examples.
So indexes are about performance. With indexes the search in the db is faster in most cases. It's something like bookmark in a book. Usually you add it to foreign keys since those are the bridges between 2 models. If there are some attributes in a model which is queried heavily then you can add it to those as well. Don't add to too many lines. You can imagine what happens when you add let's say 20 bookmarks to a book.
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
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.
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")
do i need to create foreign key in migration to let has_many belong_to work?
Can i don't create any foreign key such as :options => "CONSTRAINT fk_box_storage REFERENCES box(id)" and still have use has_many and belong_to in model?
You don't need to create any such constraint. You just need to create a stand-alone migration to create the join table.
Lets say, you have 2 models with habtm, User and Role.
>> script/generate migration roles_users
In your migration
create_table :roles_users, :id => false do |t|
t.references :user
t.references :role
end
That's it !
No need to, if you are only going to have a web app accesing the database. In case you want to access the db with another app or directly I would recommend you DO create the foreign keys since they will give you a better insight of what is going on there and won't let you insert wrong information or delete by error (in case you set up the on delete option)