Ruby on rails 使用rails ActiveRecord结构化
我实际上是Ruby和Rails框架的初学者,这就是为什么我决定在做一些打破框架惯例的事情之前寻求帮助的原因 我有一个相当扎实的Ruby on rails 使用rails ActiveRecord结构化,ruby-on-rails,ruby,activerecord,Ruby On Rails,Ruby,Activerecord,我实际上是Ruby和Rails框架的初学者,这就是为什么我决定在做一些打破框架惯例的事情之前寻求帮助的原因 我有一个相当扎实的OO编程背景,我对初学者->中级SQL查询非常熟悉。然而,我一直很难理解Rails提供的ActiveRecord类。我的直觉是完全废弃ActiveRecord类,手工编写自己的SQL查询并将它们封装在模型中。然而,我知道ActiveRecords是Rails框架中相当重要的一部分,避免使用它们只会让我将来感到痛苦 下面是我的MySQL模式(稍后我将编写一个Rails迁移)
OO
编程背景,我对初学者->中级SQL查询非常熟悉。然而,我一直很难理解Rails提供的ActiveRecord
类。我的直觉是完全废弃ActiveRecord类,手工编写自己的SQL查询并将它们封装在模型中。然而,我知道ActiveRecords是Rails框架中相当重要的一部分,避免使用它们只会让我将来感到痛苦
下面是我的MySQL
模式(稍后我将编写一个Rails迁移
)。我会尽量让这个问题尽可能简洁,但我可能需要深入一些背景知识来解释为什么我会像现在这样对模式进行建模。我不太喜欢它,所以如果人们对它的结构有更好的想法那就太好了
-- Users table is a minimalized version of what it probably will be, but contains all pertinent information
CREATE TABLE IF NOT EXISTS users (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) UNIQUE NOT NULL
) Engine=InnoDB;
CREATE TABLE IF NOT EXISTS hashtags (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
tag VARCHAR(30) UNIQUE NOT NULL
) Engine=InnoDB;
CREATE TABLE IF NOT EXISTS content_mentions (
content_id INT UNSIGNED NOT NULL,
user_id INT UNSIGNED NOT NULL,
INDEX(content_id),
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
) Engine=InnoDB;
CREATE TABLE IF NOT EXISTS content_hashtags (
content_id INT UNSIGNED NOT NULL,
hashtag_id INT UNSIGNED NOT NULL,
INDEX(content_id),
FOREIGN KEY(hashtag_id) REFERENCES hashtags(id) ON DELETE CASCADE
) Engine=InnoDB;
CREATE TABLE IF NOT EXISTS content_comments (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_id INT UNSIGNED NOT NULL,
content_id INT UNSIGNED NOT NULL,
text_body VARCHAR(1000) NOT NULL,
date_created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX(content_id)
) Engine=InnoDB;
CREATE TABLE IF NOT EXISTS polls (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_id INT UNSIGNED NOT NULL,
question VARCHAR(100) NOT NULL,
text_body VARCHAR(1000) NOT NULL,
date_created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
) Engine=InnoDB;
CREATE TABLE IF NOT EXISTS poll_options (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
poll_id INT UNSIGNED NOT NULL,
content VARCHAR(150) NOT NULL,
active VARCHAR(1) NOT NULL DEFAULT 'Y',
FOREIGN KEY(poll_id) REFERENCES polls(id) ON DELETE CASCADE
) Engine=InnoDB;
CREATE TABLE IF NOT EXISTS poll_answers (
poll_option_id INT UNSIGNED NOT NULL,
user_id INT UNSIGNED NOT NULL,
FOREIGN KEY(poll_option_id) REFERENCES poll_options(id) ON DELETE CASCADE,
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
PRIMARY KEY(poll_option_id,user_id)
) Engine=InnoDB;
如模式所示,这是一个非常基本的web轮询应用程序。每个民意测验都有多个选项,每个选项都可以有不同用户的多个答案。现在,奇怪的部分可能是content.*
表。我能解释这一点的最好方法可能是将其描述为一个抽象表。我以前从未真正做过这样的事情,通常关系是在两个或多个显式表之间,我会根据需要添加外键。然而,在这种情况下,我可能会得到多种不同类型的内容
,所有这些内容都需要hashtagg/提及/评论。我事先不知道content\u id
指的是什么表(代码将适当地处理它接收到的数据),所以我现在只对该列进行了索引。
我需要在某个阶段调整content.*
表以添加type
列,因为一旦出现多个content
表,如果两个表都使用自动递增主键,则可能会有重复的content\u id
条目,我认为这超出了问题的范围
关于ActiveRecord类的结构。第一部分是处理提及/hashtags的解析。我编写了一个抽象Content
类来处理表的“抽象”部分。它是这样的(为了简洁起见,一些解析已经被删除)
所以我想我的问题如下:
class Poll < Content
has_many :poll_options;
has_many :poll_answers, { :through => :poll_options }
attr_accessible :user_id, :question;
validates :text_body, :presence => true, :length => { :maximum => 1000 };
end
class PollOption < ActiveRecord::Base
belongs_to :poll;
has_many :poll_answers;
attr_accessible :content, :active, :poll_id;
end
class PollAnswer < ActiveRecord::Base
belongs_to :poll_option;
belongs_to :user;
attr_accessible :user_id, :poll_option_id;
end
class User < ActiveRecord::Base
attr_accessible :name;
validates :name, :presence => true, :length => { :maximum => 20 };
end
class Hashtag < ActiveRecord::Base
attr_accessible :tag;
validates :tag, :presence => true, :length => { :maximum => 30 };
end
# Join table for content->users
class ContentMention < ActiveRecord::Base
belongs_to :user;
belongs_to :content, { :polymorphic => true };
attr_accessible :content_id, :user_id;
end
# Join table for content->hashtags
class ContentHashtag < ActiveRecord::Base
belongs_to :hashtag;
belongs_to :content, { :polymorphic => true };
attr_accessible :content_id, :hashtag_id;
end
模式本身是否正确(即,它是否非常低效,并且设计得很差,无法与rails一起使用?(如果是这样,关于如何纠正它的建议将非常好)
保存前后的是否是解析和更新关联的正确位置
我的ActiveRecords是否基于当前架构结构正确设置?(具体来说,我不确定是否正确使用了polymorphic
属性)
如何向轮询
实例添加选项/答案,而不重新保存轮询的整个内容(从而触发对内容的另一个冗余解析),同时仍保留OOP
方法?(即,选项/答案通过轮询
模型中的公共API创建)
如果一个对Rails
、Ruby
和ActiveRecord
非常熟悉的人能给我一份他们将如何实现这一基本功能的快速副本,那就太好了。正如我说的,我以前从未使用过ActiveRecord
类,所以我甚至不确定这个简单代码将包含多少原始SQL查询我已经在一个save()
调用中触发。这是一个由两部分组成的railscast,涵盖了实现轮询/调查应用程序的各个方面。它涵盖了大多数与模型相关的疑问
我将在赋值过程中通过覆盖text\u body
的setter来创建依赖对象
例如:
而不是:
belongs_to :content, { :polymorphic => true };
对于代码重用,请使用模块而不是继承
对于
了解阵列上的映射
,减少
(即注入
)函数
我认为这个问题有点太大了——它几乎要求你的应用程序提供咨询服务。我想说的是,如果你从Rails开始,就去尝试一下,当你犯了错误并学习了之后,你可以回去逐步改进。不过,有一个技巧是在保存后使用,而不是在保存前后使用ld
。我知道这是一个相当大的问题,但我要问的只是少量的代码,大部分只是关于在嵌套联接表中正确使用ActiveRecords的一些建议,避免了冗余更新记录的需要。应用程序的hashtag/提及/注释端与问题无关(关于构造“抽象”表格的一般问题除外),所以本质上,它只是问如何使用ActiveRecords实现轮询
,而不是真正的“大”。至于after_save
方法,我找不到关于是否可以在其中回滚的清晰文档。你可能是对的,这是一个微不足道的代码量,但理解上下文是对于能够提供这些琐碎代码的人来说,这是一项巨大的时间投资。如果可能,最好将其分解为更小的问题。理解Rails是非常重要的,任何熟悉Rails的人都应该已经熟悉活动记录的上下文和应用程序,因此这将是一项非常重要的工作etty对他们直截了当。它基本上只有3个表…如果你知道你在做什么,就不会有很多工作。我可以在大约10-20分钟内手动完成SQL,但我宁愿使用Ac
def text_body=(val)
write_attribute(:text_body, val).tap do |v|
append_new_tags_and_mentions
end
end
def append_new_tags_and_mentions
tag_list, mention_list = extract_tags_and_mentions
new_mentions = mention_list - mentions.where(name => mention_list).pluck(:name)
mentions.concat(*new_mentions) if new_mentions.present?
new_tags = tag_list - hashtags.where(tag => tag_list).pluck(:tag)
hashtags.concat(*new_tags) if new_tags.present?
end
def extract_tags_and_mentions
tag_list = []
mention_list = []
text_body.scan(ENTITY_PATTERN) do |boundary, token, value|
if token == "@"
mention_list << value
else
tag_list << value
end
end
[tag_list, mention_list]
end
belongs_to :content, :polymorphic => true
belongs_to :content, { :polymorphic => true };