Какой тип отношения модели Rails лучше всего подойдет для агрегатных ключей + нескольких внешних ключей? - PullRequest
0 голосов
/ 13 июня 2009

Я рассматриваю следующую структуру базы данных, но я не уверен, какой тип отношений модели Rails будет поддерживать ключи базы данных, которые я определил. Кто-нибудь может подсказать, как это может работать в Rails?

Posts
id
post_type -- must be 'Q' or 'A'
author
date
content
UNIQUE KEY (post_id, post_type) -- to support foreign keys

Questions
id
post_id
post_type -- must be 'Q'
FOREIGN KEY (post_id, post_type) REFERENCES Posts(post_id, post_type)

Answers    
id
post_id
post_type -- must be 'A'
question_id
FOREIGN KEY (post_id, post_type) REFERENCES Posts(post_id, post_type)
FOREIGN KEY (question_id) REFERENCES Questions(post_id)

Comments    
id
post_id
author
date
content
FOREIGN KEY (post_id) REFERENCES Posts(post_id)

Приведенный выше эскиз будет переводиться в следующую реализацию:

CREATE TABLE Posts (
  post_id     SERIAL PRIMARY KEY,
  post_type   CHAR(1),              -- must be 'Q' or 'A'
  -- other columns common to both types of Post
  UNIQUE KEY (post_id, post_type) -- to support foreign keys
) ENGINE=InnoDB;

CREATE TABLE Comments (
  comment_id  SERIAL PRIMARY KEY, 
  post_id     BIGINT UNSIGNED NOT NULL,
  -- other columns for comments (e.g. date, who, text)
  FOREIGN KEY (post_id) REFERENCES Posts(post_id)
) ENGINE=InnoDB; 

CREATE TABLE Questions (
  post_id     BIGINT UNSIGNED PRIMARY KEY,
  post_type   CHAR(1),              -- must be 'Q'
  -- other columns specific to Questions
  FOREIGN KEY (post_id, post_type) REFERENCES Posts(post_id, post_type)
) ENGINE=InnoDB;

CREATE TABLE Answers (
  post_id     BIGINT UNSIGNED PRIMARY KEY,
  post_type   CHAR(1),              -- must be 'A'
  question_id BIGINT UNSIGNED NOT NULL,
  -- other columns specific to Answers
  FOREIGN KEY (post_id, post_type) REFERENCES Posts(post_id, post_type)
  FOREIGN KEY (question_id) REFERENCES Questions(post_id)
) ENGINE=InnoDB;

Ответы [ 2 ]

11 голосов
/ 13 июня 2009

У вас есть несколько вариантов того, как смоделировать это в рельсах, однако первое, что я хотел бы предложить, это чтобы сэкономить время и проблемы позже, вы должны подойти к своему вопросу под другим углом.

Чтобы получить максимум от Rails, не следует начинать с проектирования базы данных. Вы должны начать с Модели данных, а затем искать способ сопоставления этой Модели данных со структурой базы данных, а не наоборот. Это тонкое различие, но оно подразумевает другое мышление, когда вы рассматриваете базу данных как второстепенное значение для вашей модели, а не наоборот. Это поможет понять проблему в долгосрочной перспективе.

В этом сценарии можно использовать две конструкции ActiveRecord: наследование в одной таблице и полиморфное наследование.

Наследование в одной таблице

Single Table Inheritance (STI) хранит модели с большим количеством общих функций в одной базовой таблице базы данных. В вашем примере вопросы и ответы, и в меньшей степени комментарии - это все похожие объекты. У них есть некоторый контент, автор, некоторые поля даты и времени для созданного и обновленного в и т. Д. Единственное различие между вопросом и ответом состоит в том, что вопросы «принадлежат» ответу. Комментарии немного сложнее, поскольку вы можете комментировать как Вопросы, так и ответы, а также, возможно, комментарии, хотя ваша Схема базы данных не отражает того, что это возможно.

С STI ваши модели Вопросов и Ответов хранятся не в отдельной таблице, а в одной таблице и помечаются именами классов. Реальные классы Вопросов и Ответов затем наследуются от Базового класса, в вашем случае «Post». Существует множество ресурсов, посвященных обсуждению ИППП, но этот может помочь

Полиморфное наследование

