Can't insert MySQL query in Rails 5 (Lynda Course) - ruby-on-rails-5

I'm taking a course on Lynda.com (Ruby on Rails 5 Essential Training) and I'm having an issue with adding a record on a table. Here's some details: The objective is to create a joint table, Many-to-Many association, so we're first trying to create a record on with of the tables we want to use on the new table. And everytime I write this line:
section = Sections.create(:name => "Section One", :position => 1)
It gives me this
(0.2ms) BEGIN
(0.3ms) ROLLBACK
=> #<Section id: nil, page_id: nil, name: "Section One", position: 1, visible: false, content_type: nil, content: nil, created_at: nil, updated_at: nil>
I checked my code and everything seems fine. By the way inserting records on other tables works. It's just this table.
one important point, This table is a previously created table. It's the new one we're trying to create.
What am I doing wrong?
Here is my code from migrate:
class CreateSections < ActiveRecord::Migration[5.2]
def up
create_table :sections do |t|
t.integer "page_id"
t.string "name"
t.integer "position"
t.boolean "visible", :default => false
t.string "content_type"
t.text "content"
t.timestamps
end
add_index("sections", "page_id")
end
def down
drop_table :sections
end
end
Here is the Section model:
class Section < ApplicationRecord
belongs_to :page
has_many :section_edits
end

The error is caused by: belongs_to :page as the page_id is nil and by default Rails belongs_to helper is adding a presence validation to make sure that the association is valid.
To disable this behaviour (presence validation) you can use:
belongs_to :page, optional: true
as mentioned here: https://guides.rubyonrails.org/association_basics.html#options-for-belongs-to
or you can add page_id to your Section.create call as mentioned by others:
page_id = 1 # or Page.first.id or any page id you need
section = Section.create(name: "Section One", position: 1, page_id: page_id)

Your error comes from belongs_to :page
If you try with create!, you should see this error message:
ActiveRecord::RecordInvalid: Validation failed: Page must exist
Just add page_id in your section creation:
page_id = 1 # or Page.first.id or any page id you need
section = Section.create(name: "Section One", position: 1, page_id: page_id)

Related

Rails Custom Primary Key: SQLite3::ConstraintException: NOT NULL constraint failed

I have a custom Primary Key (PK), ASIN, in my Products table. This PK is a string.
def change
create_table :products, id: false do |t|
t.references :user, null: false, foreign_key: true
t.primary_key :asin
t.string :image
t.string :price
t.string :title
t.timestamps
end
change_column :products, :asin, :string
end
In my models for Product, I have the following:
self.primary_key = 'asin'
belongs_to :user
validates_uniqueness_of :asin
validates_presence_of :asin, :image, :title, :price, :user_id
before_save :before_save
def before_save
self.asin = self.asin.upcase!
end
I have looked over the code several times but while trying to hit this Route and create a Product, this is the error I receive.
"exception": "#<ActiveRecord::NotNullViolation: SQLite3::ConstraintException: NOT NULL constraint failed: products.asin>"
And this is the JSON that I am sending to this create Route
{
"asin": "B",
"title": "Test",
"image": "test",
"user_id": 1,
"price": "test"
}
NOTE: I know that it is generally looked down upon to use PKs in this manor and please know I recognize this but this is the ideal setup for my project's goal. Please refrain from leaving mean-spirited comments.
EDIT: For reference, here is the Create function under the Products controller.
skip_before_action :authorize_request, only: [:create, :show]
def create
Product.create!(product_params)
json_response(product_params, :created)
end
def product_params
params.permit(
:asin,
:image,
:title,
:user_id,
:price
)
end
EDIT: Part of the issue was that the asin's first letter needed to be downcased before sending to the backend.
I am not totally sure what happened here to fix this. But I ran the following commands just to test things as I was sure this database setup was perfectly fine.
First, I stopped the server using control + c on mac.
I connected to rails using rails c
I connected to the products table using Product.connection
And I created a test Product just to see what was wrong Product.create!(asin: "Test_ASIN", title: "title", image: "image", price: "price", user_id: 1)
I was surprised to see that this worked since this is exactly what I sent to the backend originally, but then I went ahead and started rails s and tried to send the request again and this time, it submitted.
I hope this helps someone out there as this was obnoxiously weird.

Pull data from multiple tables in Rails

