Ruby on Rails: new vs. create vs. build difference
Oct5
Difference by examples. Let’s say that a User has_many Blogs and a Blog belongs_to a User.
$ ruby -v ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-linux] $ rails -v Rails 2.3.4
Last updated: 27.10.2009
new
New objects can be instantiated as either empty (pass no construction parameter) or pre-set with attributes but not yet saved (pass a hash with key names matching the associated table column names). In both instances, valid attribute keys are determined by the column names of the associated table — hence you can‘t have attributes that aren‘t part of the table columns.
Model.new
Requires additional save to save to database.
>> b = Blog.new({:title => "Test"})
+-------+---------+------------+------------+
| title | user_id | created_at | updated_at |
+-------+---------+------------+------------+
| Test | 1 | | |
+-------+---------+------------+------------+
1 row in set
>> b.save
SQL (0.1ms) BEGIN
Blog Create (0.3ms) INSERT INTO `blogs` (`created_at`, `title`, `updated_at`, `user_id`) VALUES('2009-10-27 04:35:20', 'Test', '2009-10-27 04:35:20', 1)
SQL (1.9ms) COMMIT
=> true
New and associating through <<
The object is saved to the database after adding the association using <<.
>> u = User.first
>> b = Blog.new({:title => "Test"})
+-------+---------+------------+------------+
| title | user_id | created_at | updated_at |
+-------+---------+------------+------------+
| Test | 1 | | |
+-------+---------+------------+------------+
1 row in set
>> u.blogs << b
SQL (0.1ms) BEGIN
Blog Create (0.4ms) INSERT INTO `blogs` (`created_at`, `title`, `updated_at`, `user_id`) VALUES('2009-10-27 04:41:15', 'Test', '2009-10-27 04:41:15', 1)
SQL (11.6ms) COMMIT
Blog Load (0.4ms) SELECT * FROM `blogs` WHERE (`blogs`.user_id = 1)
+----+---------+---------+---------+---------+
| id | title | user_id | crea... | upda... |
+----+---------+---------+---------+---------+
| 6 | Test | 1 | 2009... | 2009... |
+----+---------+---------+---------+---------+
1 row in set
Calling new on an association
When calling new on an association, it will create the object with the foregin key correctly set, but will not save it to the database (not even when parent is saved). The created object must be saved manually with save.
>> u = User.first
>> b = u.blogs.new({:title => "Test"})
+-------+---------+------------+------------+
| title | user_id | created_at | updated_at |
+-------+---------+------------+------------+
| Test | 1 | | |
+-------+---------+------------+------------+
1 row in set
>> u.save
SQL (0.1ms) BEGIN
SQL (0.2ms) COMMIT
=> true
>> u.blogs.all
Blog Load (0.1ms) SELECT * FROM `blogs` WHERE (`blogs`.user_id = 1)
0 rows in set
>> b.save
SQL (0.1ms) BEGIN
Blog Create (0.3ms) INSERT INTO `blogs` (`created_at`, `title`, `updated_at`, `user_id`) VALUES('2009-10-27 04:54:06', 'Test', '2009-10-27 04:54:06', 1)
SQL (3.9ms) COMMIT
=> true
>> u.blogs.all
Blog Load (0.3ms) SELECT * FROM `blogs` WHERE (`blogs`.user_id = 1)
+----+---------+---------+---------+---------+
| id | title | user_id | crea... | upda... |
+----+---------+---------+---------+---------+
| 7 | Test | 1 | 2009... | 2009... |
+----+---------+---------+---------+---------+
1 row in set
Build
build(associations, parent = nil)
Model.build
Build cannot be called directly on the model. It can only be called on associations.
>> Blog.build({:title => "Test"})
NoMethodError: SQL (9.0ms) SHOW TABLES
undefined method `build' for #
Calling build on an association
When calling build on an association, the new object will be saved when the parent is saved.
>> u = User.first
>> u.blogs.build({:title => "Test"})
+-------+---------+------------+------------+
| title | user_id | created_at | updated_at |
+-------+---------+------------+------------+
| Test | 1 | | |
+-------+---------+------------+------------+
1 row in set
>> u.save
SQL (0.1ms) BEGIN
Blog Create (0.3ms) INSERT INTO `blogs` (`created_at`, `title`, `updated_at`, `user_id`) VALUES('2009-10-27 04:56:10', 'Test', '2009-10-27 04:56:10', 1)
SQL (2.6ms) COMMIT
=> true
>> u.blogs.all
Blog Load (0.3ms) SELECT * FROM `blogs` WHERE (`blogs`.user_id = 1)
+----+---------+---------+---------+---------+
| id | title | user_id | crea... | upda... |
+----+---------+---------+---------+---------+
| 7 | Test | 1 | 2009... | 2009... |
+----+---------+---------+---------+---------+
1 row in set
Create
Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.
Model.create
As the description of the method says, the object will be saved to the database if it passes validations.
>> u = User.first
>> Blog.create({:title => "Test"})
SQL (0.1ms) BEGIN
Blog Create (0.3ms) INSERT INTO `blogs` (`created_at`, `title`, `updated_at`, `user_id`) VALUES('2009-10-27 05:06:55', 'Test', '2009-10-27 05:06:55', NULL)
SQL (10.7ms) COMMIT
+----+-------+---------+-----------+-----------+
| id | title | user_id | create... | update... |
+----+-------+---------+-----------+-----------+
| 9 | Test | | 2009-1... | 2009-1... |
+----+-------+---------+-----------+-----------+
1 row in set
>> Blog.all
Blog Load (0.3ms) SELECT * FROM `blogs`
+----+-------+---------+-----------+-----------+
| id | title | user_id | create... | update... |
+----+-------+---------+-----------+-----------+
| 9 | Test | | 2009-1... | 2009-1... |
+----+-------+---------+-----------+-----------+
1 row in set
Note: In this case, there is no validation that the user_id field must be present, so such an object passes validation. If validation was added for user_id, the row would not be created in this case, because the object would not have passed validation. In that case, :user_id would have to be explicitly passed in the attrbiutes hash.
Calling create on an association
Works similar to build in this case (automatically adds the foregin_key - user_id in this case), but the new row is saved to the database already, and not only after the parent model is saved (as is the case with build).
>> u = User.first
>> u.blogs.create({:title => "Test"})
SQL (0.1ms) BEGIN
Blog Create (0.3ms) INSERT INTO `blogs` (`created_at`, `title`, `updated_at`, `user_id`) VALUES('2009-10-27 05:11:22', 'Test', '2009-10-27 05:11:22', 1)
SQL (0.7ms) COMMIT
+----+-------+---------+-----------+-----------+
| id | title | user_id | create... | update... |
+----+-------+---------+-----------+-----------+
| 10 | Test | | 2009-1... | 2009-1... |
+----+-------+---------+-----------+-----------+
1 row in set
>> u.blogs.all
Blog Load (0.3ms) SELECT * FROM `blogs` WHERE (`blogs`.user_id = 1)
+----+-------+---------+-----------+-----------+
| id | title | user_id | create... | update... |
+----+-------+---------+-----------+-----------+
| 10 | Test | | 2009-1... | 2009-1... |
+----+-------+---------+-----------+-----------+
1 row in set
>> u.save
SQL (0.1ms) BEGIN
SQL (0.2ms) COMMIT
=> true
Cheat cheet
Steps required for the object to be saved to database.
New
- Blog.new(…).save
- user.blogs << Blog.new(…)
- user.blogs.new(…).save – do not use, no practical use case
Build
- Blog.build – not possible
- user.blogs.build(…), user.save – both are required to save to DB
Create
- Blog.create(…)
- user.blogs.create(…)
Snippet of the day: C#.NET configuration files
May0
I’ll start with a longer snippet – handling configuration files in C#.NET. I used this in Windows Touch Remix.
This is the base class I use for my configuration classes:
[Serializable()]
public class XmlConf
{
public static T FromFile<T>(string filename)
{
if (!File.Exists(filename)) return default(T);
XmlSerializer ser = new XmlSerializer(typeof(T));
StreamReader fs = new StreamReader(filename);
T t = (T) ser.Deserialize(fs);
fs.Close();
return t;
}
public void Save(string filename)
{
XmlSerializer ser = new XmlSerializer(this.GetType());
StreamWriter fs = new StreamWriter(filename, false);
ser.Serialize(fs, this);
fs.Close();
}
}
Just subclass it and add your configuration properties:
[Serializable()]
public class MyConf : XmlConf
{
public int IntVal { get; set; }
public string StrVal { get; set; }
}
Make sure you use getters and setters like in the above example, so the values get serialized properly. Most .NET classes (like List) are serializable, but if you have properties of your own class, make sure they are serializable.
Here’s a usage example of saving and loading the configuration:
// Save
MyConf conf = new MyConf();
conf.IntVal = 100;
conf.StrVal = "hey";
conf.Save("my.conf");
// Load
conf = MyConf.FromFile<MyConf>("my.conf");
if (conf == null) new Exception("Configuration file not found.");
Console.WriteLine(conf.IntVal);
Console.WriteLine(conf.StrVal);