I have 2 models say 'foo' and 'bar', such as foo has_many :bars, and bar belongs to foo. And they both have a 'name' field.
active_scaffold :bar do |config|
config.columns = [:name, :foo_id]
config.columns[:foo_id].label = 'Foo Name'
end
in the controller and in the helper
def foo_id_column(b)
return link_to b.foo.name, foo_url(b.foo)
end
And this works just fine but whenever I click on the table header "Foo Name" instead of getting sort by the name displayed it is getting sorted by the foo_id, meaning, say we have 2 foo objects "a" with id 2 and "b" with id 1, then on clicking the "FooName" column it is displaying as "b" row first then 'a' instead of 'a' then 'b'. How to change the code so that the activescaffold uses the name content instead of the id for sorting?
As long as the belongs_to/has_many associations are defined in your models, then ActiveScaffold will do all of this for you (including sorting by foo.name) - no custom helper method needed:
active_scaffold :bar do |config|
config.columns = [:name, :foo]
end
This way is also kinder to your DB, since ActiveScaffold will :include the association into the finder for listing bars, avoiding the N-query issue that would be seen in your sample code (fetching each foo record separately when rendering the list of bars)
Related
So, I have the following
Dialogue.rb
belongs_to Chapter
# Get the total number of dialogues in a chapter, then see what number we are
def order_in_chapter
chapter.dialogues.index(self) + 1
end
Chapter.rb
has_many Stories
Chapters also have an "order number" so we know what order to show the chapters in.
The problem is that on active admin, the business requirement is to sort by chapter, then dialogue number. Sorting by multiple indexes isn't too difficult, but sorting by an index and then sorting the returning association by the return value of a method seems impossible?
For reference:
ActiveAdmin.register Dialogue do
controller do
def scoped_collection
super.includes :chapter, :character
end
end
index do
column :chapter, sortable: "chapters.order_num"
end
This sorts the chapters correctly (chapter 1, then 2, then 3, etc.) but the dialogues within those chapters are returned in a jumbled order when I want for them to be returned by the dialogue.order_in_chapter method.
Is there a way to do this?
The pseudocode that I need is like:
StoryVoice.order(chapter: :asc).then_by(order_in_chapter: :asc)
Found a way:
column :chapter,
sortable: lambda { Chapter.includes(:dialogue)
.order('dialogue.order_in_chapter desc') }
I'm trying to understand ORM and looking for examples of the following explanation:
tables map to classes,
rows map to objects,
columns map to object attributes
I understand that tables map to classes, but terminology of rows mapping to objects and columns mapping to object attributes have me confused.
I find actual examples help best. Try this:
Ruby and Rails:
Create the class (Create the table)
class House < ActiveRecord::Base
attr :length, :width, :finish, :price, :available
end
Create an instance (Insert a row)
my_house = House.new(:length => 23, :width => 12, :finish => 'siding',
:price => '$100,000.00', :available => false)
Get an instance (Select a row)
my_house = House.find(1)
puts my_house.length, my_house.width, my_house.price,
my_house.finish, my_house.available?
SQL:
Create the table (Create the class)
create table house(
length Integer,
width Integer,
finish Varchar(255),
price Text,
available Boolean)
# Note this is generic SQL, adapt as needed
# to your implementation - SQLserver, Oracle, mySQL, etc.
Insert a row (Create an instance)
insert into house (length,width,finish,price,available)
values (23, 12, 'siding', '$100,000.00', false)
Select a row (Get an instance)
my_house = select * from house where id = 1
Here appeared some good answer while i was painting, but still hopes my simple one would help:
(since you've tagged your question with 'rails', I apply rails code)
class User < ActiveRecord::Base
attr_accessor :first_name, :email
end
puts User.inspect # => class
puts u = User.create(:first_name => 'name',
:email => 'em#il.com') # => object (class instance)
puts u.name # => object's attribute
DB:
So, using ActiveRecord as the example, let's say you have a table posts, with columns 'id', 'title', 'date'. There are 2 rows in the posts table.
posts = Post.all
posts will be an Array of length 2. Each object in the array is one of the rows, and is an instance of class Post.
posts[0]
This is the first object in the array, which is the representation of the first row in the posts table.
posts[0].title
This will return the title attribute of the first object, which is the title column in the first row of the posts table.
Does that help?
I have two models:
class Wine
belongs_to :region
end
class Region
has_many :wines
end
I am attempting to use the #where method with a hash built from transforming certain elements from the params hash into a query hash, for example { :region => '2452' }
def index
...
#wines = Wine.where(hash)
...
end
But all I get is a column doesn't exist error when the query is executed:
ActiveRecord::StatementInvalid: PGError: ERROR: column wines.region does not exist
LINE 1: SELECT "wines".* FROM "wines" WHERE "wines"."region" =...
Of course, the table wines has region_id so if I queried for region_id instead I would not get an error.
The question is the following:
Is there a rails-y way to query the Wine object for specific regions using the id in the #where method? I've listed some options below based on what I know I can do.
Option 1:
I could change the way that I build the query hash so that each field has _id (like { :region_id => '1234', :varietal_id => '1515' } but not all of the associations from Wine are belongs_to and thus don't have an entry in wines for _id, making the logic more complicated with joins and what not.
Option 2:
Build a SQL where clause, again using some logic to determine whether to use the id or join against another table... again the logic would be somewhat more complicated, and delving in to SQL makes it feel less rails-y. Or I could be wrong on that front.
Option(s) 3..n:
Things I haven't thought about... your input goes here :)
You could set up a scope in the Wine model to make it more rails-y ...
class Wine < ActiveRecord::Base
belongs_to :region
attr_accessible :name, :region_id
scope :from_region, lambda { |region|
joins(:region).where(:region_id => region.id)
}
end
So then you can do something like:
region = Region.find_by_name('France')
wine = Wine.from_region(region)
Edit 1:
or if you want to be really fancy you could do a scope for multiple regions:
scope :from_regions, lambda { |regions|
joins(:region).where("region_id in (?)", regions.select(:id))
}
regions = Region.where("name in (?)", ['France','Spain']) # or however you want to select them
wines = Wine.from_regions(regions)
Edit 2:
You can also chain scopes and where clauses, if required:
regions = Region.where("name in (?)", ['France','Spain'])
wines = Wine.from_regions(regions).where(:varietal_id => '1515')
Thanks to all who replied. The answers I got would be great for single condition queries but I needed something that could deal with a varying number of conditions.
I ended up implementing my option #1, which was to build a condition hash by iterating through and concatenating _id to the values:
def query_conditions_hash(conditions)
conditions.inject({}) do |hash, (k,v)|
k = (k.to_s + "_id").to_sym
hash[k] = v.to_i
hash
end
end
So that the method would take a hash that was built from params like this:
{ region => '1235', varietal => '1551', product_attribute => '9' }
and drop an _id onto the end of each key and change the value to an integer:
{ region_id => 1235, varietal_id => 1551, product_attribute_id => 9 }
We'll see how sustainable this is, but this is what I went with for now.
I'm trying to test a model that has an implicit has_many association, and am having some difficulties.
I have a table A, with column B_ID, where B is basically a foreign key - except there is no table B in my database, or an active record class associated with object B. There is also table C that has column a B_ID.
In the model for table C we have:
# implicit has_many :alphas
def alphas
Alpha.where(:b_id => b_id).order(:xyz)
end
This database structure makes sense for the data I have, and the non-test code works fine.
My test code almost works, and I hope I am just missing something simple.
I have factories defined for A and C, and I have a test:
a1 = Factory(:alpha, :b_id => 123, :xyz => 100)
a2 = Factory(:alpha, :b_id => 123, :xyz => 200)
c1 = Factory(:c, :b_id => 123)
puts c1.alphas.count
puts c1.alphas.first
c1.alphas.first.should == a1
The output is:
2
nil
<test fails>
Changing the number of A objects that share the B_ID result in the c1.alphas.count changing, but I can't seem to actually inside the implicit association and get an A object back - instead I always get nil. There are other methods that in my C model that I can't test because those methods need to access fields on individual A objects.
Does anybody have any insight into what is going behind the scenes here, or what I might do to get around this? Thanks.
Take a look at this for an example to have an admin_user with the role of Admin.
https://github.com/drhenner/ror_ecommerce/blob/master/spec/factories/user.rb
In this case roles are not a factory.
I find it best to do this like the following though.
#order = Factory(:order)
order_item = Factory(:order_item, :total => 5.52 )
#order.stubs(:order_items).returns([order_item, order_item])
or
#order = Factory(:order)
order_item = Factory(:order_item, :total => 5.52, :order => #order )
Ok so I'm starting on normalising my database. Currently I have one model "Products" which is populated with about 60,000 products via a data feed (XML), which contains a product with a category name and a merchant name. I want to split these into 3 models; products, categories and merchants.
Each product has one category and one merchant so the natural idea is to create these models:
category_id | category_name
merchant_id | merchant_name
I can work out the code to associate between the models i.e. has_one, belongs_to etc but I'm struggling to work out to automatically associate a new Product with a category and a merchant programatically.
I've seen examples in books where your start with an empty database and that seems pretty straightforward. However, I'm starting off with a full database and a list of Category names.
Here is my product creation statement which is working great:
Product.create(:name => node.xpath("./text/name/text()").inner_text.downcase,
:description => node.xpath("./text/desc/text()").inner_text,
:brand => node.xpath("./brand/text()").inner_text,
:merchant => node.xpath("../#name").inner_text,
:category => node.xpath("./cat/text()").inner_text.downcase,
:price => "£" + node.xpath("./price/btext()").inner_text)
Would I need to do something like this, see the :category line, (i know the following is wrong btw!)...
Product.create(:name => node.xpath("./text/name/text()").inner_text.downcase,
:description => node.xpath("./text/desc/text()").inner_text,
:brand => node.xpath("./brand/text()").inner_text,
:merchant => node.xpath("../#name").inner_text,
:category => << Category.find_by_name(node.xpath("./cat/text()").inner_text.downcase),
:price => "£" + node.xpath("./price/btext()").inner_text)
Any ideas? Does this even make sense!?
Assuming the columns are called category_name and merchant_name, and you've set up the associations on Category and Merchant, you could do something like this:
Product.all do |product|
product.category = Category.find_or_create_by_category_name(product.category_name)
product.merchant = Merchant.find_or_create_by_merchant_name(product.merchant_name)
product.save!
end
It will take a while, so for large datasets you might need a better solution.
So would this actually set the :category value in the products table to a category_id or set the value to the category_name?
.find_or_create_by does a find on the attribute and returns the matching row, or creates one if it does not exist. When creating the association via `.category=, Rails will set the foreign key to match the id of the row in the categories table.
So to answer your question more directly:
Product.create(:category=>Category.find_or_create_by_name("Magic Beans"))
is like doing this:
category = Category.find_by_name("Magic Beans")
if category.nil?
category = Category.create(:name=>"Magic Beans")
end
product = Product.new
product.category = category
product.save
where the penultimate step sets the foreign key category_id to the value category.id. By convention associations are set up such that the foreign key is the model name suffixed with _id, so your products table should have both category_id and merchant_id.