rails has_many_through data insertion question - ruby-on-rails

I have a scenario where the models look like
create_table :users do |t|
t.string :name
t.timestamps
end
create_table :blogs do |t|
t.string :url
t.string :title
t.text :description
t.timestamps
end
create_table :posts do |t|
t.integer :user_id, :null => false
t.integer :blog_id, :null => false
t.text :post_text
end
class Blog < ActiveRecord::Base
has_many :users, :through =>:posts
has_many :posts, :dependent=>true
end
class User < ActiveRecord::Base
has_many :blogs
has_many :posts, :through=>:blogs
end
class Post < ActiveRecord::Base
belongs_to :blog
belongs_to :user
end
The question I have is:
1. When a user is created, I would like to create a blog for him automatically.
#user = User.find_or_create_by_name(user_name)
How do I go about creating a blog?
#blog = Blog.find_or_create_by_user_id(#user)
I am getting the following error:
undefined method `find_or_create_by_user_id' for #<Class:0x1044735b0>
#blogs = #user.blogs
gives me:
Mysql::Error: Unknown column 'blogs.user_id' in 'where clause': SELECT * FROM `blogs` WHERE (`blogs`.user_id=1234)
I know Blogs table does not have user_id column.
But isn't the join supposed to take care of it?
What am I doing wrong here?
Thanks for your help

To use the Post model as the association table, the User model needs to be tweaked to properly demonstrate the association. Once done, you could use after_create to create a new blog for a newly created user.
class User < ActiveRecord::Base
has_many :posts
has_many :blogs, :through=>:posts
after_create :add_blog
private
def add_blog
blogs << Blog.new
end
end
EDIT:
The best I know how to handle it is to explain what I "think" the relationships are attempting to accomplish then you tell me where I'm off and we go from there.
1) A User can "own" many blogs
2) A blog can have many posts
3) A post belongs to a single user and to a single blog
4) a blog can only have one "owner" (user)
5) Blogs can be "owned" by many users thereby giving them permission to post.
If 1-4 are true, and 5 false... that isn't a "has_many :through" scenario or many-to-many relationship, just one-to-many relationships.
Accordingly, posts should not be used as an association table. There isn't an association table needed.
add t.integer :user_id, :null => false to the blogs table
class Blog < ActiveRecord::Base
belongs_to :users,
has_many :posts, :dependent=>:destroy # rec'd error in RoR3... replaced true with :destroy
end
class User < ActiveRecord::Base
has_many :blogs, :dependent=>:destroy
has_many :posts
after_create :add_blog
private
def add_blog
blogs << Blog.new
end
end
class Post < ActiveRecord::Base
belongs_to :blog
belongs_to :user
end
If 5 is true, this would be a true many-to-many... but I don't think that's what you're attempting to do.

Related

`#post.userview.users << current_user`