Это второй метод моделирования подобного поведения в рельсах. Это использует одну таблицу, в вашем случае сообщения для хранения данных, которые являются общими для двух классов. Эта таблица содержит столбцы, которые ссылаются на имя класса и экземпляр идентификатора базового объекта. Данные, специфичные для объекта, будут затем храниться в отдельной таблице для каждой модели.

Реализация (с использованием STI)

Чтобы смоделировать ваши данные с помощью STI, вы должны создать базовую модель постов, подобных этой

  class CreatePosts < ActiveRecord::Migration  
    def self.up  
      create_table :posts do |t|  
        t.string :type 
        t.string :author   
        t.text :content  
        t.integer :parent_id   
        t.timestamps  
      end  
    end  


    def self.down  
      drop_table :posts  
    end  
end  

Тогда ваши модели будут выглядеть так

class Post < ActiveRecord::Base  
end  

class Question < Post  
  has_many :answers, :foreign_key => :parent_id  
  has_many :comments, :foreign_key => :parent_id  
end  

class Answer < Post  
  belongs_to :question, :foreign_key => :parent_id  
  has_many :comments, :foreign_key => :parent_id  
end  

class Comment < Post  
  belongs_to :question, :foreign_key => :parent_id  
  belongs_to :answer, :foreign_key => :parent_id  
end  

И пример кода

q1 = Question.new(:author => 'Steve', :content => 'What is 2 + 2')  
q1c1 = q1.comments.build(:author => 'Malcolm', 
    :content => "Good question, i'd been wondering that myself")    
q1a1 = q1.answers.build(:author => 'John', :content => '2+2 = 5')  
q1a2 = q1.answers.build(:author => 'Phil', :content => '2+2 is a sum')  

q1a1c1 = q1a1.comments.build(:author => 'Chris', 
    :content => 'Sorry John it should be 4')  
q1a2c1 = q1a2.comments.build(:author => 'Steve', 
    :content => 'Hi Phil thanks for stating the obvious!')  

q1.save  

qu = Question.find(:first)  
puts "#{qu.author} asked #{qu.content}"  
qu.comments.each {|qc| puts "\t#{qc.author} commented #{qc.content}"}  
qu.answers.each do |ans|  
  puts "\t#{ans.author} answered with #{ans.content}"  
  ans.comments.each do |comm|   
    puts "\t\t#{comm.author} commented #{comm.content}"   
  end  

end

Этот код дает следующие результаты

Steve asked What is 2 + 2
  Malcolm commented Good question, i'd been wondering that myself
  John answered with 2+2 = 5
    Chris commented Sorry John it should be 4
  Phil answered with 2+2 is a sum
    Steve commented Hi Phil thanks for stating the obvious!

В базе данных есть одна таблица сообщений со следующей структурой

CREATE TABLE "posts" (
  "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,  
  "type" varchar(255),  
  "author" varchar(255),  
  "content" text,  
  "parent_id" integer,  
  "created_at" datetime, 
  "updated_at" datetime
  );

А содержание данных после примера выглядит следующим образом: -

1|Question|Steve|What is 2 + 2||2009-06-13 09:52:20|2009-06-13 09:52:20
2|Answer|John|2+2 = 5|1|2009-06-13 09:52:20|2009-06-13 09:52:20
3|Comment|Chris|Sorry John it should be 4|2|2009-06-13 09:52:20|2009-06-13 09:52:20
4|Answer|Phil|2+2 is a sum|1|2009-06-13 09:52:20|2009-06-13 09:52:20
5|Comment|Steve|Hi Phil thanks for stating the obvious!|4|2009-06-13 09:52:20|2009-06-13 09:52:20
6|Comment|Malcolm|Good question, i'd been wondering that myself|1|2009-06-13 09:52:20|2009-06-13 09:52:20

Возможно, вам будет проще разделить модель комментариев на QuestionComments и AnswerComments. Это сделало бы прямой sql намного проще.

0 голосов
/ 01 марта 2013

Возможно ли это решение с помощью has_and_belongs_to_many, например:

Спортсмены и тренеры?

s1 = Sportsmen.new(:name=> “Günter”)
t1 = Trainer.new(:name=> „Hans“)
t2 = Trainer.new(:name=> „Joachim“)

is this correct ?
S1t1 = s1.t1
S1t2 = s1.t2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...