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.
Related
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.
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'm looking for best practices. Here's the scenario:
Customers can pay for one or more Widgets from a form. So I have a Payments model and a Widgets model. There is no association between them (Payments is associated with Customer). What's the best way to handle this?
In the Payments controller I could do:
def create
#customer = Customer.find(params[:customer_id])
if #customer.payments.create!(params[:payment])
how-many-widgets = params[:payment][:number].to_i
while how-many-widgets > 0
widget = Widgets.new
... update widget ...
widget.save!
how-many-widgets = how-many-widgets - 1
end
end
redirect_to #customer
end
Is this the best way to do this? Or is there some more elegant solution?
If you're saving and changing things, it's a good bet you should be doing this code in a model, rather than a controller. If I were to refactor your code, it would look a little like this:
def create
#customer = Customer.find(params[:customer_id])
if #customer.payments.create!(params[:payment])
params[:payment][:number].times do
Widget.create(params[:widget])
end
end
redirect_to #customer
end
If Widget.create isn't what you're looking for, come up with a custom method that takes the params in, transforms them, and then spits out the correct object. Also if the widgets should be related to either the customer or payments, don't hesitate to relate them -- like, if you look at that code and say, "I also need to pass the current user/customer/payment to the widget," that would be a good hint that the widget should be associated to that model somehow.
I used the nested model gem to create a Picture that can take tags. Now I have added an attribute to my model Picture so it has an attribute taglist. When I create a new tag, I want this to happen
class TagsController < ApplicationController
def create
#tag = Tag.new(params[:id])
if #tag.save
taglist = picture.taglist
taglist+=#tag.tagcontent
#tag.picture.update_attributes(:taglist => taglist)
end
end
end
and in my routes
resources :pictures do
resources :tags
end
When i make a new tag, nothing happens in the taglist attribute, like nothing happened, why?
It's hard to help due to lack of information, but I see two possible issues:
Tag.new(params[:id]) doesn't make sense. Assuming Tag inherits from ActiveRecord::Base, you need to pass it a hash of attributes (e.g. Tag.new(:name => 'mytag')) You are likely not getting into the if #tag.save block at all due to validation errors. Also, you don't need to provide an id to an object you want to create. The database chooses the id.
Inside the block, picture is undefined on the first line.
Why not try debugging with something like:
if #tag.save
taglist = picture.taglist
taglist+=#tag.tagcontent
#tag.picture.update_attributes(:taglist => taglist)
else
p "ERRORS:"
p #tag.errors.full_messages
end
See what errors that prints out into your console.
I definitely think that picture is probably undefined in the create method of the controller. Can you show us the view, the form you're using to create a new tag? Is there a form field through which you're choosing which photo gets the tag?
Please show us the association and your view for creating the new tag.
Actually, what I'd really recommend instead of cooking up your own is to use:
Agile Web Development's acts_as_taggable_on_steroids
It's an excellent plugin to make tagging easy; it has quite a few nifty features built in, including the searches, tag clouds, etc. We use it on our projects.
I have two Models:
Users, and Articles
Every user is able to create an article but can only edit his own Articles?
Can someone provide an example of that? (Model, Controller and View Please?)
Thanks
EDIT
I do not want the whole code. I can get the most of the code using scaffolding. I need the modifications that I have to do to achieve that. My biggest concern is how to allow only the author of an article to edit it. That's what I am asking.
I can't write the full example code out but I can at least point you in the right direction.
You're describing a one to many association between your two models. This guide is the best I've seen in figuring out how to set up those associations in Rails.
Once you've got that in place you can limit access based on ownership of the article quite easily. For something this straight forward you probably wouldn't need a permissions gem but there are some solid ones out there.
I'd protect access in the controllers and in the view. You can simply check the current_user against the article object on the view, and in the controller you can use before filters to protect the article.
If you get further down this path and have more specific questions I'm glad to try and answer them.
Assuming an Article belongs_to :user and you have an authentication setup that gives you a current_user method, you should be able to do something like this in your ArticlesController:
def edit
#article = Article.find(params[:id])
if #article.user == current_user
render :edit
else
flash[:alert] = "You don't have permission to edit this article."
redirect_to some_path
end
end
You would also need something similar for your update method.