In my online shop I have tables Product and Size, also I think I need to add a table Restocking
Instead of updating a product, I guess It's better to have a Restocking table then I could track the dates where I added any new sizes, quantity, and why not the new prices (buying and selling)... and create stats...
Do you this it is correct?
Once a Restocking is created, the corresponding Product is updated with new quantity and price?
Well,
So it started this way:
#Product
has_many :sizes
accepts_nested_attributes_for :sizes, reject_if: :all_blank, allow_destroy: true
#Size
belongs_to :product
The Restocking table needs to have sizes attributes (like product)
I believe that I have to use polymorphic associations, but how I am supposed to update my schema , what should I add, remove?
So since I added the Restocking model, my models look like this:
#Product
has_many :sizes, inverse_of: :product, dependent: :destroy, as: :sizeable
has_many :restockings
accepts_nested_attributes_for :sizes, reject_if: :all_blank, allow_destroy: true
#Restocking
has_many :sizes, as: :sizeable
belongs_to :product
accepts_nested_attributes_for :sizes, reject_if: :all_blank, allow_destroy: true
#Size
belongs_to :product
belongs_to :restocking
belongs_to :sizeable, polymorphic: true, class_name: "Size"
schema.rb
create_table "sizes", force: :cascade do |t|
t.string "size_name"
t.integer "quantity"
t.bigint "product_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "quantity_stock"
t.index ["product_id"], name: "index_sizes_on_product_id"
end
create_table "restockings", force: :cascade do |t|
t.bigint "product_id"
t.bigint "sizeable_id"
t.decimal "price", precision: 10, scale: 2
t.decimal "buying_price", precision: 10, scale: 2
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["product_id"], name: "index_restockings_on_product_id"
t.index ["sizeable_id"], name: "index_restockings_on_sizeable_id"
end
create_table "products", force: :cascade do |t|
t.string "title", limit: 150, null: false
t.text "description"
t.bigint "category_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "color"
t.integer "user_id"
t.json "attachments"
t.string "brand"
t.string "ref"
t.decimal "price"
t.decimal "buying_price", precision: 10, scale: 2
t.index ["category_id"], name: "index_products_on_category_id"
end
At this point I have several errors, like
in ProductsController
def new
#product = Product.new
#product.sizes.build
end
error:
ActiveModel::UnknownAttributeError at /admin/products/new
unknown attribute 'sizeable_id' for Size.
Can you light me on the migrations I have to change?
Suggestions are welcome
You're almost there, to use polymorphic inside your Size model, you have to change the size resource, and add two attributes to the resource: sizeable_id and sizeable_type.
The sizeable_type is a string, indicates the class of the parent element, in your case, can be Product or Restocking, and sizeable_id indicates the element_id to find the parent element, your relations are correct, but you must add this elements to your Size, see the following:
One exemple of a migration to your case:
class AddSizeableToSize < ActiveRecord::Migration
def change
add_reference :sizes, :sizeable, polymorphic: true, index: true
end
end
On your Size model:
# app/models/size.rb
class Size < ActiveRecord::Base
belongs_to :sizeable, polymorphic: true
end
In your Product or Restocking model:
has_many :sizes, as: :sizeable
This is just a simple way to make your case works! If you want to know more about rails associations and polymorphism, can take a look in this link.
Related
I've been researching friendship models using roles, custom associations, etc. But I haven't been able to connect my project to the concepts in a clear way.
I want a "User" to be able to create an event I'm calling a "Gather". A User can also attend a Gather created by other Users. By attending a Gather, the "User" can also be a "Gatherer".
The list of Gatherers will technically be considered friends of the "creator". This is how far I've gotten:
Models:
User
Gather
Gatherer (?)
User
class User < ApplicationRecord
has_many :gathers_as_creator,
foreign_key: :creator_id,
class_name: :Gather
has_many :gathers_as_gatherer,
foreign_key: :gatherer_id,
class_name: :Gather
end
Gather
class Gather < ApplicationRecord
belongs_to :creator, class_name: :User
belongs_to :gatherer, class_name: :User
end
My question is, do I need to a join table, such as Gatherer, to allow multiple attendees and then later pull a friend list for the user/creator ?
Gatherer
belongs_to :gather_attendee, class_name: "User"
belongs_to :attended_gather, class_name: "Gather"
Here's what I think that schema would look like:
create_table "gatherers", force: :cascade do |t|
t.bigint "attended_gather_id"
t.bigint "gather_attendee_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["attended_gather_id"], name: "index_gatherers_on_attended_gather_id"
t.index ["gather_attendee_id"], name: "index_gatherers_on_gather_attendee_id"
end
Help, my head is spinning trying to understand the connections and how to proceed.
Previous planning:
Schema:
create_table "activities", force: :cascade do |t|
t.string "a_type"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "gatherers", force: :cascade do |t|
t.bigint "attended_gather_id"
t.bigint "gather_attendee_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["attended_gather_id"], name: "index_gatherers_on_attended_gather_id"
t.index ["gather_attendee_id"], name: "index_gatherers_on_gather_attendee_id"
end
create_table "gathers", force: :cascade do |t|
t.integer "creator_id"
t.integer "activity_id"
t.text "gather_point"
t.boolean "active"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "interest_gathers", force: :cascade do |t|
t.string "gather_id"
t.string "interest_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "interests", force: :cascade do |t|
t.string "i_type"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "users", force: :cascade do |t|
t.string "username"
t.string "img"
t.string "first_name"
t.string "last_name"
t.string "state"
t.string "city"
t.string "bio"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "gatherers", "gathers", column: "attended_gather_id"
add_foreign_key "gatherers", "users", column: "gather_attendee_id"
end
class User < ActiveRecord::Base
has_many :gatherers, foreign_key: gather_attendee_id
has_many :attended_gathers, through: :gatherers
has_many :created_gathers, foreign_key: :creator_id, class_name: "Gather"
end
class Gather < ActiveRecord::Base
has_many :gatherers, foreign_key: :attended_gather_id
has_many :attendees, through: :gatherers, source: :gather_attendee
belongs_to :creator, class_name: "User"
end
class Gatherer < ActiveRecord::Base
belongs_to :gather_attendee, class_name: "User"
belongs_to :attended_gather, class_name: "Gather"
end
The naming here is not great. When naming your models choose nouns as models represent the actual things in your buisness logic - choosing verbs/adverbs makes the names of your assocations very confusing.
class User < ApplicationRecord
has_many :gatherings_as_creator,
class_name: 'Gathering',
foreign_key: :creator_id
has_many :attendences
has_many :gatherings,
through: :attendences
end
# think of this kind of like a ticket to an event
# rails g model Attendence user:references gathering:references
class Attendence < ApplicationRecord
belongs_to :user
belongs_to :gathering
end
# this is the proper noun form of gather
class Gathering < ApplicationRecord
belongs_to :creator,
class_name: 'User'
has_many :attendences
has_many :attendees,
though: :attendences,
class_name: 'User'
end
My question is, do I need to a join table, such as Gatherer, to allow multiple attendees and then later pull a friend list for the user/creator ?
Yes. You always need a join table to create many to many assocations. Gatherer is a pretty confusing name for it though as that's a person who gathers things.
If you want to get users attending Gatherings created by a given user you can do it through:
User.joins(attendences: :groups)
.where(groups: { creator_id: user.id })
You're on the right track.
If I understand what you're looking for correctly, you want a Gather to have many Users and a User to have many Gathers (for the attending piece). So you need a join table like this (this is similar to your gatherers table, but is in a more conventional Rails style):
create_join_table :gathers, :users do |t|
t.index [:gather_id, :user_id]
t.index [:user_id, :gather_id]
end
And then you'd want your User model to be like this:
class User < ApplicationRecord
has_many :gathers_as_creator, foreign_key: :creator_id, class_name: "Gather"
has_and_belongs_to_many :gathers
end
class Gather < ApplicationRecord
belongs_to :creator, class_name: "User"
has_and_belongs_to_many :users
end
(You can change the name of that :users association if you really want, by specifying extra options -- I just like to keep to the Rails defaults as much as I can.)
That should be the bulk of what you need. If you want to pull all the friends of a creator for a specific gather, you would just do gather.users. If you want to pull all of the friends of a creator across all their gathers, that will be:
creator = User.find(1)
friends = User.joins(:gathers).where(gathers: { creator: creator }).all
I am trying to make an association that is not working.
I have the following scope:
ActiveRecord::Schema.define(version: 2020_04_05_125812) do
create_table "accounts", force: :cascade do |t|
t.string "social_network"
t.string "name_account"
t.integer "person_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["person_id"], name: "index_accounts_on_person_id"
end
create_table "lists", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "lists_people", id: false, force: :cascade do |t|
t.integer "list_id", null: false
t.integer "person_id", null: false
end
create_table "people", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "posts", force: :cascade do |t|
t.string "post_text"
t.date "date"
t.string "link"
t.integer "account_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["account_id"], name: "index_posts_on_account_id"
end
add_foreign_key "accounts", "people"
add_foreign_key "posts", "accounts"
end
I wish I could consult, for example:
I want to consult the person named "Test_name"
That person belongs to lists, which is a collection of people. In addition, that person has accounts and those accounts have Posts.
class List < ApplicationRecord
has_and_belongs_to_many :people
end
class Person < ApplicationRecord
has_and_belongs_to_many :lists
end
class Account < ApplicationRecord
has_many :posts
belongs_to :person
end
class Post < ApplicationRecord
belongs_to :account
end
How could I have a return like the one below:
List | Name | social_network
1 | Test_name | facebook
2 | Test_name | twitter
All the queries I make, either return the wrong type, or return only the list.
Problems such as:
List | Name | social_network
1 | Test_name | facebook
2 | Test_name | twitter
1 | Second_name | twitter
And I don't want to see the data "second_name"
I try this:
#lists = List.from(
Person.left_outer_joins(:list).where('people.name LIKE ?', "Renata Rempel"),
:list
)
but, doesn't works =/
To start off with you want to setup a many to many association between Person and List. This can be done with has_and_belongs_to_many but there are many reasons why has_many through: may be a better choice. The primary one is that it will let you add features like keeping track of banned users or when a user joined a list.
# rails g model list_membership member:belongs_to user:belongs_to
class ListMembership < ApplicationRecord
belongs_to :member, class_name: 'Person'
belongs_to :list
end
We then have to fix the foreign key in the association:
class CreateListMemberships < ActiveRecord::Migration[6.0]
def change
create_table :list_memberships do |t|
t.belongs_to :list, null: false, foreign_key: true
t.belongs_to :member, null: false, foreign_key: { to_table: :people }
t.timestamps
end
# can be a good idea to add a compound index
# add_index [:list_id, :member_id], unique: true
end
end
class Person < ApplicationRecord
has_many :list_memberships, foreign_key: :member_id
has_many :lists, through: :list_memberships
has_many :accounts
has_many :posts, through: :accounts
end
class List
has_many :list_memberships
has_many :members,
through: :list_memberships
end
Your from query will not work as your actually selecting rows from people but you just alias the table lists. That won't magically select the right data! If you really wanted to use from you would do:
List.from(
List.joins(:members).where("people.name LIKE ?", "Renata Rempel"),
:lists
).eager_load(members: :posts)
If you want to create a bunch of lists with a random number of members in your seed file you can just do:
ids = 10.times.map do
Person.create!(name: Faker::Name.name).id
end
lists = 10.times.do
List.create!(member_ids: ids.sample(2))
end
I have a User table and a Booking Table that is linked by a create_join_table what holds the user id and booking ids. When a user books a room, i need the id of both the user and new booking to go into that. I am getting the error above and im not sure why.
I have looked online and saw something similar, their class names were plural however I don't think I have that.
booking.rb
class Booking < ApplicationRecord
enum room_type: ["Basic Room", "Deluxe Room", "Super-Deluxe Room", "Piton Suite"]
has_many :join_tables
has_many :users, through: :join_tables
end
user.rb
class User < ApplicationRecord
has_secure_password
validates :email, format: {with: URI::MailTo::EMAIL_REGEXP}, presence: true, uniqueness: true
has_many :join_tables
has_many :bookings, through: :join_tables
end
join_table.rb
class JoinTable < ApplicationRecord
belongs_to :users
belongs_to :bookings
end
bookings_controller.rb
def create
#booking = Booking.create(booking_params)
current_user.bookings << #booking ##Where the error happens
db/schema
ActiveRecord::Schema.define(version: 2019_12_13_181019) do
create_table "bookings", force: :cascade do |t|
t.integer "room_type"
t.date "check_in"
t.date "check_out"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "join_tables", force: :cascade do |t|
t.integer "users_id"
t.integer "bookings_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["bookings_id"], name: "index_join_tables_on_bookings_id"
t.index ["users_id"], name: "index_join_tables_on_users_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
I have just tried to reproduce your problem and I have a similar exception
irb(main):003:0> User.first.bookings
NameError (uninitialized constant User::Bookings)
but, when I change
belongs_to :users
belongs_to :bookings
to
belongs_to :user
belongs_to :booking
in app/models/join_table.rb everything works as expected.
This is how I created the JoinTable model
$ rails generate model JoinTable
class CreateJoinTables < ActiveRecord::Migration[6.0]
def change
create_table :join_tables do |t|
t.references :user
t.references :booking
t.timestamps
end
end
end
As you can see in the belongs_to docs, it is used in the singular form most of the time.
I have articles that are able to be tagged in categories. I am struggling to create a query which will extract the sum of view count (tracked using impressionist gem) of articles within each category.
Schema:
create_table "article_categories", force: :cascade do |t|
t.integer "article_id"
t.integer "category_id"
end
create_table "articles", force: :cascade do |t|
t.string "title"
t.text "description"
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "impressions_count", default: 0
end
create_table "categories", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Article model
class Article < ApplicationRecord
is_impressionable :counter_cache => true, :unique => false
belongs_to :user
has_many :impressions, as: :impressionable
has_many :article_categories
has_many :categories, through: :article_categories
end
Category model
class Category < ApplicationRecord
has_many :article_categories
has_many :articles, through: :article_categories
end
ArticleCategory model
class ArticleCategory < ActiveRecord::Base
belongs_to :article
belongs_to :category
end
Here is what i have tried so far and the errors when i test in rails console:
cat_group = Article.joins(:article_categories).group_by(&:category_ids)
// this query results in an array of articles within each category
1) cat_group.sum("impressions_count")
//TypeError: no implicit conversion of Array into String
2) cat_group.select("sum(impressions_count) AS total_count")
//ArgumentError: wrong number of arguments (given 1, expected 0)
3) Article.joins(:article_categories).group(:category_id)
//ActiveRecord::StatementInvalid: PG::GroupingError: ERROR: column "articles.id" must appear in the GROUP BY clause or be used in an aggregate function
If I understand you correctly and view count is impressions_count, here is query that you can use:
Article.joins(:categories).group('categories.name').sum(:impressions_count)
I'm trying to return JSON API where a show action will
render json: user, include [:books, :friends, :comments]
Problem is, if I try to use the inverse_of in my User and Book model classes like this:
User Serializer
class UserSerializer < ActiveModel::Serializer
...
has_many :friends
has_many :books, inverse_of: :author
...
end
Book Serializer
class BookSerializer < ActiveModel::Serializer
...
belongs_to :author, class_name: "User", inverse_of: :books
...
end
I get an error:
ActiveRecord::StatementInvalid (SQLite3::SQLException: no such column: books.user_id: SELECT "books".* FROM "books" WHERE "books"."user_id" = ?):
If I remove the inverse_of and has_many from my User serializer, then I don't get any errors, but then the JSON being returned does not contain the included association.
Likewise, the same happens between Comment and User models.
Am I doing something wrong ?
My DB Schema for my two models are:
User Schema
create_table "users", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.string "username"
t.string "email"
t.string "password_digest"
t.boolean "banned"
t.integer "role_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "photo"
t.boolean "email_confirmed", default: false
t.string "confirm_token"
t.string "password_reset_token"
t.boolean "show_private_info", default: false
t.boolean "show_contact_info", default: false
t.index ["role_id"], name: "index_users_on_role_id"
end
Book Schema
create_table "books", force: :cascade do |t|
t.string "title"
t.boolean "adult_content"
t.integer "author_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "published"
t.string "cover"
t.text "blurb"
t.index ["author_id"], name: "index_books_on_author_id"
end
When I went to generate my Book model with:
rails generate model books ... author:references
It created this migration file:
class CreateBooks < ActiveRecord::Migration[5.0]
def change
create_table :books do |t|
t.string :title
t.boolean :adult_content
t.references :author, foreign_key: true
t.timestamps
end
end
end
I assume that includes the necessary foreign key setup...
Try to change this line in your User model(user.rb):
has_many :books, inverse_of: :author
to
has_many :books, inverse_of: :author, foreign_key: :author_id
You need to tell rails what foreign_key you used if it's not the default one.And the association should be declared in your models, not serializers. In serializer you are adding keys by "has_many", inverse_of does't works here.