Not able to access related objects fields - ruby-on-rails

So I'm new to rails and I'm not entirely sure what I'm doing wrong. Everything I've read says that I'm doing this right.
I have a relationships between two models.
class Photo < ActiveRecord::Base
has_many :votes
belongs_to :user
end
And
class User < ActiveRecord::Base
attr_accessible :username, :email, :password, :password_confirmation, :remember_me
has_many :votes
has_many :photos
end
Here are my Controller methods
def index
#photos = Photo.order("created_at desc").to_a
end
def create
#photo = Photo.new(params[:photo])
#photo.user_id = current_user.id
if !#photo.save
#error = #photo.errors.full_messages.join('. ')
render view_for_new
return
end
end
I know the relationship works because in my view when I do this: <%= photo.user %> I get a user object back, and when I do <%= photo.user.inspect %> it shows all the expected fields with the correct keys and values.
However I want to access fields such as username, email, etc and display those on the page. How do I do this? I've tried doing <%= photo.user.email %> and some other fields that are available but it doesn't seem to be working

Alright figured this out, or at least partially.
Instead of <%= photo.user.email %> I did <%= photo.user.try(:email) %> and that brought the correct attribute back that I was looking for. It seems the association is done correctly. I don't know why <%= photo.user.email %> doesn't work, everywhere I look on line seems to use that sort of syntax.

Related

Can a validation weed out bad data, but still let the rest of a form to continue through creation?

