Extra variables in form - Rails - ruby-on-rails

I think Im taking the wrong approach to this and have tried to find the best approach on the web but so far no luck.
I have a projects model, which has many messages and users. The messages belong to both projects and users (as displayed below). So I need to pass in both the project id and user id into the message form. I know this should be pretty straightforward, but Im obviously messing it up. Not sure at this stage wether using http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-hidden_field_tag is necessarily the best idea either.
Any help would be awesome.
Project Model:
class Project < ActiveRecord::Base
belongs_to :user
has_many :users
has_many :messages, :dependent => :destroy
end
User Model:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :first_name, :last_name, :username, :email, :password, :password_confirmation
has_many :projects
belongs_to :projects
has_many :messages
end
Message Model:
class Message < ActiveRecord::Base
attr_accessible :title, :message
belongs_to :project
validates :title, :presence => true
validates :message, :presence => true
end
Projects show:
def show
#project = Project.find(params[:id])
#title = #project.title
#curent_user = current_user
#message = Message.new
begin
#messages = #project.messages
rescue ActiveRecord::RecordNotFound
end
end
/shared/_message.html.erb
<%= form_for #message do |f| %>
<%= f.label :title %>:
<%= f.text_field :title %><br>
<%= f.label :message %>
<%= f.text_area :message %>
<%= f.submit %>
<% end %>
Message create action
def create
#message = #project.messages.build(params[:message])
if #message.save
flash[:success] = "Message created!"
redirect_to root_path
else
render 'pages/home'
end
end
Appreciate your time, just trying to identify how I transfer the user_id/project_id into the from field so it's passed in at message creation.

Set the project_id/user_id in the controller so they can't be modified by end users when submitting the forms.
As you're using #project.messages.build in the message controller create action the project_id should automatically be set.
You can then set the user with #message.user = #current_user

Related

Validate presence of fields from another model

With the following associations:
class Profile < ActiveRecord::Base
belongs_to :visitor
belongs_to :contact_point
validates :contact_point, presence: true
end
class Visitor < User
has_one :profile, dependent: :destroy
has_one :contact_point, through: :profile
end
class ContactPoint < User
has_many :profiles
has_many :visitors, through: :profiles
end
Each ContactPoint has a email. When the visitor creates her profile using the following form, she needs to determine the profiles contact point using the email address belonging to ContactPoint. The contact point users are already created and the visitors should not be able to update ContactPoint model.
<%= form_for #profile do |f| %>
<%= f.label 'First Name' %>
<%= f.text_field :first_name %>
<%= f.label 'Last Name' %>
<%= f.text_field :last_name %>
<%= fields_for :contact_point, #profile.contact_point do |ff| %>
<%= ff.label 'Contact point email' %>
<%= ff.text_field :email %>
<% end %>
<% end %>
In ProfilesController I am passing parameters to profile model this way:
def create
#profile = Profile.create(profile_params)
end
def profile_params
contact_point = ContactPoint.find_by_email(params[:contact_point][:email])
params.require(:profile).permit(:first_name, :last_name)
.merge(visitor_id: current_user.id, contact_point: contact_point)
end
With the above setup, when there is no ContactPoint with the provided email address, the contact_point variable will set to be nil and the validator can't distinguish whether the contact point email in the filled in from was empty or not.
Now, how can I add a validation to check the presence of this email address in contact_points table and show a custom error message?
You would have to do it yourself in your controller, something like:
def create
#profile = Profile.create(profile_params)
if !#profile.contact_point
if params[:contact_point][:email].present?
#profile.errors.add(:contact_point, 'No contact with found')
else
#profile.errors.add(:contact_point, 'Please provide an email')
end
render :new
end
end
The best would be to use a custom validation that checks if contact_pounts.email is blank? If yes, then return false.
EDIT:
My brain is functioning better now after some sleep. You can do this using rails. This is how I would do it.
class Profile < ActiveRecord::Base
belongs_to :visitor
belongs_to :contact_point
validates :contact_point, presence: true
accepts_nested_attributes_for :contact_point
end
class Visitor < User
has_one :profile, dependent: :destroy
has_one :contact_point, through: :profile
end
class ContactPoint < User
has_many :profiles
has_many :visitors, through: :profiles
validates :email, presence: true
end
What is going on here? We accept nested attributes for the association (ContactPoint) from Profile, so we can pass them through the #profile form you have to the controller. The models will handle the validation and set the error messages accordingly.
Does this makes sense?

has_one nested association nullifies foreign key on edit route

