Note that there are some explanatory texts on larger screens.

plurals
  1. PODifferences between Rails' has_one and has_many when it comes to automatic data building
    primarykey
    data
    text
    <p><em>Greetings StackOverflow community!</em></p> <p>I have been learning Rails for the past few days and extending the basic blog example in the official Rails guide. I wanted to learned more about one-to-one connections since I'm planning on extending the Devise login system with a custom user data table and the rules of normalization dictate that I should make a different table for the actual auth data (the devise user table) and another for the app-specific user info (my own user table).</p> <p>But now back to <a href="http://guides.rubyonrails.org/getting_started.html" rel="nofollow">simple rails example</a>. The example basically describes an app that can create blog posts, tag them and accept comments (comment's don't really matter right now). A post <code>has_many</code> tags and a tag <code>belongs_to</code> a post. Pretty straightforward. (same thing can be said about the comments, too, but I'll just stick with the tags for this example)</p> <p>When a user wishes to create a NEW post, the controller has a call to <code>@post = Post.new</code> which prepares an empty post to be edited and created. It also needs to call post.tags.build to make sure that at least one 'baby' tag which would belong to the post is ready for editing <code>5.times do @post.tags.build end</code>, for instance would prepare not one but five tags for editing.</p> <p>In the controller's create method, it's enough to create a new post from <code>params[:post]</code> and <code>@post.save</code> it. It saves the tags automatically without any extra function calls needing to be made.</p> <p>Here's where I started extending. I just wanted to add another table, called post_data, that would get linked one-to-one to the original posts table. post_data contains a foreign key to the post it belongs to, as well as a <code>belongs_to :post</code> instruction in its model. The post <code>has_one :post_data</code>. It also <code>accepts_nested_attibutes_for :post_data</code>. </p> <p>Again, in the new method of the post controller, post_data needs to be initialized.<code>@post.build_post_data</code> does just this. The form displays just fine the two different models - one as post[title]...etc and the other as post_data[data_field_name_here].</p> <p>However, in the create name, the post_data entry needs to be <em>manually</em> saved: <code>@post.create_post_data(params[:post_data])</code> in order for it to be entered into the db. Otherwise it just doesn't get saved.</p> <p>Now my question is this - how come the has_many objects of the post, the tags, just need to be prepared in the controller's new method, and then get saved in create automatically, while has_one links also need to be manually saved?</p> <p>I'm just wandering as to why Rails would work like this.</p> <p>Thanks in advance for your time and patience! :)</p> <h1>Edit: The code files!</h1> <h2>posts_controller</h2> <pre><code>class PostsController &lt; ApplicationController # GET /posts # GET /posts.json def index @posts = Post.all respond_to do |format| format.html # index.html.erb format.json { render :json =&gt; @posts } end end # GET /posts/1 # GET /posts/1.json def show @post = Post.find(params[:id]) respond_to do |format| format.html # show.html.erb format.json { render :json =&gt; @post } end end # GET /posts/new # GET /posts/new.json def new @post = Post.new @post.build_post_data 5.times do @post.tags.build end end # GET /posts/1/edit def edit @post = Post.find(params[:id]) end # POST /posts # POST /posts.json def create # This works now - it creates all the needed resources automatically @post = Post.new(params[:post]) respond_to do |format| if @post.save format.html { redirect_to @post, :notice =&gt; 'Post was successfully created.' } else format.html { render :action =&gt; "new" } end end end # PUT /posts/1 # PUT /posts/1.json def update @post = Post.find(params[:id]) respond_to do |format| if @post.update_attributes(params[:post]) format.html { redirect_to @post, :notice =&gt; 'Post was successfully updated.' } else format.html { render :action =&gt; "edit" } end end end # DELETE /posts/1 # DELETE /posts/1.json def destroy @post = Post.find(params[:id]) @post.destroy respond_to do |format| format.html { redirect_to posts_url } end end end </code></pre> <h2>posts/_form.html.erb</h2> <pre><code>&lt;%= form_for(@post) do |post_form| %&gt; &lt;% if @post.errors.any? %&gt; &lt;div id="error_explanation"&gt; &lt;h2&gt;&lt;%= pluralize(@post.errors.count, "error") %&gt; prohibited this post from being saved:&lt;/h2&gt; &lt;ul&gt; &lt;% @post.errors.full_messages.each do |msg| %&gt; &lt;li&gt;&lt;%= msg %&gt;&lt;/li&gt; &lt;% end %&gt; &lt;/ul&gt; &lt;/div&gt; &lt;% end %&gt; &lt;div class="field"&gt; &lt;%= post_form.label :name %&gt;&lt;br /&gt; &lt;%= post_form.text_field :name %&gt; &lt;/div&gt; &lt;%= post_form.fields_for(:post_data) do |pdf| %&gt; &lt;div class="field"&gt; &lt;%= pdf.label :herp, "DERP POST DATA" %&gt;&lt;br /&gt; &lt;%= pdf.text_field :herp %&gt; &lt;/div&gt; &lt;% end %&gt; &lt;div class="field"&gt; &lt;%= post_form.label :title %&gt;&lt;br /&gt; &lt;%= post_form.text_field :title %&gt; &lt;/div&gt; &lt;div class="field"&gt; &lt;%= post_form.label :content %&gt;&lt;br /&gt; &lt;%= post_form.text_area :content %&gt; &lt;/div&gt; &lt;h2&gt;Tags&lt;/h2&gt; &lt;%= render :partial =&gt; 'tags/form', # send some custom variables to the # rendered partial's context :locals =&gt; { :form =&gt; post_form } %&gt; &lt;div class="actions"&gt; &lt;%= post_form.submit %&gt; &lt;/div&gt; &lt;% end %&gt; </code></pre> <h2>and the posts.rb model</h2> <pre><code>class Post &lt; ActiveRecord::Base validates :name, :presence =&gt; true validates :title, :presence =&gt; true, :length =&gt; { :minimum =&gt; 5 } has_many :comments, :dependent =&gt; :destroy attr_accessible :post_data_attributes, :name, :title, :content #let's just assume this makes sense, k? has_one :post_data accepts_nested_attributes_for :post_data # TAGS has_many :tags accepts_nested_attributes_for :tags, :allow_destroy =&gt; :true, # reject if all attributes are blank :reject_if =&gt; lambda { |attrs| attrs.all? { |k, v| v.blank? } } end </code></pre> <h1>Final edit:</h1> <p>Fixed up all the code, syncing it with my working code! :D If anybody from the distant future still has this issue and my code doesn't work, send me a personal message and we'll sort it out! :)</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload