I want to try to apply a one to one relationship between two models inside Rails.
In modelX.rb file i added belongs_to :modelY. In modelY.rb file I added has_one :modelX
Does the database schema change automatically? Or do I need to run a command for this association to be committed?
Short answer: no
You can only use these methods (e.g. belongs_to) when you have made necessary changes ( usually with migrations ) in your data structure to support these relations.
The thing is that in Rails, usually, the database don't 'see' the relationship between the two entities with foreign keys and so on. The 'intelligence' of the relationships should be present on your models (as you already did using the belongs_to, has_one, etc).
Also, in order to have the schema changed you need to run the rake tasks for the database as,
rake db:migrate, db:rollback, etc.
For more information about this you can also check the
Rails guide
cheers.
Related
I'm just playing with RoR and I've noticed that ActiveRecord associations such as has_many or belongs_to are decoupled from the database running behind, i.e., these association are set regardless of the the constraints set by the database. For example, I have a table comments and a table users and they are related through has_many and belongs_to statements (a comment belongs to a user and a user has many comments). However these associations still let me assign a comment to a, for example, non-existing user. The reason of this is that there's no foreign key defined in the database.
My question is: should I just rely on ActiveRecord's associations to handle data integrity or should I also add foreign keys in migration files?
Thank you.
Rails holds some conventions that enforcement of data integrity should be done in the application, not in the database.
To keep data integer on application-level, you can use model validations to enforce the presence of associations.
You have to add foreign keys to migration file to make your associations work correctly. With reference to the example mentioned, you have to add an attribute user_id to comments table. For more information on how Active Record Associations work, follow this rails guide.
I've been reading about Rails Migrations to help me start building a rails project.
I'm a bit confused on the generation of the files in db/migrate.
The way I've been designing my application is by starting with the models... outlining all of the objects that I'm going to have in the system as best I can. I would like to generate the migration files FROM these models automatically with the rails migration generator.
Yes, I know "creating migrations manually is easy." And I know I could do it manually, but what I don't understand is why the tool is separated from the pre-created models.
Based on my understanding from the article and other migration questions on SO, I can generate a migration like so:
rails generate migration SomeObj field:string some_other_field:integer
What I don't get is why I need to pass in the fields when my model already exists for SomeObj? Couldn't Rails detect it from the some_obj.rb and create the migration from there?
Also, when I have a more complex model, with has_many, belongs_to, and has_to_and_belongs_to_many relationships, it would be really nice if it autocreated the JOIN tables and fields with the correct names (e.g. foreign_obj_id, foreign_obj_ids)
In a previous project I didn't have to deal with migrations because I used Mongo+Mongoid - and collections were automatically generated due to the nature of how Mongo works (collections that doesn't exist get automatically created upon inserting or updating). Now with this Rails app I'm using Postgres (but I'm sure the same thing would happen with MySQL or any other relational database).
Anyway, is there no helper for this kind of thing? Do I have to create all these migrations manually?
You've got this backwards. You need to build your migrations first, and your models second. Migrations advance the state of the database. Your models reflect the current state of the database. There is not a 1-to-1 mapping of models to migrations.
I would like to generate the migration files FROM these models automatically with the rails migration generator.
Can't be done.
You use rails generate migration to generate the migration, which, almost as a side-effect, generates a stub model file. You cannot use your existing model file to generate the model's migration, because the model doesn't contain any information about the actual columns that make up the model. Rails would have to extract and imply all the necessary information from your associations/validations, and even then it would get most of the columns wrong.
Also, when I have a more complex model,... it would be really nice if it autocreated the JOIN tables and fields with the correct names
Again, you can't. You're responsible for defining your database schema, and the way you do so is by building migrations. You can do so manually, or via rails generate, but the process you should be following is building your migrations first, and your models second.
By way of example, here is a complete model, ready for production:
class MyModel < ActiveRecord::Base
end
That model definition might wrap a table that contains 10 columns or 1000 columns; you (and Rails) have absolutely no way of knowing based on the model definition.
Here's another model, which contains at least one column, but again, you have no way of knowing what kind of column:
class MyModel < ActiveRecord::Base
validates :code, presence: true, uniqueness: true
end
Is code a string or a number? Should there be an index on the column? There is absolutely no way of knowing.
All of the tutorials I've seen so far for RoR have shown me generating models like:
rails generate User name:string placeofbirth:string
This generates a class for the model, and only actually references an attribute if I apply a validation of some kind.
So my question is, how do I use a 'code' first approach when creating my models. Or is it the rails way to just right down on paper the attributes you want, run the generate command with each attribute you want and it's type, then run the rake db:migrate command?
I'd love some more proven patterns on this subject because so far the way I've seen seems too empty.
Yes, this is the rails way- migration comes first and generates the code and the database- and the model class inspects the database to see what fields are there and make accessible via methods.
You can do gem install annotate_models if you want to get some comments in your model class with the attribute names and types.
See here for an example: https://github.com/ctran/annotate_models
Rails uses an active record pattern for models which basically means that a model object will automatically map each DB column to an attribute so you don't have to specify all attributes in the model. It's a feature, but I agree that it might not be perfect for everyone. If you're using Rails 3 it should be easy to use another ORM of your choice if ActiveRecord's approach doesn't suit you. Here are some alternative ORMs that you could use.
Usually when you are developing some database backed web application, you know the database design(name of the tables, name of the columns in those tables and associations between different tables) beforehand.
Rails, as mentioned by maarons in his answer, uses Active Record pattern. Your models are classes that represent a table in your database, an instance of your model class a row in that table and different attributes of an object represent values under different columns in the same table.
When you create a model, usually, you are creating a class that represents one of the tables in your database. And while you are creating a model, you are also going to create a table in your database. That means knowing the name of the table and columns within that table.
So to answer your question, you must know all the columns, required for the time being, that will be in your tables. And hence available as attribute methods for your model objects. You specify these columns to added in the table in the migration generated by rails generator while generating this model. This is what usually everyone does.
You can take a code first approach by creating a class, without running the rails model generator,under app/models/ but not inheriting it from ActiveRecord::Base. As you move forward in your development, you can generate migrations by $ rails generate migration MigrationName and creating table and adding columns using [add_column][2]to that table as required. Once you have created a table for this model, you will have to inherit that model from ActiveRecord::Base so that you can get all the Rails magic in your application.
I'm working through the Ruby on Rails tutorial and just made a Comment model with three properties.
rails generate model Comment commenter:string body:text post:references
It generated an ActiveRecord class with post but not commenter and body.
class Comment < ActiveRecord::Base
belongs_to :post
end
Why doesn't rails formally define the non-reference properties anywhere other than the DB migration scripts?
Rails dynamically loads attributes - specifically, the names of the columns and their types - based on the database schema. There is no need to define or declare them in your models. For apps running in production, it does this once, at load time. For development, it will reload them as often as every request, but only loads them when each model is used.
Rails does not infer other things from your database, though. For instance, if you were to place a unique index on a name column, it would not automatically add a validates_uniqueness_of :name to your model. Of course, the database would still enforce this constraint when you save the record, causing an exception to be raised should the name field contain a duplicate value. The recommendation, in this case, is to do both.
Why doesn't rails formally define the non-reference properties anywhere other than the DB migration scripts?
Well, where do you need them "defined" anyways? Migrations are the only place where these attributes matter coz its responsibility is to create database tables with those attributes.
If you do a scaffold on comments with similar parameters, it would also generate the views and it would be using the attributes. They don't need to be "defined" as such anywhere else.
The short answer to your question is "no". Even the migration is not a definitive place to look as there might be many migrations related to a model.
However, you may have a look at the generated "db/schema.rb" which is an aggregation of all migrations. It contains the schema definition of all activerecord models. This maybe your best bet.
Additionally, you may want to use the https://github.com/ctran/annotate_models plugin that inserts a comment in your model to help you keep track of all your model's attributes.
I'm changing a relationship in my database from has_many to a has_many :through. So right now I have:
class Brand < Ar::Base
has_many :products
end
class Product < AR::Base
belongs_to :brand
end
and I'm going to add a join table.
But of course I need to update the database with data after that. I have seen that it is not good practice to do this in the confines of the migration. Where is the best place to perform this, knowing that I have to then run another migration after the data update is complete (i.e. removing the original brand_id column from the products table)?
Unless I misunderstand your question, the migration is the place to make that transformation. The purpose of the migration is to change your schema and migrate existing data to use the schema. Migrations capture the temporal aspect of layering schema changes, so that you can go forward and backward in time without leaving data in an inconsistent state. If you were to migrate your rows anywhere else, you have no guarantee that when that code runs, the schema is as it was when you wrote your migration code.
I believe that you will find support for my position in the examples on the Active Record Migrations api documentation. You might be confusing migrations with populating seed data (rake db:seed) which is handled in db/seeds.rb.
One-time changes like this can be done with ruby code in the migration. Migrations aren't just for schema changes. The idea is that migrations, by there version/datecode, are ensured to only be run once.
I think you should at least include the call to the code (maybe a rake task) that runs the data manipulation within the migration, since you have to run the 2nd migration right after the data manipulation.
If it were me, I would create a rake task that manipulates the data. This will at least remove the code from the migration and allow you to run it manually if it were ever necessary. Then code your migration and include a call to that rake task. I honestly don't see what the big deal is about not using data manipulation in a migration. Especially when you have to do things in a specific order as you are doing. They are tied so closely together, so why completely separate them?