I am trying to create a simple blog application. In which each post can be associated with the tags. This is my view
_form.hmtl.erb
<%= form.collection_select(:tag_ids, #tags, :id, :name, {}, :multiple => true) %>
This is my controller
posts_controller.rb
def new
#post = #topic.posts.new
#tag = #post.tags.new
#tags = Tag.all
end
def create
#post = #topic.posts.new(post_params)
if #post.save
redirect_to topic_posts_path
else
render 'new'
end
end
def post_params
params.require(:post).permit(:title,:content,:post_image, tag_ids: [])
end
end
Model Post.rb
has_and_belongs_to_many :tags
I am getting an error while creating new post "NoMethodError in Posts#create", "undefined method `map' for nil:NilClass". I am not able to find where the error is.
You should have a join table with name posts_tags.rb
class Post_Tag < ApplicationRecord
belongs_to :tag
belongs_to :post
end
so you have already some tags which you want to be selected with post and join table should be updated with post_id and tag_id
change this in your form_partial: -
<%= select_tag "tag_ids", options_for_select(#tags,:id,:name),{}, multiple: true%> <br/><br/>
Note: - you should not use form.collection_select(:tag_ids...) Reason being form_for is used here for #post object and tag_ids is not attribute of #post object.
so on submitting form you should get array of tag's ids in params[:tag_ids]
def create
#post = #topic.posts.new(post_params)
if #post.save
#create join table for tags that are associated with post
#post.tags << Tag.find(params[:tag_ids])
redirect_to topic_posts_path
else
render 'new'
end
end
so here you can get
#post.tags => which will return you collection of tags associated with the post
if you are using this scenario you have to make MANY TO MANY relation between these two models.
has_and_belongs_to_many :tags
then in form you should use like
form.collection_select(:post, :tag_id, Tag.all, :id, :name_with_initial, prompt: true)
The #tags variable is missing from your create action in controller - you have declared it in new, but not in create. It may cause error in view if save was unsucessful and action tries to re-render the form.
Not sure if that's all, because I'm not sure where the error is thrown exactly. But as you said in one of the comments - it works when you use Tag.all instead of #tags.
Related
My app is a web forum. Root page is a list of user-submitted
categories. I click one and it links to a list of user-submitted posts
about that category. I click a post and it links to a list of comments
about that post. Those are the 3 tiers.
CATEGORIES INDEX This is a list of clickable categories
<% #categories.each do |category| %>
<%= link_to category.title, category %>
<% end %>
CATEGORIES SHOW I clicked a category, now I'm here looking at a list of posts
<%= render :partial => #category.posts %>
<% end %>
_POSTS The posts are rendered from this here partial
<%= div_for(post) do %>
<%= link_to post.body, Post %>
Clicking that post link takes me to POSTS INDEX.
I'm not sure if this is a desirable flow of a Rails app. It seems odd
to go from Categories, to Posts, to Comments using Categories_Index,
Categories_Show, and Posts_Index, respectively. I don't know how to display or submit comments from this POSTS INDEX. #comments.each do |comments| provides an error and so does the render: partial method. I can not use the same methods for Comments that I used for Categories and Posts.
MODELS Models are complete with has_many, belongs_to, etc.
CATEGORIES CONTROLLER
def index
#categories = Category.all
end
def create
#category = current_user.categories.build(categories_params)
end
POSTS CONTROLLER
def create
#category = Category.find(params[:category_id])
#post = #category.posts.new(post_params)
COMMENTS CONTROLLER
def index
#subcomments = Subcomment.all
end
def create
#subcomment = current_user.subcomments.build(subcomment_params)
end
ROUTES
Rails.application.routes.draw do
resources :comments
resources :posts
devise_for :users
resources :categories do
resources :posts do
end
resources :comments
end
root "categories#index"
I successfully added posts to categories. How can I add comments to posts? Is my approach correct?
I assumed you have the following Model Relationships:
Model Category
has_many :posts
Model Post
has_many :comments
belongs_to :category
Model Comment
belongs_to :post
You are asking "How can I add comments to posts?"
In the page where you render all POSTS data,
you should USE posts ID as you main parameter.
So, meaning you should have post_id column/field inside Comments Table.
After saving the comments data, usually like [title, message, date ....].
In your Post Controller, you can get comments like:
// multiple Posts data
#posts = Post.all
#post.each do |post|
post.comments
...
end
//single Post
#post = Post.first // or Post.find(:id => params[:post_id])
#post.comments
If you are sending data using form, just put some hidden text field,
setting the name & value:
name="post_id"
// or something like:
name="comment[:post_id]"
//depends on how you constract the form.
Then set the value:
value="<%= params[:post_id ]%>"
Finnally, you can get the value like getting the other comments_field names.
Usually you should have this in in config/routes.rb,
resources :commets
Then your FORM path is like:
<%= form_for #comment, :url => #comments_path %>
Your Comments Controller should have like:
def index
...
end
def show
...
end
def edit
...
end
def new
#comment = Comment.new
end
def create
#comment = Comment.create(comment_params)
if #comment.save
....
redirect_to comments_path
else
.....
end
end
# For params permit in Rails 4 ^
def comment_params
params.require(:comment).permit!
end
I'm currently sitting with a problem where I am trying to add dynamic fields, and it works, however I need to created a second model for the nested attributes that are dynamically generated on the field.
I have a very easy and simple form which asks for a user's name, I want to be able to allow the user to click a button ("Add additional name") and have that field added dynamically then add another name. Can I do this using only the one model and controller, and without using nested forms?
This is what I currently have, but I do not want it working this way:
Controller
class GuestsController < ApplicationController
skip_before_filter :authenticate_user!, only: [:new, :create]
def index
#guest = Guest.all
end
def new
guest = Guest.new
#guest_form = GuestForm.new(guest)
end
def show
#guest = Guest.find(params[:id])
end
def create
guest = Guest.new
#guest_form = GuestForm.new(guest)
#guest_form.submit(guest_params)
if #guest_form.save
respond_to do |format|
format.html { redirect_to :back, notice: 'Thank you for replying' }
format.js
end
else
respond_to do |format|
format.html { render :new }
format.js
end
end
end
def destroy
#guest = Guest.find(params[:id])
#guest.destroy
redirect_to guests_path
end
private
def guest_params
params.require(:guest).permit(:status, :name, :message, plusones_attributes: [:id, :name, :oldness, :_destroy])
end
end
Models:
class Guest < ActiveRecord::Base
has_many :plusones, dependent: :destroy
belongs_to :user
end
class Plusone < ActiveRecord::Base
belongs_to :guest
end
So ideally I would like to use one Model, and allow additional fields to be entered of the exact same attribute, meaning if I have a Name: field, I should be able to click "Add another name" and add that name, and that would be saved as an individual guest in the table, meaning the first guest would have guest_id of 1, and the dynamically added field for another guest would add a guest with guest_id of 2.
I think you are making a mistake in thinking that there must be a one to one relationship between controllers and models. Have a look at ActiveRecord's Nested Attributes. That shows that you can have a single form submit data to an object and it's sub-objects in one action.
Have a look at fields_for, for information about how to configure a form to allow you to enter data for sub-objects.
When I've worked with nested set, I've found using a JavaScript tool like dynatree is the best way to display the nested data to users. I've also created a gem TreeDecorator, to make it easy to render nested sets as nested HTML lists, that can then be passed to dynatree.
I managed to find a solution that allowed me to keep my nested attributes and still be able to display the related child elements in the index page.
This is what I did to get the plusones related to the guest_id to display within index.html.erb
<% #guest.each do |guest| %>
<% guest.plusones.each do |plus| %>
<%= plus.name %>
<%= plus.oldness %>
<% end %>
<% end %>
Hello all i am trying to insert multiple records into the table using the same form so far i have achieved the following
class ProjectController < ApplicationController
def new
#project = Project.new
end
def create
#projec = Project.new(project_params)
respond_to do |format|
if #project.save
format.html { flash[:notice] = 'User successfully created.' and redirect_to action: "index"}
format.json { render :show, status: :created, location: #project }
else
format.html { render :new }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
def project_params
params.require(:project).permit(:PROJECT_ID,:COMPANY_ID, :ASSESSMENT_ID, :PROJECT_SCORE , :CREATED_BY, :UPDATED_BY)
end
end
so in my view i have used like this since i want only the project level score to be saved into the database with all other values remaining the same
<% 10.times do %>
<%= f.range_field :PROJECT_SCORE[], :min=>0, :max=>10, :class=>"slide", :id=>"slider1", name: 'PROJECT_SCORE[of_values][]'%>
<% end %>
next in my model i have used like this
class Project< ActiveRecord::Base
serialize :PROJECT_SCORE ,Array
end
but i receive an error
Attribute was supposed to be a Array, but was a Fixnum. -- 0
SO is there any alternate ways to insert multiple records in the table at the same time ? or how do i solve this issue ?
There is an alternate way for inserting multiple records and I would highly recommend doing so. You could get this to work but it feels a bit hacky and not very flexible to me. Try using a has_many association and nested forms instead. If you're not familiar with has_many associations there is an introductory course at codeschool.com called Models Taste Like Chicken. There is also a great RailsCasts episode (#196) that goes into detail about nested forms with some cool AJAX features.
To do this you could create a Model called Score and tell it to belong the projects:
rails g model Score score:integer project:references
class Score < ActiveRecord::Base
belongs_to :project
end
And each project will have many scores:
class Project < ActiveRecord::Base
has_many :scores, dependent: :destroy
accepts_nested_attributes_for :scores, allow_destroy: true
end
The dependent destroy makes sure the associated scores get deleted if a Project is deleted. You also need to tell it to accept nested attributes for the scores model. Details here.
Next, set up the strong parameters with things_attributes:[:thing1, :thing2, :etc]
class ProjectController < ApplicationController
### other stuff
def project_params
params.require(:project).permit(:PROJECT_ID, :etc, scores_attributes: [:id, :score])
end
One thing I would like to mention here is the best practice for
naming conventions is to use snake_case for your
variables and database names. So instead of :PROJECT_ID name it
:project_id
ALL_CAPS is usually used for constants.
Now your view you can use the fields_for form helper to create another form block within the form block.
<%= form_for #project do |f| %>
<div class="field">
<!-- all normal form inputs -->
</div>
<!-- and now the nested form -->
<%= f.fields_for :scores do |ff| %>
<div class="field">
<%= ff.range_field :score %>
</div>
<% end %>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
This won't allow you to create new scores yet, but that starts to get really complicated and is explained in the RailsCasts I mentioned above (However he is using an older version of rails so make sure and set the strong parameters for the nested attributes).
I'm a new rails developer who has a basic scaffolded crud application that I modified a bit.
I'm getting this error:
undefined method description for #<ActiveRecord::Relation:0x00000102df26d8>
when I visit john/recipes/46. Here's my view:
<h1 itemprop="name"><%= #recipe.name %></h1>
<ul>
<li><%= link_to 'Edit', edit_recipe_path(#recipe) %></li>
</ul>
<p itemprop="description"><%= #recipe.description %></p>
here's my routes:
match "/:username" => "recipes#index"
scope ':username' do
resources :recipes
end
here's my show index:
def show
#user = User.find_by_username params[:username]
#recipe = Recipe.where(:user_recipe_id => params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #recipe }
end
end
and my model:
before_save :set_next_user_recipe_id
belongs_to :users
validates :user_recipe_id, :uniqueness => {:scope => :user_id}
def to_param
self.user_recipe_id.to_s
end
def set_next_user_recipe_id
self.user_recipe_id ||= get_new_user_recipe_id
end
def get_new_user_recipe_id
user = self.user
max = user.recipes.maximum('user_recipe_id') || 0
max + 1
end
attr_accessible :description, :duration, :author, :url, :name, :yield, :ingredients_attributes, :user_recipe_id, :directions_attributes, :tag_list, :image
The reason I'm doing a Recipe.where(:user_recipe_id => params[:id]) instead of Recipe.where(:id => params[:id]) is because I'm trying to get so instead of john/recipes/46 showing the 46th recipe in the database, instead to show the 46th recipe that belongs to John.
Thanks for all help!
You're only trying to look for one recipe, but your query is searching for multiples. When you use a plain where(...) without ending it with .first, Rails interprets it as "show me all (multiple) Recipes with this user id" instead of "show me the (one) recipe with this id".
So you need to either put .first at the end of your query:
#recipe = Recipe.where(:user_recipe_id => params[:id]).first
or use an ActiveRecord finder that only returns one record:
#recipe = Recipe.find_by_user_recipe_id(params[:id])
I am new to Rails.I am creating a Rails project in which Product and Character are the models.I have the following Questions.
Q1. Is the given association between the two models is right?
class Product < ActiveRecord::Base
has_many :characters
end
class Character < ActiveRecord::Base
belongs_to :product
end
Q2. I created a link to add new character in the 'show' page of the products such if we click add new character it should display a 'new' page of characters in which i should have a dropdown list of character names(i.e., height,width,weight and color) but it is not.
It is showing NoMethodError in Characters#new error.
Error raised in the below line of my characters/new file.
collection_select(:product, :c_id, #character, :id, :name)
Note: I had created the values for name attribute as height,weight,width,color in the characters before migrating it.
Q3. If that works(i.e., Q2), i want to show the character name and value in the 'show page of products.For this how can i redirect to 'show' page of products..?
My characterscontroller for new,show and create:
def show
#product = Product.find(params[:id])
#character = Character.find(params[:id])
end
def new
#character = Character.new(params[:character])
#product = Product.find(params[:id])
end
def create
#character = Character.new(params[:character])
if #character.save
redirect_to :action => 'show', :id => #product.id
else
render :action => 'new'
end
end
Well now, after entering the values for the character in the characters/new and clicking create button it is trowing the following error
ActiveRecord::RecordNotFound in ProductsController#show
Couldn't find Product without an ID
I want to show the character name and value in the products/show. the above error is stopping me to do that..
My show method in productscontroller:
def show
#product = Product.find(params[:id])
#character = Character.find(:all)
end
The classes are correct to reflect the One-to-Many Relationship.
Problem with NoMethodError appears to be related to how you setup model, routes, controller & view.
Lets say in your model you have
attr_accessible :id, :name (I don't see this in your model and this is why the error!)
In character controller you need something like this:
def new
#character = Character.new
#product = Product.find(params[:character_id])
end
In the view(app/views/products/new.html.erb), you can add the link for new characters as:
<% link_to "Character", new_product_character_path(product) %>
the above assumes, you set your routes correct by having following in your config/routes.rb:
"resources :products"
"resources :characters"