Rails: Find by related model attribute? - sql

So, I tried to search for an example of how to do this, and I'm not sure I even know how to describe what I'm trying to do. I'm an utter noob when it comes to SQL, and I'm sure this is really basic but I'm totally lost:
I have a model, Photo, which has_many :tags, :through => :taggings. Tags have a name and an id.
I want to do something like: Photo.where( #tag_name in [array] )
... but like I said I have no idea how to write something like that, or what to search to see an example on Google.
Can anyone give me an example of that kind of query, and what it might be called?
Thanks!

Just tried this on a similar model of my own and seemed to work fine:
Photo.joins(:tags).where('tags.name' => ['herp','derp']).group(:id)
Also, here's a great resource on the AREL querying interface rails 3 uses, with information on these and other ActiveRecord calls related to querying.

So, as a twist on Brett's method, it turns out the following works without breaking PostgreSQL:
def self.tagged_with( string )
array = string.split(',').map{ |s| s.lstrip }
select('distinct photos.*').joins(:tags).where('tags.name' => array )
end
Problem solved! See this article for a great explanation as to why this is a better idea than 'group'.

Related

Explain please how to use WITH() in Kohana ORM

$contract = ORM::factory('Contract', 1);
I can get shipper like this: $contract->shipper;
I can get owners like this: $contract->owners->find_all();
But I cant use
$contract->owners and preload them like ORM::factory('Contract', 1)->with("owners");
So why do I need this, I cant understand what it does, and how it works ?
unfortunately ->with() only works with has_one or belongs_to relationships, not has_many. The only way to expand many<->many relations is with ->find_all()
From the with method description:
Binds another one-to-one object to this model.
You can check the source code for the with function here: https://github.com/kohana/orm/blob/3.3/master/classes/Kohana/ORM.php#L901

How can I query a rails 3 app efficiently?

I have a search form that queries one table in the database but there are many parameters (language, level, creator etc). The code below works provided the fields in question are filled in but I want to change it to:
a) add more parameters (there are several);
b) allow for a field to be empty
Here's the code in the controller:
#materials = Material.find(:all, :conditions => {:targ_lang => params["targ_lang"],
:inst_lang => params["inst_lang"],
:level => params["level"]})
Totally new to this I'm afraid but a lot of the documentation suggests I should be using "where".
Since Rails 3 you can use the where() function:
#materials = Material.where(targ_lang: params["targ_lang"], inst_lang: params["inst_lang"], level: params["level"])
Also, you could take a look at scopes
These allow you to set what you want to do in the model and call it in the controller for example:
class Material < ActiveRecord::Base
scope :active, where(active_state: true)
end
Then in the controller you do something like:
#active_materials = Material.active
This can be useful if you are joining several models and want to keep your controllers less messy.
To conclude, like #RVG said, seachlogic is quite useful as well as, there are others like Sphinx and Elastic Search. You should take a quick look at these and use the one you feel most confortable with.
If you are using search functionality in your app I suggest using SearchLogic gem
It is easy to use and effective..
SearchLogic
RailsCasts for searchlogic

Rails, gem with functionality in between enum and many to many

i have im my rails app model which has few options (no more than 10 i think).
Something like Product - Category, where product can be part of 1 or many categories.
But i think i have too few categories to engage fully fledged many-to-many construct.
Moreover the list of categroies is predefined and will almost never change.
I think from sql side this could look like string field categories with such content:"Fruits|Vegetables|..."
Maybe someone know preexisting gem for such functionality, or maybe it is no real advantage doing so and i should choose standart many-to-many ?
I checked acts-as-taggable-on plugin, but it is i think fits not very well for this task.
Enum gems like enumerize i think fit just best, but they are allow only single single value to be choosen.
Currently came out with following combination:
This gem:
https://github.com/pboling/flag_shih_tzu
In model:
class Product < ActiveRecord::Base
KINDS = { 1 => :fruit, 2 => :vegetable }
include FlagShihTzu
attr_accessible *KINDS.values
as_flags KINDS
Then in view (haml):
=form_for [#product] do |f|
-Product::KINDS.each do |k, v|
=f.check_box v
=f.label v
UPDATE:
Yet another gem adressing this problem: https://github.com/joelmoss/bitmask_attributes

Rails eager loading and conditions

I have the following associations set up
class bookinghdr
belongs_to :agent
end
class bookingitem
belongs_to :bookinghdr, :include => agent
end
So I was expecting to be able to do the following:
named_scope :prepay, :include=>["bookinghdr"], :conditions => ["bookinghdr.agent.agenttype = 'PP'"]
and in my controller do:
b = Bookingitem.prepay
But that gives me a ActiveRecord::StatementInvalid: Mysql::Error: Unknown column 'bookinghdr.agent.agenttype'
However if I don't include the conditions clause then I get a recordset on which I can do:
b = Bookingitem.prepay
b[0].bookinghdr.agent.agenttype
without any error!
I don't want to have to get all the records and then iterate over them to find the ones whose agent has a 'PP# flag. I was hoping that AR would do that for me.
Anybody got any ideas on how to achieve this?
Your question shows that you have not yet fully understood how associations and named scopes work. Since I cannot tell from your question what parts aren't clear, I suggest you read the Association Basics guide at http://guides.rubyonrails.org/v2.3.11/association_basics.html. This should bring you up to speed regarding the concepts you want to implement. After you have read the guide it should all make sense.

Do Rails 3 Active Record dynamic find_or_create finder methods have some undocumented inconsistencies?

Apologies for the long title, but this is bothering me. I'm new to Rails, so this is my first project. Rails 3.0.3.
In my model, a User may or may not have read many Entries; this is tracked in a model called ReadEntries. This many-to-one relationship is properly defined in the code, I think.
User.rb:
has_many :read_entries
Entry.rb:
has_many :read_entries
ReadEntry.rb:
belongs_to :entry
belongs_to :user
This table has to be populated at some point. If I try to do this:
user.read_entries.find_or_create_by_entry_id(entry.id, :read => false)
I get the error Unknown key(s): read. Leave out trying to set :read, and it works.
However, if I create the same row with this, it works:
ReadEntry.find_or_create_by_entry_id_and_user_id(entry.id, user.id, :read => false)
Logically, these methods should be identical, right? Thanks.
I've also had weird experiences with find_or_create. I would love it if it worked, but it seems inconsistent.
I'm currently having the same issue as you, and I think it may be due to calling find_or_create on an association as opposed to the model directly. Here's my example:
permission_assignments.find_or_create_by_role_id(:role_id => role_id, :is_allowed => false)
This works to create the assignment, except the "is_allowed" field gets set to it's default of "true". This code works for me (in the Permission model, hence the self reference)
PermissionAssignment.find_or_create_by_permission_id_and_role_id(:permission_id => self.id, :role_id => role_id, :is_allowed => false)
It's more verbose, unfortunately, but it works. The only problem that I still notice is that the object that is returned has no id assigned (the record does get created in the database, however, but if I wanted to update any more attributes I wouldn't be able to without the id). Don't know if that's a separate issue or not.
Rails 3.0.4 here with Postgres 8.4
You cannot pass in other fields like that as Rails will assume they are options for the find. Instead, you will need to make your method call longer:
user.read_entries.find_or_create_by_entry_id_and_read(entry.id, false)
Or alternatively use a shorter, custom syntax for that.
For your final example, my thoughts are that Rails will take the second argument and use that as options. Other than that, I am not sure.