group controller:
def show
#cat = Category.find_by_id(params[:id])
if #cat.group
#group = #cat.group
#members = #cat.group.group_members.all
#mem = #group.group_members.build
else
#cat.build_group
#cat.save
#mem = #cat.group.group_members.build
end
end
def add_member
#cat = Category.find_by_id(params[:id])
#group = #cat.group
#group.group_members.build(member_params)
if #group.save
redirect_to group_path
end
view:
- if #members.length > 0
- #members.each do |member|
%ul
%li
= member.first_name
= simple_form_for #mem, url: member_add_path(#cat.id), html: {:id => 'step_two_form'} do |f|
= f.label "First name"
= f.input :first_name, label: false
= f.label "Last name"
= f.input :last_name, label: false
= f.label "Email"
= f.input :email, label: false
= f.label "Phone number"
= f.input :telephone, label: false
= f.button :button, "Add member"
When I submit this form I can see that a new object is created as there is a new <li> in the source however the object has blank values, regardless of the input.
params (in the group controller):
def member_params
params.require(:group_member).permit(group_members_attributes: [:first_name, :last_name, :email, :telephone, :relationship, :status])
end
In the terminal I can see that the values I input are being passed but for some reason are not being saved. Here is the terminal output:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"7odxnZzHoyjgF/oougDIVKNR/9RkZOlK3IOpCaUVvpQ=", "group_member"=>{"first_name"=>"name", "last_name"=>"name", "email"=>"name#name.com", "telephone"=>"1234567890"}, "button"=>"", "id"=>"22"}
All help is appreciated, thank you.
EDIT:
group_member.rb:
class GroupMember < ActiveRecord::Base
belongs_to :group
attr_accessor :first_name, :last_name, :email, :telephone, :relationship
end
Your member_params method doesn't need to specify group_members_attributes as a nested hash, you should just be able to permit the attributes directly (they'll be permitted on whatever you put in the require(...) part):
def member_params
params.require(:group_member).permit(:first_name, :last_name, :email, :telephone, :relationship, :status)
end
Extending #DylanMarkow's answer, if you are trying to save first_name, last_name, email, telephone, relationship fields in database then you need to remove the following line from GroupMember model:
attr_accessor :first_name, :last_name, :email, :telephone, :relationship
Due to the attr_accessor, the above mentioned fields are considered as virtual attributes and hence not saved in database.
UPDATE
Can you briefly explain what the purpose of attr_accessor is? I
thought it creates a getter and setter methods for the listed
attributes?
Yes, attr_accessor is a Ruby method and it creates getter and setter methods for an attribute. When you use attr_accessor within a Rails model, the listed attributes are treated as virtual attributes i.e., there values would be in memory/ accessible only till the instance of the model lives because it is not stored in the database fields (as it is marked as virtual attribute). In a Rails model you don't need to worry about getters and setters of attributes as ActiveRecord would take care of that.
combined with #DylanMarkows answer you are saving the group but not necessarily the member
def add_member
#cat = Category.find_by_id(params[:id])
#group = #cat.group
#group_member = #group.group_members.build(member_params)
if #group_member.save
redirect_to group_path
else
render ...
end
end
that might help also your method is missing an end and probably an else on save
Related
I have a very simple action text model and form
class Course < ApplicationRecord
validates :title, presence: true
has_rich_text :content
end
<%= form_with model: #course do |f| %>
<%= f.text_field :title %>
<%= f.rich_text_area :content %>
<% end %>
It's all working great but since the content field is optional is it possible to create a course model without creating action_text_rich_texts entries that are empty/blank? Even if the user only enters the title without any content it's currently creating them and there's a lot of unnecessary and empty action_text_rich_texts rows in the database
The way I handled this in my application is with a before_save callback that removes the ActionText::RichText database record if the body is blank.
This avoids polluting the controller and works on both create and update actions. The body attribute of the action_text attribute is still accessible even without a corresponding database record, because ActionText will instantiate a new object if the record cannot be found (which allows you to test for blank? in either scenario).
Try this:
class Course < ApplicationRecord
validates :title, presence: true
has_rich_text :content
before_save :clean_up_content
private
def clean_up_content
self.content.destroy if self.content.body.blank?
end
end
I'm not sure about anything built into Actiontext for this, but I would imagine you could handle this at the controller level.
The first thing I would try is to see if not setting anything to content prevents Rails from creating an associated record:
class CourseController
def create
# remove course_params[:content] if it's blank
course_values = course_params[:content].blank? ? course_params.except(:content) : course_params
Course.create(course_values)
...
end
end
Extending Eric Powell's approach:
# app/models/concerns/do_not_save_blank_rich_text.rb
module DoNotSaveBlankRichText
extend ActiveSupport::Concern
included do
before_validation :do_not_save_blank_rich_text
end
private
def do_not_save_blank_rich_text
rich_text_attributes = self.class.reflections.values.select do |reflection|
reflection.options[:class_name] == "ActionText::RichText"
end.map(&:name)
rich_text_attributes.each do |rich_text_attribute|
if self.public_send(rich_text_attribute) && self.public_send(rich_text_attribute).body.blank?
self.public_send(rich_text_attribute).mark_for_destruction
end
end
end
end
I have a rails application where the user creates a new job, and on the job/new _form i am having the user enter the information for the Company that the Job belongs too.
A job in the database is simply the Date it was created, the User who created it, and a additional foreign keys to tables like Company, Contact, Jobsite, etc.
A Company has_many jobs and a Job belongs_to a Company in my application.
So, before I can persist the Job into the database, I must first save the new Company so that I can then grab its primary key and put that in the Jobs company_id foreign key tuple.
I have everything coming back properly in the params hash like so:
>> params
=> {"utf8"=>"✓", "authenticity_token"=>"NiQO7Wlq87h3+YM//yIEMnctVKectfmZBb74suFOCcmg7g4YyCmGo2OiciOd3VuRDR52tKoE0v9nq1LYoTqHOQ==", "job"=>{"date"=>"2016-09-04 16:06:49 -0700", "user_id"=>"1", "company_id"=>"", "subcontractor_id"=>"", "jobsite_id"=>"", "companies"=>{"name"=>"Test Company", "phone"=>"(530)111-2222", "street"=>"3177 testing st", "city"=>"cameron park", "state"=>"ca", "zip"=>"95682", "email"=>"testemail#mail.com", "website"=>"testcompany.com"}}, "commit"=>"Create Job", "controller"=>"jobs", "action"=>"create"}
>>
And i can access the company information with params[:job][:companies]
So my theory is, in the Jobs controller, in def create, before i call #job.save, I must first do a #company.new(company_params) and #company.save so that I can save the company and then grab its primary key from the database.
But I need a little help with this.
Does anyone have some tips to give?
Edit:
This is a sample from my _form,
<%= f.fields_for #company do |company| %>
<div class="field">
<%= company.label :name %>
<%= company.text_field :name %>
</div>
Inside the Job Controller I have,
def new
#job = Job.new
#company = Company.new
end
def create
#job = Job.new(job_params)
#company = Company.new(params[:job][:company])
Job.rb has
accepts_nested_attributes_for :company
And the params are:
def job_params
params.require(:job).permit(:date, :user_id, :company_id, :subcontractor_id, :jobsite_id, :company_attributes => [:name, :phone, :street, :city, :state, :zip, :email, :website])
end
When i submit my form, the attributes come back as company instead of company_attributes, so it is telling me "Unpermitted parameter: company".
The way i have it set up, #job has not function to do #job.company.build, they are not nested that way. Company is its own table and a company can have many jobs, and all a job can do is perform #job.company
Which is why i need to create the company first so i can get its primary key and then say #job.company_id = #company.id
I hope this makes things more clear.
You could use nested_attributes_for :company or companies, looking at your code and seeing company_id, I'm assuming it's company.
=>{
...
"job"=>{
"date"=>"2016-09-04 16:06:49 -0700",
"user_id"=>"1", # <= Really shouldn't do this...
"company_attributes"=>{
"name"=>"Test Company",
"phone"=>"(530)111-2222",
"street"=>"3177 testing st",
"city"=>"cameron park",
"state"=>"ca", "zip"=>"95682",
"email"=>"testemail#mail.com",
"website"=>"testcompany.com"
}
},
...
}
Then in your permitted params you can do something like:
def job_params
params.require(:job).permit(:date, :company_id, :subcontractor_id, :company_attributes => [:name, :phone, :street, :city, :state, :email, :website])
end
You can then use fields_for :company in your form_tag. Also, add a blank company to the new job action.
def new
#job = Job.new
#job.build_company
end
EDIT
You can't use #company on feilds_for like this.
Perhaps fields_for :company, #company, but not just #company.
<%= f.fields_for :company do |company| %>
You have to use a symbol to represent the record's name.
Otherwise the fields name will be "job[company]" or in your case "job[companies]" instead of "job[company_attributes]".
And either way, you can just add the new objects to the new job's association instead of adding an instance variable.
#job.build_company
You can try it out yourself with this working repo: https://www.github.com/frank184/surveysays
And for reference:
http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for
I'm creating my own website using Ruby on Rails. One thing that I've failed to comprehend is why and when to use attr:accessors in place of a permanent column for a model. For instance, let's say that I created a 'posts' model which would have a title, description and some content associated with it. Now should I do rails g model Post title:string description:text content:text or should I declare them as attr:accessible :title, :description, :content.
I'm not very experienced in rails, so please bear with me if this sounds too silly to you.
You can use attr_accessor if you need virtual attributes in model.
For eg: In contact us form you need not to see form data, but you need to send that data using email. So you can create attr_accessor for adding virtual attributes and can also apply validations on that.
class Contact
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :content
validates_presence_of :name
validates_format_of :email, :with => /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_length_of :content, :maximum => 500
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
ref
attr_accessible is to white list of attributes that can be mass assigned in model.
class Comment < ActiveRecord::Base
attr_accessible :user_id, :content
end
def create
#so here params[:comment], have all parameters. But if those params are not in attr_accessible list it will not save it.
# you can try that by remove attr_accessible from model
#comment = Comment.new(params[:comment])
if #comment.save
flash[:notice] = "Successfully created comment."
redirect_to #comment
else
render :action => 'new'
end
end
Comment Form:
<%= form_for #comment do |f| %>
<%= f.error_messages %>
<%= f.hidden_field :user_id, value: current_user.id %>
<p>
<%= f.label :content %><br />
<%= f.text_area :content %>
</p>
<p><%= f.submit %></p>
<% end %>
Happy Coding...
To add to Pardeep's epic answer, you'll want to look at this RailsCast (RE "virtual attributes"):
attr_accessor basically creates a setter & getter method in the model.
Probably doesn't make any sense; what you have to remember is that each Rails model is a class. Classes form the backbone of object-orientated programming.
Since Ruby is object orientated, each time you do anything with the language, it expects classes to be invoked & manipulated. The basis of OOP is to load classes into memory & play with them; good write-up here.
In classic OOP, your classes would be hard-coded with a series of attributes:
class Mario
def jump
pos_y + 5
end
def pos_y
# gets y val from the viewport
end
end
This will allow you to send instructions to the program, in turn modifying the class:
#mario.jump
... this should modify the viewport etc in the way you defined within the class.
--
Rails is very similar to the above, except most of the attributes are defined by ActiveRecord;
#app/models/mario.rb
class Mario < ActiveRecord::Base
# attributes from db include height, weight, color etc
end
Rails models allow you to call:
#mario = Mario.find x
#mario.height = "255"
... however, they don't allow you to create attributes which are stored in memory only.
For example...
#app/models/mario.rb
class Mario < ActiveRecord::Base
attr_accessor :grown
end
The above will give you an instance value of grown, which will allow you to populate this independently of the database.
So say you wanted to...
#mario = Mario.find x
#mario.grown = true if params[:grown]
#mario.height += "150" if #mario.grown
Regarding the difference between attr_accessor and attr_accessible, you'll want to look up Rails 3 and mass assignment.
I came into Rails ~ 4.0, so I didn't have to deal with attr_accessible so much; it was basically the way to permit parameters in Rails 3 models.
In Rails 4/5, you use strong params in the controller:
#app/controllers/mario_controller.rb
class MarioController < ApplicationController
def create
#mario = Mario.new mario_params
#mario.save
end
private
def mario_params
params.require(:mario).permit(:x, :y, :z)
end
end
node(:status) { #status }
node(:message) { #message }
object #patient
attributes :medication, :guardian_id, :id, :name, :email, :username, :address, :age, :gender
node :errors do |e|
e.errors
end
I want something like this
if object is patient
do this
eleif object is doctor
do this
How to apply condition in rabl file?
In rabl, you would just use normal Ruby for if-statements.
if !#patient.nil?
# do this
elsif !#doctor.nil?
# do that
end
I didn't know how you intended to use your objects, just guessed. Could also be something like if object == 'patient' etc...
I am using the Reform gem and want to create 2 objects (an instance of Foo, and one of Bar) which both have a 'name' attribute:
class MarflarForm < Reform:Form
include DSL
include Reform::Form::ActiveRecord
property :name, on: :foo
property :name, on: :bar
end
But I cant do this for obvious reasons:
= simple_form_for #form do |f|
= f.input :file
= f.input :file
The only way I can think of getting round this is by renaming one of database columns to 'title'. Is there another way?
Its just simple, For achieve this goal please use:
class MarflarForm < Reform:Form
include DSL
include Reform::Form::ActiveRecord
property :name, on: :foo
property :name, on: :bar
end
and In the view
= simple_form_for #form do |f|
= f.input "foo[name]"
= f.input "bar[name]"
This avoid name collision.
I've never used the Reform gem, but it looks tome like you can call the properties whatever you want. So try
property :foo_file, on: :foo
property :baz_file, on: :baz
Then, on save, you'll just have to be responsible for mapping those properties back to the correct model attributes.
#form.save do |data, nested|
#foo.file = nested[:foo_file]
#baz.file = nested[:baz_file]
# etc...
end
Does something like that work?