Search code examples
ruby-on-railsformsruby-on-rails-4relational-databaseinstances

Rails 4: new instance is created and saved to database but not displayed in view


In our Rails 4 app, there are four models:

class User < ActiveRecord::Base
  has_many :administrations, dependent: :destroy
  has_many :calendars, through: :administrations
end

class Administration < ActiveRecord::Base
  belongs_to :user
  belongs_to :calendar
end

class Calendar < ActiveRecord::Base
  has_many :administrations, dependent: :destroy
  has_many :users, through: :administrations
end

class Post < ActiveRecord::Base
    belongs_to :calendar
end

Here are the corresponding migrations:

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :first_name
      t.string :last_name
      t.string :email
      t.integer :total_calendar_count
      t.integer :owned_calendar_count

      t.timestamps null: false
    end
  end
end

class CreateAdministrations < ActiveRecord::Migration
  def change
    create_table :administrations do |t|
      t.references :user, index: true, foreign_key: true
      t.references :calendar, index: true, foreign_key: true
      t.string :role

      t.timestamps null: false
    end
  end
end

class CreateCalendars < ActiveRecord::Migration
  def change
    create_table :calendars do |t|
      t.string :name

      t.timestamps null: false
    end
  end
end

class CreatePosts < ActiveRecord::Migration
  def change
    create_table :posts do |t|
        t.references :calendar, index: true, foreign_key: true
        t.date :date
        t.time :time
        t.string :focus
        t.string :format
        t.string :blog_title
        t.text :long_copy
        t.text :short_copy
        t.string :link
        t.string :hashtag
        t.string :media
        t.float :promotion
        t.string :target
        t.integer :approval
        t.text :comment

      t.timestamps null: false
    end
  end
end

We have the following form to create posts:

<h2>Create a new post</h2>
<%= form_for(@post) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
    <tr>
        <td class="field"><%= f.date_field :date, placeholder: "When do you want to publish this post?" %></td>
        <td class="field"><%= f.time_field :time, placeholder: "What time do you want to publish this post?" %></td>
        <td class="field"><%= f.text_field :focus, placeholder: "What is this post about?" %></td>
        <td class="field"><%= f.text_field :format, placeholder: "What type of post is this?" %></td>
        <td class="field"><%= f.text_field :blog_title, placeholder: "If this post is about a blog post, what is the title of the blog post?" %></td>
        <td class="field"><%= f.text_area :long_copy, placeholder: "What is the copy of the post?" %></td>
        <td class="field"><%= f.text_area :short_copy, placeholder: "What is the short copy of the post (to be used on Twitter for instance)?" %></td>
        <td class="field"><%= f.url_field :link, placeholder: "Which link to you want to embed in this post?" %></td>
        <td class="field"><%= f.text_field :hashtag, placeholder: "Which hashtag(s) do you want to you in this post?" %></td>
        <td class="field"><%= f.text_field :media, placeholder: "Which media file (image, video) do you want to include in this post?" %></td>
        <td class="field"><%= f.number_field :promotion, placeholder: "What advertising budget should be allocated to this post?" %></td>
        <td class="field"><%= f.text_field :target, placeholder: "Who do you want to target with this post?" %></td>
        <td class="field"><%= f.select(:approval, %w[Approved Needs edits To be deleted], {prompt: 'How does this post look?'}) %></td>
        <td class="field"><%= f.text_area :comment, placeholder: "Any comment?" %></td>

        <td><%= f.submit "Create", class: "btn btn-primary" %></td>

    </tr>

<% end %>

This form is embedded into the Calendars#Show view, so that as soon as a post is created, it appears in the corresponding calendar.

EDIT 2: And here is ou PostsController:

class PostsController < ApplicationController

  def create
    @post = Post.create!
    if @post.save
      redirect_to root_url
    else
      render root_url
    end
  end

end

Currently, whenever we submit the form, a post is actually created (we checked through the console).

EDIT: Actually, the new @post is created and saved to the database but all values are set to nil. It seems we also have a problem with the saving of data from the form and into the database.

However, it does not appear in the corresponding calendar.

We believe this is due to the fact that, when a post is created, the calendar_id of the active calendar (the one in which the form is embedded in) is not added to the new post instance.

However, we don't know how to implement it:

  • Should we create an active_calendar method and then use it to automatically assign the active_calendar id of the active calendar to the newly created @post?
  • Should we simply declare the active calendar id as a variable, either in the post controller or the post model, so that we can add it when a new @post is created?
  • Should we include the active calendar id in the newly created @post through a hidden field in the form?

We kind of hit a wall here and are not sure of what we are doing wrong.

Any idea of how to fix this?

UPDATE: as suggested by @Fire-Dragon-DoL in the comments of his answer, we are going to use a hidden field.

Would the following code work?

<td type="hidden" value="#{@calendar.id}"><% f.number_field :calendar_id %></td>

Solution

  • I'm not sure how or where is stored your "active calendar" information, you definitely need it. Make it a field for User, it's a good option.

    Then I would create an application controller method like

    def active_calendar
      # Assuming you have a method to return current logged in user
      current_user.try(:active_calendar)
    end
    

    Then when creating post you can easily perform the following

    attrs = post_params.merge(calendar: active_calendar)
    @post = Post.new(attrs)
    # Or whatever logic you want
    @post.save
    

    Use the hidden field only if user is allowed to specify different calendars than the active one, otherwise it might just be a security hole

    Notice that the code I wrote is also nil-proof