Where do we declare attributes of a rails model? - ruby-on-rails

I come from a Java background and I have started learning Ruby on Rails. Consider the following code mentioned in http://guides.rubyonrails.org/active_record_basics.html
class Product < ActiveRecord::Base
end
The guide mentions that this creates a model Product mapped to a table products (using the pluralized mechanism of ruby). It also mentions, 'By doing this you'll also have the ability to map the columns of each row in that table with the attributes of the instances of your model.'
But we did not declare any attributes inside the model Product. How does it know what are its attributes?
One assumption: Every attribute of table is made as an attribute of model. Is it true? Then, do we create the SQL table first? If I change the table later on (adding new columns, say) does it also change my model dynamically?

The important distinction is that we're talking about ActiveRecord models, i. e. subclasses (direct and indirect) of ActiveRecord::Base, those that use its persistence mechanism. The following is not true for Rails models in general. But then again, for non-AR models the question makes no sense :)
Every attribute of table is made as an attribute of model. Is it true?
Yes.
Then, do we create the SQL table first?
Exactly. rails g model creates a model file and a migration that contains a declaration for a table behind the model. So before using your model, you have to run the migration first.
If I change the table later on (adding new columns, say) does it also change my model dynamically?
Now that's tricky. It's most certainly true if the application is reloaded after the changes (e. g. in development mode this happens every now and then), since the model class will be reconstructed. So the answer is yes, most of the time.
This is, however, only about the internal structures of the model class (visible in e. g. Model.columns) you don't always have to care about. When fetching data, all the columns of the result set will be mapped to attributes of the model objects. So this keeps even custom columns you specify in SELECTs:
Thing.select(:id, "1 AS one").first.attributes
#> SELECT "things"."id", 1 AS one FROM "things" ORDER BY "things"."id" ASC LIMIT 1
# => {"id"=>1, "one"=>1}

It works like this:
class Product < ActiveRecord::Base
Product is subclassed to ActiveRecord::Base (you know subclassing from Java, right?).
ActiveRecord::Base can be seen here:
Active Record objects don't specify their attributes directly, but rather infer them from
# the table definition with which they're linked. Adding, removing, and changing attributes
# and their type is done directly in the database. Any change is instantly reflected in the
# Active Record objects. The mapping that binds a given Active Record class to a certain
# database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.
You can read through the other code; in short, it means that ActiveRecord uses the SQL schema to populate the respective attributes.
--
Because your model is a Class, ActiveRecord will basically create a series of setter/getter instance methods with the values from your db.
When you invoke said class, ActiveRecord::Base will populate the respective instance methods with the values in your db, allowing you to call #product.name etc.

Models in rails are just an easy way of binding the data from the database. Model is the data.
A model represents a table and will have all the columns of that table as its attributes.
The model in point is Product. By rails conventions, this model is directly mapped to products table in the database and will have all the attributes that the table has as its columns.
Models and tables are interlinked and models serve as an easy abstract layer over the actual data to provide ease of work and additional validations and stuff like that.

You only have to declare specific attributes in a migration (which creates the tables). Otherwise, ActiveRecord makes a some key assumptions:
name of the table = lowercase version of class name = products
primary key = id
Then it can use raw SQL when it starts the connection to get a list of attributes from the table:
DESCRIBE table products;
This gives it a full listing of the fields in the table. It sets up attributes in each instance of the class based on these fields.

Related

When to assign a foreign key and when to just create an association?

I'm learning about model associations in rails. I've learned how to give the table for a model a column to hold foreign keys of another model like so:
rails generate model User account_id:integer
I would then take the primary key of an account from an Account table and assign it to the account_id for the designated user.
However, I am also told to create the association in User.rb like so:
has_one :account
I understand the difference between these two things. One creates a column in the table (the first line after the migration), and the other generates a series of helpers (the latter).
However, what I am seeing in tutorials is that sometimes both are done, while other times, only the association (has_one :account) is done. How do I go about deciding when to create a column in the table to hold foreign keys, and when to just create the association in the model .rb file?
You will always have to create the column in the Database (1)
It is not mandatory to define the relation(s) inside the models, but it is highly recommended (2)
(1) : The Database needs this column of foreign keys to be able to retrieve the corresponding record. Without the column, the DB cannot find back the related record. You can use a Migration to create this column, not only a scaffold.
(2) : You can skip the relations declaration in the models, but it is highly recommended because:
it generate methods corresponding to the relations (ex: User belongs_to :role, then you can do user.role directly instead of Role.where(id: user.role_id).first)
not every human can remember all the associations. It is better to show/list everything that is linked to your model
You asked:
How do I go about deciding when to create a column in the table to hold foreign keys, and when to just create the association in the model .rb file?
I would answer:
Always create the column (cannot work if you don't) AND define every association(s) (relation(s)) inside the model.
Your first example is a scaffold that creates the model file and the migration.
has_one helps ActiveRecord understand the relationships between the tables so that it can, among other things, generate proper SQL queries for you.
But #1 has to be in place for #2 to even work. However, creating the db column via a scaffold command isn't necessary - it's just convenient sometimes, because it creates both the model file, and the associated migration.
You could just write the migration by hand. Just because whatever thing you're following doesn't always mention adding the db column doesn't mean it's not necessary. It's probably just assumed that you've already done it because these foreign key migrations are so common after you get up and running with Rails that they sort of go without saying after a while.

Ruby on Rails: How to implement this particular model (with inheritance, N-to-M relationship, etc)

I've already defined the data model for my application, which will contain this particular part:
Now, my particular issues are related to modelling "Value" and it's child models "Value_Decimal" and "Value_Text". Basically I wan't to have this hierarchy as it's expected to have several other value types, and each value model with have several other different columns (I'm not considering Single Table Inheritance due to this fact).
How can I implement this with Rails' Active Record, can someone point some directions?
Many thanks in advance!
The item_property_value table should have a value_type column allowing for polymorphic associations. Then you can create different tables named for each of your various 'value' models inheriting from an abstract value model (that has no table). This way you can avoid STI, but still inherit from one base model.
Because the item_property_value model is combining 3 different relationships it will need to be treated as a first-class Rails model which means it will also need an id as a primary key. You can then use a has-many-through mapping to access the actual objects it is joining.
Read up here on polymorphic associations. Abstract classes are simply flagged as such:
class Value < ActiveRecord::Base
self.abstract_class = true
end

Rails - Basic model conventions and basic association confusion

My understanding of the conventions I'm currently dealing with is as follows:
Model names should be in TitleCaps.
Variable names should be lower_cased_and_under_scored.
Associations should be to the singular version of the foreign table name with _id appended, e.g. user_uploaded_picture_id
When doing "rails generate model ModelName" - should it be in TitleCaps at that point?
Should every database table I need have every association specified? On this note, do associations both ways need to be specified, i.e. should every has_one/has_many be matched to a belongs_to?
Should there be a model for every database table I intend to create?
Multiple questions in one :)
Both model_name and ModelName are accepted and generates the correct model and table names.
You don't need to specify every associations only those which you want to use.
No, you don't need to create a model for every table. But if you'll use that table from Rails it's easier to have one.

Add fields to ActiveRecord model dynamically in Rails 2.2.2?

Say I wanted to allow an administrative user to add a field to an ActiveRecord Model via an interface in the Rails app. I believe the normal ActiveRecord::Migration code would be adequate for modifying the AR Model's table structure (something that would not be wise for many applications - I know). Of course, only certain types of fields could be added...in theory.
Obviously, the forms that add (or edit) records to this newly modified ActiveRecord Model would need to be build dynamically at run-time. A common form_for approach won't do. This discussion suggests this can only be accomplished with JavaScript.
http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/fc0b55fd4b2438a5
I've used Ruby in the past to query an object for it's available methods. I seem to remember it was insanely slow. I'm too green with Ruby and Rails to know an elegant way to approach this. I hope someone here may. I'm also open to entirely different approaches to this problem that don't involve modifying the database.
To access the columns which are currently defined for a model, use the columns method - it will give you, for each column, its name, type and other information (such as whether it is a primary key, etc.)
However, modifying the schema at runtime is delicate.
The schema is pre-loaded (and cached, from the DB driver) by each model class when it is first loaded. In production mode, Rails only does this once per model, around startup.
In order to force Rails to refresh its cached schema following your modification, you should force Ruby to reload the affected model's class (pretty much what Rails does for you automatically, after each request, when running in development mode - see how to reload a class using remove_const followed by load.)
If you have a Mongrel cluster, you also have to inform the other processes in the cluster, which run in their own separate memory space, to also reload their model's classes (some clusters will allow you to create a 'restart.txt' file, which will cause an automatic soft-restart of all processes in your cluster with no additional work required on your behalf.)
Now, these having been said, depending on the actual problem that you need to solve you may not need to dynamically alter the schema after all. Instead of adding, say, columns col1, col2 and col3 to some table entries (model Entry), you can use a table called dyn_attribs, where Entry has_many :dyn_attribs, and where dyn_attribs has both a key column (which in this case can have values col1, col2 or col3) and a value column (which lists the corresponding values for col1, col2 etc.)
Thus, instead of:
my_entry = Entry.find(123)
col1 = my_entry.col1
#do something with col1
you would use:
my_entry = Entry.find(123, :include => :dyn_attribs)
dyn_attribs = my_entry.dyn_attribs.inject(HashWithIndifferentAccess.new) { |s,a|
s[a.key] = a.value ; s
}
col1 = dyn_attribs[:col1]
#do something with col1
The above inject call can be factored away into the model, or even into a base class inherited from by all models that may require additional, dynamic columns/attributes (see Polymorphic associations on how to make several models share the same dyn_attribs table for dynamic attributes.)
UPDATE
Adding or renaming a column via a regular HTML form.
Assume that you have a DynAttrTable model representing a table with dynamic attributes, as well as a DynAttrDef defining the dynamic attribute names for a given table.
Run:
script/generate scaffold_resource DynAttrTable name:string
script/generate scaffold_resource DynAttrDef name:string
rake db:migrate
Then edit the generated models:
class DynAttrTable < ActiveRecord::Base
has_many :dyn_attr_defs
end
class DynAttrDef < ActiveRecord::Base
belongs_to :dyn_attr_table
end
You may continue to edit the controllers and the views like in this tutorial, replacing Recipe with DynAttrTable, and Ingredient with DynAttrDef.
Alternatively, use one of the plugins reviewed here to automatically put the dyn_attr_tables and dyn_attr_defs tables under management by an automated interface (with all its bells and whistles), with virtually zero implementation effort on your behalf.
This should get you going.
Say I wanted to allow an
administrative user to add a field to
an ActiveRecord Model via an interface
in the Rails app.
I've solved this sort of problem before by having an extra model called AdminAdditions. The table includes an id, an admin user id, a model name string, a type string, and a default value string.
I override the model's find and save methods to add attributes from its admin_additions, and save them appropriately when changed. The model table has a large text field, initially empty, where I save nondefault values of the added attributes.
Essentially the views and controllers can pretend that every attribute of the model has its own column. This means form_for and so on all work.
ActiveRecord::Migration.add_column(User, "email", :string)
You could use Flex Attributes for this, though if you want to be able to search or order by these new columns you'll have to write (a lot of) custom SQL.
I have seen the dynamic alteration/migration of tables offered as a solution many times but I have never actually seen it implemented. There are many reasons why this solution is rarely implemented.
If the table is large then the table may/will be locked for extended periods of what is supposed to be up-time.
Why is your model changing dynamically? It is quite rare for a models structure to need to change dynamically. It is more often an indication that you are trying to model something specific in a generalised way.
This is often an attempt a producing a "Categorised" model than could be better solved by another approach.
DDL statements are often not allowed by the same user that is being used for day to day DML requirements. Whilst this could be the case, and often is in the ROR arena it is not always the "right" way to do it.
What are you trying to achieve here? A better understanding of the problem would probably reveal a more natural solution.
If you were doing this with PostgreSQL now you could probably get away with a JSON type field and then just store whatever in the json hash.

