Query with OR condition through an association - mysql

I'm trying to facilitate finding a user record via either username or email. THe only issue is that the username is stored on an associated UserDetail model.
class User
has_one :user_detail
def self.find_by_username_or_email(value)
# This is effectively pseudocode for the query I'd like to write.
query = "lower(email) = :value OR user_detail.username = :value"
where([query, { value: value }]).first
end
end
How can I write a query which matches either the email or the username on the associated user_detail record?

You were almost there!
This should work for you:
class User < ActiveRecord::Base
has_one :user_detail
def self.find_by_username_or_email(value)
query = "lower(users.email) = :value OR user_details.username = :value"
includes(:user_detail).where(query, value: value).first
end
end

seems like you need to write
self.includes(:user_detail).where(" user.email = ? OR user_detail.user_name =?", param[1], param[2])

Related

Updating MySQL field based on other fields added upon creation in Rails

With Facebook API discontinuing username retrieval, I need to create a username in Rails by adding together fields such as:
User.username = first_name + last_name + rand(99999)
Any idea on how to go about this?
Find all users that you want to update, and iterate trough them. In this case i will update every user, in batches of 1000. Docs about find_each
User.find_each do |user|
username = "#{user.user_first_name}_#{user.last_name}_#{rand(99999)}"
user.update_attribute(:username, username)
end
For new users, just add one before create callback, to your user model
before_create :generate_username
....
def generate_username
begin
self.username ="#{self.user_first_name}_#{self.last_name}_#{rand(99999)}"
end while User.exists?(:username => self.username)
end

Rails 4: ActiveRecord or MySQL query where no related models have attribute

Having a tough time with this one. I have a Job model, and a JobStatus model. A job has many statuses, each with different names (slugs in this case). I need an 'active' method I can call to find all jobs where none of the associated statuses has a slug of 'dropped-off'.
class Job < ActiveRecord::Base
belongs_to :agent
has_many :statuses, :class_name => "JobStatus"
validates :agent_id,
:pickup_lat,
:pickup_lng,
:dropoff_lat,
:dropoff_lng,
:description,
presence: true
class << self
def by_agent agent_id
where(agent_id: agent_id)
end
def active
#
# this should select all items where no related job status
# has the slug 'dropped-off'
#
end
end
end
Job Status:
class JobStatus < ActiveRecord::Base
belongs_to :job
validates :job_id,
:slug,
presence: true
end
The closest I've gotten so far is:
def active
joins(:statuses).where.not('job_statuses.slug = ?', 'dropped-off')
end
But it's still selecting the Job that has a dropped-off status because there are previous statuses that are not 'dropped-off'. If i knew the raw sql, I could probably work it into activerecord speak but I can't quite wrap my head around it.
Also not married to using activerecord, if the solution is raw SQL that's fine too.
Job.where.not(id: JobStatus.where(slug: 'dropped-off').select(:job_id))
will generate a nested subquery for you.
Not the cleanest method, but you could use two queries.
# Getting the ID of all the Jobs which have 'dropped-off' JobStatuses
dropped_off_ids = JobStatus.where(slug: 'dropped-off').pluck(:job_id)
# Using the previous array to filter the Jobs
Job.where.not(id: dropped_off_ids)
Try this:
def active
Job.joins(:statuses).where.not('job_statuses.slug' => 'dropped-off')
end
or this:
def active
Job.joins(:statuses).where('job_statuses.slug != ?', 'dropped-off')
end
I think you may want to reevaluate your data model somewhat. If the problem is that you're turning up old statuses when asking about Job, you likely need to have column identifying the current status for any job, i.e. job.statuses.where(current_status: true)
Then you can very easily grab only the rows which represent the current status for all jobs and are not "dropped-off".
Alternatively, if I'm misunderstanding your use case and you're just looking for any job that has ever had that status, you can just go backwards and search for the status slugs first, i.e.
JobStatus.where.not(slug: "dropped-off").map(&:job)

rails scope for map table

