I would like to display Brand and Subbrand together on a view page. I am getting an error when I try to declare:
class BrandsController < ApplicationController
def index
#brands = Brand.all
#subbrands = #brands.subbrands #error is coming from this line
Error:
undefined method `subbrands' for #<Array:0x9af1898>
I can't seem to get this working, for the life of me!
I originally posted about my problem in this post:
Undefined method
I have tried to put the logic into the controller as above to see if that helps, but I am still getting the error. The details of the models and routing can be found in my original post.
Please help!
#brands is an Array, not a single ActiveRecord object, so you can't call the subbrands association directly on it. Since you're calling Brand.all, and I assume all Subbrand items are associated with a Brand, you might as well just query all of the Subbrand objects separately:
#brands = Brand.all
#subbrands = Subbrand.all
However, if you were trying to only get a subset of the brands, you could do this. (The second line queries all the subbrands that have a brand_id included in #brands).
#brands = Brand.where(...)
#subbrands = Subbrand.where(:brand_id => #brands.collect(&:id))
Related
I'm having trouble retrieving virtual attributes when making database queries. The following works as expected:
s = Story.includes(:scenes).select("stories.*, 3 as testval")
s.first.title
=> "My Story"
s.first.testval
=> 3
But when I put in a where clause, it stops working:
s = Story.includes(:scenes).select("stories.*, 3 as testval").where("scenes.id < ?",1000).references(:scenes)
s.first.title
=> "My Story"
s.first.testval
NoMethodError: undefined method `testval' for #<Story:0x007fcd6b93eb68>
I'm guessing the issue is that ActiveRecord doesn't know that 'testval' should belong to 'stories' instead of 'scenes', but I'm not sure. Does anyone know how to resolve this?
Sorry to be the bearer of bad news but you can't do a custom select and eager load as references/eager_load overrides the select with a bunch of t%d_r%d.
You'll need to do approach with a different ActiveRecord strategy if you need virtual attributes.
Let's suppose we have this model
class Account < ActiveRecord::Base
after_initialize :set_name
def set_name
self.name = ‘My Account’
end
end
Now I want run a query that returns only some attributes of the model but not all of them, in particular is not returning the "name" attribute that it is used in after_initialize callback
Account.group(:name).select("count(*), id").first
And then this execution raises the following error because the set_name callback uses an attribute that has not been "loaded" or selected into the records returned by the query.
ActiveModel::MissingAttributeError: missing attribute: name
Fortunately for some particular cases I can execute the same sql query without using the Account model at all to get the desired result
sql = Account.group(:name).select("count(*), id").to_sql
ActiveRecord::Base.connection.execute(sql).first
=> #<Mysql2::Result:0x00000106eddbc0>
But the point is, what if I want to get Account objects instead of a Mysql2::Result one? Should the .select method return "complete" objects with all their attributes (e.g. filling the missing columns with Nil's)? Or is just a very bad idea to use after_initialize callbacks for our ActiveRecord models? Of course we can also add some code in the callback to check if the property exists or not but, in my opinion, this is unnatural or sounds weird working in an OO language.
Most uses of after_initialize can be (and SHOULD be) replaced with defaults on the corresponding database columns. If you're setting the property to a constant value, you may want to look into this as an alternative.
EDIT: if the value isn't constant, a call to has_attribute?(:name) will guard against this error - ActiveModel::MissingAttributeError occurs after deploying and then goes away after a while
No, it is not a bad idea, in fact I use it very often at work. The valid use case for this would be when you want code to run before you try and do anything with the object. Here is a breakdown of some of the filters offered.
# Before you intend to do anything with the object
after_initialize
# Before you intend to save the object
before_save
# After you've saved the object
after_save
# Before you save a new record
before_create
# After you create a new object
after_create
I'm building a web service app and I'm trying to handle nicely a 422 page sending back to the user the JSON the POSTed to better debug the error. To do this, I use request.request_parameters which get me back the JSON I sent, but it happens to be organized (for me) in a wired way and I can't really get it back only with the original data
What I send as JSON is this.
{
"name":"New set intensity",
"properties":
[
{"uri":null,"value":"on"},
{"uri":"https://type.lelylan.com/properties/intensity","value":"100.0"}
]
}
What I get from request.request_parameters is this.
{"{\"name\":\"New set intensity\",\"properties\":"=>{"{\"uri\":null,\"value\":\"on\"}, {\"uri\":\"https://type.lelylan.com/properties/intensity\",\"value\":\"100.0\"}"=>{"}"=>nil}}}
My main problem is that somehow the content becomes the key, and this recursively inside. Is there a way to get back the clean data? Thanks a lot.
UPDATE: I'm trying to better understand where and why this problem occurs.
In my controller I tried to access in the two available ways I know.
# request.body.read.inspect
"{\"name\":\"New set intensity\",\"properties\":[{\"uri\":\"not_valid\"}]}"
# request.request_parameters
{"{\"name\":\"New set intensity\",\"properties\":"=>{"{\"uri\":\"not_valid\"}"=>{"}"=>nil}}}
The request is made from Capybara
page.driver.post(#uri, #params.to_json)
The controller returns only JSON so this is the way I defined it. I din't put 'respond_to' and 'respond_with' and when I make the request it renders the json view show.rabl.json. This makes me think that it recognize the correct format.
class FunctionsController < ApplicationController
before_filter
...
def index
...
end
def show
..
end
def create
body = JSON.parse(request.body.read)
#function = Function.new(body)
if #function.save
render 'show', status: 201, location: FunctionDecorator.decorate(#function).uri
else
render_422 "notifications.resource.not_valid", #function.errors
end
end
Thanks.
I need to retrieve information from two separate models which are similar but not the same. I am trying to do things like
I have looked into a few methods however they return an array of active objects rather than an
ActiveRecord::Relation which is required for many of the features of my app to work.
Is there any way to return an ActiveRecord::Relation object containing a union of both tables?
I have tried things like
#group = Mymodel.find_by_sql("SELECT id FROM Mymodels
UNION SELECT id FROM AnotherModels")
and also explored using the Model.where method however cannot return an ActiveRecord::Relation
EDIT:
Just to be clear I need to return ActiveRecord::Relation that is a union or a merge of the two tables
Have you tried MyFirstModel.joins(:my_second_models)? Check out details joins in the API here.
EDIT: Single Table Inheritance is a better solution to this problem. See comments below.
Try something like this:
Model.joins(:other_model).where("attr1" = :attr1,
{ attr1: "example" }).group(:attr1)
Since you commented about where, I added the where method on the call. You can also group everything using :group in the end.
I have a big, flat table:
id
product_id
attribute1
attribute2
attribute3
attribute4
Here is how I want users to get to products:
See a list of unique values for attribute1.
Clicking one of those gets you a list of unique values for attribute2.
Clicking one of those gets you a list of unique values for attribute3.
Clicking one of those gets you a list of unique values for attribute4.
Clicking one of those shows you the relevant products.
I have been coding Rails for about 4 years now. I just can't unthink my current approach to this problem.
I have major writer's block. Seems like such an easy problem. But I either code it with 4 different "step" methods in my controller, or I try to write one "search" method that attempts to divine the last level you selected, and all the previous values that you selected.
Both are major YUCK and I keep deleting my work.
What is the most elegant way to do this?
Here is a solution that may be an option. Just off the top of my head and not tested (so there is probably a bit more elegant solution). You could use chained scopes in your model:
class Product < ActiveRecord::Base
scope :with_capacity, lambda { |*args| args.first.nil? ? nil : where(:capacity=>args.first) }
scope :with_weight, lambda { |*args| args.first.nil? ? nil : where(:weight=>args.first) }
scope :with_color, lambda { |*args| args.first.nil? ? nil : where(:color=>args.first) }
scope :with_manufacturer, lambda { |*args| args.first.nil? ? nil : where(:manufacturer=>args.first) }
self.available_attributes(products,attribute)
products.collect{|product| product.send(attribute)}.uniq
end
end
The code above will give you a scope for each attribute. If you pass a parameter to the scope, then it will give you the products with that attribute value. If the argument is nil, then the scope will return the full set (I think ;-). You could keep track of the attributes they are drilling down in in the session with 2 variables (page_attribute and page_attribute_value) in your controller. Then you call the entire chain to get your list of products (if you want to use them on the page). Next you can get the attribute values by passing in the set of products and the attribute name to Product.available_attributes. Note that this method (Product.available_attributes) is a total hack and would be inefficient for a large set of data, so you may want to make this another scope and use :select=>"DISTINCT(your_attribute)" or something more database efficient instead of iterating thru the full set of products as I did in the hack method.
class ProductsController < ApplicationController
def show
session[params[:page_attribute].to_sym] = params[:page_attribute_value]
#products = Product.all.with_capacity(session[:capacity]).with_weight(session[:weight]).with_color(session[:color]).with_manufacturer(session[:manufacturer])
#attr_values = Product.available_attributes(#products,params[:page_attribute])
end
end
Again, I want to warn you that I did not test this code, so its totally possible that some of the syntax is incorrect, but hopefully this will give you a starting point. Holla if you have any questions about my (psuedo) code.