Add categories for posts - Rails - ruby-on-rails

Could you please help with understanding why categories does not working in a right way? So I read quite a lot manuals about, and did not find how to resolve it
I created migration for category
class CreateCategories < ActiveRecord::Migration
def change
create_table :categories do |t|
t.string :name
t.text :description
t.integer :count
t.timestamps null: false
end
end
end
And added for posts new field - category
Then created model for category
class Category < ActiveRecord::Base
has_many :posts
end
Edit post model
class Post < ActiveRecord::Base
acts_as_ordered_taggable
belongs_to :category
validates :title, presence: true
validates :category, presence: true
..
end
Created template
<%= form_for #post do |f| %>
<p>
<%= f.label :title %> <br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :category %>
<%= f.select :category, Category.all.collect {|c| [c.name, c.name]} %>
</p>
<% end %>
Edited a bit post controller
def update
#post = Post.find(params[:id])
if #post.update(params[:post].permit(:title, :thumbnail, :body, :description, :tag_list, :#post.category))
redirect_to #post
else
render 'edit'
end
end

change this line in your controller:
if #post.update(params[:post].permit(:title, :thumbnail, :body, :description, :tag_list, :category_id))
I changed
:#post.category
to
:category_id
This passes a symbol to the permit method to allow the request parameter for the category

Related

Adding nested json in tables - rails

I am new to ruby on rails and I am trying to store nested json in the table.
json:
articles: {
title: "abc",
text: "a",
address: {
flat: "abc",
city: "bang"
}
}
Migrations:
class CreateArticles < ActiveRecord::Migration[5.2]
def change
create_table :articles do |t|
t.string :title
t.text :text
t.string :address
t.timestamps
end
end
end
class CreateAddresses < ActiveRecord::Migration[5.2]
def change
create_table :addresses do |t|
t.string :flat
t.string :city
t.timestamps
end
end
end
models:
class Article < ApplicationRecord
has_one :address
accepts_nested_attributes_for :address
end
class Address < ApplicationRecord
end
controller:
class ArticlesController < ApplicationController
def create
#article = Article.new(params.require(:article).permit(:title, :text, :address))
#article.save
redirect_to #article
end
def show
#article = Article.find(params[:id])
end
end
form(new.html.erb):
<%= form_with scope: :article, url: articles_path, local: true do |form| %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<%=form.fields_for :address do |a| %>
<div>
<%=a.label :flat%><br>
<%= a.text_field :flat%><br>
<%=a.label :city%><br>
<%= a.text_field :city%>
</div>
<%end%>
<p>
<%= form.submit %>
</p>
I am not able to store the adrress to the table. address is always saved as nil. Can anyone guide me if what i am doing wrong. I want to parse the json to the table and store the json as string. Updated the question with controller and form that i am using.
When you want to permit nested attributes you do specify the attributes of nested object within an array. Pls, try this one #article = params.require(:articles).permit(:text, :title, :address =>[:flat, :city])
Rails has a very good documentation pls take a look https://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-permit

Nested form fields not showing in polymorphic association

Hi I have a polymorphic association with for a Document model for storing document uploads. I'm trying to submit the document attributes as a nested attribute via the associated model.
However, when I load the form, the nested field does not show. What am I missing?
Schema:
create_table "documents", force: :cascade do |t|
t.json "links"
t.integer "linkable_id"
t.string "linkable_type"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "documents", ["linkable_type", "linkable_id"], name: "index_documents_on_linkable_type_and_linkable_id", using: :btree
Models:
class Document < ActiveRecord::Base
belongs_to :linkable, polymorphic: true
belongs_to :user
belongs_to :company
mount_uploaders :links, DocUploader
end
class CustomerPlan < ActiveRecord::Base
has_many :documents, as: :linkable
accepts_nested_attributes_for :documents
end
Controller:
class CustomerPlanController < ApplicationController
def new
#customer_plan = current_company.customer_plans.build
end
def create
#customer_plan = current_company.customer_plans.build(customer_plan_params)
if #customer_plan.save
redirect_to #customer_plan, notice: 'Customer plan was successfully created.'
else
render :new
end
end
private
def cusomter_plan_params
params.require(:cusomter_plan_params).permit(:date, :name, :plan_type,
documents_attributes: [:id, links: []])
end
end
Form:
<%= simple_nested_form_for #stock_plan, :html => { :multipart => true } do |f| %>
<%= f.error_notification %>
<%= f.input :date %>
<%= f.input :name %>
<%= f.input :plan_type %>
<%= f.simple_fields_for :documents do |d| %>
<p><b>Upload here:</b></p>
<%= d.file_field :links, multiple: true %>
<br>
<% end %>
<%= f.button :submit%>
<% end %>

