Ruby on rails 2 level model - ruby-on-rails

I need some help creating a very simple forum in a existing model.
What I want in a Game page, have a mini forum, where is possible create some topics and some comments to this topics. In the beginning I'm only implement topics.
This is the error I have:
Mysql2::Error: Column 'user_id' cannot be null: INSERT INTO `topics` (`game_id`, `question`, `user_id`) VALUES (1, 'asd', NULL)
This is my main model:
game.rb
class Game < ActiveRecord::Base
attr_accessible :name
validates :user_id, presence: true
validates :name, presence: true, length: { maximum: 50 }
belongs_to :user
has_many :topics, dependent: :destroy
end
topic.rb
class Topic < ActiveRecord::Base
validates_presence_of :question
validates_presence_of :game_id
attr_accessible :question, :user_id
validates :question, length: {maximum: 50}, allow_blank: false
belongs_to :game
belongs_to :user
end
topic_controller.rb
def create
#game = Game.find(params[:game_id])
#topic = #game.topics.create(params[:topic])
#topic.user_id = current_user.id
respond_to do |format|
if #topic.save
format.html { redirect_to #game, notice: 'Topic was successfully created.' }
else
format.html { render action: "new" }
end
end
end
game/show.html.erb
<h2>Topics</h2>
<% #game.topics.each do |topic| %>
<p>
<b>Question:</b>
<%= topic.question %>
</p>
<% end %>
<h2>Add a topic:</h2>
<%= form_for([#game, #game.topics.build]) do |f| %>
<div class="field">
<%= f.label :question %><br />
<%= f.text_field :question %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Thanks ;)

I believe the issue you're experiencing is the difference between create and new in Rails.
Using new merely initializes the model, allowing you to save/validate later; using create will perform all of those steps in one command, causing the database row to be created.
So when you attempt to perform
#game.topics.create(params[:topic])
Rails attempts to create a Topic using params[:topic] and setting the game_id to #game.id, after which it immediately attempts to validate this new Topic that it created and save it to the database.
Potential options that you can consider:
1) Use #game.topics.new(params[:topic])
2) merge {:user_id => current_user.id} in: #game.topics.create(params[:topic].merge({:user_id => current_user.id})
I'd personally suggest option 1 (i.e. using new instead), but I have seen option 2 used before.
EDIT: Another issue that it looks as though you might be experiencing: should current_user be #current_user in your code?
Side note:
generally, if create fails to create database row, it will still work (returning the initialized model instead), but in your case it looks like this won't happen due to database-level restrictions on user_id being NOT NULL, causing an uncaught error.

You may want to consider reading the Rails Guide on nested resources. I've been where you are now , take a look at this discusion.

I guess you're accessing the site without being logged in, so user_id will not be set. You should ensure that there is a logged in user for all actions that are modifying or creating a topic. A simple approach can be found in this Railscast.

