How to add relation to existing object in Rails - ruby-on-rails

I want to add relation between existing post and author.
I was trying to modify created_by attribute but it's not accessible from object.
def set_author
if (#post.created_by.empty? && #post.author_code.present?)
if #post.author_code == params[:author_code]
#post.created_by = current_user
else
raise(ExceptionHandler::InvalidAuthorCode, Message.invalid_author_code)
end
else
raise(ExceptionHandler::DisallowedAction, Message.action_not_allowed)
end
end
It's not working because there is no method #post.created_by even if it's present db.
Post model from schema.rb
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "content"
t.boolean "accepted", default: false
t.string "created_by"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "author_code"
end
Edit:
part of post.rb
belongs_to :user, optional: true, foreign_key: :created_by
part of user.rb
has_many :confessions, foreign_key: :created_by

Assuming that it only fails for the #post which don't have any created_by OR user then you can use following:
#post.try(:created_by).blank? && #post.author_code.present?

I was trying to update #post attributes a wrong way. Changed
#post.created_by = current_user
to
#post.update_attribute(:created_by, current_user)
and it's working.
It's not a Devise user model, just self written.

Related

ActiveModel::MissingAttributeError (can't write unknown attribute `user_id`)

I have problems with my comments when my app is deployed. Locally everything is working. The logs from heroku says:
ActiveModel::MissingAttributeError (can't write unknown attribute `user_id`):
2018-01-02T15:52:43.461794+00:00 app[web.1]: [79cd7190-e2d9-4dd0-bf71-
709552e5c6e5] app/controllers/comments_controller.rb:15:in `create'
I have no ideas what is occuring the error. Maybe some database thing?
My CommentsController
class CommentsController < ApplicationController
def create
#post =Post.find(params[:post_id])
#comment =#post.comments.create(params[:comment].permit(:name, :body).merge(user: current_user))
redirect_to post_path(#post)
end
def destroy
#post = Post.find(params[:post_id])
#comment= #post.comments.find(params[:id])
if current_user.id == #comment.user_id
#comment.destroy
end
redirect_to post_path(#post)
end
end
My Models
class User < ApplicationRecord
has_many :posts
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
class Post < ApplicationRecord
belongs_to :user, optional: true
has_many :comments, dependent: :destroy
validates :title, presence: true, length: {minimum: 5}
validates :body, presence: true
end
class Comment < ApplicationRecord
belongs_to :post
belongs_to :user
end
My migration-file
class CreateComments < ActiveRecord::Migration[5.1]
def change
create_table :comments do |t|
t.string :name
t.text :body
t.references :post, index: true
t.timestamps
end
end
end
if you need more code or have any ideas please let me know
EDIT: if i add a user_id column i get a SQLite3::SQLException: duplicate column name: user_id: ALTER TABLE "comments" ADD "user_id" integer error
My schema.rb
`create_table "comments", force: :cascade do |t|
t.string "name"
t.text "body"
t.integer "post_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.index ["post_id"], name: "index_comments_on_post_id"
end
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.string "theme"
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
You'll need to add a user_id column to your comments table. The belongs_to requires this. You're also going to need a post_id column, and user_id for your posts table to.
You can customise the column name, but the convention is to use the format parent_table_id.
Here's the key quote, from the docs:
Associations are implemented using macro-style calls, so that you can
declaratively add features to your models. For example, by declaring
that one model belongs_to another, you instruct Rails to maintain
Primary Key-Foreign Key information between instances of the two
models, and you also get a number of utility methods added to your
model.
This means, for example, if your first user has an id of 1, all of their comments and posts will have a user_id value of 1, which does the actual tying together of the records.
Here's an example migration with the relevant line included:
def change
create_table :comments do |t|
...
t.belongs_to :user, index: true
...
end
end
Does that make sense? Let me know if you've any questions and I can update as needed :)
You need to add user_id
Create the migration with
rails g migration AddUserIdToComment user:references
Then do
rake db:migrate
And you should be fine.
Your migration have missing
t.references :user, index: true
So you need to add user_id column within comments table
Update : It seems like you have some migration problem. I suggest you to check for rake db:migrate:status comment and look for any down migration. Once all are up then just run rake db:migrate:down VERSION='VERSION_NUMBER_HERE' and add your user t.references :user, index: true to the same migration and migrate.
PS: Change existing migration if and only if you have not pushed it.

Rails InvalidForeignKey

I am developing a portfolio for my website, I decided to add skills to each portfolio item.
class PortfolioSkill < ApplicationRecord
belongs_to :portfolio
belongs_to :skill
end
class Portfolio < ApplicationRecord
has_many :portfolio_skills
has_many :skills, through: :portfolio_skills
def all_tags=(names)
self.skills = names.split(",").map do |name|
Skill.where(name: name.strip).first_or_create!
end
end
def all_tags
self.skills.map(&:name).join(", ")
end
def remove_skill_tags
PortfolioSkill.where(portfolio_id: id).destroy_all
end
end
create_table "portfolio_skills", force: :cascade do |t|
t.integer "portfolio_id"
t.integer "skill_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["portfolio_id"], name: "index_portfolio_skills_on_portfolio_id"
t.index ["skill_id"], name: "index_portfolio_skills_on_skill_id"
end
create_table "portfolios", force: :cascade do |t|
t.string "name"
t.string "client"
t.date "completed"
t.text "about"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "long_landscape"
t.string "cover"
t.integer "category_id"
t.index ["category_id"], name: "index_portfolios_on_category_id"
end
When I click destroy on the index page I get the
SQLite3::ConstraintException: FOREIGN KEY constraint failed: DELETE FROM "portfolios" WHERE "portfolios"."id" = ?
error. All the associations look right. I used this same pattern for my tags on other models and it worked with no issues. Any help would be great.
You are deleting from portfolios table, but table portfolio_skills has a column referencing it as foreign key. Hence the error.
Trying to delete a parent without checking and deleting its associated children can lead to data inconsistency. This exception is in place to prevent that.
Rails dependent destroy will take care of removing associated children rows while removing a parent.
Try using a dependent destroy:-
class Portfolio < ApplicationRecord
has_many :portfolio_skills, :dependent => :destroy
...
end

Can't write unknown attribute using rails_admin

Im' using rails_admin to save a project that has a category within. I didn't define project_id and category_id because I thought they should be created by rails. The problem I got were using the method def category_id=(id) defined in project model (see below). The error is:
can't write unknown attribute `project_id`
My models are:
Category
class Category < ActiveRecord::Base
belongs_to :project, :inverse_of => :category
end
Project
class Project < ActiveRecord::Base
has_one :category, :dependent => :destroy, :inverse_of => :project
def category_id
self.category.try :id
end
def category_id=(id)
self.category = Category.find_by_id(id)
end
end
My schema:
create_table "categories", force: :cascade do |t|
t.string "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "projects", force: :cascade do |t|
t.string "title"
t.text "text"
t.string "url"
t.string "key_feature"
t.string "image_1"
t.string "image_2"
t.string "image_3"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
How do you connect Projects to Categories?
Base on your schema Projects table has no category_id.
Neither you Category table has project_id
I would add category_id to Projects table.
rails g migration add_category_id_to_projects category_id:integer
rake db:migrate
The solution at the end was to manually add the project_id to categories table.
rails g migration add_project_id_to_categories project_id:integer
Thanks #Misha for you suggestion.

Error in rails saving attribute to object

I have 2 models: User and UserLvl.
class User < ActiveRecord::Base
has_one :user_lvl, primary_key: 'user_lvl_id', foreign_key: 'id'
end
class UserLvl < ActiveRecord::Base
belongs_to :user
end
Controller action
def change_lvl
#user.user_lvl = UserLvl.first
#user.save
end
UserLvl.first is returned fine,with id and all but it failes at the first line with : "Column 'id' cannot be null"
why is this happening?
EDIT:
schema.rb
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.integer "user_lvl_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "user_lvls", force: true do |t|
[omited some information]
t.datetime "created_at"
t.datetime "updated_at"
end
You probably want to use a belongs_to association rather than a has_one, like this:
class User < ActiveRecord::Base
belongs_to :user_lvl
end
Your foreign key is placed in the users table, which makes User the associated model, that belongs to the UserLvl.

Polymorphic Association and extra id field

I need some help with polymorphic associations. Below is my structure:
class Category < ActiveRecord::Base
has_ancestry
has_many :categorisations
end
class Categorisation < ActiveRecord::Base
belongs_to :category
belongs_to :categorisable, polymorphic: true
end
class classifiedAd < ActiveRecord::Base
belongs_to :user
has_one :categorisation, as: :categorisable
end
And here is my schema.rb
create_table "classifiedads", force: true do |t|
t.string "title"
t.text "body"
t.decimal "price"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "classifiedads", ["user_id"], name: "index_classifiedads_on_user_id", using: :btree
create_table "categories", force: true do |t|
t.string "name"
t.string "ancestry"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "categories", ["ancestry"], name: "index_categories_on_ancestry", using: :btree
create_table "categorisations", force: true do |t|
t.integer "category_id"
t.integer "categorisable_id"
t.string "categorisable_type"
t.datetime "created_at"
t.datetime "updated_at"
end
It seems like the associations is correct as when I'm in the console I can do the appropriate commands and all seems to return the right results, for example: Category.first.categorisations or ClassifedAd.first.categorisation. But what I don't understand is saving the association from the Create and editing the record via the Update actions. I'm using simple_form to create my forms and when looking at the params I get the below:
{"title"=>"Tiger",
"body"=>"Huge Helicopter",
"price"=>"550.0",
"categorisation"=>"5"}
and this fails to update or even create as I get this error : Categorisation(#70249667986640) expected, got String(#70249634794540) My controller actions code are below:
def create
#classified = Classifiedad.new(classifiedad_params)
#classified.user = current_user
#classified.save
end
def update
#classified.update(classifiedad_params)
end
def classifiedad_params
params.require(:classifiedad).permit(:title, :body, :price)
end
I think it has something to do with the params as categorisation should be within a sub hash of results, is this right? Also, I need to do something extra within the actions, but what? What the params[:categorisation] value needs to do is save the number into the Categorisations.category_id table column and also save the polymorphic association. This table will be used across other models, which will also be a has_one association, as the user will only be able to select one category for each record. I really hope someone can help me here as the more I look into it the more I get confused :S Please let me know if you ned anymore info from me.
I'm using Rails 4 and Ruby 2
EDIT 2
I managed to get something working but I'm still not sure if its right. Below is the update code for the Create and Update actions. Would be good to know if there is a better way of doing this?
def create
#classified = Classifiedad.new(classifiedad_params)
#classified.user = current_user
**** NEW
cat = Categorisation.new(category_id: params[:classified][:categorisation])
#classified.categorisation = cat
**** END NEW
#classified.save
end
def update
**** NEW
#classified.categorisation.update_attribute(:category_id, params[:classified][:categorisation])
**** END NEW
#classified.update(classifiedad_params)
end

Resources