I am new in Ruby on Rails and I am trying to make a book registration website. Everything works fine except category section. When a user assign a category to his book, my database copies book_categories.id and put it in book.book_categories_id. The website also have a profile page, where you can view user's book(s). My problem is to display a category.name, I searched a lot of similar problems but I have not found the right answer.
Here is my book controller:
before_action :set_book, except: [:index, :new, :create]
before_action :authenticate_user!, except: [:show]
def show
#photos = #book.photos
end
def index
#books = current_user.books
end
def new
#book = current_user.books.build
end
def create
#book = current_user.books.build(books_params)
if #book.save
redirect_to listing_book_path(#book), notice: "Saved."
else
flash[:alert] = "Failed."
render :new
end
end
private
def set_book
#book = Book.find(params[:id])
end
def book_params
params.require(:book).permit(:book_categories_id, :book_name, :summary, :address, :price, :company_name)
end
As long as I understand, I have to allow my controller to have access to my category table but I do not know how. Also, all of my categories store in seed.rb.
This is my BookCategory.rb model:
class BookCategory < ActiveRecord::Base
has_many :books, :foreign_key => :book_categories_id
end
Book.rb model:
class Book < ApplicationRecord
belongs_to :user, :foreign_key => 'user_id'
has_many :photos, dependent: :delete_all
validates :book_name, presence: true
validates :book_categories_id, presence: true
def cover_photo
if self.photos.length > 0
self.photos[0].image.url
else
"default/image-default.jpg"
end
end
end
My schema.rb:
create_table "book_categories", force: :cascade do |t|
t.string "name"
t.string "subcategory"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "book", force: :cascade do |t|
t.string "book_name"
t.text "summary"
t.string "address"
t.decimal "price", precision: 8, scale: 2
t.boolean "active"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.float "latitude"
t.float "longitude"
t.string "company_name"
t.integer "book_categories_id"
t.index ["book_categories_id"], name: "index_books_on_book_categories_id"
end
Usually, when I type #book_categories.name, I get an error:
undefined method `name' for nil:NilClass
And at the very end, it says Parameters: {"id"=>"15"} whereas the category.id is different, and it passes book.id instead.
What I am doing wrong?
First fix the naming problem you have going. I don't know how the books table got called "book" instead of "books" in the schema, but you might have to fix that first.
Then category should be singular everywhere except the database table name. You need to make a migration to fix the foreign key and its index in the books table something like this:
class FixCategoryNaming < ActiveRecord::Migration
def change
remove_column :books, :book_categories_id
add_reference :books, :book_category, index: true, foreign_key: true
end
end
Then run rails db:migrate and check the schema looks ok
Then in the BookCategory class change the line to simply
has_many :books
You need to add this line to your Book class
belongs_to :book_category
and change the validation for book_category to be this
validates :book_category_id, presence: true
Then in the view for book index you have a #books variable. You can get the book name and category name for each book like this:
<% #books.each do |book| %>
<p><%= book.book_name %></p>
<p><%= book.book_categories.name %></p>
<% end %>
I think you need to setup as has_many through relationship. This is a good place to start http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association

Rails - Saving Objects with NOT NULL constraint

I have a model called Issue that has the following database structure :
class CreateIssues < ActiveRecord::Migration
def change
create_table :issues do |t|
t.string :path, null: false
t.string :filename, null: false
t.string :name, null: false
t.string :year
t.integer :number
t.timestamps null: false
end
end
end
In my model tests, I use the following line :
issue = Issue.create path: "test_path", filename: "test_filename", name: "test_name"
... which raises an exception when saving :
SQLite3::ConstraintException: NOT NULL constraint failed:
issues.path: INSERT INTO "issues" ("created_at", "updated_at") VALUES (?, ?)
From what I understand, when calling create or new/save, rails starts by inserting an entry with only the timestamps. I would expect the insert to contain all of the values that I have passed to the create method. Am I missing a step in the creation process?
EDIT :
Basic information and steps:
Using SQLite3
Using Rails 4.2.1
Using RSpec
Generated the Issue model using be rails generate model Issue
Added the NULL constraints afterwords by hand
Did be rake db:migrate successfully
Tried my code in various model spec files
Tried other models, I get generated SQL that only includes the timestamps.
EDIT 2:
Here is my Issue model :
class Issue < ActiveRecord::Base
has_one :pending_issue
has_one :watched_issue
has_many :unmatched_issues
validates :path, presence: true
validates :filename, presence: true
validates :name, presence: true
attr_accessor :path
attr_accessor :filename
attr_accessor :name
attr_accessor :year
attr_accessor :number
end
Your result is anomalous. This is what I get:
# migration
class CreateIssues < ActiveRecord::Migration
def change
create_table :issues do |t|
t.string :path, null: false
t.timestamps null: false
end
end
end
Then in the console:
Issue.create path: "path"
# (0.1ms) begin transaction
# SQL (0.3ms) INSERT INTO "issues" ("path", "created_at", "updated_at") VALUES (?, ?, ?) [["path", "path"], ["created_at", "2015-06-21 16:55:08.304653"], ["updated_at", "2015-06-21 16:55:08.304653"]]
# (0.8ms) commit transaction
# => #<Issue id: 1, path: "path", created_at: "2015-06-21 16:55:08", updated_at: "2015-06-21 16:55:08">
My test:
require "test_helper"
describe Issue do
it "can be created" do
Issue.create path: "test_path"
end
end
passes. No exception.
What I think may have happened is that your database schema has become out of date on your test database.
Firstly, does a similar 'create' work in the development (default) rails console? If so, it's a big indication that something is very different in your test and development environments.
What happens if you force the console to use the test environment?
RAILS_ENV=test rails c
Does it work here?
I think you may need to manually rollback your test database to the migration which first created your issues table, then re-migrate
RAILS_ENV=test rake db:rollback
Keep applying until you reach the appropriate migration, then:
RAILS_ENV=test rake db:migrate
Does that help?
EDIT:
This appears to be the issue:
attr_accessor :path
attr_accessor :filename
attr_accessor :name
attr_accessor :year
attr_accessor :number
These will overwrite the Rails default accessors, at a guess. They are certainly not needed.
Did you accidentally omit attr_accessible for :path?

ActiveRecord: complex query with Sum, Join, Group By and Select multiple fields

I've waisted few days for this query but still not figured out the solution.
There are my models
class UserObject < ActiveRecord::Base
self.table_name = "user_objects"
belongs_to :user, class_name: 'User'
belongs_to :the_object, class_name: 'Object'
end
class Object < ActiveRecord::Base
self.table_name = 'objects'
has_many :user_objects, class_name: 'UserObject', foreign_key: :the_object_id, inverse_of: :the_object
has_many :users, through: :user_objects
end
class User < ActiveRecord::Base
self.table_name = "users"
end
Here is my schema
create_table "objects", force: true do |t|
t.float "importance", default: 0.5, null: false
end
create_table "user_objects", force: true do |t|
t.integer "user_id"
t.integer "the_object_id"
t.float "score", default: 0.0, null: false
end
create_table "users", force: true do |t|
t.string "first_name"
t.string "last_name"
end
I need a query for select objects.importance and sum of user_objects.score. But also I have to select for query only those objects which belongs to user1 and user2.
I wrote a query for select objects.importance
Object.select("objects.importance").joins(:user_objects).
where(user_objects: {user_id: [user1_id,user2_id]}).
group(objects: :id).
having('COUNT("user_objects"."id")=2')
And it's converted to
SELECT objects.importance FROM "objects" INNER JOIN "user_objects" ON "user_objects"."the_object_id" = "objects"."id" WHERE "user_objects"."user_id" IN (2, 7) GROUP BY "objects"."id" HAVING COUNT("user_objects"."id")=2
When I executed this query I got this response
[#<Object id: nil, importance: 0.5>, #<Object id: nil, importance: 0.5>]
Quantity of objects in response is OK. But I still don't know how to count in this query sum of user_objects.score. Next query doesn't work
Object.select("objects.importance, SUM(user_objects.score)").
joins(:user_objects).
where(user_objects: {user_id: [user1_id,user2_id]}).
group(objects: :id).having('COUNT("user_objects"."id")=2')
I expected in response something like this
[#[<Object id: nil, importance: 0.5>, 0.2], #[<Object id: nil, importance: 0.5>,0.3]]
I would greatly appreciate any help you can give me in working this problem.
Ок. I've found a solution. Here is right query.
Object.joins(:user_objects).
where(user_objects: {user_id: [user1_id,user2_id]}).
group(objects: :id).
having('COUNT("user_objects"."id")=2').
pluck("objects.importance, SUM(user_objects.score)")
Problem was in select method because it creates an object of a class which invoke this method. So it is impossible to choose in one select attributes of few models. But pluck return resulting array with values of fields So we can choose fields from different tables.
That`s it. So simple!

Mixing has_one and has_and_belongs_to_many associations

I'm trying to build a database of urls(links). I have a Category model that has and belongs to many Links.
Here's the migration I ran:
class CreateLinksCategories < ActiveRecord::Migration
def self.up
create_table :links_categories, :id => false do |t|
t.references :link
t.references :category
end
end
def self.down
drop_table :links_categories
end
end
Here's the Link model:
class Link < ActiveRecord::Base
validates :path, :presence => true, :format => { :with => /^(#{URI::regexp(%w(http
https))})$|^$/ }
validates :name, :presence => true
has_one :category
end
Here's the category model:
class Category < ActiveRecord::Base
has_and_belongs_to_many :links
end
And here's the error the console kicked back when I tried to associate the first link with the first category:
>>link = Link.first
=> #<Link id: 1, path: "http://www.yahoo.com", created_at: "2011-01-10...
>>category = Category.first
=> #<category id : 1, name: "News Site", created_at: "2011-01-11...
>>link.category << category
=> ActiveRecord::StatementInvalid: SQLite3::Exception: no such column :
categories.link_id:
Are my associations wrong or am I missing something in the database? I expected it to find the links_categories table. Any help is appreciated.
Why are you using HABTM here at all? Just put a belongs_to :category on Link, and a has_many :links on Category. Then in the db, you don't need a join table at all, just a :category_id on the links table.
But, if you do want a HABTM here, from a quick glance, the first thing I noticed is that your join table is named incorrectly -- it should be alphabetical, categories_links.
The second thing is that you can't mix has_one and has_and_belongs_to_many. HABTM means just that -- A has many of B and A belongs to many of B; this relationship implies that the opposite must also be true -- B has many of A and B belongs to many of A. If links HABTM cateogries, then categories must HABTM links.
See http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_and_belongs_to_many