
Findings of the working of relations in rails3
Reported by bterkuile | August 23rd, 2010 @ 04:27 AM | in 1.1
To find the source of strange problems with DataMapper I started
a fresh project with a "standard" relation pattern. The relation
pattern is:
Customer => (has n) => Projects => (has n) => Tasks
Having rails 3.0.0.rc:
rails new datamapper_test -m http://datamapper.org/templates/rails.rb
Change sqlite3 to mysql (5.1.41)
Defining the following models:
class Customer
include DataMapper::Resource
property :id, Serial
property :name, String
has n, :projects
has n, :tasks, :throught => :projects
end
class Project
include DataMapper::Resource
property :id, Serial
property :name, String
belongs_to :customer
has n, :tasks
end
class Task
include DataMapper::Resource
property :id, Serial
property :name, String
belongs_to :project
has 1, :customer, :through => :project
end
Now starting a console:
rails c
Check output and migrate:
DataMapper.logger = Logger.new STDOUT
DataMapper.auto_migrate!
Gives:SQL (0.118ms) SET sql_auto_is_null = 0 SQL (0.113ms) SET SESSION sql_mode = 'ANSI,NO_BACKSLASH_ESCAPES,NO_DIR_IN_CREATE,NO_ENGINE_SUBSTITUTION,NO_UNSIGNED_SUBTRACTION,TRADITIONAL' SQL (0.318ms) SHOW TABLES LIKE 'customers' SQL (0.217ms) SHOW TABLES LIKE 'projects' SQL (0.208ms) SHOW TABLES LIKE 'tasks' SQL (0.161ms) DROP TABLE IF EXISTS
customers SQL (0.219ms) SHOW TABLES LIKE 'customers' SQL (0.083ms) SET sql_auto_is_null = 0 SQL (0.092ms) SET SESSION sql_mode = 'ANSI,NO_BACKSLASH_ESCAPES,NO_DIR_IN_CREATE,NO_ENGINE_SUBSTITUTION,NO_UNSIGNED_SUBTRACTION,TRADITIONAL' SQL (0.798ms) SHOW VARIABLES LIKE 'character_set_connection' SQL (0.769ms) SHOW VARIABLES LIKE 'collation_connection' SQL (92.800ms) CREATE TABLEcustomers
(id
INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,name
VARCHAR(50), PRIMARY KEY(id
)) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci SQL (0.263ms) DROP TABLE IF EXISTSprojects
SQL (0.368ms) SHOW TABLES LIKE 'projects' SQL (79.525ms) CREATE TABLEprojects
(id
INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,name
VARCHAR(50),customer_id
INT(10) UNSIGNED NOT NULL, PRIMARY KEY(id
)) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci SQL (176.830ms) CREATE INDEXindex_projects_customer
ONprojects
(customer_id
) SQL (0.244ms) DROP TABLE IF EXISTStasks
SQL (0.375ms) SHOW TABLES LIKE 'tasks' SQL (79.611ms) CREATE TABLEtasks
(id
INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,name
VARCHAR(50),customer_id
INT(10) UNSIGNED NOT NULL,project_id
INT(10) UNSIGNED NOT NULL, PRIMARY KEY(id
)) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci SQL (236.262ms) CREATE INDEXindex_tasks_customer
ONtasks
(customer_id
) SQL (185.145ms) CREATE INDEXindex_tasks_project
ONtasks
(project_id
) SQL (0.796ms) SELECT COUNT() FROM "information_schema"."table_constraints" WHERE "constraint_type" = 'FOREIGN KEY' AND "table_schema" = 'dm_constraints_test' AND "table_name" = 'projects' AND "constraint_name" = 'projects_customer_fk' SQL (182.526ms) ALTER TABLEprojects
ADD CONSTRAINTprojects_customer_fk
FOREIGN KEY (customer_id
) REFERENCEScustomers
(id
) ON DELETE NO ACTION ON UPDATE NO ACTION SQL (0.758ms) SELECT COUNT() FROM "information_schema"."table_constraints" WHERE "constraint_type" = 'FOREIGN KEY' AND "table_schema" = 'dm_constraints_test' AND "table_name" = 'tasks' AND "constraint_name" = 'tasks_project_fk' SQL (207.509ms) ALTER TABLEtasks
ADD CONSTRAINTtasks_project_fk
FOREIGN KEY (project_id
) REFERENCESprojects
(id
) ON DELETE NO ACTION ON UPDATE NO ACTION
First error that appears is the customer_id column in the tasks
table. Manually removing this column and proceding.
has 1, :customer, :through => :project, :constraint => :skipc = Customer.new(:name => 'Customer') c.save # true p = c.projects.new(:name => 'Project') c.save # Same as p.save => true t = p.tasks.new(:name => 'Task') # if customer_id not removed from tasks table: p.save => false, p.errors => nothing to report # t.save => false, t.errors => no customer_id t.save => true # Now create a fresh instance t = Task.first t.destroy # => false # SELECT
customers.id
,customers
.name
FROMcustomers
INNER JOINprojects
ONcustomers
.id
=projects
.customer_id
INNER JOINtasks
ONprojects
.id
=tasks
.project_id
WHEREprojects
.id
= 1 GROUP BYcustomers
.id
,customers
.name
ORDER BYcustomers
.id
LIMIT 1
There seems to be a check on the customer which makes the destroy action fail. belongs_to :customer, :throught => :project in the Task model gave a deprication warning.
Adding a skip constraint to the customer association:
Fixes the issue, but should not be needed since this is just a normal parent -> children -> children relation I think.
Any suggestions for this?
Benjamin
Comments and changes to this ticket
-
Martin Gamsjaeger (snusnu) October 24th, 2010 @ 02:24 PM
- State changed from new to unconfirmed
- Assigned user set to Martin Gamsjaeger (snusnu)
-
-
gix January 15th, 2011 @ 12:43 AM
There's a typo in the Tasks model (throught instead of through):
has n, :tasks, :throught => :projects
With the correct key it works fine on 1.0.2 and master.
-
Martin Gamsjaeger (snusnu) January 15th, 2011 @ 06:05 AM
- State changed from unconfirmed to resolved
- Milestone set to 1.1
- Milestone order changed from 196264 to 0
-
Please Sign in or create a free account to add a new ticket.
With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »