0

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>
Thibaud Clement
  • 6,607
  • 10
  • 50
  • 103

2 Answers2

1

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

Francesco Belladonna
  • 11,361
  • 12
  • 77
  • 147
  • Thanks for your help. Actually, we would like to avoid making the "active calendar" a field for the `User`, since we specifically create the `Administration` model / join table to associate calendars and users (many to many relationship). However, we do have a `current_user` method in our `sessions_helper.rb` file. Is there a way to achieve what you recommend, from there? – Thibaud Clement Jul 06 '15 at 23:45
  • You are missing the point: what is, in your business, the `active_calendar` ? You have a many to many relationship and you talk about an active calendar, but you need it, a many to many relationship is just the "pot" where you can pick your active calendar, you have to store it somewhere. It might not be on the user, but you need it somewhere. I clearly see that `User` can have multiples `Administrations` so you are not able to identify an active calendar from here. Also `session_helper.rb` is not standard in rails, can you paste method content? Or won't be able to help – Francesco Belladonna Jul 07 '15 at 00:02
  • Sorry, I am not very experimented. Here is what I meant by `active_calendar`: on each `calendars#show` view, we embedded a form to add a new post to this very calendar, and we would like the form to grab the `id` of this `calendar`, `calendar_id`, so that the newly created post is associated to this calendar. Our thought was that, in this way, the post would appear on the `calendars#show` once we reload the page or are redirected to it. Sorry I am restating the obvious, I am just trying to make my initial statement more clear. What would be a good way to store `active_calendar` ? – Thibaud Clement Jul 07 '15 at 01:50
  • Thanks for saying that `session_helper.rb` is not standard in rails, I did not know. This is something that was inspired by Michael Hartl's Rails Tutorial. – Thibaud Clement Jul 07 '15 at 01:51
  • Last, can I ask you what you meant by "paste method content"? I am glad to update the question with, just need to know what you are looking for. Again, sorry if this sounds dumb, I am a newbie. – Thibaud Clement Jul 07 '15 at 01:52
  • 1
    Ok this changes everything, the user is on a calendar show page so you have the calendar id because you are on that page. If that's the case, you should use an hidden input field, definitely, then in the create action for post, redirect to the calendar show page – Francesco Belladonna Jul 07 '15 at 16:30
  • Ok, thanks. I updated the question with a first guess at what the hidden input field should be. Would that work, or should we place it outside of the table? And, most importantly, is it the right syntax to accept assign `@calendar.id` to `:calendar_id`? – Thibaud Clement Jul 07 '15 at 17:20
  • 1
    No, the syntax is completely incorrect. Skip the `` part which is entirely wrong, you only need `<%= f.hidden_field :calendar_id %>` and that's all, remember to allow it in `permit` if you use strong parameters. Values will be auto fetched by rails if your form is (and should be) correctly set with a `form_for(@object)` – Francesco Belladonna Jul 07 '15 at 23:15
  • Thanks a lot for your patience and your help. It seems there is another problem on this page, as nothing happens when we click the create button, and the @post is not created (checked in the Rails console). I am going to state this in another question to be more accurate. Thanks again. – Thibaud Clement Jul 07 '15 at 23:42
0

You may want to nest your routes, for example:

resources :calendars do
    resources :posts
end

Now change your form to also include the calendar you want to be associated with the post like:

form_for([@calendar, @post])...

Then in your posts controller create path you can find your calendar and construct your post with something like:

def create
  my_calendar = Calendar.find(params[:calendar_id])
  post = my_calendar.posts.create(post_params)
  redirect_to root_path 
end

private

def post_params
  params.require(:post).permit(:date, :time, :focus, :format, :blog_title, :long_copy, :short_copy, :link, :hashtag, :media, :promotion, :target, :approval, :comment)
end
Jake Kaad
  • 103
  • 10
  • Thanks for the help. We implemented what you recommended. And, although the `@post create` is not working yet, it allowed to figure out that the posts that were created when submitting the form were actually all "empty", ie containing `nil` values in all fields, leaving us with the following error: 'NoMethodError in CalendarsController#show undefined method `strftime' for nil:NilClass` Any idea of what is causing the problem? – Thibaud Clement Jul 06 '15 at 23:35
  • You actually need to add the data passed from the form into the post you are creating. I will add an example to my answer above... – Jake Kaad Jul 07 '15 at 16:34