I am building a nested form in ruby on rails.
The addition of a nested has_one association works fine. However, when I load the edit page, the foreign key company_id of the nested association is nullified.
I have tried update_only: true in accepts_nested_attributes_for and including :id in strong params as suggested in other similar answered questions on stackoverflow but nothing works for me.
Could anyone tell me what is actually causing the nested association to update and nullify its foreign key itself? My codes are as shown below. Thanks!
# company.rb
class Company < ApplicationRecord
has_one :mission
accepts_nested_attributes_for :mission, update_only: true
end
# mission.rb
class Mission < ApplicationRecord
belongs_to :company, optional: true
validates :description, presence: true, length: { maximum: 100 }
end
# companies_controller.rb
class CompaniesController < ApplicationController
def edit
#company = Company.find(params[:id])
#company.build_mission if #company.build_mission.nil?
end
def update
#company = Company.find(params[:id])
#company.assign_attributes(company_params)
if #company.valid?
#company.save
redirect_to companies_path
end
end
private
def company_params
params.require(:company).permit(mission_attributes: [:id, :description, :_destroy])
end
end
# edit.html.erb
<%= form_for #company, :url => company_path(#company), :html => {class: 'ui form', method: :put} do |f| %>
<%= f.fields_for :mission do |mission| %>
<div class="field">
<%= mission.label :mission %>
<%= mission.text_field :description %>
</div>
<% end %>
<%= f.button :submit => "", class: "ui button" %>
<% end %>
Hey I manage to solve the problem after a good sleep. Turns out i just have to play around with the if else condition at the companies controller level. The edit method should be amended to such:-
def edit
#company = Company.find(params[:id])
if #company.mission
else
#company.build_mission
end
end

Rails nested fields creation in loop

