Note: Rails newb here.
So, I recently created a Rails app with mongoid gem for use of MongoDB. I have a namespace route of :blog with a nest of resource of posts
Routes.rb:
Rails.application.routes.draw do
namespace :blog do
resources :posts
end
end
The error comes from app/controllers/blog/posts_controller.rb:
Class Blog::PostsController < ApplicationController
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
private
def post_params
params.require(:post).permit(:title, :body)
end
end
I also have a 'post' model that comes with a title and body:
Class Post
include Mongoid::Document
field :title, type: String
field :body, type: String
end
In new.html.erb:
<h1>Rails Sample Blog</h1>
<%= form_for #post, url: blog_post_path do |f| %>
<div><%= f.label :title %><%= f.text_field :title %></div>
<div><%= f.label :body %><%= f.text_area :body %></div>
<% end %>
Is there something I left out that I didn't catch? It's slowly haunting me.
Edit: See this Git repo: https://github.com/hackathons-com/Rails-MongoDB-Devise-test
Edit 2:
undefined method `post_url' for #<Blog::PostsController:0x007f3d19105ee8>
Try this:
def create
#blog = Blog.find(params[:blog_id])
#post= #blog.posts.build(post_params)
(This assumes that you already added a column of blog_id and have your associations correct, although looking at your model it looks like you may have missing associations?)
app/controllers/blog/post_controller.rb:
Shouldn't this be posts_controller.rb?
Also
Class Blog::PostController < ApplicationController
should be
Class Blog::PostsController < ApplicationController
Related
My goal is to when adding a new product with the new product form, to have an input where one can add a list of emails separated by a space. The list of emails in this string field would be saved as an array of emails in the email_list array attribute of the Product model. This way each product has many emails. (later an email will be sent to these users to fill out questionaire, once a user fills it out there name will be taken off this list and put on completed_email_list array.
I am relatively new to rails, and have a few questions regarding implementing this. I am using postgresql, which from my understanding I do not need to serialize the model for array format because of this. Below is what I have tried so far to implement this. These may show fundamental flaws in my thinking of how everything works.
My first thinking was that I can in my controllers create action first take params[:email].split and save that directly into the email_list attribute (#product.email_list = params[:email].split. It turns out that params[:email] is always nil. Why is this? (this is a basic misunderstanding I have)(I put :email as accepted param).
After spending a long time trying to figure this out, I tried the following which it seems works, but I feel this is probably not the best way to do it (in the code below), which involves creating ANOTHER attribute of string called email, and then splitting it and saving it in the email_list array :
#product.email_list = #product.email.split
What is the best way to actually implement this? someone can clear my thinking on this I would be very grateful.
Cheers
Products.new View
<%= simple_form_for #product do |f| %>
<%= f.input :title, label:"Product title" %>
<%= f.input :description %>
<%= f.input :email %>
<%= f.button :submit %>
<%end %>
Products Controller
class ProductsController < ApplicationController
before_action :find_product, only: [:show, :edit, :update, :destroy]
def index
if params[:category].blank?
#products= Product.all.order("created_at DESC")
else
#category_id=Category.find_by(name: params[:category]).id
#products= Product.where(:category_id => #category_id).order("created_at DESC")
end
end
def new
#product=current_user.products.build
#categories= Category.all.map{|c| [c.name, c.id]}
end
def show
end
def edit
#categories= Category.all.map{|c| [c.name, c.id]}
end
def update
#product.category_id = params[:category_id]
if #product.update(product_params)
redirect_to product_path(#product)
else
render 'new'
end
end
def destroy
#product.destroy
redirect_to root_path
end
def create
#product=current_user.products.build(product_params)
#product.category_id = params[:category_id]
#product.email_list = #product.email.split
if #product.save
redirect_to root_path
else
render 'new'
end
end
private
def product_params
params.require(:product).permit(:title, :description, :category_id, :video, :thumbnail,:email, :email_list)
end
def find_product
#product = Product.find(params[:id])
end
end
To solve your original issue
#product.email_list = params[:email].split. It turns out that params[:email] is always nil
:email is a sub key of :product hash, so it should be:
#product.email_list = params[:product][:email].split
Demo:
params = ActionController::Parameters.new(product: { email: "first#email.com last#email.com" })
params[:email] # => nil
params[:product][:email] # => "first#email.com last#email.com"
I'd say that what you have is perfectly fine, except for the additional dance that you're doing in #product.email_list=#product.email.split, which seems weird.
Instead, I'd have an emails param in the form and an #emails= method in the model (rather than email and #email=):
def emails=(val)
self.email_list = val.split
end
Alternatively, you could do that in the controller rather than having the above convenience #emails= method, similar to the way you're handling the category_id:
#product = current_user.products.build(product_params)
#product.category_id = params[:category_id]
#product.email_list = product_params[:emails].split
Because you need validations on your emails and to make it cleaner I would create an email table, make Product table accept Email attribues and use cocoon gem to have a nice dynamic nested form with multiple emails inputs.
1) models
class Product < ActiveRecord::Base
has_many :emails, dependent: :destroy
accepts_nested_attributes_for :emails, reject_if: :all_blank, allow_destroy: true
end
class Email < ActiveRecord::Base
belong_to :product
validates :address, presence: true
end
2) Controller
class ProductsController < ApplicationController
def new
#product = current_user.products.build
end
def create
#product = current_user.products.build(product_params)
if #product.save
redirect_to root_path
else
render 'new'
end
end
private
def product_params
params.require(:project).permit(:title, :description, :category_id, :video, :thumbnail, emails_attributes: [:id, :address, :_destroy])
end
end
3) View
<%= simple_form_for #product do |f| %>
<%= f.input :title, label:"Product title" %>
<%= f.input :description %>
<%= f.association :category %>
<div id="emails">
<%= f.simple_fields_for :emails do |email| %>
<%= render 'emails_fields', f: email %>
<div class="links">
<%= link_to_add_association 'add email', f, :emails %>
</div>
<%= end %>
</div>
<%= f.button :submit %>
<% end %>
In your _emails_fields partial:
<div class="nested-fields">
<%= f.input :address %>
<%= link_to_remove_association "Remove email", f %>
</div>
Then setup cocoon's gem and javascript and you'll be good.
Reference: https://github.com/nathanvda/cocoon
My problem is connected with my previous problem. It was solved by using nested routing. But there was different idea without using nested resources. I tried it as an exercise. But when I submit data, ActiveRecord::RecordNotFound in SentencesController#create shows up.
Rails.application.routes.draw do
root 'stories#index'
get 'stories/show'
get 'stories/new'
post 'stories/:story_id', to: 'sentences#create'
resources :stories
resources :sentences, only: [:create]
end
shared/_sentence_form.html.erb is part of story/show_form.html.erb
<%= form_for(#sentence) do |f| %>
<%= hidden_field_tag :story_id, value: #story.id %>
<%= f.text_area :content, placeholder: "Compose new sentence..." %>
<%= f.submit "Save"%>
<% end %>
SentencesController
class SentencesController < ApplicationController
before_action :sentence_params
before_action :find_story
def create
#sentence = find_story.sentences.build(sentence_params)
if #sentence.save
flash[:success] = "You wrote the continuation!"
redirect_to root_url
else
flash[:danger] = "I did not save your words!"
redirect_to "#"
end
end
private
def sentence_params
params.permit(:content, :story_id)
end
def find_story
#story = Story.find(params[:story_id])
end
end
and model:
class Sentence < ApplicationRecord
belongs_to :story
validates :story_id, presence: true
validates :content, presence: true, length: { maximum: 150 }
end
I tried many combinations in controller and view. When I put some information in the text_area and click submit they don't save.
The param is nested:
def find_story
#story = Story.find(params[:sentance][:story_id])
end
But nesting the route is a better RESTful design anyways.
The route POST /stories/:story_id/sentances makes it very clear what the action does.
resources :stories do
resources :sentences, only: [:create]
end
<%= form_for([#story, #sentence]) do |f| %>
<%= f.text_area :content, placeholder: "Compose new sentence..." %>
<%= f.submit "Save"%>
<% end %>
This will properly pass params[:story_id] as a segment of the URL.
I am not sure that's a solution to your problem, but I believe that
def sentence_params
params.permit(:content, :story_id)
end
needs a :sentence in there somewhere. Your form is a form_for #sentence so I guess your params are params[:sentence][:content] or similar.
I believe your error happens in the line #story = Story.find(params[:story_id]), right? Make sure that the #story variable is present in your form and that the record can be found.
I have two models one Topic and Topic_Content.
With the following code
Route
resources :topics do
resources :topic_contents
end
Topic
class Topic < ActiveRecord::Base
has_one :topic_content
accepts_nested_attributes_for :topic_content
end
TopicContent
class TopicContent < ActiveRecord::Base
belongs_to :topics
end
Controller
class TopicsController < ApplicationController
def new
#topic = Topic.new
end
def create
# render text: params[:topic].inspect
#topic = Topic.new(topic_params)
#topic.save
end
private
def topic_params
params.require(:topic).permit(:title, topic_content_attributes: [:text])
end
end
View
<%= form_for #topic do |f| %>
<%= f.label 'Topic:' %>
<%= f.text_field :title %>
<%= f.fields_for :topic_contents do |tf| %>
<%= tf.label :text %>
<%= tf.text_area :text %>
<% end %>
<%= f.submit %>
<% end %>
The title will be saved correct in the topic table but the topic_content(text) wouldn't saved in the database, and I couldn't find the problem.
I'm not a Rails expert, but I'm certain you need to build the association in your controller.
In your new and edit actions you need to have:
def new
#topic = Topic.new
#topic_content = #topic.build_topic_content
end
Because this is a has_one/belongs_to you need to have it look that way. If it was a many association you'd build it with something like #topic_content = #topic.topic_contents.build.
I'm pretty sure it's just a matter of building the association in the right controller, which, I believe, for you, is the topic controller.
Your view should be as follow:
f.fields_for :topic_content do |content_fields|
^
I am new to ruby on rails,and I am learning by creating a blog. I am not able to save to my blogs table and I get this error "can't write unknown attribute url"
Blogs migration: db/migrate/
class CreateBlogs < ActiveRecord::Migration
def change
create_table :blogs do |t|
t.string :title
t.text :description
t.string :slug
t.timestamps
end
end
end
Blogs Model: /app/models/blogs.rb
class Blogs < ActiveRecord::Base
acts_as_url :title
def to_param
url
end
validates :title, presence:true
end
Blogs Controller: /app/controllers/blogs_controller.rb
class BlogsController < ApplicationController
before_action :require_login
def new
#blogs = Blogs.new
end
def show
#blogs = Blogs.find_by_url(params[:id])
end
def create
#blogs = Blogs.new(blogs_params)
if #blogs.save
flash[:success] = "Your Blog has been created."
redirect_to home_path
else
render 'new'
end
end
def blogs_params
params.require(:blogs).permit(:title,:description)
end
private
def require_login
unless signed_in?
flash[:error] = "You must be logged in to create a new Blog"
redirect_to signin_path
end
end
end
Blogs Form:/app/views/blogs/new.html.erb
Blockquote
<%= form_for #blogs, url: blogs_path do |f| %><br/>
<%= render 'shared/error_messages_blogs' %><br/>
<%= f.label :title %><br/>
<%= f.text_field :title %><br/>
<%= f.label :description %><br/>
<%= f.text_area :description %><br/>
<%= f.submit "Submit Blog", class: "btn btn-large btn-primary" %><br/>
<% end %><br/>
and I have also added "resources :blogs" to my routes.rb file.
I get this error in controller at
if #blogs.save
The error states exactly what your issue is: "can't write unknown attribute url" Implies that the url variable you are trying to access in def to_params of your Blogs model to which is unknown as you wish to store this in slug. The acts_as_url method is added and used by the stringex gem and because you aren't getting an error regarding an unknown method acts_as_url, the gem must successfully be installed, just misconfigured.
Looking further into the documentation for the gem, the gem sets the value of the url to a db column that should already exists in your model. From your database schema, the url should use the column slug and not url (the default of stringex). Changing your Blogs Class to the following should fix the issue:
class Blogs < ActiveRecord::Base
acts_as_url :title, url_attribute: :slug
def to_param
slug
end
validates :title, presence:true
end
As a first-time Rails user, I have to say I'm loving the Rails way of doing things. However, I'm running into an issue trying to create a simple form. I get the following error:
undefined method `categories_path' for #<#<Class:0x007f0440365880>:0x007f0430256cd8>
I tried creating a categories_path method in the controller (though I'm not sure what it would be for), but that didn't fix the error. Any rails experts out there know what's going on?
Here's the relevant code:
views/category/new.html.erb
<%= form_for #category do |f| %>
<%= f.label :category %>
<%= f.text_field :name %><br />
<%= f.submit %>
<% end %>
routes.rb
Jackeyes::Application.routes.draw do
scope "/admin" do
resources :product, :category
end
end
category_controller.rb
class CategoryController < ApplicationController
def index
#category = Category.all
end
def new
#category = Category.new
end
def create
#category = Category.new(params[:category])
#category.save
end
end
Make your resources plural:
resources :products, :categories
And try again.