I have the following models:
class Message < ActiveRecord::Base
has_many :read_messages
module Scopes
def by_read_status(read_status, user_id)
case read_status
when 'Unread'
when 'Read'
joins(:read_messages).where(:read_messages => {:read => true, :admin_user_id => user_id})
else
where('1 = 1') # no scope necessary
end
end
end
extend Scopes
end
and...
class ReadMessage < ActiveRecord::Base
has_many :admin_users
has_many :messages
end
I have a controller method called 'mark_as_read' for the message. It just creates a new read_message record with that message id, and the admin user id of the person that marked as read. Since messages are global, I want the ability for each user of the system to manage the read status seperately (which is why I have that extra layer in there). So as you can see by my scope, the by_read_status('Read', user_id) would return all records where it finds a mapped record with read true. The problem is, how can I do the opposite? (return all records where it does not find a map record, OR the map record :read is set to false)?
I am using the scopes like this:
#search = Message.search(params[:q])
messages = #search.result.accessible_by(current_ability)
messages = messages.by_company(session[:company_filter]) if session[:company_filter] && session[:company_filter] > 0
messages = messages.by_campaign(session[:campaign_filter]) if session[:campaign_filter] && session[:campaign_filter] > 0
read_filter = params[:read].blank? ? 'Unread' : params[:read]
messages = messages.by_read_status(read_filter, current_admin_user.id)
messages = messages.page(params[:page]).per(20)
#messages = MessageDecorator.new(messages)
So you can see in the middle there of my scopes, i've got the by_read_status. If I return an array, or something other than a scoped object, it will throw a fit. Can anyone help me figure out how to do the 'Unread' portion of my scope?
Thanks!
Edited Answer
exclude = Message.by_read_status('Read', user_id).map(&:id)
exclude = [0] unless exclude.size > 0
where("id not in (?)", exclude)
where("id not in (?)", by_read_status('Read', user_id))

How do I iterate over an array with Ruby so that it saves individual records in MySQL?

In my HTML, I am able to return an array from a select multiple box using
<select multiple id="purchases" name="purchases[]">
<option value="purchase1">Shoes</option>
<option value="purchase2">Dress Shirts</option>
</select>
My goal is to create a new database record for each of the options selected (I'm using Ruby on Rails and MySQL.) However, my controller isn't saving each value in its own record:
Controller
#purchase = Purchase.new(params[:purchases])
#purchase.purchaseinfo = params[:purchases]
Purchase Model
class Purchase < ActiveRecord::Base
belongs_to :customer
end
Customer Model
class Customer < ActiveRecord::Base
belongs_to :account
has_many :purchases
end
I know I should iterate through the controller, but I'm not sure how. Thanks in advance for your advice!
Edit 1
No matter what I do in the controller, the log tells me that the whole array, "purchases", is being sent, not the individual records. Here is the log and here is the current controller.
LOG
Processing SavedcustomerController#create (for IP at DATE TIME) [POST]
Parameters: {"purchases"=>["Item1", "Item2", "Item3"]}
Redirected to http://example.com/maptry
Completed in 21ms (DB: 2) | 302 Found [http://example.com/]
SavedcustomerController#Create
items_array = params[:purchases].split('"\"\"",')
items_array.each do |arrayitem|
#purchase = Purchase.new(params[:purchases])
#purchase.purchaseinfo = arrayitem
end
If you are on Rails 3 and you add attr_accessible :purchase_info to your Purchase model you could do this.
purchases = params[:purchases]
#purchases = purchases.map { |purchase| Purchase.create(purchase_info: purchase) }
UPDATE
In the most simple way you should be able to just do
purchases = params[:purchases]
purchases.each do |purchase_info|
purchase = Purchase.new
purchase.purchase_info = purchase_info
purchase.save
end
I'm not sure if attr_accessible was in Rails 2 but that code up there should work... are you getting any exceptions/errors with the code I provided?
Can you try this:
items_array = params[:purchases]
items_array.each do |arrayitem|
#purchase = Purchase.new()
#purchase.purchaseinfo = arrayitem
end
In Purchase.new() you should put all other attributes you want

ROR: MySQL query expression

I have set up the Model, but I don't know how to write the code in controller to get result of the following SQL query..
SELECT users.name, events.* FROM users, events WHERE users.id=events.uid
Thanks a lot.
I rename events.uid -> events.user_id
And set up the Model for both of them, attributes of events are
t.integer :user_id
t.string :title
t.datetime :hold_time
t.string :place
t.text :description
And new error is
undefined method title for #<User:0x3fb04d8>
Sorry for bothering you guys..
Assuming you have set up your associations you should be able to do it like so:
u = Users.find(:all, :include => :events, :conditions => "users.id == events.uid")
You can use the find_by_sql function to execute arbitrary SQL, so in your case you could do:
e = Event.find_by_sql( 'SELECT events.*, users.name as user_name from users, events where user.id = events.uid')
e would now contain all events with matching user names and you can access the user name just like any other attribute, e.g. puts e.user_name. Of course it is not possible to change the username since it was generated in a SQL query.
This approach may not be in accordance with Rails philosophy of DB agnostic applications, however I find that it is sometimes much easier (and probably faster) to use SQL directly instead of trying to achieve the same thing with ActiveRecord.
I cant comment yet (not enough rep), so I am posting an answer, but the:
"users.id == events.uid"
should be:
"users.id = events.uid"
I am not sure that will solve your problem, but it may.
if you want to find all users and events, you can do it like this:
users = User.all
users.each do |user|
= user.name
user.events.each do |event|
= event.name
end
end
and your models would look like this:
class User < ActiveRecord::Base
has_many :events
end
class Event < ActiveRecord::Base
belongs_to :user
end
I am sure there is another way to do it with event.uid, but I am not that advanced yet.