I have an issue with inserting data via rails command. Bellow you can see my model and the issue. The title is displaying nil even tho I created a new instance of Post with the title hash. I am aware you can do this in a different way. I am using this simple example to figure out why can't I insert or display data from the database.
Model
category.rb
class Category < ApplicationRecord
attr_accessor :name
has_many :posts
end
post.rb
class Post < ApplicationRecord
attr_accessor :title, :body, :category_id, :author_id
belongs_to :category
end
Rails c
post = Post.new(:title => 'Test')
=> #<Post id: nil, title: nil, body: nil, category_id: nil, author_id: nil, created_at: nil, updated_at: nil>
You should not be using the attr_accessor in your Rails class. Rails automatically make these attributes readable, and you should generally only be writing by saving records to the database.
Related
I'm inheriting a codebase for a Rails app that uses a blog engine -- and I am not making sense of how the models interact.
What I want to do is show the author who is associated with a specific article.
There is a table for articles called LinesArticle. An example entry:
<LinesArticle id: 2, title: "Example Article", sub_title: "Example Title",
content: "Example Content", published: true, published_at: "2017-08-22 00:00:00",
created_at: "2017-08-23 06:15:33", updated_at: "2017-08-23 06:15:36", slug: "whatever",
featured: false, document: nil, short_hero_image: "", teaser: "">
Next, there is a table for Authors called LinesAuthor. An example entry that should be associated:
#<LinesAuthor id: 1, name: "John Doe", email: "jd#examplesitedotcom", created_at: "2017-08-19 07:46:04", updated_at: "2017-08-19 07:46:04">
So if I compare these two tables, there isn't a connection between the data that would make sense for the models. So then I found LinesAuthorable that I think connects them. An entry:
<LinesAuthorable id: 2, author_id: 1, article_id: 2,
created_at: "2017-08-23 06:15:33", updated_at: "2017-08-23 06:15:33">
So my thinking is, if my controller calls an article like #articles = LinesArticle.last and I want to show the author who wrote that article by matching article_id to the matching result in LinesAuthorable and then query LinesAuthor for the matching author_id.
Reading through the documentation, I have now created models where an Article model belongs_to :lines_authorable and Author has_many :Lines_Authorable. If that is the right approach, how would you call that in the view to actually show the Author?
The LinesAuthorable table is acting as a through table, you can use a has_many through relationship to connect the data, something like:
class LinesArticle < ApplicationRecord
has_many :lines_authorables, foreign_key: :article_id
has_many :lines_authors, through: :lines_authorables
end
class LinesAuthor < ApplicationRecord
has_many :lines_authorables, foreign_key: :author_id
has_many :lines_articles, through: :lines_authorables
end
class LinesAuthorable < ApplicationRecord
belongs_to :line_article, foreign_key: :article_id
belongs_to :line_author, foreign_key: :author_id
end
You can then access an article's authors, and likewise an author's articles directly through the relationship:
#article = LinesArticle.first
#article.lines_authors #=> #<LinesAuthor::ActiveRecord_Relation...>
#author = LinesAuthor.first
#author.lines_articles #=> #<LinesArticle::ActiveRecord_Relation...>
Does Rails allow conditional validations for validates_associated? I'm seeing the following on Rails 4.2.0. Am I trying to do it incorrectly?
Models:
class User < ActiveRecord::Base
has_many :books
validates_associated :books, if: :should_validate_book?
def should_validate_book?
return false
end
end
class Book < ActiveRecord::Base
belongs_to :user
validates_presence_of :title
end
The presence validation on Book's title attribute still runs (Rails console):
> u = User.create!
=> #<User id: 2, created_at: "2015-02-24 19:34:51", updated_at: "2015-02-24 19:34:51">
> u.books.build
=> #<Book id: nil, user_id: 3, title: nil, created_at: nil, updated_at: nil>
> u.valid?
=> false
> u.books.first.errors
=> #<ActiveModel::Errors:0x007fa256b210d8 #base=#<Book id: nil, user_id: 3, title: nil, created_at: nil, updated_at: nil>, #messages={:title=>["can't be blank"]}>
It turns out that validates_associated is ON by default for has_many relationships. To make it conditionally, you'd need to add validate: false to the has_many declaration:
has_many :books, validate: false
In Rails since time immemorial validates_associated has only taken a list of attributes. Besides that, you'd kind of be mixing up behavior between your models from what I gather from the criteria you've pasted. A better approach would be to adjust your validations in the Book model to account for the variation and let Book decide for itself whether an object should be validated or not.
This section of railsguide says:
You should use this helper when your model has associations with other models and they also need to be validated.
So I thought validation of associated models wouldn't be run without validates_associated.
But actually, It was run without it.
There are two models, School and Student.
class School < ActiveRecord::Base
has_many :students
validates :name, presence: true
end
class Student < ActiveRecord::Base
belongs_to :school
validates :name, presence: true
end
On rails console,
school = School.new
=> #<School id: nil, name: nil, created_at: nil, updated_at: nil>
school.students << Student.new
=> #<ActiveRecord::Associations::CollectionProxy [#<Student id: nil, name: nil, school_id: nil, created_at: nil, updated_at: nil>]>
school.name = "test shcool"
=> "test shcool"
school.save
(0.1ms) begin transaction
(0.1ms) rollback transaction
=> false
school.errors.full_messages
=> ["Students is invalid"]
If with validates_associated like below:
class School < ActiveRecord::Base
has_many :students
validates :name, presence: true
validates_associated :students
end
class Student < ActiveRecord::Base
belongs_to :school
validates :name, presence: true
end
On rails console, I ran the exact same commands as above. But the last command school.errors.full_messages returned different result. (It is strange that there are duplicate error messages.)
school.errors.full_messages
=> ["Students is invalid", "Students is invalid"]
My questions are
Is this a RailsGuide's mistake?
Why does validates_associated exist?
Or do I have any mistaken idea?
My environment is
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin14.0]
Rails 4.2.0
it checks to see if the associated objects are valid before saving
Note: This question was spawned from another question I had regarding the use of accepts_nested_attributes_for. You may reference that question for additional context, if needed.
I believe this question is best explained with a simple example:
class Foo < ActiveRecord::Base
has_many :bars, inverse_of: :foo
end
class Bar < ActiveRecord::Base
validates :foo_id, presence: true
belongs_to :foo, inverse_of: :bars
end
f = Foo.new()
=> #<Foo id: nil, created_at: nil, updated_at: nil>
b = f.bars.build()
=> #<Bar id: nil, foo_id: nil, created_at: nil, updated_at: nil>
f.save!
=> ActiveRecord::RecordInvalid: Validation failed: Bars foo can't be blank
Is there an easy way to fix this problem? I know that I could save f first and then build b, but my situation is a little more complex than this example (see the question I referenced above) and I'd prefer to avoid that if possible.
The child records get created at the same time as parent, this is why your validation is failing, your child is not yet persisted. to make it work i would write a custom validation like this
class Foo < ActiveRecord::Base
has_many :bars
accepts_nested_attributes_for :bars, :allow_destroy => true
end
class Bar < ActiveRecord::Base
belongs_to :foo
validates_presence_of :bar
end
You can use a callback to create the object (Maybe before_save?). See here.
I have a very odd mass assignment error that shows up when I use association methods to create new objects.
I have a user model that looks like this:
class User < ActiveRecord::Base
has_many :posts, :dependent => :destroy
end
I also have a posts model that looks like this:
class Post < ActiveRecord::Base
belongs_to :user
attr_accessible :body, :title
end
If I do the following in console, I get a mass assignment warning:
> user = User.create(:name => "Daniel");
> user.posts.create(:title => "Hello World")
=> #<Post id: 1, body: nil, title: "Hello World", created_at: "2011-11-03
18:24:06", updated_at "2011-11-03 18:24:06", user_id = 1>
> user.posts
=> WARNING: Can't mass-assign attributes: created_at, updated_at, user_id
When I run user.posts again, however, I get:
> user.posts
=> [#<Post id: 1, body: nil, title: "Hello World", created_at: "2011-11-03
18:24:06", updated_at "2011-11-03 18:24:06", user_id = 1>]
There are a couple of other tricks I can do to avoid the mass assignment error, such as calling user.posts before I do users.posts.create.
Why is this happening and how can I prevent it?
I'm using Rails 3.0.7.
How about changing your user model to include attr_accessible for posts association
class User < ActiveRecord::Base
has_many :posts, :dependent => :destroy
attr_accessible :posts
end