I am trying to fetch a random record in rails, to render in my home page.
I have a post model with content and title attributes. Lets say i wanted to fetch a random post(content and title) for some reason, How can i go about it in ruby. Thanks in advance.
You might find this gem handy : Faker
It allows to generate random strings with some meaning.
For example, a name :
Faker::Name.name => “Bob Hope”
Or an e-mail
Faker::Internet.email
In addition to this gem, if you want to be able to generate mock models very easily, I recommend the gem Factory Girl
It allows you to create factories for your model, sou you can generate a model with random attributes quickly.
Posting another answer since the first one answered to an unclear question.
As #m_x said, you can use RANDOM() for SQL.
If you don't mind loading all the dataset, you can do it in ruby as well :
Post.all.sample
This will select one random record from all Posts.
I know this is an old question, but since no answer was chosen, answering it might be helpful for other users.
I think the best way to go would be generating a random offset in Ruby and using it in your Active Record statement, like so:
Thing.limit(1).offset(rand(Thing.count)).first
This solution is also performant and portable.
In your post controller,
def create
#post = Post.new(params[:post])
if you_want_some_random_title_and_content
title_length = 80 #choose your own
content_length = 140 #choose your own
#post.title = (0...title_length).map{(65+rand(26)).chr}.join
#post.content = (0...content_length).map{(65+rand(26)).chr}.join
end
if #post.save
redirect_to #post
else
render 'new'
end
end
Using Kent Fedric's way to generate random string
unfortunately, there is no database-agnostic method for fetching a random record, so ActiveRecord does not implement any.
For postgresql you can use :
Post.order( 'RANDOM()' ).first
To fetch one random post.
Additionnally, i usually create a scope for this:
class Post < ActiveRecord::Base
scope :random_order, ->{ order 'RANDOM()' }
end
so if you change your RDBMS, you just have to change the scope.
Related
I have three models Exam, User and ExamResult. ExamResult contains the records for all the students (User) for exams (Exam). For one particular Exam record, there should be one record in ExamResult for each student. In the edit method of ExamController, depending if the ExamResult record has been created for one student, I need to build one new record or just skip it. Not sure if this is idiomatic Rails way of doing it.
# ExamController
def edit
User.students.each do |s|
#exam.exam_results.build(user_id: s.id) unless #exam.exam_results.find_by(user_id: s.id)
end
end
or this way:
def edit
newIds = User.students.map(&:id) - #exam.exam_results.map(&:user_id)
newIds.each do |id|
#exam.exam_results.build(user_id: id)
end
end
Maybe neither is idiomatic Rails. Any suggestions are welcome.
Edit
Bring find_or_initialize_by (recommended by #user3334690) on the table. If I understand the doc correctly, this should do the same as previous two implementations.
def edit
User.students.each do |s|
#exam.exam_results.find_or_initialize_by(user_id: s.id)
end
end
Instead of build you can use find_or_create_by_user_id in above case.
def edit
User.students.each do |s|
ExamResult.find_or_create_by_exam_id_and_user_id(exam_id, user_id)
end
end
there is this way:
def edit
User.students.each do |s|
ExamResult.where(exam_id: #exam.id, user_id: s.id).first_or_create
end
end
Use your second example:
def edit
newIds = User.students.map(&:id) - #exam.exam_results.map(&:user_id)
newIds.each do |id|
#exam.exam_results.build(user_id: id)
end
end
As I said in comments above this will query the database twice regardless of the number of students which will scale much better. If you're in a really extreme environment you can write it to use only one query along with "pluck" to pull only the "id" column (and avoid the object creation overhead) like this:
newIds = User.students.where("users.id not in (select user_id from exam_results where exam_id=?)", #exam.id).pluck(:id)
However, the readability is reduced for that. Your original would also benefit from using "pluck" instead of "map".
One other style note - I would use "new_ids" which is the standard idiomatic way of doing it with Rails.
Scenario: I have a has_many association (Post has many Authors), and I have a nested Post form to accept attributes for Authors.
What I found is that when I call post.update_attributes(params[:post]) where params[:post] is a hash with post and all author attributes to add, there doesn't seem to be a way to ask Rails to only create Authors if certain criteria is met, e.g. the username for the Author already exists. What Rails would do is just failing and rollback update_attributes routine if username has uniqueness validation in the model. If not, then Rails would add a new record Author if one that does not have an id is in the hash.
Now my code for the update action in the Post controller becomes this:
def update
#post = Post.find(params[:id])
# custom code to work around by inspecting the author attributes
# and pre-inserting the association of existing authors into the testrun's author
# collection
params[:post][:authors_attributes].values.each do |author_attribute|
if author_attribute[:id].nil? and author_attribute[:username].present?
existing_author = Author.find_by_username(author_attribute[:username])
if existing_author.present?
author_attribute[:id] = existing_author.id
#testrun.authors << existing_author
end
end
end
if #post.update_attributes(params[:post])
flash[:success] = 'great!'
else
flash[:error] = 'Urgg!'
end
redirect_to ...
end
Are there better ways to handle this that I missed?
EDIT: Thanks for #Robd'Apice who lead me to look into overriding the default authors_attributes= function that accepts_nested_attributes_for inserts into the model on my behalf, I was able to come up with something that is better:
def authors_attributes=(authors_attributes)
authors_attributes.values.each do |author_attributes|
if author_attributes[:id].nil? and author_attributes[:username].present?
author = Radar.find_by_username(radar_attributes[:username])
if author.present?
author_attributes[:id] = author.id
self.authors << author
end
end
end
assign_nested_attributes_for_collection_association(:authors, authors_attributes, mass_assignment_options)
end
But I'm not completely satisfied with it, for one, I'm still mucking the attribute hashes from the caller directly which requires understanding of how the logic works for these hashes (:id set or not set, for instance), and two, I'm calling a function that is not trivial to fit here. It would be nice if there are ways to tell 'accepts_nested_attributes_for' to only create new record when certain condition is not met. The one-to-one association has a :update_only flag that does something similar but this is lacking for one-to-many relationship.
Are there better solutions out there?
This kind of logic probably belongs in your model, not your controller. I'd consider re-writing the author_attributes= method that is created by default for your association.
def authors_attributes=(authors_attributes)
authors_attributes.values.each do |author_attributes|
author_to_update = Author.find_by_id(author_attributes[:id]) || Author.find_by_username(author_attributes[:username]) || self.authors.build
author_to_update.update_attributes(author_attributes)
end
end
I haven't tested that code, but I think that should work.
EDIT: To retain the other functionality of accepts_nested_Attributes_for, you could use super:
def authors_attributes=(authors_attributes)
authors_attributes.each do |key, author_attributes|
authors_attributes[key][:id] = Author.find_by_username(author_attributes[:username]).id if author_attributes[:username] && !author_attributes[:username].present?
end
super(authors_attributes)
end
If that implementation with super doesn't work, you probably have two options: continue with the 'processing' of the attributes hash in the controller (but turn it into a private method of your controller to clean it up a bit), or continue with my first solution by adding in the functionality you've lost from :destroy => true and reject_if with your own code (which wouldn't be too hard to do). I'd probably go with the first option.
I'd suggest using a form object instead of trying to get accepts_nested_attributes to work. I find that form object are often much cleaner and much more flexible. Check out this railscast
I am creating a instance variable that gets passed to my view. This variable 'post' has a user_id associated with it and I wanted to add an extra attribute called 'username' so I can also pass that and use it in the view.
Here is an example of what I would like to do.
#post = Post.find(params[:id])
#post.username = User.find(#post.user_id).username
A username column does exist on my Users model but not my Songs model. So it won't let me use
#post.username
I know I can just make an entirely new instance variable and put that information in there but I would like to keep everything nice and neat, in one variable. Which will also make my json rendered code look cleaner.
Any ideas on how I can accomplish this?
Thanks!
Based on the presence of a user_id in your Post model, you probably already have an association set up that can retrieve the username. It will probably save a lot of trouble to simply use the existing association:
#post = Post.find(params[:id])
username = #post.user.username
If you're likely to be querying more than one post at a time (e.g., on an index page, calling .includes to tell Rails to eager-load an association will help you avoid the N+1 problem:
#posts = Post.includes(:user).all
Finally, to include the associated record in your JSON output, pass the :include parameter as you serialize:
# in controller
render :json => #post.to_json(:include => :user)
This question includes a much more comprehensive discussion of serialization options. Well worth a read.
No need to pass a separate instance variable.
1. You can use #post.user.username in view itself.
2. Or you can create a helper and pass #post.user
def username user
user.username
end
I have a rails app that involves users, essays, and rankings. It's pretty straightforward but I'm very new to rails. The part I'm having trouble with right now is the create method for the rankings.
The Essay class has_many :rankings and the Ranking class belongs_to :essay
in the rankings controller I have:
def create
#ranking = #essay.rankings.build(params[:ranking])
flash[:success] = "Ranking created!"
redirect_to root_path
end
but I get the error: undefined method `rankings' for nil:NilClass
I need each ranking to have an essay_id, and I believe build updates this for me.
I thought that rails give me the rankings method because of the relation I set, and why is #essay nil?
Thanks in advance
Build doesn't save. You should be using new and then save. I'd provide you sample code, but you haven't really given us a clear picture of what you have going on. That #essay instance variable is being used before it's defined, and I'm not really sure how your application is determining which essay the ranking belongs to.
You might wanna give Rails Guides a read.
I think this is what you intend to do:
# EssayController#new
def new
#essay = Essay.new(params[:essay])
#ranking = #essay.rankings.build(params[:ranking])
#...
end
Take a look at nested model forms , it should get you in the right direction.
I have a basic CRUD with "Company" model. To make the company name show up, I did
def to_param
name.parameterize
end
Then I accessed http://localhost:3000/companies/american-express which runs show action in the companies controller.
Obviously this doesn't work because the show method is as following:
def show
#company = Company.find_by_id(params[:id])
end
The params[:id] is american-express. This string is not stored anywhere.
Do I need to store the short string (i.e., "american-express") in the database when I save the record? Or is there any way to retrieve the company data without saving the string in the database?
Send the ID with the parameterized value;
def to_param
new_record? ? super : "#{id}-#{name}"
end
And when you collect the data in the show method, you can use the whole parameter;
def show
#company = Company.find("12-american-express"); // equals to find(12)
end
There's also a plugin called permalink_fu, which you can read more about here.
I think friendly_id is more usable.
I do something similar with the Category model in my blog software. If you can guarantee that the only conversion the parameterize method is doing to your company names is replacing space characters with dashes then you can simply do the inverse:
def show
#company = Company.find_by_name(params[:id].gsub(/-/, ' '))
end
Try permalink_fu plugin, which creates SEO friendly URLs in rails
http://github.com/technoweenie/permalink_fu
cheers
sameera
I would suggest the friendly_id gem also.
It gives you the flexibility to use persited permalink slugs, also strip diacritics, convert to full ASCII etc.
Basically it makes your life a lot easier, and you get "true" permalinks (no to_param and the id workaround needed) with little effort.
Oh and did i mention that the permalinks are also versioned, so you can make old outdated permalinks to redirect to the current one? :)