Rails - checkbox form from array

I have a admin controller to create "Locations", inside of which is a form. In the form you can create a new "Location" and give it a name, description, and "exits".
<%= form_for #location do |f| %>
<p>
<%= f.label :name %><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
Exits:
<br/>
<% #locations.each do |e| %>
<%= f.label :locations %>
<%= f.check_box :locations %>
<% end %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
#locations is all of the available locations to set up as an "exit". I'm trying to create an array of locations inside of the new location using checkboxes to check whether or not they should be set as "exits", but not really sure how to proceed since i'm just teaching myself and it's day one.
I'm also wondering how to set up the "exits" in the Location model. Right now i have:
class Location < ActiveRecord::Base
has_many :locations
validates :name, presence: true, length: { minimum: 1 }
validates :description, presence: true
end
created with:
class CreateLocations < ActiveRecord::Migration
def change
create_table :locations do |t|
t.string :name
t.text :description
t.references :location, index: true, foreign_key: true
t.timestamps null: false
end
end
end
But i would like to have them referenced as "exits" not "locations" (ie Location.exits instead of Location.locations).
UPDATE
I've updated the form to use the collection_check_boxes helper
<%= f.collection_check_boxes(:exit_ids, #locations, :id, :name) do |cb| %>
<%= cb.label %> <%= cb.check_box %> <br/>
<% end %>
So I have this in the create action now
def create
#location = Location.new(location_params)
if #location.save
params[:location][:exit_ids].each do |e|
if e.to_i > 0
tempLocation = Location.find(e.to_i)
#location.allowExitTo(tempLocation)
end
end
redirect_to #location
else
render 'new'
end
end
Which goes allows exits through:
def allowExitTo(other_location)
exit_locations.create(to_id: other_location.id)
end
creating an exit relationship with:
class Exit < ActiveRecord::Base
belongs_to :from, class_name: 'Location'
belongs_to :to, class_name: 'Location'
validates :from_id, presence: true
validates :to_id, presence: true
end
Hope I'm on the right track. It seems to work so I guess so.
Edit: Sorry, I must have had a brain-fart or something. You're not doing a simple has-many and belongs-to relationship, you want a has-many-and-belongs-to-many relationship. That's more complicated than I know how to explain. But this answer explained it very well: Many-to-many relationship with the same model in rails?
You can get Location#exits instead of Location#locations by changing the name of the has_many relation like this:
class Location < ActiveRecord::Base
# has_many :locations Instead of this...
has_many :exits, :class_name => 'Location' # ...Do this
validates :name, presence: true, length: { minimum: 1 }
validates :description, presence: true
end
This is called a self-join. You're associating a model with itself.
Keep in mind that this will make Location#locations unavailable.
Then in your form you would do something like:
<% #locations.each do |e| %>
<%= f.label :exit %>
<%= f.check_box :exit %>
<% end %>
The Ruby on Rails site has a nice guide about associations, includeing self-joins like what you're asking about: http://guides.rubyonrails.org/association_basics.html#self-joins
If you like RailsCasts: http://railscasts.com/episodes/163-self-referential-association

Nested Forms with Strong Parameters; object_attribute method not available?

This example is kind of cliche. I am following the railscasts episode #196 (http://railscasts.com/episodes/196-nested-model-form-revised?autoplay=true) for double nested forms.
The error I get is that my accepts_nested_attributes_for command is not generating an answers_attributes for strong parameters.
Models:
class Survey < ActiveRecord::Base
has_many :questions
accepts_nested_attributes_for :questions
end
class Question < ActiveRecord::Base
belongs_to :survey
has_many :answers
has_many :users, through: :answers
accepts_nested_attributes_for :answers
end
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :user
end
Then my surveys controller:
class SurveysController < ApplicationController
def index
#survey = current_user.surveys
end
def new
#survey = Survey.new
#question = #survey.questions.build # the nested form won't show up if I don't
#answer = #question.answers.build #Not sure if I need this line. doesn't work either way.
end
def create
#survey = Survey.new(survey_params)
if #survey.save
redirect_to #survey, notice: "Survey successfully created."
else
render 'new'
end
end
# rest.. show, edit, update, destroy
private
def survey_params
params.require(:survey).permit!
end
#def survey_params
#params.require(:survey).permit(:user_id, :name, { questions_attributes: [:_destroy, :id, :survey_id, :content, { answers_attributes: [:_destroy, :id, :content, :user_id, :question_id]}]})
#end
end
The issue I'm having is this (empty form submit). When I run my real code, I get this:
{"utf8"=>"✓", "authenticity_token"=>"[token]=", "survey"=>{"user_id"=>"1", "name"=>"test", "questions_attributes"=>{"0"=>{"content"=>"test", "_destroy"=>"0"}}, "answers"=>{"content"=>"test", "_destroy"=>"0"}}, "commit"=>"Create", "action"=>"create", "controller"=>"surveys"}
Unpermitted attributes: answers
Then when I run the permit! method to test a force though I get this:
unknown attribute: answers
Rails should be looking for answers_attributes, not answers. That makes me think it's not recognizing the model. So, here is the schema.
create_table "questions", force: true do |t|
t.integer "survey_id"
t.string "content"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "answers", force: true do |t|
t.integer "question_id"
t.string "content"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
end
Let me know if you have any ideas on how to fix my error of somehow telling rails to look for answers instead of answers_attributes.
UPDATE: Here is my form.
<%= form_for #survey do |f| %>
<%= f.label :user_id %>
<%= f.collection_select :user_id, User.all, :id, :email, {}, { :multiple => false } %><br>
<%= f.text_field :name, placeholder: "Survey name"%>
<%= f.fields_for :questions do |builder| %>
<%= builder.label :content, "Question 1"%>
<%= builder.text_area :content %>
<%= builder.check_box :_destroy %>
<%= builder.label :_destroy, "Remove Question" %>
<%= f.fields_for :answers do |builder| %>
<%= builder.text_field :content, placeholder: "Answer 1" %>
<%= builder.check_box :_destroy %>
<% end %>
<% end %><br>
<%= f.submit "Create", :class => "btn btn-large btn-warning" %>
<% end %>
The solution was an oversight on the forms. I used...
<%= f.fields_for :questions do |builder| %>
...to build the association under the survey and then mistakenly used...
<%= f.fields_for :answers do |builder| %>
...to build the answers under question. I needed to change the f variable so it shoulder have been something like this:
<%= builder.fields_for :answers do |x| %>
So, it was looking for answers under survey instead of under questions. Thanks guys.

How do I add a data association between records in a HABTM many to many relationship?

I have set up a HABTM relationship between two table creating a many to many relationship between items and categories. I want to add an item connected with one or more categories via the add item form. when I submit the form I am getting the error "Can't mass-assign protected attributes: categories".
Here are my models:
class Item < ActiveRecord::Base
attr_accessible :description, :image, :name
has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }
belongs_to :user
has_and_belongs_to_many :categories
validates :name, presence: true, length: {maximum: 50}
accepts_nested_attributes_for :categories
end
class Category < ActiveRecord::Base
attr_accessible :description, :name
has_and_belongs_to_many :items
validates :name, presence: true, length: {maximum: 50}
end
And my migrations:
class CreateItems < ActiveRecord::Migration
def change
create_table :items do |t|
t.string :name
t.text :description
t.has_attached_file :image
t.timestamps
end
end
end
class CreateCategories < ActiveRecord::Migration
def change
create_table :categories do |t|
t.string :name
t.string :description
t.timestamps
end
end
end
class CreateCategoriesItems < ActiveRecord::Migration
def up
create_table :categories_items, :id => false do |t|
t.integer :category_id
t.integer :item_id
end
end
def down
drop_table :categories_items
end
end
And my form looks like this:
<%= form_for(#item, :html => { :multipart => true }) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :description %>
<%= f.text_field :description %>
<%= f.file_field :image %>
<%= f.collection_select(:categories, #categories,:id,:name)%>
<%= f.submit "Add Item", :class => "btn btn-large btn-primary" %>
<% end %>
and here's my Items Controller:
class ItemsController < ApplicationController
def new
#item = Item.new
#categories = Category.all
end
def create
#item = Item.new(params[:item])
if #item.save
#sign_in #user
flash[:success] = "You've created an item!"
redirect_to root_path
else
render 'new'
end
end
def show
end
def index
#items = Item.paginate(page: params[:page], per_page: 3)
end
end
Thanks for all of your help :)
-Rebekah
Mass Assignment usually means passing attributes into the call that creates an object as part of an attributes hash.
Try this:
#item = Item.new(name: 'item1', description: 'description1')
#item.save
#category = Category.find_by_name('category1')
#item.categories << #category
Also see:
http://guides.rubyonrails.org/association_basics.html#the-has_and_belongs_to_many-association
http://api.rubyonrails.org/classes/ActiveModel/MassAssignmentSecurity/ClassMethods.html
I hope this helps.
IAmNaN posted the comment above that was the missing link in my code working properly. I have since written a blog post that details the process of getting the HABTM set-up. Thanks IAmNaN!

Resources