저는 Ruby와 Rails 프레임 워크에서 초보자입니다. 프레임 워크 규칙을 어기는 것을하기 전에 도움을 청하기로 결심했습니다.레일을 사용하여 구조화하기 ActiveRecord
프로그래밍 배경이 상당히 견고하며, 초급 - 중급 SQL 쿼리에 익숙합니다. 그러나 레일즈가 제공하는 ActiveRecord
클래스에서 머리를 감싸는 데 어려움을 겪고 있습니다. 나의 즉각적인 본능은 ActiveRecord 클래스를 완전히 스크랩하고 내 자신의 SQL 쿼리를 직접 작성하여 모델로 마무리하는 것이다. 그러나 ActiveRecords는 Rails 프레임 워크의 핵심 요소이며, 이러한 피하는 것이 미래의 고통을 유발할 것임을 알고 있습니다.
다음은 내 MySQL
스키마입니다 (나중에 Rails Migration
로 작성합니다). 가능한 한 간결하게이 질문을하려고 노력 하겠지만 필자는 내가 가진 것처럼 스키마를 왜 모델링했는지 설명하기 위해 약간의 배경 지식으로 들어가야 할 수도 있습니다. 나는 그것에 지나치게 붙어 있지 않으므로, 사람들이 구조에 대해 더 좋은 생각을하면 좋을 것입니다.
-- 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;
스키마에서 알 수 있듯이 이것은 매우 기본적인 웹 폴링 응용 프로그램입니다. 각 설문에는 여러 옵션이 있으며 각 옵션마다 다른 사용자가 여러 가지 대답을 할 수 있습니다. 이제 이상한 부분은 content_*
테이블입니다. 이것을 설명 할 수있는 가장 좋은 방법은 아마도 이것을 abstract
테이블로 설명하는 것일 것입니다. 이전에는 그런 짓을 한 적이 없었습니다. 일반적으로 관계는 두 개 이상의 명시 적 테이블 사이에 있으며 필요에 따라 외래 키를 추가합니다. 그러나이 경우 여러 가지 유형의 content
으로 끝날 수 있습니다. 모두 해시 태그/언급/주석이 필요합니다. 나는 content_id
이 어떤 테이블을 참조하는지 미리 알지 못한다. (코드는 적절하게 받아 들여지는 데이터를 다룰 것이다.) 그래서 나는 단지 indexed
컬럼을 가지고있다. content_*
테이블을 조정하여 일부 단계에서 type
열을 추가해야합니다. 두 테이블에 자동 증가 기본 키를 사용하는 경우 content_id
항목이 중복 될 수 있으므로 content
테이블이 두 번 이상 존재할 수 있습니다. 그래도 문제의 범위.
ActiveRecord 클래스의 구조화에 대해 설명합니다. 첫 번째 부분은 멘션/해시 태그 구문 분석을 처리합니다. 나는 추상적 인 Content
클래스를 써서 테이블의 "추상"면을 처리했다. 이것은 이와 유사합니다 (간결성을 위해 구문 분석의 일부가 제거되었습니다).
class Content < ActiveRecord::Base
self.abstract_class = true;
# relationships
belongs_to :user
has_many :content_mentions;
has_many :content_hashtags;
has_many :mentions, { :through => :content_mentions, :source => :user, :as => :content };
has_many :hashtags, { :through => :content_hashtags, :as => :content };
# available columns (in the abstract side of things)
attr_accessible :text_body, :date_created;
# database hooks
around_save :around_save_hook
# parsing
ENTITY_PATTERN = /removed_for_brevity/iox;
def render_html()
# parsing of the text_body field for hashtags and mentions and replacing them with HTML
# goes in here, but unrelated to the data so removed.
end
protected
# Is this the best way to do this?
def around_save_hook()
# save the main record first (so we have a content_id to pass to the join tables)
yield
# parse the content and build associations, raise a rollback if anything fails
text_body.scan(ENTITY_PATTERN) do |boundary,token,value|
m = $~;
if m[:token] == '@'
# mention
unless mentions.where(:name => m[:value]).first
mention = User::where(:name => m[:value]).first;
next unless mention;
raise ActiveRecord::Rollback unless content_mentions.create({ :content_id => id, :user_id => mention.id });
end
else
# hashtag
unless hashtags.where(:tag => m[:value]).first
hashtag = Hashtag.where(:tag => m[:value]).first;
unless hashtag
hashtag = Hashtag.new({ :tag => m[:value] });
raise ActiveRecord::Rollback unless hashtag.save();
end
raise ActiveRecord::Rollback unless content_hashtags.create({ :content_id => id, :hashtag_id => hashtag.id });
end
end
end
end
end
내가 여기에있는 가장 큰 문제는 around_save_hook
함께,이 구문 분석하고 연결을 저장하는 가장 좋은 장소입니다? text_body
이 업데이트되고 해시 태그/멘션 중 일부가 원본에서 삭제 된 경우 이러한 변경 사항은 삭제를 확인하지 않고 추가 된 새로운 해시 태그/멘션이 아닌 content_*
연관에 반영됩니다.
- 자체가 (즉이 심하게 비효율적이고 잘못이다 corrent 스키마가 :
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
그래서 나는 다음과 같이 제 질문은 추측 다음과 같이
ActiveRecord
클래스의 나머지 부분은 정의 레일과 함께 사용하도록 설계 되었습니까? (그렇다면 올바른 방법에 대한 제안은 환상적 일 것입니다.) Around Save
은 연관을 파싱하고 업데이트 할 수있는 적절한 장소입니까?- 내 ActiveRecord가 현재 스키마 구조를 기반으로 올바르게 설정되어 있습니까? (특히 내가
polymorphic
속성을 올바르게 사용하고 있는지 잘 모르겠다.) - 설문 조사의 전체 내용을 다시 저장하지 않고
Poll
인스턴스에 옵션/답변을 추가하는 방법은 무엇입니까? 콘텐츠)이 여전히OOP
의 접근 방식을 유지하고 있습니까?Rails
,Ruby
및ActiveRecord
정말 편안 누군가의 고속 복사 날을 실행할 수 있다면
그것은 정말 좋은 것 (예 : 옵션/답변이 Poll
모델 공개 API를 통해 만들어지는) 그들이 어떻게 것 이것의 베어 본을 구현하십시오. 내가 말했듯이 ActiveRecord
클래스를 사용한 적이 없기 때문에이 간단한 코드가 단일 save()
호출에서 얼마나 많은 원시 SQL 쿼리를 트리거하는지조차 알지 못합니다.
이 질문은 너무 크다고 생각합니다. 앱에서 컨설팅을 거의 요구합니다. 나는 레일스로 시작한다면, 그냥 가서 시도해보고, 실수를하고 배울 때 점진적으로 돌아가고 향상시킬 수 있다고 말하고 싶습니다. 그러나, 한 가지 팁은'around_save'와'yield' 대신'after_save'를 사용하는 것입니다. –
상당히 큰 문제라는 것을 알고 있습니다 만, 나는 단지 사소한 양의 코드 일뿐입니다. 대부분은 중첩 된 조인 테이블에서 ActiveRecord를 올바르게 사용하여 레코드를 중복 업데이트하지 않아도된다는 조언입니다. 응용 프로그램의 해시 태그/언급/주석 측면은 "추상"테이블 구조에 대한 일반적인 질문 이외의 질문과 관련이 없으므로 기본적으로 ActiveRecords를 사용하여 '투표'를 구현하는 방법을 묻는 것입니다. 'after_save' 메서드에 관해서는, 내부에서 롤백 할 수 있는지 여부에 대한 명확한 문서를 찾을 수 없었습니다. –
당신은 아마 사소한 양의 코드일지도 모르겠지만 누군가가 사소한 코드를 제공 할 수 있도록 많은 시간을 투자해야하는 상황을 이해하고있는 것은 틀림 없습니다. 가능한 경우 작은 질문으로 나누는 것이 낫습니다. –