Getting value from associated model - ruby-on-rails

My setup: Rails 2.3.10, Ruby 1.8.7
Here are my models
class User
has_many :user_projects
end
class Project
has_many :user_projects
#has a column named "project_type"
end
class UserProject
belongs_to :user
belongs_to :project
#fields: project_id, user_id
end
When I return a JSON string of a user and his related user_projects records, I also want to include in the user_project record the project.project_type column. Note: I don't want to also include the entire project record in the results. A possible solution is dup the project_type field in user_projects but I prefer not to do that if possible, is there another way to accomplish this during the find/read action?
Just to be clear, here's the JSON output I'm looking for
{
"user": {
"username": "bob",
"id": 1,
"email": "bob#blah.com"
"user_projects": [
{
"id": 15,
"user_id": 1,
"project_id": 10,
"project_type": "marketing"
}
{
"id": 22,
"user_id": 1,
"project_id": 11,
"project_type": "sales"
}
]
}

class UserProject
belongs_to :user
belongs_to :project
#fields: project_id, user_id
attr_reader :type
def type
self.project.type
end
end
class MyController < AC
def action
#model = whatever
respond_to do |format|
format.json { render :json => #model.to_json(:methods => :type)}
end
end
end
Hope this helps.

You could try using the :only key in a nested include:
user.to_json(:include => {:user_projects => {:include => {:project => {:only => :type}}}})
But I would add has_many :projects, :through => :user_projects to User so you can do the simpler:
user.to_json(:include => {:projects => {:only => [:id, :type]}})
Also, an off-topic cautionary note: never use 'type' as a column name in Rails unless you are using STI (ie. the project types are ruby subclasses of Project).
-
Edit
Here's a way to add project_type to UserProject like you want
class UserProject
belongs_to :user
belongs_to :project
delegate :type, :to => :project, :prefix => true
end
user.to_json(:include => {:user_projects => {:methods => :project_type}})

Related

Rails render object in json with model associations

I'm trying to render the user model along with the posts model, but I'm having trouble figuring out what the syntax for it would be
class Post < ApplicationRecord
belongs_to :user
end
class User < ApplicationRecord
has_many :posts, dependent: :destroy
end
Controller
def map_locations
#posts = Post.where.not(location: [nil, ""])
render :json => #posts.as_json(only: [:topic, :location, :latitude, :longitude],)
end
Output:
[{"topic":"Garret ATX","location":"Hornitos, CA, USA","latitude":37.5021592,"longitude":-120.238241}]
Desired Output:
[{"user_name":"Randy","topic":"Garret ATX","location":"Hornitos, CA, USA","latitude":37.5021592,"longitude":-120.238241}
The user model has #user.user_name which is the one I need for each post.
How do I render the user associated with each post?
I hope this works
def map_locations
#posts = Post.where.not(location: [nil, ""])
render :json => #posts.as_json(:only => [:topic, :location, :latitude, :longitude], :include => {:user => {:only => :user_name}})
end
Please check the apidock for more details.

Rails association : update association parameter (make admin/remove admin)

So I have a User model, and a Group model which has several users thanks to the GroupUserAssociation model. Here's how my relationships are defined:
class Group < ActiveRecord::Base
has_many :group_users, :class_name => 'GroupUserAssociation', :foreign_key => :group_id
has_many :group_admins, :class_name => 'GroupUserAssociation', :foreign_key => :group_id, :conditions => ['level = 1']
has_many :group_not_admins, :class_name => 'GroupUserAssociation', :foreign_key => :group_id, :conditions => ['level = 0']
has_many :users, :through => :group_users, :source => :user
has_many :admins, :through => :group_admins, :source => :user
has_many :not_admins, :through => :group_not_admins, :source => :user
end
If I want to add/remove users to group, there is an elegant way to write it (elegant because it doesn't involves the GroupUserAssociation object):
Group.first.users << User.first # Adds to group
Group.first.users.delete(User.first) # Removed from group
But if I do
Group.first.admins << User.first
Group.first.admins.delete(User.first)
it also deletes the association (hence has the same effect as the first lines).
Is there an elegant way (without handling the GroupUserAssociation object to promote/demote admin (= to update GroupUserAssociation.level from 1 to 0) ?
I could do
Group.first.users.delete(User.first) # Removed from group
Group.first.admins << User.first
But that would mean 2 times commiting to DB which is not really good...
I read there are some nice things for this in Rails 4, but unfortunately I'm using Rails 3.2...
Thanks
We do this using this code:
#config/routes.rb
resources :entries do
post :category
delete ":category_id", to: :category, as: "remove_category"
end
#Categories
def category
entry = #entry = Entry.find(params[:entry_id])
category = #category = Category.find(params[:category_id])
#Actions
entry.categories << category if request.post? && !entry.categories.include?(category)
entry.categories.delete(category) if request.delete?
#Return
respond_to do |format|
format.html { redirect_to collection_path }
format.js
end
end

Rails 3.1: Trying to update ActiveRecord: NoMethodError: undefined method `keys' for #<ActiveRecord::Relation:0x007fdfb82aca98>

I'm trying to update an ActiveRecord, which seems like it SHOULD be easy enough. Methods I've tried in my
Controller (metadata_controller.rb):
def update_metadata_type
#metadata_type = MetadataType.find(params[:id])
if #metadata_type.update_attributes(params)
render :template => 'metadata/show_metadata_type'
else
# return error template
end
end
def update_metadata_type
if MetadataType.update(1, { :name => params[:name] })
render :template => 'metadata/show_metadata_type'
else
# return error template
end
end
def update_metadata_type
#metadata_type = MetadataType.find(params[:id])
#metadata_type.name = params[:name]
#metadata_type.save
render :template => 'metadata/show_metadata_type'
end
Model (metadata_type.rb):
class MetadataType < ActiveRecord::Base
has_many :metadata_type_attributes, :dependent => :destroy
has_many :attributes, :through => :metadata_type_attributes, :dependent => :destroy
attr_accessible :name, :attribute_type_id
validates :name, :uniqueness => true
end
Different model with same problem (attribute_type.rb):
class AttributeType < ActiveRecord::Base
belongs_to :attributes
attr_accessible :data_type
validates :data_type, :uniqueness => true
end
routes.rb:
match '/metadata_type/:id' => 'metadata#update_metadata_type', :via => [:put], :as => 'update_metadata_type'
Each time I'm sending the data using RESTClient, with the appropriate headers to send json, with the following data in a PUT command:
{
"name" : "NewName"
}
I've also used binding.pry in the controller to make sure #metadata_type is found (and not nil) and it always looks right:
[1] pry(#<MetadataController>)> #metadata_type = MetadataType.find(params[:id])
=> #<MetadataType id: 1, name: "Category", created_at: "2011-11-15 16:02:53", updated_at: "2011-11-15 16:02:53">
Params also looks right:
[7] pry(#<MetadataController>)> params
=> {"name"=>"NewName",
"controller"=>"metadata",
"action"=>"update_metadata_type",
"id"=>"1",
"metadatum"=>{"name"=>"NewName"}}
But for some reason, no matter how I try and save, I get the same error:
NoMethodError (undefined method `keys' for #<ActiveRecord::Relation:0x007fdfb7637fd8>):
app/controllers/metadata_controller.rb:50:in `update_metadata_type'
Any idea on what's causing this? All help is appreciated
Sorry I'm a little late to the party but you can't define an association with the name attributes as it ends up overriding the built in attributes accessor method defined and used by ActiveRecord itself.
Well hopefully this helps anyone else who happens to come across this obscure error.
The problem is with these lines:
has_many :metadata_type_attributes, :dependent => :destroy
has_many :attributes, :through => :metadata_type_attributes, :dependent => :destroy
I'm a little confused as to what exactly you're trying to do, but I suspect you mean:
has_many :attributes, :class_name => "MetadataTypeAttributes", :dependent => :destroy

has_many with two has_one's

I have a User model that has_many parents.
I want that user model to have one father and one mother.
So my class Parent belongs_to user
Currently I have
class User < ActiveRecord::Base
has_many :parents
has_one :father, :class_name => 'Parent', :foreign_key => 'user_id', :conditions => {:type => 'male'}
has_one :mother, :class_name => 'Parent', :foreign_key => 'user_id', :conditions => {:type => 'female'}
end
class Parent < ActiveRecord::Base
belongs_to :user
end
The problem is in my controller.
...
def edit
#user = User.find(params[:id])
#user.mother = Parent.new(:type => 'female')
#user.father = Parent.new(:type => 'male')
...
When I go into the edit, it creates and throws the 2 parents into the database without even having changed anything in the form. For example, when I click edit on a user, I go to the edit page. When I look into the database, they're already created.
My form looks like so:
= form_for #user do |f|
= f.fields_for :father do |father_form|
etc...
= f.fields_for :mother do |mother_form|
etc...
I've tried doing something alone the lines of this in my controller:
...
#user.parents.build(:type => 'male')
#user.parents.build(:type => 'female')
...
But the form doesn't show up.
Any help would be greatly appreciated.
Try to use
#user.build_father(:type => 'male')
#user.build_mother(:type => 'female')
instead of
#user.mother = Parent.new(:type => 'female')
#user.father = Parent.new(:type => 'male')
in your action

automatically generate instances of a model when a different one is created?

I really didn't know how to phrase the question better but here goes:
Lets say i have 2 models
class Record < ActiveRecord::Base
has_many :roles, :dependent => :destroy
end
class Role < ActiveRecord::Base
belongs_to :record
end
I want a way to make sure that every time a new record instance is created 2 new roles are created automatically by the system for that record.
each role will have a name, record_id, Boolean called edit and Boolean called review.
so if i create a record called Hello and it has an ID 1 then the system should create these 2 new roles at the same time:
Role 1: name: Hello edit, record_id: 1, edit: true, review: false
Role 2: name: Hello review, record_id: 1, edit: false, review: true
ActiveRecord callbacks to the rescue:
class Record < ActiveRecord::Base
after_create :create_roles
def create_roles
Role.create :name => "Hello edit", :record_id => self.id, :edit => true, :review => false
Role.create :name => "Hello review", :record_id => self.id, :edit => false, :review => true
end
end
Try this:
class Record < ActiveRecord::Base
has_many :roles
after_create :add_roles
def add_roles
roles.create [
{:name => "Hello edit", :edit => true, :review => false},
{:name => "Hello review", :edit => false, :review => true}]
end
end

Resources