I have three model classes related to each other.
class Student < ActiveRecord::Base
has_many :marks
belongs_to :group
accepts_nested_attributes_for :marks,
reject_if: proc { |attributes| attributes['rate'].blank?},
allow_destroy: true
end
This class describes a student that has many marks and I want to create a Student record along with his marks.
class Mark < ActiveRecord::Base
belongs_to :student, dependent: :destroy
belongs_to :subject
end
Marks are related both to the Subject and a Student.
class Subject < ActiveRecord::Base
belongs_to :group
has_many :marks
end
When I try to create the nested fields of marks in loop labeling them with subject names and passing into in it's subject_id via a loop a problem comes up - only the last nested field of marks is saved correctly, whilst other fields are ignored. Here's my form view code:
<%= form_for([#group, #student]) do |f| %>
<%= f.text_field :student_name %>
<%=f.label 'Student`s name'%><br>
<%= f.text_field :student_surname %>
<%=f.label 'Student`s surname'%><br>
<%=f.check_box :is_payer%>
<%=f.label 'Payer'%>
<%= f.fields_for :marks, #student.marks do |ff|%>
<%#group.subjects.each do |subject| %><br>
<%=ff.label subject.subject_full_name%><br>
<%=ff.text_field :rate %>
<%=ff.hidden_field :subject_id, :value => subject.id%><br>
<%end%>
<% end %>
<%= f.submit 'Add student'%>
<% end %>
Here`s my controller code:
class StudentsController<ApplicationController
before_action :authenticate_admin!
def new
#student = Student.new
#student.marks.build
#group = Group.find(params[:group_id])
#group.student_sort
end
def create
#group = Group.find(params[:group_id])
#student = #group.students.new(student_params)
if #student.save
redirect_to new_group_student_path
flash[:notice] = 'Студента успішно додано!'
else
redirect_to new_group_student_path
flash[:alert] = 'При створенні були деякі помилки!'
end
end
private
def student_params
params.require(:student).permit(:student_name, :student_surname, :is_payer, marks_attributes: [:id, :rate, :subject_id, :_destroy])
end
end
How can I fix it?
#student.marks.build
This line will reserve an object Mark.
If you want multi marks, May be you need something like this in new action :
#group.subjects.each do |subject|
#student.marks.build(:subject=> subject)
end
Hope useful for you.

Multi-model nested form, can't add users to current account

I've searched everywhere for a solution but haven't come up with any.
The part that works: My app allows customers to create an account using a nested form. The data collected creates records in four models - accounts, users, accounts_users (because a user can be associated with many accounts), and profile (to store the user's fname, lname, phone, etc).
That part that doesn't work: Once logged in, I want the users to be able to add more users to their account using the form below. I don't receive any errors upon submit but I am brought back to the same form with no additional records created. Any help would be awesome!
Here is the nested form...
<%= form_for #user, :validate => true do |f| %>
<fieldset>
<%= f.fields_for :profile do |p| %>
<div class="field">
<%= p.label :first_name %>
<%= p.text_field :first_name %>
</div>
<div class="field">
<%= p.label :last_name %>
<%= p.text_field :last_name %>
</div>
<div class="field">
<%= p.label :phone %>
<%= p.text_field :phone %>
</div>
<% end %>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit 'Create New User', :class => "btn btn-large btn-success" %>
<%= cancel %>
</div>
</fieldset>
The ApplicationController scopes everything to the current_account like so:
def current_account
#current_account ||= Account.find_by_subdomain(request.subdomain) if request.subdomain
end
The UsersController
def new
#user = User.new
#user.build_profile()
#current_account.accounts_users.build() #Edit2: This line was removed
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
def create
#user = User.new(params[:user])
#user.accounts_users.build(:account_id => current_account.id) #Edit2: This line was added
if #user.save
# Send Email and show 'success' message
flash[:success] = 'An email has been sent to the user'
else
# Render form again
render 'new'
end
end
Models look like this:
class Account < ActiveRecord::Base
attr_accessible :name, :subdomain, :users_attributes
has_many :accounts_users
has_many :users, :through => :accounts_users
accepts_nested_attributes_for :users
end
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :profile_attributes
has_many :accounts_users
has_many :accounts, :through => :accounts_users
has_one :profile
accepts_nested_attributes_for :profile
end
class AccountsUser < ActiveRecord::Base
belongs_to :account
belongs_to :user
end
class Profile < ActiveRecord::Base
belongs_to :user
attr_accessible :first_name, :last_name, :phone
end
Edit2: It turns out that I had required a password + password_comfirmation validation in the User model which prevented me from adding another user without these fields. I commented out these validations plus removed the line: current_account.accounts_users.build() in the 'new' action and added the line: #user.accounts_users.build(:account_id => current_account.id) in the 'create' action.
"I want the users to be able to add more users to their account using the form below." I assume you mean profiles (since your nested form is on profiles)?
If that's the case, I think your UsersController's create action isn't associating the profiles with users by using new.
Try this...
def new
#user = User.build
#profile = #user.profiles.build #build adds the profile to user's associated collection of profiles, but new doesn't
...
end
def create
#user = User.build(params[:user])
if #user.save
....
end
end
If you want the user to be associated with account, then you need to put the new and create actions in the AccountsController and do something similar to nest association of the users and profiles records.
Btw, the reason that it went back to new is because you render new at the end of the create, in case that's also part of the question. Hope that helps!

Rails 3.1: Nested attributes won't save through the form

I suspect this might be a very simple mistake but I've spent 3 hours looking for it so I thought I might ask for some help from the community.
I'm running through Ryan Bates' excellent screencasts on Nested Models Forms and trying to apply them to my own project. The problem is the nested attribute doesn't seem to save using the form. I can get it to save through the console but it only shows up as empty brackets when going through the form.
Here's the relevant code:
The form view (using haml)
= form_for(#article) do |f|
- if #article.errors.any?
#error_explanation
%h2
= pluralize(#article.errors.count, "error")
prohibited this article from being saved:
%ul
- #article.errors.full_messages.each do |msg|
%li= msg
.field
= f.label :title
%br/
= f.text_field :title
.field
= f.label :intro
%br/
= f.text_area :intro
= f.fields_for :subsections do |builder|
= render 'subsections_fields', :f => builder
.field
= f.label :published_at
%br/
= f.text_field :published_at
.actions
= submit_or_cancel(f)
subsection_fields form view
= f.label :header
%br/
= f.text_field :header
= f.label :order_id
= f.number_field :order_id
%br/
= f.label :body
%br/
= f.text_area :body
%br/
= f.check_box :_destroy
= f.label :_destroy, "Remove Subsection"
%br/
Controller
class ArticlesController < ApplicationController
def new
#article = Article.new
3.times { #article.subsections.build }
end
def create
#article = Article.new(params[:article])
if #article.save
flash[:notice] = "Successfully created article."
redirect_to #article
else
render :action => 'new'
end
end
def edit
#article = Article.find(params[:id])
end
def update
#article = Article.find(params[:id])
if #article.update_attributes(params[:article])
flash[:notice] = "Successfully updated article."
redirect_to #survey
else
render :action => 'edit'
end
end
def destroy
Article.find(params[:id]).destroy
flash[:notice] = "Succesfully destroy article."
redirect_to articles_url
end
def show
#article = Article.find(params[:id])
end
def index
#articles = Article.all
end
end
And the models
class Article < ActiveRecord::Base
attr_accessible :title, :intro
has_many :subsections, :dependent => :destroy
accepts_nested_attributes_for :subsections, :reject_if => lambda { |a| a[:body].blank? },
:allow_destroy => true
has_and_belongs_to_many :categories
validates :title, :presence => true
end
class Subsection < ActiveRecord::Base
attr_accessible :header, :body, :order_id
belongs_to :article
validates :header, :presence => true
validates :body, :presence => true
end
Any help figuring this out is much appreciated.
I'm not quite sure, but try it with attr_accessible :article_id as well in your Subsection model?
Adding "attr_accessible" to a model changes the way mass assignment works in rails.
If you remove the "attr_accessible" lines in your models then all your code will work perfectly as it is.
The class method "accepts_nested_attributes_for" adds a "subsections_attributes=(value)" method to your model.
The second you add "attr_accessible" to a model you now are forced into adding extra "attr_accessible" entries for each field that you want to assign via mass assignment. i.e. when you use Article.new(params[:article]).
I hope that was clear.
I figured out the answer to this one from another question.
The answer was to set my subsections_attributes as an attr_accessible, so the above Article model should look like this (I also added published_at as an attr_accessible):
class Article < ActiveRecord::Base
attr_accessible :title, :intro, :subsections_attributes, :published_at
has_many :subsections, :dependent => :destroy
accepts_nested_attributes_for :subsections, :reject_if => lambda { |a| a[:body].blank? },
:allow_destroy => true
has_and_belongs_to_many :categories
validates :title, :presence => true
end

Resources