creating subrecords in rails 3 - ruby-on-rails-3

my territory model
class Territory < ActiveRecord::Base
attr_accessible :name, :publisher
has_many :addresses, :dependent => :destroy
validates :name, :presence => true, :uniqueness => true
end
my address model
class Address < ActiveRecord::Base
attr_accessible :name, :street, :district, :note
belongs_to :territory
end
I have a form for creating territories and a view to show a singe territory.
I've added a form for adding addresses to territories to the territory show view.
This is my address controller
class AddressesController < ApplicationController
def new
#address = Address.new
end
def create
#address = territory.addresses.build(params[:address])
if #address.save
flash[:success] = "Address saved!"
redirect_to '/territories'
else
redirect_to '/territories'
end
end
end
It looks that I can't get hold of the id of the current territory, hence can't connect the address to the territory. How can I do that?
Also, after the save I'd like to show the current view, i.e. a territory show view again. Not show how to do this redirect...
my routes
TerritoryManagement::Application.routes.draw do
resources :addresses
resources :territories
end
Thanks
Thomas

You need to use nested resources for this, which is explained in the official Routing Guide. Another great example of this nested resources is in the official Getting Started Guide.
With this, you should receive a params[:territory_id] variable in your AddressesController when you make a new address for that territory, and from that you can then find the Territory by doing this:
Territory.find(params[:territory_id])
But rather than me repeating it here, I would really recommend you read both those guides.

Related

Nested json jbuilder for photo feed having n+1 major issues