Every post has a userview, and each userview has many users. I want one single many to many to have a simple .add() and .remove() function like django. How do I place the current_user into the many-to-many relationship of the views?
I found this:
#post.userview.users << current_user
But it brings up some SQL error. It's suggesting I add a post_id:
ERROR: column userviews.post_id does not exist
LINE 1: SELECT "userviews".* FROM "userviews" WHERE "userviews"."po...
^
HINT: Perhaps you meant to reference the column "userviews.posts_id".
After the answer, now the error is:
can't write unknown attribute `userview_id`
Because I changed the migrations around a little, userview has references to posts and users. Post has_one userview, has_many users through userview, userview has_many posts and has_many users.
create_table "posts", force: :cascade do |t|
t.integer "userview_id"
t.bigint "userviews_id"
t.index ["userviews_id"], name: "index_posts_on_userviews_id"
end
create_table "userviews", force: :cascade do |t|
t.bigint "users_id"
t.bigint "posts_id"
t.integer "post_id"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["posts_id"], name: "index_userviews_on_posts_id"
t.index ["users_id"], name: "index_userviews_on_users_id"
end
class User < ActiveRecord::Base
has_many :viewed_posts, through: :userview, class_name: 'Post'
...
class Userview < ApplicationRecord
belongs_to :post
belongs_to :user
end
class Post < ApplicationRecord
belongs_to :user
has_many :userviews
has_many :viewers, through: :userview, class_name: 'User'
...
Could not find the source association(s) "viewer" or :viewers in model Userview. Try 'has_many :viewers, :through => :userviews, :source => <name>'. Is it one of post or user?
Although, that's for unless #post.viewers.include?(current_user)
Not sure if I understood correctly what are you trying to achieve, but I'll try to fix your associations.
To associate 2 models you need to store foreign key in one of them. Model which stores the key belongs_to other model. Eg in posts table you have user_id column, it means Post belongs_to: user and User has_many :posts (or has_one :post if you need one-to-one association). For such association you can write:
user = User.first
post = Post.last
user.posts << post
If you want all this stuff to work automatically you should follow the convention about naming. Foreign keys should be in singular form, like user_id, not users_id
If you want many-to-many association you need to create an intermediate table, which stores both foreign keys. It can be done using simple direct has_and_belongs_to_many association or with more complex has_many through:
I suppose that in your case it should be:
class User < ActiveRecord::Base
has_many :userviews
# it is posts viewed by the user
# you need to specify class name because it differs from association name
has_many :viewed_posts, through: :userviews, class_name: 'Post'
# posts written by the user
has_many :posts
end
class Userview < ApplicationRecord
belongs_to :post
belongs_to :user
end
class Post < ApplicationRecord
belongs_to :user
has_many :userviews
has_many :viewers, through: :userviews, source: :user
end
It means that you need post_id and user_id columns in userviews table and user_id in posts table. Please, remove useless columns and add needed in migration. When you set it all correctly you'll be able to do
#post.viewers << current_user
to add current_user to viewers list. Corresponding userview instance is created automagically
Changed has_many :viewers, through: :userviews, class_name: 'User'
to has_many :viewers, through: :userviews, source: :user

How to set up an admin user in rails

I have a simple relationship
class School < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :schools
end
A user can be part of many schools but at the same time a user might be the admin of a number of schools. I set up a many-to-many relationship to represent this however I'm not sure how I would distinguish between admins and simple users.
I initially thought of setting a table which has a school_id and a user_id and every entry will represent the school id and the user id of any admins that the school has however I'm not sure how I would represent this in rails or if it's the best way to solve this problem? And if it is, how do I access the table without a model associated to it?
What I mean by what I said above:
school_id user_id
1 3
1 4
Which means that the school with id 1 has 2 admins (3 and 4)
What you are looking for is a more complex many_to_many relationship between school and user called has_many :through. This relationship allows you to have many to many relationship with access to the table that represents the relationship. If you use that relationship, your models should look something like this:
class User < ActiveRecord::Base
has_many :school_roles
has_many :schools, through: :school_roles
end
class SchoolRole < ActiveRecord::Base
belongs_to :school
belongs_to :user
end
class School < ActiveRecord::Base
has_many :school_roles
has_many :users, through: :school_roles
end
And the migrations of those tables would look something like this:
class CreateSchoolRoles < ActiveRecord::Migration
def change
create_table :schools do |t|
t.string :name
t.timestamps null: false
end
create_table :users do |t|
t.string :name
t.timestamps null: false
end
create_table :school_roles do |t|
t.belongs_to :school, index: true
t.belongs_to :user, index: true
t.string :role
t.timestamps null: false
end
end
end
I would suggest to make the "role" field in the "school_roles" migration an integer and then use an enum in the model like so:
class SchoolRole < ActiveRecord::Base
belongs_to :school
belongs_to :user
enum role: [ :admin, :user ]
end
which allows you to add more roles in the future, but it's your call
combining polymorphic association with has_many :through in my opinion is best option.
Let's say you create supporting model SchoolRole, which
belongs_to :user
belongs_to :school
belongs_to :rolable, polymorphic:true
This way:
class School ...
has_many :administrators, :as => :schoolroles
has_many :users, :through => :administators
#school.administrators= [..., ...]
It is quite agile.
#user=#school.administrators.build()
class User
has_many :roles, :as => :rolable
def admin?
admin=false
self.roles.each do |r|
if r.role_type == "administator"
admin=true
break
end
end
admin
end
....