I think current_user.id is not setting properly, Do inspect these issue are almost all the other issues, ruby debugger is the beset way
in your GEM file add
GEM file
gem 'debugger'
run bundle install
then in your controller
def create
#game = Game.find(params[:game_id])
#topic = #game.topics.create(params[:topic])
#topic.user_id = current_user.id
debugger
respond_to do |format|
if #topic.save
format.html { redirect_to #game, notice: 'Topic was successfully created.' }
else
format.html { render action: "new" }
end
end
end
and this will stop you in the the debugger line, then from the console you could see if the values are set or not. check this for more

Related

How to let Guest Users comment on a platform when there is a belong_to relationship

I'm learning Rails 6 implementing a blog and I want to let users comment on the post even if they are not users in the platform.
I'm not sure how to do this, because currently I'm creating the comment like this
def create
#comment = Comment.new(
guest_user: params[:guest_user],
post_id: params[:post_id],
user_id: current_user.id,
)
...
end
And Comments belong_to User.
I have created a GuestUser model to use it as a placeholder but I'm not sure what to do with the id
class GuestUser < User
attr_accessor :name, :first_name, :last_name, :email
end
What do you think is the best approach for this?
There's a few different ways I believe you could do it, here's one possibly way. Let's say your comment form looked something like this (lots of assumptions given the below, but it should be not too bad to modify this to your needs):
= simple_form_for :comment do |f|
= f.input :comment_body # Not sure what your comment model looks like here, just a guess
- unless current_user.present? # Don't show the GuestUser fields unless no current_user, will also validate this on the backend
= f.input :first_name
= f.input :last_name
= f.input :email
# ...
In your controller, you could then do something like:
def create
user = current_user || GuestUser.create(guest_params) # Take the current logged in user, or generate a new GuestUser
redirect_to(#bad_user_path, notice: 'User invalid') unless user.valid? # Mostly a check on GuestUser here
#comment = user.comments.new(comment_params)
if #comment.save!
redirect_to (#wherever), notice: 'Saved comment!'
else
redirect_to (#wherever), notice: 'Comment invalid'
end
end
private
def comment_params
params.require(:comment).permit(:comment_body)
end
def guest_params
params.require(:comment).permit(:first_name, :last_name, :email)
end
end
Again there'd be a few assumptions here, like that your Comment model held some attr_accessors for :first_name, :last_name, :email, just so you could use f.input in the simple form vs f.text_field, but that would be one way.

Saving a record with Has_Many_Through records in Rails 5.2

I'm getting "spans invalid" error when saving a job record that:
#job.rb
class Job < ApplicationRecord
has_many :workspans
has_many :spans, through: :workspans
end
I didn't get this error in rails 5.0, but on upgrading, I can't associate the spans.
The data is coming from a fairly standard rails form, with a checkbox for each span.
#new.html.erb
<%= Span.each do |span| %>
<%= check_box_tag "job[span_ids][]", span.id %>
<% end %>
What has changed and how should I now set up the form to associate the spans with the #job ?
UPDATE, Detail
#jobs_controller
def create
#job = Job.new(job_params)
if #job.save
flash[:success] = "Job Saved"
redirect_to action: :index
else
flash[:alert] = "Job Not Saved"
render 'new'
end
end
From Rails 5.2 belongs_to is required by default. You need to mention optional: true to remove the error.
The related PR: https://github.com/rails/rails/pull/18937
The related issue on Rails repo: https://github.com/rails/rails/issues/23960

Inserting Multiple values from a single form Rails 4

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).

Couldn't find ArticlesSkill with ID=1 for Article with ID=

I am trying to create an article.
class Article < ActiveRecord::Base
belongs_to :article_skill
attr_accessible :articles_skill_attributes
accepts_nested_attributes_for :articles_skill
end
class ArticlesSkill < ActiveRecord::Base
attr_accessible :description, :name
has_many :articles
end
This is my form in the article/new.html.erb
<%= article_form.fields_for :articles_skill, #article.articles_skill do |b|%>
<label class="medium"><span class="red">*</span> Skill</label>
<%= b.select :id, options_for_select(ArticlesSkill.all.collect{|m| [m.name, m.id]}) %>
<%end%>
Here the article_form is the builder for the #article form object.
If I try to save the #article object its showing this error.
Couldn't find ArticlesSkill with ID=1 for Article with ID=
I've been struggling with this problem for a few days. Did a lot of searching.. it took going to the rails console and searching by the exception being thrown instead to make any progress with this.
Check out this answer on this question for why it's happening, and possible workarounds.
Use rails nested model to *create* outer object and simultaneously *edit* existing nested object?
Be aware that using the first option presented here creates a security hole as described in http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2010-3933
The second parameter in your fields_for call seems unnecessary. ActiveRecord is performing a lookup on the association articles_skill for #article when it reaches that param, but since the #article is new and has yet to be saved, it has no ID and triggers an error.
<%= article_form.fields_for :articles_skill do |b|%>
<label class="medium"><span class="red">*</span> Skill</label>
<%= b.select :id, options_for_select(ArticlesSkill.all.collect{|m| [m.name, m.id]}) %>
<% end %>
I can suggest only a workaround. It works, but I don't like it - I want some out-of-the-box solution.
I assume you have a function:
def articles_skill_params
params.require(:articles_skill).permit(:description, :name,
article_attributes: []) end
Add a function
def articles_skill_params2
params.require(:articles_skill).permit(:description, :name)
end
Add another function:
def set_article
article_id = articles_skill_params[:article_attributes][:id]
article = Article.find(article_id)
#articles_skill.articles << article
#articles_skill.save
end
Change your ArticlesSkillController#create:
def create
#articles_skill = ArticlesSkill.new(articles_skill_params2)
set_article
respond_to do |format|
if #articles_skill.save
format.html { redirect_to #articles_skill, notice: 'Article skill was successfully created.' }
format.json { render :show, status: :created, location: #articles_skill }
else
format.html { render :new }
format.json { render json: #articles_skill.errors, status: :unprocessable_entity }
end
end
end
As you can see, we simply exclude the nested attributes from the parent object creation (thus eliminating the error), then manually add them later.
If you just want people to be able to select an existing skill you don't need nested attributes at all (that's useful for when you might want people to be able to create an article skill from the same form that creates an article). You just want to set article_skill_id to an existing value, so you can just do
<%= form_for(#article) do |f| %>
...
<label class="medium"><span class="red">*</span> Skill</label>
<%= f.select :article_skill_id, ArticlesSkill.all.collect{|m| [m.name, m.id]}) %>
<% end %>

Rails ArgumentError in OrdersController#new validation

Second question on here, I'd really like to solve this one myself but I just don't know where to start to debug it.
So here is my error in the browser (which occurs when I go to check out and enter my details in order/_form.html.erb)
ArgumentError in OrdersController#new
You need to supply at least one validation
Rails.root: C:/Users/Ruby/rails_practice/depot4
Application Trace | Framework Trace | Full Trace
app/models/payment_type.rb:6:in <class:PaymentType>'
app/models/payment_type.rb:1:in'
app/models/order.rb:7:in <class:Order>'
app/models/order.rb:1:in'
app/controllers/orders_controller.rb:1:in `'
And here is my def new in OrdersController:
def new
#cart = current_cart
if #cart.line_items.empty?
redirect_to store_url, :notice => "Your cart is empty"
return
end
#hide_checkout_button = true
#order = Order.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #order }
end
end
The thing is that I haven't touch def new, I've been working on def create, which is here:
def create
#order = Order.new(params[:order])
#order.add_line_items_from_cart(current_cart)
#cart = current_cart
#hide_checkout_button = true
pay_type = PaymentType.find( :conditions => ['pay_type = ?', #order.pay_type] )
#order.payment_type_id = pay_type.id
respond_to do |format|
if #order.save
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
format.html { redirect_to(store_url, :notice => 'Thank you for your order.') }
format.json { render json: #order, status: :created, location: #order }
else
format.html { render action: "new" }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
What I am trying to do there is create an order which is which belongs_to a payment_type and has_many line_items which belongs_to a cart.
Incidentally, I am also trying to hide_checkout_button with an instance variable when I am on the order page.
The Orders table has a foreign key to the PaymentTypes table and I am trying to find the correct id from this PaymentTypes table for the payment_type submitted by the user.
If I comment out these two lines:
pay_type = PaymentType.find( :conditions => ['pay_type = ?', #order.pay_type] )
#order.payment_type_id = pay_type.id
Sometimes I get a different error:
NoMethodError in OrdersController#new
undefined method `key?' for nil:NilClass
I think this is to do with incorrect caching in the browser but I'm not sure what the connection is.
I will update with the rest after I post this first
Part deux
I know that this is about validation, but I can't see what I am doing wrong... order.rb:
class Order < ActiveRecord::Base
attr_accessible :address, :email, :name, :pay_type, :payment_type_id, :cart_id,
:product_id
has_many :line_items, :dependent => :destroy
belongs_to :payment_type
PAYMENT_TYPES = PaymentType.pluck(:pay_type)
validates :name, :address, :email, :pay_type, :presence => true
validates :pay_type, :inclusion => PAYMENT_TYPES
And then you've got the other side of that belongs_to in payment_type.rb
class PaymentType < ActiveRecord::Base
attr_accessible :pay_type
has_many :orders
validates ***:pay_type,*** :uniqueness
end
I know that I am totally just confusing things but I have one fail in the functionals tests and one error that has something to do with updating an order but I don't know what yet. I am going to work on them to see if by solving them I inadvertently solve this weird error.
If anyone can give me tips on hacking and debugging in rails that would be great. I would love to be able to solve this without typing all of this in here.
I don't think the server trace gives any more information than the browser window in this case but if you need it, or anything else please ask.
UPDATE:
So my problem is that I know how to solve it with a global variable in payment_type.rb, but this means that I have one column of payment types in the Orders table and another of names and payment_type_ids in another column, which is the foreign key.
Since I have the foreign key I shouldn't need a specific column for payment_types in the Orders table. I should just be able to see the value from the PaymentType table in the Orders view.
How do you do this without a Global variable?
UPDATE deux:
Ok, so I never posted this before (from orders_form.html.erb):
26: <div class="field">
27: <%= f.label :pay_type %><br />
28: <%= f.select :pay_type, PaymentType::PAYMENT_TYPES,
29: :prompt => 'Select a payment method' %>
30: </div>
31: <div class="actions">
So I've tried to select for :pay_type in Orders but given options from :pay_type in PaymentTypes.
I can't imagine that matters does it? Seems to be where my problem lies, but can't be sure.
I think the syntax of your validate inclusion of is wrong. It should be something like:
validates :pay_type, :inclusion => { :in => PAYMENT_TYPES }

Resources