#1393 ✓resolved
bterkuile

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 TABLE customers (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 EXISTS projects
  SQL (0.368ms)  SHOW TABLES LIKE 'projects'
  SQL (79.525ms)  CREATE TABLE projects (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 INDEX index_projects_customer ON projects (customer_id)
  SQL (0.244ms)  DROP TABLE IF EXISTS tasks
  SQL (0.375ms)  SHOW TABLES LIKE 'tasks'
  SQL (79.611ms)  CREATE TABLE tasks (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 INDEX index_tasks_customer ON tasks (customer_id)
  SQL (185.145ms)  CREATE INDEX index_tasks_project ON tasks (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 TABLE projects ADD CONSTRAINT projects_customer_fk FOREIGN KEY (customer_id) REFERENCES customers (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 TABLE tasks ADD CONSTRAINT tasks_project_fk FOREIGN KEY (project_id) REFERENCES projects (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.

  c = 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 FROM customers INNER JOIN projects ON customers.id = projects.customer_id INNER JOIN tasks ON projects.id = tasks.project_id WHERE projects.id = 1 GROUP BY customers.id, customers.name ORDER BY customers.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:
has 1, :customer, :through => :project, :constraint => :skip
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

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.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

Pages