When a form of data is submitted to creation, can the validations just weed out the bad data and not create those instances, but allow the other "good data" continue through?
So for a more clear scenario, say I am submitting a form that is creating a new Book, but on this form there are 10 fields to create 10 different Books at once. I have a validation on Book that says presence is true. When the user fills in the form lets say they just have 8 of the 10 book slots filled out. Since the last 2 empty Book slots are blank, the validation will work to stop that creation, but I still want the rest of the data to go through with being created.
I'm new to coding so I was really struggling to find the right words so I could find a clear answer via Google. Thank you for the help!
EDIT: My actual project is using a Card model:
class Card < ApplicationRecord
belongs_to :group
validates :front,:back, presence: true
end
Form:
<h3>Cards</h3>
<%= f.fields_for :cards do |c| %>
<%= c.label :front %>
<%= c.text_field :front %>
<%= c.label :back %>
<%= c.text_field :back %>
<br />
<% end %>
Its a nested form so this is the new, create and strong params in my Group controller:
def new
if params[:user_id] && #user = User.find_by_id(params[:user_id])
#group = #user.groups.build
#group.build_category
5.times {#group.cards.build}
else
#group = Group.new
end
end
def create
#group = current_user.groups.build(group_params)
if #group.save
redirect_to user_groups_path(current_user)
else
redirect_to new_user_group_path(current_user)
end
end
private
def group_params
params.require(:group).permit(:title, :description, :category_id, category_attributes: [:name],
cards_attributes: [:front, :back])
end
Error: There is no error output its just rerendering the new form page and highlighting the 'field_with_error' in red. Like its expecting all fields to be complete.
The validation is ok and necessary to avoid saving records with missing fields, so keep it.
The cleanest way to achieve what you're looking for, is to set accepts_nested_attributes_for :cards, reject_if: :all_blank on your Group model. The :all_blank will reject records which have all their attributes blank. Also you can set your own criteria for rejecting records like follows:
class Group < ApplicationRecord
has_many :cards
accepts_nested_attributes_for :cards, reject_if: :card_is_empty?
private
def card_is_empty?(c)
c.front.blank? && c.back.blank?
end
end
An other way is to put a before_validation :remove_empty_cards on your Group model and in remove_empty_cards method remove all cards that have empty front and back.

Update a rails parent record with information from the child record upon creation

this is my first post so I'm not exactly sure how to format this. Lmk if I'm doing anything wrong.
I'm fairly new to rails and I am making an app that basically just allows users to submit 'grimes' (which you can think of as reviews) for other users. The app has two basic models: users and grimes.
User Model:
class User < ApplicationRecord
has_many :grimes
end
Grimes Model:
class Grime < ApplicationRecord
belongs_to :user
validates :user, presence: true
end
each grime has a griminess attribute, which is basically the rating from 1-5 for the grime. Each user also has a griminess attribute, which I would like to be the sum of the griminess of each of a user's associated grimes. ie. if user1 has two grimes, g1 with griminess 3 and g2 with griminess 2, then user1 griminess should be 5.
I could do this by iterating through each of the user's grimes when I would like to display the user's griminess, but that seems very unnecessary. Instead, I would like to just increment the user's griminess each time a new grime associated with them is added, but I can't figure out how to do that right now.
the form to create a grime looks like this:
<%= form_for #grime do |f| %>
Title: <%= f.text_field :title %><br />
Description: <%= f.text_area :description %><br />
Griminess Level: <%= f.select :griminess, (0..5) %>
Select Grimer: <%= f.collection_select(:user_id, User.all, :id, :name) %><br />
<%= f.submit %>
<% end %>
and my grimes#create looks like this:
def create
#grime = Grime.new(grime_params)
if #grime.save
flash[:success] = "Grime Saved Successfully"
redirect_to #grime
else
flash[:error] = "Error: Could Not Save Grime!"
render :new
end
end
I feel like I should be able to do something like #grime.user.griminess += #grime.griminess in the create method, but that doesn't seem to work for me. Can anyone recommend a better way to approach this?
Add after_save hook in Grime which will call a method on a grime whenever that grime is saved.
class Grime < ApplicationRecord
after_save :update_user_griminess # register the callback
belongs_to :user
validates :user, presence: true
#callback body
def update_user_griminess
user.griminess = user.grimes.sum(:griminess) # update user griminess as sum of it's all grimes' griminess
user.save
end
end
There is an alternative after_create hook in ActiveRecord which runs just once when the object is created for the first time, but I preferred after_save here to adjust for cases when a grime's griminess is updated.

Create parent model from child controller

I'm working on on a web app that takes in information about companies. Information can be taken for a PreferredOffering (of stock) or an Incorporation. So in other words, when I create a new entry to either one of those models, a new company is formed.
It works out that my database is cleaner if PreferredOffering and Incorporation are children of Company, even though I'm trying to go through the preferred_offerings_controller or theincorporations_controller to create a new Company. Here lies my question; I'm trying to figure out how to configure my view and controllers to create a parent model from a child controller. I've done some research and have seen two other S/O posts on how to accomplish this with Rails 3, however it would seem that the addition of strong params adds another layer of complexity to the endeavor.
So I have my models set up like this
class Company < ActiveRecord::Base
belongs_to :user
has_one :incorporation, dependent: :destroy
has_many :preferred_offerings, dependent: :destroy
accepts_nested_attributes_for :preferred_offerings, allow_destroy: true
accepts_nested_attributes_for :incorporation, allow_destroy: true
end
.
class Incorporation < ActiveRecord::Base
belongs_to :company
end
.
class PreferredOffering < ActiveRecord::Base
belongs_to :company
end
The controller and view are what I'm iffy on.
Let's just take a look at the incorporation view/controller. If I were to configure it so that Incorporation has_one :company, I would set it up as follows:
class IncorporationsController < ApplicationController
def index
end
def new
#user=current_user
#incorporation = #user.incorporations.build
#company = #incorporation.build_company
end
def create
#incorporation = current_user.incorporations.build(incorporation_params)
end
private
def incorporation_params
params.require(:incorporation).permit(:title, :trademark_search, :user_id, :employee_stock_options, :submit, :_destroy,
company_attributes: [:id, :name, :employee_stock_options, :options_pool, :state_corp, :street, :city, :state, :zip, :issued_common_stock, :outstanding_common_stock, :fiscal_year_end_month, :fiscal_year_end_day, :user_id, :_destroy]
)
end
end
And the view would be:
<%= simple_form_for #incorporation, html: {id:"incorporationform"}, remote: false, update: { success: "response", failure: "error"} do |f| %>
(incorporation-specific fields)
<%= f.simple_fields_for :company do |company| %>
(Company-specific fields)
<% end %>
<% end %>
So my question is:
How do I need to modify my controller and view to create a Company from the incorporations_controller IF Company has_one :incorporation
Any suggestions would be much appreciated.
While it isn't the "Rails Way", there is nothing really wrong with having #company being the parent in your form, even though it is in the incorporations#new action. Your view would change to this:
<%= simple_form_for #company, html: {id:"companyform"}, remote: false, update: { success: "response", failure: "error"} do |f| %>
(company-specific fields)
<%= f.simple_fields_for :incorporation do |incorporation| %>
(incorporation-specific fields)
<% end %>
<% end %>
And your strong params would change so that Company is the parent and Incorporation is the child.
Another option would be to simply go through the Company controller. You could create two new actions: new_preferred_offering and new_incorporation. You would then create the objects in those actions. Or you could pass in some kind of :type param so that the normal new action renders one of two forms based on which one you want.

Nested_form has_one association

I have 2 models dog and litter_field:
class Dog < ActiveRecord::Base
belongs_to :user
has_one :litter_field
accepts_nested_attributes_for :litter_field
attr_accessible :litter_field_attributes
end
class LitterField < ActiveRecord::Base
belongs_to :dog
attr_accessible :breed_type
end
In my controller I have:
class DogsController < ApplicationController
def edit
#dog = Dog.find(params[:id])
#dog.build_litter_field
end
And in my view I have:
<%= simple_form_for #dog do |f| %>
<%= f.fields_for :litter_field do |l| %>
<div>
<%= l.label :breed_type %>
<%= l.input_field :breed_type %>
</div>
<%= f.button :submit, "Save" %>
<% end %>
I've looked at the documentation and from what I can tell this should work, however this page is not on the main edit page which I'm assuming is where the problem lies. Should I be adding what's in the edit action to a new action which displays the litter_field edit form?
EDIT:
What I am trying to do is split the edit form into separate pages, I've done this by adding additional actions that render extra pages so a user would go to dogs/settings/litter for example to see the litter_field nested form. I've tried adding #dog.build_litter_field to the litter action which displays the fields but when I try and save the form I am getting the error:
Failed to remove the existing associated litter_field. The record failed to save when after its foreign key was set to nil.
EDIT 2:
Fixed the above with adding:
has_one :litter_field, :dependent => :destroy
accepts_nested_attributes_for :litter_field, update_only: true
To dog.rb, the only problem I have now is it won't display the saved value on edit.
If you call #dog.build_litter_field in the edit action, it will build a new LitterField overtop of whatever had been saved previously. I would suggest trying something like this to see if it solves the problem you are currently seeing:
def edit
#dog = Dog.find(params[:id])
#dog.build_litter_field if #dog.litter_field.nil?
end
It can also help to checkout the debugger gem. You can use that to step through controller actions and see what the objects look like after each statement.

has_one relationship not working on new/create

I have these models:
class User < ActiveRecord::Base
has_one :user_tms, :dependent => :destroy
accepts_nested_attributes_for :user_tms
end
class UserTms < ActiveRecord::Base
belongs_to :user
end
In the UsersController I have this:
def new
#user = User.new
#user.build_user_tms
end
And the user form looks like this:
<%= form_for(#user) do |f| %>
<%= f.collection_select(:company_id, #companies, :id, :name, :include_blank => true) %>
<%= f.fields_for(:user_tms) do |tms_form| %>
<%= tms_form.collection_select(:department, #departments, :id, :description) %>
<% end %>
<% end %>
Pretty basic stuff I think, but when submitting the form I get the error:
User tms user can't be blank
And the weird thing is that when editing an exisiting user, everything works fine. Any idea what is going wrong here? Thanks!
Not sure but it is unusual to has a class end with an "s".
What is the table name? user_tms or user_tmses. I would set the plural name in your model.
I could have missed something else if so I'll delete. It is best to have a better model name for new developers though. tms doesn't mean much to most people.
Hmm, this is really weird because I thought rails is taking care of this automatically but it seems your nested model is missing the reference to the 'nester'. Try to supply it manually.
#user.user_tms.user = #user
You have to do this in the create action ie. where the record gets saved.
What version of rails are you using?
I found out what's wrong, I had a method causing some trouble with a false return value, which interfered with the normal handling of the relations with these two models.

Resources