Rails - Paperclip ROLLBACK due to associations - ruby-on-rails

I have 3 models. The question, answer and photo. I am using paperclip to save images. Questions and answers can have multiple images.
However, I get ROLLBACK when saving images for answer model. I don't get ROLLBACK when saving images for question model. I think I have a problem with model associations.
#photo model
class Photo < ApplicationRecord
belongs_to :question
belongs_to :answer
has_attached_file :image :path => ":rails_root/public/img/:filename", validate_media_type: false
do_not_validate_attachment_file_type :image
end
#answer model
class Answer < ApplicationRecord
belongs_to :question
has_many :photos
end
#question model
class Question < ApplicationRecord
has_many :answers
has_many :photos
end
My Controller :
p.answers.each do |a|
new_answer = q.answers.create(body: a[:body])
if a[:images]
a[:images].each do |e|
new_answer.photos.create(image: URI.parse('www.abc.com/'+e))
end
end
end
Any thoughts?

Since rails 5 the belongs_to associations are by default required.
There are two possible solutions, either write
belongs_to :question, optional: true
in your Photo model, or, as you are saving them through the nested association (a bit like a nested-form), you have to clearly indicate which association is the inverse of which.
So in your answer class, specify the inverse of the :photos association
has_many :photos, inverse_of: :answer
(to allow rails to correctly check that the belongs_to is set correctly)

Related

Validation in Polymorphic association with validates_associated

I have these models: Post, TextPost and PhotoPost and I use polymorphic association.
You can find these three models here or look below.
post.rb
class Post < ActiveRecord::Base
belongs_to :user
default_scope { order ("created_at DESC")}
belongs_to :content, polymorphic: true
has_reputation :votes, source: :user, aggregated_by: :sum
end
photo_post.rb
class PhotoPost < ActiveRecord::Base
has_attached_file :image, styles: {
post: "200x200>"
}
end
text_post.rb
class TextPost < ActiveRecord::Base
attr_accessible :body
end
What I want, is to validate the presence of :body and :image when a users submits a text_post or photo_post respectively. So far, I found out that I have to use validates_associated.
The full project can be found on Github.
I have experiment a lot to find out how validates_associated works and searched for examples online but I don't have a clue what's going on.
(If you need any more info please let me know)
Any help/guidance is greatly appreciated.
I think for starters there needs to be some form of association between these 3 models here. You have Post, PhotoPost and TextPost. As a post can has a type of post. Also remember that type in rails is a reserved word. But anyways your models should be set out as followed:
class Post< ActiveRecord::Base
belongs_to :postable, polymorphic: true
end
class PhotoPost < ActiveRecord::Base
has_many :posts, as: :postable
end
class TextPost < ActiveRecord::Base
has_many :posts, as: :postable
end
As from looking at the snippet you provided it doesn't show any set up of polymorphic association. With regards to validating the association of the polymorphic association you might want to take a read of this answer: Validate presence of polymorphic parent. Furthermore also might want to read this article: Validating a polymorphic association for a new record . The purpose of the validate_associated validation helper simply ensures that the association between the two records are valid or as they put it works. In your case if you were to use that validation helper. This would not be what you'd want because all that would be doing is validating the association between the two models. See the second link I provided this is what I believe you are after.
Also in your controller you need to build the relationship between the models. So in your controller I think you could have something like this inside your new action:
def new
#Post = Postfind(params[:id])
#PhotoPost = #post.photoposts.build
#TextPost = #post.textposts.build
end

Rails: Connecting Model to another Model

I just created new columns in my database for my micropost table and these columns were vote_count comment_count and I want to connect it to the Vote models vote_up count and the Comment models comment count. Since I just added these columns although there were votes and comments, how do I connect these other models to the micropost model to fill in the new columns. Any suggestions are much appreciated!
Micropost Model
class Micropost < ActiveRecord::Base
attr_accessible :title, :content, :view_count
acts_as_voteable
belongs_to :school
belongs_to :user
has_many :comments
has_many :views
accepts_nested_attributes_for :comments
end
It looks like what you're trying to do is use a counter_cache, which rails supports, but you've got the names of the columns wrong.
You want to add a comments_count and a votes_count column to your database instead of the ones that you have.
Then you can hook it up to your models as follows:
class Micropost< ActiveRecord::Base
attr_accessible :title, :content, :view_count
acts_as_voteable
belongs_to :school
belongs_to :user
has_many :comments, :counter_cache => true
has_many :views
accepts_nested_attributes_for :comments
end
The votes half of it is a bit more tricky since you're using some extra code with your acts_as_votable module, but counter caches are the way that you want to go if I understand you correctly.
Here is more info on them: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Comment belongs_to one of several models

My scenario is that there are several different models which can have comments. Trying to figure out the relationships:
Post
has_many :comments
Update
has_many :comments
Comment
belongs_to EITHER :post OR :update (but not both)????
Whats the proper way to set up the comment relationships? I want to be able to call Post.comments and Update.comments
Smells like a polymorphic association:
With polymorphic associations, a model can belong to more than one other model, on a single association. For example, you might have a picture model that belongs to either an employee model or a product model.
So you'd want something like this:
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
class Post < ActiveRecord::Base
has_many :comments, :as => :commentable
end
class Update < ActiveRecord::Base
has_many :comments, :as => :commentable
end
You'd have to set up a few things in the database for this to work as well. See the Polymorphic Associations section of the Active Record Associations Guide for details on the columns you'll need.

Rails: Implementing a reuseable Comment model

I have a Comments model, and I also have a Video, and Photo model. Now, I want for my Video and Photo models to have_many comments, but that means my Comment model will have to have a belongs to :video and a belongs_to :model (as well as foreign keys for each model in the database). Now say I create a Post model in that same application and I want it to have many comments, that would mean I would have to add belongs_to :post to my Comment class. In rails is there a better way to implement a Comment model when there are many other models that are going to have an association with it, or is this just how it is done? Any advice would be much appreciated.
You're looking for polymorphic associations.
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
class Photo < ActiveRecord::Base
has_many :comments, :as => :commentable
end
class Video < ActiveRecord::Base
has_many :comments, :as => :commentable
end
You also have to make some changes to your migrations, see the linked documentation for more information.

belongs_to through associations

Given the following associations, I need to reference the Question that a Choice is attached through from the Choice model. I have been attempting to use belongs_to :question, through: :answer to perform this action.
class User
has_many :questions
has_many :choices
end
class Question
belongs_to :user
has_many :answers
has_one :choice, :through => :answer
end
class Answer
belongs_to :question
end
class Choice
belongs_to :user
belongs_to :answer
belongs_to :question, :through => :answer
validates_uniqueness_of :answer_id, :scope => [ :question_id, :user_id ]
end
I am getting
NameError uninitialized constant User::Choice
when I try to do current_user.choices
It works fine, if I don't include the
belongs_to :question, :through => :answer
But I want to use that because I want to be able to do the validates_uniqueness_of
I am probably overlooking something simple. Any help would be appreciated.
You can also delegate:
class Company < ActiveRecord::Base
has_many :employees
has_many :dogs, :through => :employees
end
class Employee < ActiveRescord::Base
belongs_to :company
has_many :dogs
end
class Dog < ActiveRecord::Base
belongs_to :employee
delegate :company, :to => :employee, :allow_nil => true
end
Just use has_one instead of belongs_to in your :through, like this:
class Choice
belongs_to :user
belongs_to :answer
has_one :question, :through => :answer
end
Unrelated, but I'd be hesitant to use validates_uniqueness_of instead of using a proper unique constraint in your database. When you do this in ruby you have race conditions.
A belongs_to association cannot have a :through option. You're better off caching the question_id on Choice and adding a unique index to the table (especially because validates_uniqueness_of is prone to race conditions).
If you're paranoid, add a custom validation to Choice that confirms that the answer's question_id matches, but it sounds like the end user should never be given the opportunity to submit data that would create this kind of mismatch.
My approach was to make a virtual attribute instead of adding database columns.
class Choice
belongs_to :user
belongs_to :answer
# ------- Helpers -------
def question
answer.question
end
# extra sugar
def question_id
answer.question_id
end
end
This approach is pretty simple, but comes with tradeoffs. It requires Rails to load answer from the db, and then question. This can be optimized later by eager loading the associations you need (i.e. c = Choice.first(include: {answer: :question})), however, if this optimization is necessary, then stephencelis' answer is probably a better performance decision.
There's a time and place for certain choices, and I think this choice is better when prototyping. I wouldn't use it for production code unless I knew it was for an infrequent use case.
So you cant have the behavior that you want but you can do something that feels like it. You want to be able to do Choice.first.question
what I have done in the past is something like this
class Choice
belongs_to :user
belongs_to :answer
validates_uniqueness_of :answer_id, :scope => [ :question_id, :user_id ]
...
def question
answer.question
end
end
this way the you can now call question on Choice
It sounds like what you want is a User who has many Questions.
The Question has many Answers, one of which is the User's Choice.
Is this what you are after?
I would model something like that along these lines:
class User
has_many :questions
end
class Question
belongs_to :user
has_many :answers
has_one :choice, :class_name => "Answer"
validates_inclusion_of :choice, :in => lambda { answers }
end
class Answer
belongs_to :question
end
The has_many :choices creates an association named choices, not choice. Try using current_user.choices instead.
See the ActiveRecord::Associations documentation for information about about the has_many magic.

Resources