I am having performance issues on one of my api views so I ran the Bullet gem and found some major N+1 issues with the view.
The api is being consumed so the format has to remain identical.
Bullet N+1 output:
localhost:3000/api/v1/games/1/game_feed N+1 Query detected
CompletedQuest => [:comments] Add to your finder: :include =>
[:comments] N+1 Query method call stack
/app/views/api/v1/games/game_feed.json.jbuilder:3:in block in
_b3b681b668d1c2a5691a5b3f7c15bb8e' /app/views/api/v1/games/game_feed.json.jbuilder:1:in
_b3b681b668d1c2a5691a5b3f7c15bb8e'
But I don't know how to accomplish the fix. Here are the relevant parts.
View:
json.game_feed(#game_photos) do |f|
json.extract! f, :id, :user_id, :username, :image_url_original, :comments_count, :likes_count
json.comments f.comments do |comment|
json.(comment, :username, :comment)
end
json.likes f.likes do |like|
json.(like, :id, :user_id, :username)
end
end
Controller:
#game_photos = CompletedQuest.game_photos(#game.id)
Model:
def self.game_photos(game_id)
where("completed_quests.game_id = ?", game_id).order("completed_quests.id DESC").just_photos
end
scope :just_photos, -> { where.not( image_file_name: nil ) }
Model relationships:
# CompletedQuests:
belongs_to :user
has_many :comments, dependent: :destroy
has_many :likes, dependent: :destroy
# Comments:
belongs_to :completed_quest, counter_cache: true
belongs_to :user
So basically for each photo in the feed, it then grabs every comment & likes for ever record - obviously this is bad and I see why, but I can't figure out how to fix it with my current structure.
Any help would be appreciated - but one thing is the structure of the JSON must remain identical.
You could include the associated comments in the query as follows:
# app/models/completed_quest.rb
def self.game_photos(game_id)
includes(:comments).where("completed_quests.game_id = ?", game_id).order("completed_quests.id DESC").just_photos
end
This will include all the associated comments in the result, so when you do f.comments in your view, there won't be a database query for comments of each f instance.

trouble saving to intersect table following railscast

I'm trying to create nested attributes as outlined in this railscast http://railscasts.com/episodes/167-more-on-virtual-attributes?view=asciicast
In my example, I am trying to associate an activity to an image, so I have a structure of activity -> activity_image -> image.
When I save my activity, rails creates the activity and the image, but I'm not getting the intersecting table of activity_image being saved. Of course, I never actually tell rails to save this, but neither does the railscast I'm following.
Is there some way this is supposed to be defined in the models? What have I got wrong?
class Activity > ActiveRecord::Base
attr_accessible :title, :description, :activity_image_url
belongs_to :user
has_many :activity_images
has_many :image_urls, :through => :activity_images
#validates_presence_of :title, :description, :url
attr_accessor :activity_image_url
after_save :assign_image
private
def assign_image
if #activity_image_url
self.activity_image_url = ImageUrl.find_or_create_by_url(#activity_image_url)
end
end
end

Cant mass-assign protected attributes issue

I know all the security reasons behind why mass-assigning is bad, what I cant figure out is why my app is trying to do a mass assign.
I am just trying to create a new record of my Section model and I am getting the "Can't mass-assign protected attributes" error. Below are the possible involved models. Can someone please explain to me how this is a mass-assigning? I am new to rails, so I could be missing something very simple.
class Section < ActiveRecord::Base
belongs_to :project
belongs_to :type, :foreign_key => 'type_id', :class_name => 'SectionType'
attr_accessor :order
end
class SectionType < ActiveRecord::Base
attr_accessible :name, :template
end
class Project < ActiveRecord::Base
has_many :sections
attr_accessible :description, :name, :short, :status, :subtitle, :version
def to_param
return name.gsub(/\s+/, '%20')
end
end
Any help would be greatly appreciated, I am new to rails and know this is probably a simple problem, but I have been trying to find an answer and can not.
If you're attempting to create a new Section object and that's failing, that'd be because you don't have any attributes listed as accessible inside that model. You will need to do that, using a similar call to attr_accessible as the one you have in your Project model already.

Can't mass-assign protected attributes for nested form using cocoon and simple_forms in Rails 3 App

The Problem:
I am getting an error message when submitting my form that says:
ActiveModel::MassAssignmentSecurity::Error in AdmissionRecordsController#create
Can't mass-assign protected attributes: admission_record
My Setup:
I am using Rails 3.2.3, with extra gems including Cocoon 1.0.14 and Simple_Form 2.0.2
The View:
My app/views/admission_records/_form.html.haml looks like:
= simple_form_for [#admission, #record] do |f|
= f.simple_fields_for :vital_signs, #record.vital_signs.build do |vs|
= render :partial => "vital_sign_fields", :locals => { :f => vs }
= link_to_add_association "Add Vital Signs", f, :vital_signs
= f.submit
And my app/views/admission_records/_vital_sign_fields.html.haml looks like:
.nested-fields
= f.label :sbp
= f.text_field :sbp
...
= link_to_remove_association "Remove Vital Sign"
What I am basically trying to do is that I have a resource called AdmissionRecord nested within another resource called PatientAdmission (route.rb shown below). I have another resource called VitalSign which I want to be able to create via a nested form (using cocoon and simple_form) when creating the AdmissionRecord
My config/routes.rb file looks like:
resources :patient_admissions do
resources :admission_records
end
The Models:
My app/models/patient_admission.rb looks like:
class PatientAdmission < ActiveRecord::Base
has_many :admission_records, :dependent => :destroy
end
My app/models/admission_record.rb looks like:
class AdmissionRecord < ActiveRecord::Base
belongs_to :patient_admission
has_many :vital_signs, :dependent => :destroy
accepts_nested_attributes_for :vital_signs, :rejects_if => :all_blank, :allow_destroy => true
attr_accessible :vital_signs_attributes
end
And my app/models/vital_sign.rb looks like:
class VitalSign < ActiveRecord::Base
belongs_to :admission_record
attr_accessible # just fields that appear in the form
end
The Controller:
The new and create methods in my AdmissionRecordsController looks like:
before_filter do
#admission = PatientAdmission.find(params[:patient_admission_id])
end
def new
#record = #admission.admission_records.build
end
def create
#record = #admission.admission_records.build(params[:admission_record])
#vital_sign = #record.vital_signs.build(params[:vital_signs])
#vital_sign.save
if #record.save
# Flash success and redirect to the right place
else
# Flash error and render :new
end
end
The Plea:
Please help me find where I'm going wrong. I've googled for hours and have looked at other examples and source code for demo apps such as those found in cocoon_simple_form_demo, but still can't seem to fix this error. If there's any other piece of information needed to debug this problem, please let me know. Thanks!
Okay I just had this problem and fixed it by entering one line of code in the belongs_to model.
# patient_admission.rb
Class PatientAdmission < ActiveRecord::Base
attr_accessible :admission_record_attributes
accepts_nested_attributes_for :admission_record
...
end
Here is another solution to it :)

How do i create an object if it has more than one belongs_to?

I have the following:
class Org < ActiveRecord::Base
has_many :users
has_many :entries
end
class Entry < ActiveRecord::Base
belongs_to :org
belongs_to :user
validates_presence_of :entry_text
end
class User < ActiveRecord::Base
belongs_to :org
has_many :entries
validates_uniqueness_of :user_name
validates_presence_of :user_name, :length => { :minimum => 3 }
end
I can Create Orgs and Users... How do i create an entry if there are two belongs_to? and what is this pattern called?
Double nested resources are tricky. The trick with users usually is to keep it out of your desired entry path.
Your question is kind of broad, but if you specify more information, people would be able to help you better. Also, I would recommend using the gem Devise for your user management system. Since you're using 'users' I would assume you want users from orgs to create entries. The entry created would be a part of org and the user would be the session's current user. Sorry if I am wrong to assume this.
Your routes.rb file can look something like this (assuming rails 3):
resources :orgs do
resources :entries
end
Then the create of your entry controller would look like:
#entry = #org.entries.new(params[:topic])
#entry.user = current_user #or however you are managing the current user's session.
And you'd want to set the org for the entire class by making a method that loads your current org and do a before_filter :loadOrg
def loadOrg
#org = Org.find(params[:id])
end
This is of course assuming your path is something like: /org/(id)/entry/(entry_id)
and not
/org/(id)/user/(user_id)/entry/(entry_id)
which in my opinion is unnecessary and can lead to more problems. You can always create a userpage model that calls all entries by users, but the default route doesn't necessarily have to include users in the path.
I don't see any problem.
#entry = Entry.create(:entry_text => "Hello World!")
Now questions to clarify what do you need:
Can #entry belongs both org and user at the same time? Or it can belongs to only one of them?
Should #entry belongs to at least one of them?
If #entry supposed to belong only one of them, so you should use Polymorphism
http://railscasts.com/episodes/154-polymorphic-association
class Entry < ActiveRecord::Base
belongs_to :textable, :polymorphic => true
validates_presence_of :entry_text
end
class Org < ActiveRecord::Base
has_many :users
has_many :entries, :as => :textable
end
class User < ActiveRecord::Base
belongs_to :org
has_many :entries, :as => :textable
validates_uniqueness_of :user_name
validates_presence_of :user_name, :length => { :minimum => 3 }
end