Rails XML :include - ruby-on-rails

Here are my models:
class User < ActiveRecord::Base
...
belongs_to :picture, :foreign_key => 'picture_id',
:class_name => 'UploadedFile',
:dependent => :destroy
has_many :enrolled_groups, :through => :interests
...
end
class Group < ActiveRecord::Base
has_many :enrolled_users, :through => :interests,
:source => :user
end
I want to get an XML feed for my Groups with the enrolled users and their picture information.
The following line works fine (just with the enrolled users):
render :xml => #group.to_xml(:include => [:enrolled_users] )
How can I also include the picture info in the feed? I tried a bunch of things but can't figure it out... any idea?

If doing .to_xml(:include => [:enrolled_users, :picture]) doesn't work, then the hacky way would be to add (in User class):
def attributes
super.merge(:picture => picture)
end

You should be able to access nested resources by something like this:
render :xml => #group.to_xml(:include => [{:enrolled_users => :picture}])

I couldn't get it to work with the other proposed solutions. The way I ended up doing it was as follow:
In the controller:
render :xml => #group.to_xml(:include => {:enrolled_users => {:methods => :picture_url}})
In the model:
def picture_url
HOST+picture.public_filename(:avatar)
end

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

Whats wrong with this form routing?

I have a users model and a book model. Users can read books (as a reader) which creates an entry in the Readings model:
id | reader_id | book_id
Users also have a list of books that they have read. These are stored in the Red (I use Red because the present and past tense of the word 'read' are the same) model which looks the same as the Reading model above.
Now when a user is reading a book, I would like to display a button which represents finishing the book.
The finish action is in the ReadingsController and looks like this:
def finish
#book = current_user.readings.find(params[:id]).book
current_user.stop_reading!(#book)
current_user.make_red! #book
redirect_to :back
end
As you can probably tell, this takes in the id of a record in the readings table, destroys it and makes a new record in the table for recording books red.
The form helper for the "Finish Reading" button currently looks like this:
<%= form_for :reading, current_user.readings.find_by_book_id(book.id), :url => { :controller => :readings, :action => "finish" }, :method => :delete do |f| %>
<div class="actions"><%= f.submit button_text %></div>
<% end %>
But for some reason this renders a form with the wrong id because "9781440506604" is not the id of a record in the readings table, it's the id of a record in the books table (the ISBN-13 of a book to be precise).
<form accept-charset="UTF-8" action="/readings/9781440506604/finish" method="post">
</form>
What is it I'm doing wrong?
EDIT to add reading.rb
class Reading < ActiveRecord::Base
attr_accessible :book_id
# one person reading a new book may cause feed_item creations in multiple users feeds
has_many :feed_items, :as => :event
has_many :comments, :as => :parent, :dependent => :destroy
scope :from_users_followed_by, lambda { |user| followed_by(user) }
# need to pass the class name here because there is no Reader model
belongs_to :reader, :class_name => "User"
belongs_to :book
validates :reader_id, :presence => true
validates :book_id, :presence => true
def self.followed_by(user)
...
end
end
# and user.rb
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :avatar, :remember_me, :avatar_url
has_many :readings, :dependent => :destroy,
:foreign_key => "reader_id"
has_many :reads, :through => :readings, :source => :book
has_many :reds, :foreign_key => "reader_id",
:dependent => :destroy
has_many :red, :through => :reds, :source => :book
def reading? book
self.readings.find_by_book_id(book)
end
def read! book
self.readings.create!(:book_id => book.id)
end
def stop_reading! book
self.readings.find_by_book_id(book).destroy
end
def red? book
self.reds.find_by_book_id(book)
end
def make_red! book
unless red? book
self.reds.create!(:book_id => book.id)
end
end
end
By the way I tried making a user who is reading book 1 and doing user.readings.find_by_book_id(1) in the console and it returns a record from the readings table.
as requested
# routes.rb
resources :readings, :only => [:create, :destroy, :show] do
member do
post :create_comment
delete :finish
end
end
Looks like you have got to_param method in your Reading model
try to call id clearly:
current_user.readings.find_by_book_id(book.id).id
UPD
remove :only => [:create, :destroy, :show] from your routes
use this <%= form_for :reading, current_user.readings.find_by_book_id(book.id), :url => { :controller => :readings, :action => "finish", :id => current_user.readings.find_by_book_id(book.id).id }, :html => {:method => :delete} do |f| %>
I'm not particularly knowledgeable about rails 3 (still using rails 2), but shouldn't you be passing more information to the :url param?
This doesn't seem to mention anything about the ID you want to post to:
:url => { :controller => :readings, :action => "finish" }
Shouldn't it be something closer to this:
:url => { :controller => :readings, :action => "finish", :id => reading_id }
(Assuming reading_id to be substituted for the actual ID)

Resources