I'm playing with rails console:
a = Event.where("location_id IS NULL")
Event Load (0.7ms) SELECT "events".* FROM "events" WHERE (start_date >= '2015-02-20' OR end_date >= '2015-02-20') AND (location_id IS NULL)
=> #<ActiveRecord::Relation [#<Event id: 58, name: "BENNY GREB CLINIC w Hard Rock Cafe Warszawa", start_date: "2015-02-21", end_date: nil, location: "Hard Rock Cafe Warsaw,ul. Złota 59,Warsaw,Poland", logo_url: "https://fbcdn-sphotos-c-a.akamaihd.net/hphotos-ak-...", website: "", facebook: "https://www.facebook.com/events/535924839876600", created_at: "2015-02-19 17:38:29", updated_at: "2015-02-19 17:38:29", logo_file_file_name: nil, logo_file_content_type: nil, logo_file_file_size: nil, logo_file_updated_at: nil, location_id: nil>]>
So location: "Hard Rock Cafe Warsaw,ul. Złota 59,Warsaw,Poland"
But when I do:
2.2.0 :002 > a.first.location
I get
=> nil
Why?
You have a location attribute and a location association and rails is using the association. You need to rethink your database structure so they are named differently.
As per the first comment in the question:
You might be using an active record association:
has_one :location
or
belongs_to :location
Active record will give precedence to the relation over the model attribute.
Try adding:
has_one/belongs_to :location, as: 'related_location'
To the model in order to ensure location is always defining the attribute. And you will access to the related location (using the location_id) using the aliased method.
Related
Let's work with these classes:
class User < ActiveRecord::Base
has_many :project_participations
has_many :projects, through: :project_participations, inverse_of: :users
end
class ProjectParticipation < ActiveRecord::Base
belongs_to :user
belongs_to :project
enum role: { member: 0, manager: 1 }
end
class Project < ActiveRecord::Base
has_many :project_participations
has_many :users, through: :project_participations, inverse_of: :projects
end
A user can participate in many projects with a role as a member or a manager. The connecting model is called ProjectParticipation.
I now have a problem using the associations on unsaved objects. The following commands work like I think they should work:
# first example
u = User.new
p = Project.new
u.projects << p
u.projects
=> #<ActiveRecord::Associations::CollectionProxy [#<Project id: nil>]>
u.project_participations
=> #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil>]>
So far so good - AR created the ProjectParticipation by itself and I can access the projects of a user with u.projects.
But it does not work if I create the ProjectParticipation by myself:
# second example
u = User.new
pp = ProjectParticipation.new
p = Project.new
pp.project = p # assign project to project_participation
u.project_participations << pp # assign project_participation to user
u.project_participations
=> #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil>]>
u.projects
=> #<ActiveRecord::Associations::CollectionProxy []>
Why are the projects empty? I cannot access the projects by u.projects like before.
But if I go through the participations directly, the project shows up:
u.project_participations.map(&:project)
=> [#<Project id: nil>]
Shouldn't it work like the first example directly: u.projects returning me all projects not depending on whether I create the join object by myself or not? Or how can I make AR aware of this?
Short answer: No, second example won't work the way it worked in first example. You must use first example's way of creating intermediate associations directly with user and project objects.
Long answer:
Before we start, we should know how has_many :through is being handled in ActiveRecord::Base. So, let's start with has_many(name, scope = nil, options = {}, &extension) method which calls its association builder here, at the end of method the returned reflection and then add reflection to a hash as a cache with key-value pair here.
Now question is, how do these associations gets activated?!?!
It's because of association(name) method. Which calls association_class method, which actually calls and return this constant: Associations::HasManyThroughAssociation, that makes this line to autoload active_record/associations/has_many_through_association.rb and instantiate its instance here. This is where owner and reflection are saved when the association is being created and in the next reset method is being called which gets invoked in the subclass ActiveRecord::Associations::CollectionAssociation here.
Why this reset call was important? Because, it sets #target as an array. This #target is the array where all associated objects are stored when you make a query and then used as cache when you reuse it in your code instead of making a new query. That's why calling user.projects(where user and projects persists in db, i.e. calling: user = User.find(1) and then user.projects) will make a db query and calling it again won't.
So, when you make a reader call on an association, e.g.: user.projects, it invokes the collectionProxy, before populating the #target from load_target.
This is barely scratching the surface. But, you get the idea how associations are being build using builders(which creates different reflection based on the condition) and creates proxies for reading data in the target variable.
tl;dr
The difference between your first and second examples is the way their association builders are being invoked for creating associations' reflection(based on macro), proxy and target instance variables.
First example:
u = User.new
p = Project.new
u.projects << p
u.association(:projects)
#=> ActiveRecord::Associations::HasManyThroughAssociation object
#=> #proxy = #<ActiveRecord::Associations::CollectionProxy [#<Project id: nil, name: nil, created_at: nil, updated_at: nil>]>
#=> #target = [#<Project id: nil, name: nil, created_at: nil, updated_at: nil>]
u.association(:project_participations)
#=> ActiveRecord::Associations::HasManyAssociation object
#=> #proxy = #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil, created_at: nil, updated_at: nil>]>
#=> #target = [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil, created_at: nil, updated_at: nil>]
u.project_participations.first.association(:project)
#=> ActiveRecord::Associations::BelongsToAssociation object
#=> #target = #<Project id: nil, name: nil, created_at: nil, updated_at: nil>
Second example:
u = User.new
pp = ProjectParticipation.new
p = Project.new
pp.project = p # assign project to project_participation
u.project_participations << pp # assign project_participation to user
u.association(:projects)
#=> ActiveRecord::Associations::HasManyThroughAssociation object
#=> #proxy = nil
#=> #target = []
u.association(:project_participations)
#=> ActiveRecord::Associations::HasManyAssociation object
#=> #proxy = #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil, created_at: nil, updated_at: nil>
#=> #target = [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil, created_at: nil, updated_at: nil>]
u.project_participations.first.association(:project)
#=> ActiveRecord::Associations::BelongsToAssociation object
#=> #target = #<Project id: nil, name: nil, created_at: nil, updated_at: nil>
There's no proxy for BelongsToAssociation, it has just target and owner.
However, if you are really inclined to make your second example work, you will just have to do this:
u.association(:projects).instance_variable_set('#target', [p])
And now:
u.projects
#=> #<ActiveRecord::Associations::CollectionProxy [#<Project id: nil, name: nil, created_at: nil, updated_at: nil>]>
In my opinion which is a very bad way of creating/saving associations. So, stick with the first example itself.
This is more of a rails structure thing at the level of the ruby data structures.
To simplify it lets put it this way.
First of all imagine User as a data structure contains:
project_participations Array
projects Array
And Project
users Array
project_participations Array
Now when you mark a relation to be :through another (user.projects through user.project_participations)
Rails implies that when you add a record to that first relation (user.projects) it will have to create another one in the second realation (user.project_participations) that is all the effect of the 'through' hook
So in this case,
user.projects << project
#will proc the 'through'
#user.project_participations << new_entry
Keep in mind that the project.users is still not updated because its a completely different data structure and you have no reference to it.
So lets take a look what will happen with the second example
u.project_participations << pp
#this has nothing hooked to it so it operates like a normal array
So In conclusion, this acts like a one way binding on a ruby data structure level and whenever you save and refresh your objects, this will behave the way you wanted.
At the risk of some serious oversimplification let me try to explain what is going on
What Most of the other answers are trying to tell you is that these objects have not been linked yet by active record until they are persisted in the DB. Consequently the association behavior that you are expecting is not fully wired up.
Notice that this line from your first example
u.project_participations
=> #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil>]>
Is identical to the result from your second example
u.project_participations
=> #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil>]>
This statement from your analysis of what you think rails is doing is inaccurate:
So far so good - AR created the ProjectParticipation by itself and I
can access the projects of a user with u.projects.
AR record has not created the ProjectParticipation. You have declared this relationship in your model. AR is just returning proxy for the collection that it will have at some point in the future, which when populated assigned, etc, you will be be able to iterate over and query its members etc.
The reason that this works:
u.projects << p
u.projects
=> #<ActiveRecord::Associations::CollectionProxy [#<Project id: nil>]>
But this doesn't
pp.project = p # assign project to project_participation
u.project_participations << pp # assign project_participation to user
u.project_participations
=> #<ActiveRecord::Associations::CollectionProxy [#<ProjectParticipation id: nil, user_id: nil, project_id: nil, role: nil>]>
u.projects
=> #<ActiveRecord::Associations::CollectionProxy []>
Is that in the first case you are just adding objects to an array that your user instance has direct access to. In the second example the has_many_through relationship reflects a relationship that happens at the database level. In the second example in order for the your projects to be accessible through your user, AR has to actually run a query that joins the tables and returns the data you are looking for. Since none of these objects is persisted yet that database query can't happen yet so all you get back are the proxies.
The last bit of code is misleading because it is not actually doing what you think.
u.project_participations.map(&:project)
=> [#<Project id: nil>]
In this case you have a user which is directly holding an array of ProjectParticipations one of which is directly holding a project so it works. It is not actually using the has_many_through mechanism in they way you think.
Again this is a bit of an oversimplification but that is the general idea.
Associations are defined on database level and make use of database table's primary key (and in polymorphic cases, class name). In case of has_many :through the lookup on association (say, User's Projects) is:
Fetch all User-Project pairs, whose user_id is a certain value (primary key of an existing User in the database)
Fetch all project_id (primary keys of projects) from these pairs
Fetch all Projects by resulting keys
Of course, these are simple terms, in database terms it's much shorter and uses more complicated abstractions, such as an inner join, but the essence is the same.
When you create a new object via new, it is not yet saved in the database, and therefore has no primary key (it's nil). That said, if the object is not in a database yet, you have no way of referencing it from any ActiveRecord's association.
Side note:
There is a possibility, however, that a newly created (and not saved yet) object will act as if something is associated with it: it might show entries belonging to NULL. This usually means you have an error in your database schema that allows such things to happen, but hypothetically, one could design his database to make use of this.
I'm using Closure_Tree gem and one of its instance methods, tag.descendants, returns a scope of all children, children's' children.
**tag.descendants** returns a scope of all children, childrens' children, etc., excluding self ordered by depth.
My questions are:
What is scope? Is it different from the name_scope?
It seems like the tag.descendants method is returning a hash. Please correct me if I'm wrong. And how can I access and return the name values?
This is what I received from rails console:
2.0.0-p353 :010 > #tag.descendants
Tag Load (0.5ms) SELECT "tags".* FROM "tags" INNER JOIN "tag_hierarchies" ON "tags"."id" = "tag_hierarchies"."descendant_id" WHERE "tag_hierarchies"."ancestor_id" = ? AND ("tags"."id" != 1) ORDER BY "tag_hierarchies".generations asc [["ancestor_id", 1]]
=> #<ActiveRecord::AssociationRelation [#<Tag id: 4, name: "Drinks", created_at: "2014-01-25 09:53:20", updated_at: "2014-01-25 09:53:20", parent_id: 1>, #<Tag id: 5, name: "Alcoholic", created_at: "2014-01-25 16:12:43", updated_at: "2014-01-25 16:12:43", parent_id: 4>, #<Tag id: 6, name: "Non-Alcoholic", created_at: "2014-01-25 16:14:13", updated_at: "2014-01-25 16:14:13", parent_id: 4>]>
2.0.0-p353 :011 >
I would like to know how I could call the name values of all the descendants. I've tried #tag.descendants.name but it returned "tag".
2.0.0-p353 :011 > #tag.descendants.name
=> "Tag"
Scopes are what allow you to take one big Active Record object and split it up into small different parts. For example you can scope projects so that user A can only see project A and user B can only see Project B, while all projects are on the project table. Check out the api it may clear things up about how you set a scope http://api.rubyonrails.org/classes/ActiveRecord/Scoping/Named/ClassMethods.html#method-i-scope
#tag.descendants.pluck(:name)
the reason #tag.descendants.name doesn't work is because you are calling the name method on a collection of decendants and it doesn't know which name to give you.
I'm trying to do something fairly simple. I have two models, User and Group. For simplicity's sake, let's say they look like this:
class User < ActiveRecord::Base
has_and_belongs_to_many :groups
end
and
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
end
Now, for some reason, I have a user that has the same group twice. In the Rails Console:
user = User.find(1000)
=> #<User id: 1000, first_name: "John", last_name: "Doe", active: true, created_at:
"2013-01-02 16:52:36", updated_at: "2013-06-17 16:21:09">
groups = user.groups
=> [#<Group id: 1, name: "student", is_active: true, created_at: "2012-12-24 15:08:59",
updated_at: "2012-12-24 15:08:59">, #<Group id: 1, name: "student", is_active: true,
created_at: "2012-12-24 15:08:59", updated_at: "2012-12-24 15:08:59">]
user.groups = groups.uniq
=> [#<Group id: 1, name: "student", is_active: true, created_at: "2012-12-24 15:08:59",
updated_at: "2012-12-24 15:08:59">]
user.save
=> true
And there is some SQL output that I've silenced. I would think that everything should be all set, but it's not. The groups aren't updated, and that user still has both. I could go into the join table and manually remove the duplicates, but that seems cludgy and gross and unnecessary. What am I doing wrong here?
I'm running Rails 3.2.11 and Ruby 1.9.3p392
Additional note: I've tried this many different ways, including using user.update_attributes, and using group_ids instead of the groups themselves, to no avail.
The reason this doesn't work is because ActiveRecord isn't handling the invalid state of duplicates in the habtm association (or any CollectionAssociation for that matter). Any ids not included in the newly assigned array are deleted - but there aren't any in this case. The relevant code:
# From lib/active_record/associations/collection_association.rb
def replace_records(new_target, original_target)
delete(target - new_target)
unless concat(new_target - target)
#target = original_target
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
"new records could not be saved."
end
target
end
The 'targets' being passed around are Arrays of assigned records. Note the call to delete(target - new_target) is equivalent in your case to delete(user.groups - user.groups.uniq) which results in an empty Array passed (since comparison is based on the id attribute of each record).
Instead, you'll need to clear out the association and then reassign the single group again:
group = user.groups.first
user.groups.clear
user.groups << group
This might be a way to cleanup those duplicates (it handles any number of groups of duplicate associations):
user = User.find(1000)
user.groups << user.groups.group_by(&:id).values.find_all {|v| v.size > 1}.each {|duplicates| duplicates.uniq_by! {|obj| obj.id}}.flatten.each {|duplicate| user.groups.delete(duplicate)}
I'm doing an application search that might filter on several different fields - some of which are not present in every index.
At the moment sorting out the correct classes to search on in the controller, using a complicated if elsif elsif thing.
Is there a way to get thinking sphinx to automatically not search a model if one a condition field isn't present, rather than ignoring the condition as seems to happen at the moment.
Eg
ThinkingSphinx.search(#query, :conditions => {:genres =>"classical-music"}, :match_mode => :extended, :classes => [Performer, Promoter, Tour, Venue, User], :order => :name_sort, :sort_mode => :asc)
User doesn't have genres, but is included in the search results
update
It definitely doesn't work as I'd like. My user model doesn't have the genres field in the index, so I'd like it to be excluded when I search on genres.
Example searches
User.search("anne")
Sphinx Query (3.6ms) anne
Sphinx Found 1 result
User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` IN (80)
=> [#<User id: 80, first_name: "Anne", last_name: "Bowers", bio: nil, email: "anne#bowers.com", phone: nil, created_at: "2012-11-20 09:36:05", updated_at: "2012-11-20 09:36:05", role: nil, promoter_id: nil, performer_id: nil, job_title: nil>]
ThinkingSphinx.search("anne", :conditions => {:genres => "music"}, :match_mode => :extended, :classes => [User], :order => :name_sort, :sort_mode => :asc)
Sphinx Query (3.2ms) anne #genres music
Sphinx Found 1 result
User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` IN (80)
=> [#<User id: 80, first_name: "Anne", last_name: "Bowers", bio: nil, email: "anne#bowers.com", phone: nil, created_at: "2012-11-20 09:36:05", updated_at: "2012-11-20 09:36:05", role: nil, promoter_id: nil, performer_id: nil, job_title: nil>]
It'd be really good if the user was excluded.
You can try to pass classes to search in params.
But your way should work too i think.
For me
ThinkingSphinx.search(conditions: { type: "text" })
it just writes an error
Sphinx Daemon returned warning: index bulletin_core,bulletin_delta,user_delta ...: query error: no field 'type' found in schema
but it doesnt crash. It returns only instances of classes which have indexed field type
You also can try a way which is written here https://groups.google.com/forum/?fromgroups=#!topic/thinking-sphinx/rrAPXtxUMjg but I haven't tryed it yet
I'm using Rails 1.2.3 (yeah, I know) and am confused about how has_many works with respect to object persistence.
For the sake of example, I'll use this as my declaration:
class User < ActiveRecord::Base
has_many :assignments
end
class Assignment < ActiveRecord::Base
belongs_to :user
end
As I understand it, this generates, among others, a method User#assignments.build, which creates an Assignment object whose user_id is the receiving instance's id (and whose other fields are as specified in the argument), but does not save this object in the database. The object can be saved later by calling Assignment#save!.
However, The Pragmatic Programmers' Agile Web Development with Rails, Second Edition, which I've been using as a tutorial and reference, says:
If the parent object exists in the database, then adding a child
object to a collection automatically saves that child.
There seems to be a contradiction here. What I'd like to know is:
If I do some_user.assignments.build, is the Assignment object saved?
If I do some_user.assignments << Assignment.new, is the Assignment object saved?
If I do some_user.assignments << Assignment.create, are two database calls made, or just one? What about if I modify the Assignment object between creating it and adding it to some_user.assignments?
What happens if I save! an Assignment object whose corresponding User has not yet been saved in the database?
P.S. The reason I don't just use User#assignments.create for everything is because it doesn't let me farm out initialization to an external method, which I'd like to be able to do. I also don't want to make multiple trips to the database.
NOTE: All the console tests below are run in Rails 3. You might get a different output in Rails 1, you'll have to run the tests yourself to compare.
Having your rails console handy is extremely valuable if you want to understand what's happening behind the scenes with Active Record. Here's what happens with a non saved object:
u = User.new
#<User id: nil, name: nil, created_at: nil, updated_at: nil>
u.assignments.build(:name => "example")
#<Assignment id: nil, name: "example", user_id: nil, created_at: nil, updated_at: nil>
u.save
#SQL (0.2ms) INSERT INTO `users` (`created_at`, `name`, `updated_at`) VALUES ('2012-06-01 19:25:45', NULL, '2012-06-01 19:25:45')
#SQL (0.2ms) INSERT INTO `assignments` (`created_at`, `name`, `updated_at`, `user_id`) VALUES ('2012-06-01 19:25:45', 'example', '2012-06-01 19:25:45', 1)
As you can see, both are saved at the same time when the new user was saved. Now let's try scenario two:
u = User.create!(:name => "test")
#SQL (0.2ms) INSERT INTO `users` (`created_at`, `name`, `updated_at`) VALUES ('2012-06-01 19:27:21', 'test', '2012-06-01 19:27:21')
#<User id: 2, name: "test", created_at: "2012-06-01 19:27:21", updated_at: "2012-06-01 19:27:21">
u.assignments.build(:name => "example")
#<Assignment id: nil, name: "example", user_id: 2, created_at: nil, updated_at: nil>
So, from this we can conclude:
If I do some_user.assignments.build, is the Assignment object saved?
Nope
If I do some_user.assignments << Assignment.new, is the Assignment object saved?
No. This is exactly what assignments.build does, no difference.
If I do some_user.assignments << Assignment.create, are two database calls made, or just one?
Just the assignments.
What about if I modify the Assignment object between creating it and adding it to some_user.assignments?
Don't understand, sorry.
What happens if I save! an Assignment object whose corresponding User has not yet been saved in the database?
It is saved to the database without a user_id. When you then call save on your user, an update command is issued to the assignment to add in the user id. Here it is in console:
u = User.new(:name => "John Doe")
#<User id: nil, name: "John Doe", created_at: nil, updated_at: nil>
a = Assignment.new(:name => "test")
#<Assignment id: nil, name: "test", user_id: nil, created_at: nil, updated_at: nil>
u.assignments << a
#[#<Assignment id: nil, name: "test", user_id: nil, created_at: nil, updated_at: nil>]
a.save!
#SQL (0.2ms) INSERT INTO `assignments` (`created_at`, `name`, `updated_at`, `user_id`) VALUES ('2012-06-01 19:33:24', 'test', '2012-06-01 19:33:24', NULL)
a.user_id
#nil
u.save!
#INSERT INTO `users` (`created_at`, `name`, `updated_at`) VALUES ('2012-06-01 19:33:36', 'John Doe', '2012-06-01 19:33:36')
#UPDATE `assignments` SET `user_id` = 3, `updated_at` = '2012-06-01 19:33:36' WHERE `assignments`.`id` = 3
Hope this helps.