I'm working on implementing a tagging system and I'm having problem querying for tagged objects with a scope.
For example, I would like to find all the user's items with a certain tag. With a class method I can currently find all the objects:
def self.tagged_with(name)
Tag.find_by_name(name).items
end
However, this has a problem. If I were to do something like: current_user.items.tagged_with(name) won't this existing method return ALL the items and not just items owned by the current_user? I suppose this is a simply querying issue but I can't figure out how to change a class method into something called on a collection. I have tried going the opposite way, to get a the collection through the tags, something like... tag.items.where(:user_id => current_user.id) but in this case, it's a many-to-many relationship and I haven't been able to get on thumb on this either.
What's the proper way to restrict a query like this?
Create an association on your User class that points to your Tag class.
class User < ActiveRecord::Base
has_many :tags
end
Then you can do:
current_user.tags.where(...)
If you don't already have an association in place, you'll need to create a migration to have the tags table reference your users table with a foreign key.
I think this will help you:
class Account < ActiveRecord::Base
has_many :people do
def find_or_create_by_name(name)
first_name, last_name = name.split(" ", 2)
find_or_create_by_first_name_and_last_name(first_name, last_name)
end
end
end
person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
person.first_name # => "David"
person.last_name # => "Heinemeier Hansson"
So, basically you can define your method tagged_with directly into the association!
This example is took from the documentations ActiveRecord::Associations
Related
I have 2 classes with:
class User < ApplicationRecord
has_one :address
end
class Address < ApplicationRecord
belongs_to :user
I need to write code, which will print the value of attribute with the name 'postcode' of 100 users from database.
I have some code on this point, but not sure that it's a good way to solve the problem:
#users = User.all
#users.limit(100).each do |user|
puts "#{user.postcode}"
end
Who has better ideas?
I'd use pluck
puts User.limit(100).pluck('postcode')
# or
puts User.joins(:address).limit(100).pluck('addresses.postcode')
Pluck is best suited for your scenario.
User.where(condition).pluck(:postcode)
(#where condition is optional)
Event if you want to fetch other column with postcode you can simply include that in pluck. for e.g.
User.where(condition).pluck(:id, :postcode)
(#using multiple column inside pluck will only work with rails4 and above)
Say I have a Users table with a first_name field. Automatically I have a User model.
What I wan't is to know if it is possible to make a method also named first_name in the User model work?
I might do some modification inside the method. Say the model and the method would look like this:
class User < ApplicationRecord
def first_name
# I know there are many ways to do this, this is just an example.
"Mr. #{first_name}"
end
end
I'm getting SystemStackError: stack level too deep in the console. Just wanna know if this is possible or if this can work.
You can use read_attribute method
class User < ApplicationRecord
def first_name
# I know there are many ways to do this, this is just an example.
"Mr. #{read_attribute(:first_name)}"
end
end
I have a has_may through association and I'm trying to change records in the association in memory and then have all the associations updated in a single transaction on #save. I can't figure out how to make this work.
Here's a simplifiction of what I'm doing (using the popular Blog example):
# The model
class Users < ActiveRecord::Base
has_many :posts, through: user_posts
accepts_nested_attributes_for :posts
end
# The controller
class UsersController < ApplicationController
def update
user.assign_attributes(user_params)
replace_existing_posts(user)
user.save
end
private
def replace_existing_posts(user)
user.posts.each do |post|
existing = Post.find_by(title: post.title)
next unless existing
post.id = existing
post.reload
end
end
end
This is a bit contrived. The point is that if a post that the user added already exists in the system, we just assign the existing post to them. If the post does not already exist we create a new one.
The problem is, that when I call user.save it saves any new posts (and the user_post association) but doesn't create the user_post association for the existing record.
I've tried to resolve this by adding has_many :user_posts, autosave: true to the User model, but despite the documented statement "When :autosave is true all children are saved", that doesn't reflect the behavior I see.
I can make this work, with something hacky like this, but I don't want to save the association records separately (and removing and replacing all associations would lead to lots of callback I don't want to fire).
posts = user.posts.to_a
user.posts.reset
user.posts.replace(posts)
I've trolled through the ActiveRecord docs and the source code and haven't found a way to add records to a has_many through association that create the mapping record in memory.
I finally got this to work, just by adding the association records manually.
So now my controller also does this in the update:
user.posts.each do |post|
next unless post.persisted?
user.user_posts.build(post: post)
end
Posting this as an answer unless someone has a better solution.
I have a controller/model hypothetically named Pets. Pets has the following declarations:
belongs_to :owner
has_many :dogs
has_many :cats
Not the best example, but again, it demonstrates what I'm trying to solve. Now when a request comes in as an HTTP POST to http://127.0.0.1/pets, I want to create an instance of Pets. The restriction here is, if the user doesn't submit at least one dog or one cat, it should fail validation. It can have both, but it can't be missing both.
How does one handle this in Ruby on Rails? Dogs don't care if cats exists and the inverse is also true. Can anyone show some example code of what the Pets model would look like to ensure that one or the other exists, or fail otherwise? Remember that dogs and cats are not attributes of the Pets model. I'm not sure how to avoid Pets from being created if its children resources are not available though.
errors.add also takes an attribute, in this case, there is no particular attribute that's failing. It's almost a 'virtual' combination that's missing. Parameters could come in the form of cat_name="bob" and dog_name="stew", based on the attribute, I should be able to create a new cat or dog, but I need to know at least one of them exists.
You're looking for errors.add_to_base. This should do the trick:
class Pet < ActiveRecord::Base
belongs_to :owner
has_many :dogs
has_many :cats
validate :has_cats_or_dogs
def has_cats_or_dogs
if dogs.empty? and cats.empty?
errors.add_to_base("At least one dog or cat required")
end
end
end
If you want to pass cat_name or dog_name to the controller action, it may look like this:
class PetsController < ApplicationController
# ...
def create
#pet = Pet.new(params[:pet])
#pet.cats.build(:name => params[:cat_name]) if params[:cat_name]
#pet.dogs.build(:name => params[:dog_name]) if params[:dog_name]
if #pet.save
# success
else
# (validation) failure
end
end
end
Alternatively, for some more flexibility you can use nested attributes to create new cats and dogs in your controller.
I'm working on a project for my school on rails (don't worry this is not graded on code) and I'm looking for a clean way to traverse relationships in ActiveRecord.
I have ActiveRecord classes called Users, Groups and Assignments. Users and Groups have a HABTM relationship as well as Groups and Assignments. Now what I need is a User function get_group(aid) where "given a user, find its group given an assignment".
The easy route would be:
def get_group(aid)
group = nil
groups.each { |g| group = g if g.assignment.find(aid).id == aid }
return group
end
Is there a cleaner implementation that takes advantage of the HABTM relationship between Groups and Assignments rather than just iterating? One thing I've also tried is the :include option for find(), like this:
def get_group(aid)
user.groups.find(:first,
:include => :assignments,
:conditions => ["assignments.id = ?", aid])
end
But this doesn't seem to work. Any ideas?
First off, be careful. Since you are using has_and_belongs_to_many for both relationships, then there might be more than one Group for a given User and Assignment. So I'm going to implement a method that returns an array of Groups.
Second, the name of the method User#get_group that takes an assignment id is pretty misleading and un-Ruby-like.
Here is a clean way to get all of the common groups using Ruby's Array#&, the intersection operator. I gave the method a much more revealing name and put it on Group since it is returning Group instances. Note, however, that it loads Groups that are related to one but not the other:
class Group < ActiveRecord::Base
has_and_belongs_to_many :assignments
has_and_belongs_to_many :users
# Use the array intersection operator to find all groups associated with both the User and Assignment
# instances that were passed in
def self.find_all_by_user_and_assignment(user, assignment)
user.groups & assignment.groups
end
end
Then if you really needed a User#get_groups method, you could define it like this:
class User < ActiveRecord::Base
has_and_belongs_to_many :groups
def get_groups(assignment_id)
Group.find_all_by_user_and_assignment(self, Assignment.find(assignment_id))
end
end
Although I'd probably name it User#groups_by_assignment_id instead.
My Assignment model is simply:
class Assignment < ActiveRecord::Base
has_and_belongs_to_many :groups
end