How add association with custom FK in Ruby on Rails?

I have two models: Users and Microposts. There is an association between one-to-many. I would like to add to Microposts field replic_to and replics_from to Users, and association with many-to-many relationships that will not only be stored in the model Microposts the author, but the user ID, which is addressed to the message. Because of already having associations between models, I think you need to specify the force field as FK for new association. I ask you to show how it can be done. Thank you in advance.
This`s sources:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.string :email
t.timestamps
end
end
end
class CreateMicroposts < ActiveRecord::Migration
def change
create_table :microposts do |t|
t.string :content
t.integer :user_id
t.timestamps
end
add_index(:microposts, [:user_id, :created_at]);
end
end
class User < ActiveRecord::Base
....
has_many(:microposts, dependent: :destroy);
....
end
class Micropost < ActiveRecord::Base
....
belongs_to :user;
....
end
Add to micropost.rb
belongs_to :replics_to, class_name: 'User'
The corresponding field should be
add_column :microposts, :replics_to_id, :integer
For user.rb
has_many :replics_from, class_name: 'User', foreign_key: 'replics_to_id'

Creating a database relationship that allows for multiple assignments of the same id

I'm really new to RoR so I apologize if I'm not thinking about this right. I have a Report where I need to be able to assign multiple users to that report. A user can be assigned to more than one report and a report can have multiple users. How do I create the database relationship where this would be allowed. I understand how to assign one user to one report but not many users to a single report.
I'd use a joining class to make this happen:
class Report
has_many :assignments
has_many :users :through => :assignments
end
class User
has_many :assignments
has_many :reports, :through => :assignments
end
class Assignment
belongs_to :report
belongs_to :user
end
The class Assignment has two fields: report_id and user_id to create the relationship.
Read the Ruby on Rails Guide to Active Record Associations: http://guides.rubyonrails.org/association_basics.html
I highly recommend you familiarize yourself with the Ruby on Rails guides. They will prove to be an invaluable asset!! For this task the site would be RailsGuides Active Record Associations.
As far as the code goes you want to create three database tables: reports, reports_users, and users, with reports_users being a join table.
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name, :null => false
t.timestamps
end
end
end
class CreateReports < ActiveRecord::Migration
def change
create_table :reports do |t|
t.string :name, :null => false
t.timestamps
end
end
end
class ReportsUsers < ActiveRecord::Migration
def change
create_table :reports_users, :id => false do |t|
t.references :user, :null => false
t.references :report, :null => false
end
end
end
Once you run this migration you need to set up the active record associations in your models.
class User < ActiveRecord::Base
has_and_belongs_to_many :reports
end
class Report < ActiveRecord::Base
has_and_belongs_to_many :user
end
This will set up the database and the many-to-many models connections. This will get you started. Now you have to go create some views

Problem with associations in Rails

I've been working on my Rails app and I am stuck with some associations that I can't get my head around.
Here are my models:
User Model
class User < ActiveRecord::Base
has_many :events, :dependent => :destroy
end
Event Model
class Event < ActiveRecord::Base
belongs_to :user
has_many :items, :dependent => :destroy
end
Item Model
class Item < ActiveRecord::Base
belongs_to :event
end
When I head into the rails console and do something like this:
> User.last.events.last.items
I get an error like this:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: items.event_id: SELECT "items".* FROM "items" WHERE ("items".event_id = 1)
I have set my migrations like this:
class CreateItems < ActiveRecord::Migration
def self.up
create_table :items do |t|
t.string :description
t.string :name
t.integer :event_id
t.timestamps
end
add_index :items, :event_id
end
def self.down
drop_table :items
end
end
Is it something to do with my associations and the way that I have laid it out?
Or is the the depth of the associations?
I hope that I've provided enough information.
Thanks everyone!
I'd suggest that you double check that you've run your migration (rake db:migrate).
It shouldn't matter, but you might also try "t.references :event" instead of "t.integer :event_id".

Resources