Rails ActiveRecord Relationships

How do the relationships magically function when only the models are altered?
If I want a "has__and___belongs___to__many" relationship, what should I name the table (so Rails can use it) that contains the two foreign keys?
Short answer: You can't just tell the models that they're related; there have to be columns in the database for it too.
When you set up related models, Rails assumes you've followed a convention which allows it to find the things you wrote. Here's what happens:
You set up the tables.
Following conventions in Rails, you name the table in a particular, predictable way (a plural noun, e.g. people). In this table, when you have a relationship to another table, you have to create that column and name it in another predictable way (e.g. bank_account_id, if you're relating to the bank_accounts table).
You write a model class inheriting from ActiveRecord::Base
class Person < ActiveRecord::Base
When you instantiate one of these models, the ActiveRecord::Base constructor looks at the name of the class, converts it to lowercase and pluralizes it. Here, by reading Person, it yields people, the name of the table we created earlier. Now ActiveRecord knows where to get all the information about a person, and it can read the SQL output to figure out what the columns are.
You add relationships to the model: has_many, belongs_to or has_one.
When you type something like, has_many :bank_accounts, it assumes a few things:
The name of the model that you relate to is BankAccount (from camel-casing :bank_accounts).
The name of the column in the people table which refers to a bank account is bank_account_id (from singularizing :bank_accounts).
Since the relationship is has_many, ActiveRecord knows to give you methods like john.bank_accounts, using plural names for things.
Putting all of that together, ActiveRecord knows how to make SQL queries that will give you a person's bank accounts. It knows where to find everything, because you followed a naming convention that it understands when you created the table and its colums.
One of the neat things about Ruby is that you can run methods on a whole class, and those methods can add other methods to a class. That's exactly what has_many and friends are doing.
This works because you are following "Convention over Configuration".
If you state that a customer model has many orders then rails expects there to be a customer_id field on the orders table.
If you have followed these conventions then rails will use them and will be able to build the necessary SQL to find all the orders for a given customer.
If you look at the development.log file when you are developing your application you will be able to see the necessary SQL being built to select all orders for a given customer.
Rails does not create tables without you asking it to. The creation of tables is achieved by generating a migration which will create/alter tables for you. The fact that you create a customer model and then state within it that it has_many :orders will not create you an orders table. You will need to do that for yourself within a migration to create an orders table. Within that migration you will need to either add a customer_id column or use the belongs_to: customer statement to get the customer_id field added to the orders table.
The rails guide for this is pretty useful

Resources