I need some help here. I have a community model with belongs to an account. I use devise for the authentication stuff. Now the issue is when I try to submit/create a community I get this error 'undefined method `account_id=' for #Community:0x00007febf2e806f8 Did you mean? account='
controller
def create
#community = Community.new comunity_values
#community.account_id = current_account.id
if #community.save
redirect_to community_path
else
render :new
end
end
private
def comunity_values
params.require(:community).permit(:name, :url,:rules)
end
end
model
class Community <ApplicationRecord
belongs_to :account
validates_presence_of :url, :name , :rules
end
migrations
class CreateCommunities < ActiveRecord::Migration[6.0]
def change
create_table :communities do |t|
t.references :account
t.string :name
t.string :url
t.text :rules
t.string :total_members
t.timestamps
end
end
end
In model account you have to put the reference to communities model too. Something like:
has_many :community
Other think you have to know, is that you don't need to specify the id when you create using relations, you could use in a more readable way:
#community.account = current_account
#community.save
And I think that you are inverting the order of things, because one account has many communities so... You could do that as follows:
current_account.community.create!(comunity_values)
Hope this helps
Related
I have users and books. It's a many to many relationship, so I created a join table based on questions and answers I found on this site.
class Book < ApplicationRecord
has_and_belongs_to_many :users
end
class User < ApplicationRecord
has_and_belongs_to_many :books
end
class BooksUsersJoinTable < ActiveRecord::Migration[5.0]
def change
create_table :books_users_join_table, :id => false do |t|
t.integer :book_id, index: false
t.integer :user_id, index: false
end
end
add_index(:books_users, [:book_id, :user_id], :unique => true)
add_index(:books_users, :book_id)
add_index(:books_users, :user_id)
end
So I suppose my questions are:
Will this join_table work how I have it or is there a better way to have written it?
I just wrote the add_index lines in this file. Do I have to create indexes from the command line and if so, how do I do that?
How do I use this join_table in a controller?
This is what I use do a join
class BooksController < ApplicationController
def index
#books Book.all.joins(:user)
end
end
I hope that this helps
In my domain, many models have names, descriptions, etc. These properties need translations. I know how to represent this in a database. However I struggle finding a way to represent this with Rails.
|-------translations-table--------|
|translation_id|locale|translation|
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|----------------------modelx-table---------------------|
|id|name_translation_id|description_translation_id|price|
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
|-------modely-table--------|
|id|name_translation_id|date|
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
You do not need to create extra models for translations, yo just need to set up locales in .yml format, check this for further instructions
Update
Ok now I understood your point, you want to add translatable fields on your entities/models, so users can manage those translations through a UI right?, well your approach is correct, however there is a gem called Globalize that does the exact same thing but with more toys, and much more standardized as you want.
This is the solution I eventually came up with:
#Models
class Translation
has_many :translation_records
end
class TranslationRecord
(translation_records.find_by :locale => I18n.locale).text
end
class ModelX
belongs_to :name_translation, :class_name => 'Translation'
belongs_to :description_translation, :class_name => 'Translation'
def name
name_translation.current
end
def description
description_translation.current
end
end
#Migrations
class CreateTranslationRecords < ActiveRecord::Migration[5.0]
def change
create_table :translation_records do |t|
t.references :translation
t.string :locale
t.string :text
end
add_index :translation_records, :locale
end
end
class CreateTranslation < ActiveRecord::Migration[5.0]
def change
create_table :translations do |t|
# only id column
end
end
end
class AddTranslationToModelXs < ActiveRecord::Migration[5.0]
def change
add_reference :model_xs, :name_translation
add_reference :model_xs, :description_translation
end
end
In our Rails 4 app, there are four models:
class User < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :calendars, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Calendar < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :users, through: :administrations
has_many :posts, dependent: :destroy
end
class Post < ActiveRecord::Base
belongs_to :calendar
end
Here are the corresponding migrations:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.integer :total_calendar_count
t.integer :owned_calendar_count
t.timestamps null: false
end
end
end
class CreateAdministrations < ActiveRecord::Migration
def change
create_table :administrations do |t|
t.references :user, index: true, foreign_key: true
t.references :calendar, index: true, foreign_key: true
t.string :role
t.timestamps null: false
end
end
end
class CreateCalendars < ActiveRecord::Migration
def change
create_table :calendars do |t|
t.string :name
t.timestamps null: false
end
end
end
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.references :calendar, index: true, foreign_key: true
t.date :date
t.time :time
t.string :focus
t.string :format
t.string :blog_title
t.text :long_copy
t.text :short_copy
t.string :link
t.string :hashtag
t.string :media
t.float :promotion
t.string :target
t.integer :approval
t.text :comment
t.timestamps null: false
end
end
end
So basically, a user can have many calendar and a calendar can have many user.
When a #user creates a new #calendar, we generate a new #administration: this is currently working.
What we would like to do now is allowing a #user to invite other #user — already registered or not — to join an existing #calendar.
This would create a new #administration, between the invited #user and an existing #calendar.
The tricky part here — or at least the blurry part for us — seems to be to handle both registered and unregistered users:
For unregistered users: when the "inviting" user shares a calendar with the "invited" user, how do we allow the latter to get the invitation through the email address typed in by the former, while allowing him to register to the app through an email address of his choice (either the same or another one)?
For registered users: when the "inviting" user shares a calendar with the "invited" user, how do we allow the latter to get the invitation through the email address typed in by the former, while allowing him to either log in to the app with his existing account (either the same or a different email address than the one used by the "inviting" user) or sign up with an email address of his choice (either the one used by the "inviting" user or another address)?
Most importantly, how do we persist the information related to the calendar (let's say calendar_id) so that, once the "invited" user has either logged in or signed up, he has access to the calendar the "inviting" user wanted to grant him access to?
Note: We are currently using a custom authentication/authorization system as suggested in Michael Hartl's Rails Tutorial and are not using Devise or another gem for that matter.
Now, the question is how to achieve this:
Should we simply create an invite method in the Administration model and use it in the AdministrationsController#new?
Should we create a whole new InvitationsController?
Should we use a gem like Devise Invitable*?
Or is there a completely different, obvious solution we are missing?
Ok, Here is my 2 cents. Please let me know if you need more information.
Because you have made a custom Authentication and the Authorisation then this solution might help you.
I assume your access list should be something like this as I can't see your Access list in the posted migration.
Anyway, at the registration you an assign a token to every user.
Something like this:
before_create :generate_token
private
def generate_token
begin
self.a_token = SecureRandom.hex
end while self.class.exists?(a_token: access_token)
end
Then when user invites simply allocate this token.
Now you need a method at the registration to simply recognise the token, then filled the Invite table.
rest should be easy.
Inside your Authentication module simply have a method like this:
def invited_by
Invite.where(:user_id => current_user.id).first
end
( I don't like Rails find_by_ methods you can use find_by_user_id - I am a bit old fashioned ;)
( I assume you made a module that you can use it throughout your application ) If not then let me know I will help you out mate.
So now you got the user who invited your current user. Now simply return the Permissions which you have allocated for the user who is invited your current user and continue with the application.
Hope it helps. Let me know if you need more direction. I used the same method for one of my application for affiliate program and member benefits of being affiliate.
Cheers.
Update
Because you asked about the Authorisation Module and you have a custom Authentication. Here some code. (Partially tested and Needs to be refactored please use it as a guide, they are some common tasks I could think of)
module AuthorisationRelated
def what_are_current_user_roles
objArray = []
current_user.roles.each do |role|
objArray.push role.name
end
return objArray
end
def does_user_have_this_role?(role_name)
result = false
obj_array = what_are_current_user_roles
obj_array.each do |a_role|
if a_role == role_name
result = true
end
end
result
end
def is_admin?
athu = false
if signed_in?
current_user.roles.each do |role|
if role.name == 'admin'
athu = true
end
end
end
return athu
end
# class_instance MUST be a parent class
# If you need to Authenticate Model then your class_instance has to be #instance
def user_allowed_create_and_edit?(class_model, class_instance)
user_is_allowed = false
if permitted_to? :create, class_model.new and has_user_own_this(class_instance)
user_is_allowed = true
else
user_is_allowed = false
end
# Override everything if user is admin
if is_admin?
user_is_allowed = true
end
return user_is_allowed
end
# relation has to be set to access_list
def has_user_own_this(model)
user_who_owns_this = model.access_list.user_id
if current_user.id == user_who_owns_this
return true
else
return false
end
end
def find_current_user_acls
acl = []
acls = AccessList.joins(:user).where("users.id = ?",current_user.id)
acls.each do |an_acl|
acl.push an_acl.id
end
acl
end
end
Update
Following your comments regarding a good source for Authorisation.
I would suggest use Gems or at least one Authorisation Gem that can handle complex assignments.
One of My All time sweethearts Declarative Authorization . I have implemented the same DB structure with this Gem on a website with 6 different Groups ACL and bucket loads of permissions with over 500K active users.
Check out this Tutorial: http://railscasts.com/episodes/188-declarative-authorization Very good starting point.
Because you don't have Devise or something like this, just make sure you have the current_user handy. You can define it in the ApplicationController
This tutorial will tell you how to make the current user (paid tutorial - it worth it - trust me!) http://railscasts.com/episodes/250-authentication-from-scratch-revised
I am in quite the pickle , I think I overreach with my current ruby knowledge but I don't want to give up.
I currently have a tweeter that can post and people can follow other people thanks to https://www.railstutorial.org/book/ . I do want to add hashtags to this tutorial tweeter. In order to do I created 2 tables since tweet and hashtag is a many to many relationship . The tables are :
class CreateHashrelations < ActiveRecord::Migration
def change
create_table :hashrelations do |t|
t.integer :tweet_id
t.integer :hashtag_id
t.timestamps null: false
end
add_index :hashrelations, [:tweet_id, :hashtag_id], unique: true
end
end
which is the extra table you need to keep the keys of the tweet and hashtag . And the other table is the hashtag table where I have the id and the name of the hastag
class CreateHashtags < ActiveRecord::Migration
def change
create_table :hashtags do |t|
t.string :name
t.timestamps null: false
end
end
end
In the models I put the following relathipships:
class Hashtag < ActiveRecord::Base
has_many :hashtagrelations, dependent: :destroy
has_many :tweets, through: :hashtagrelations
validates :name, presence: true
end
class Hashrelation < ActiveRecord::Base
belongs_to :tweet
belongs_to :hashtag
validates :tweet_id, presence: true
validates :hashtag_id, presence: true
end
class Tweet < ActiveRecord::Base
.....
has_many :hashtagrelations, dependent: :destroy
has_many :hashtags, through: :hashtagrelations
....
end
When a tweet is submited I save it and if it is saved I want to see if it has hashtags and if it does I want to add the necessary data in the Hashtagrelations and Hashtags tables.
I try to do this this way :
class TweetsController < ApplicationController
......
def create
#tweet = current_user.tweets.build(tweet_params)
if #tweet.save
add_hashtags(#tweet)
flash[:success] = "Tweet created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
......
private
........
def add_hashtags(tweet)
tweet.content.scan(/(?:\s|^)(?:#(?!(?:\d+|\w+?_|_\w+?)(?:\s|$)))(\w+)(?=\s|$)/){ |tag|
newhash[:new] = tag
#hashtag = Hashtag.new(new_hash[:new])
#hashtag.save
data[:new] = [tweet.id,#hashtag.id]
#hashrel = Hashtagrel.new(data[:new])
#hashrel.save
}
end
end
Which is not the right way. I tried to add the newhash and data because if I only did tag there I would get
When assigning attributes, you must pass a hash as an argument.
I realise that this is kind of a silly question but I have found no tutorial that teaches me how should I add this data to my tables. I would be grateful for your help
This is an array of values:
data[:new] = [tweet.id,#hashtag.id]
Before moving things inside another variable (or data structure), try being explicit first.
#hashrel = Hashtagrelation.new(tweet_id: tweet.id, hashtag_id: #hashtag.id)
The rest of the code looks good, I think you've got it.
I want to fetch all posts posted by those users who have gone to same college as the current users...So inside my welcome controller i have written following code..
class WelcomesController < ApplicationController
def index
#col = Education.select(:college_id).where(:user_id => #current_user)
#user = Education.select(:user_id).where(:college_id => #col)
#welcome = Welcome.where(:user_id => #user)
end
end
Following is my shema code for welcome and education model:
create_table "welcomes", :force => true do |t|
t.text "message"
t.integer "user_id"
end
create_table "educations", :force => true do |t|
t.integer "college_id"
t.integer "user_id"
end
#col = Education.select(:college_id).where(:user_id => #current_user)....this line returns college ids associated with current logged in user.This is working perfectly on my console which is returning following output..
[#<Education college_id: 1>, #<Education college_id: 2>]
but i dont know how to use this output in my next line,so i have written this statement which should return all the users whose college id is the output of prevous statement
#user = Education.select(:user_id).where(:college_id => #col)
and my last line should return all the posts posted by those users whose ids are inside the #user array:
#welcome = Welcome.where(:user_id => #user)
but this is not working.When i run my project i cant see any output on my page and on console i am getting following output :
SELECT welcomes.* FROM welcomes WHERE (welcomes.user_id IN (NULL))
which means its not getting any user ids..
How can i solve this ...
You can try this:
#col = Education.select(:college_id).where(:user_id => #current_user.id).all
#users = Education.select(:user_id).where(:college_id => #col.collect(&:college_id)).all
#welcome = Welcome.where(:user_id => #users.collect(&:user_id)).all
The best way I see to accomplish this is to set up a has_many_and_belongs_to_many relationship between your User and Education models. (Each Education will have many Users and each User may have multiple Eductions.) You will need to create a joining table in your database to support this type of relationship - see the Rails Guide for more information on this.
I would set up your models in this manner:
class User < ActiveRecord::Base
has_one :welcome
has_and_belongs_to_many :educations
end
class Education < ActiveRecord::Base
has_and_belongs_to_many :users
end
class Welcome < ActiveRecord::Base
belongs_to :user
end
The join table for the has_many_and_belongs_to_many join table migration (be sure to double check this code, not sure I got this exactly right):
def self.up
create_table 'education_user', :id => false do |t|
t.column :education_id, :integer
t.column :user_id, :integer
end
end
Your controller code is now much simpler and looks like this:
#welcomes = #current_user.eductions.users.welcome.all
In your view:
<% #welcomes.each do |welcome| %>
<p><%= welcome.message %></p>
<% end %>
One of the more powerful features of Ruby on Rails is the model relationships. They are a little more work up front, but if you take the time to set them up correctly they can make your life much easier, as is evidenced by the simplified #welcomes query above.
I'd recommend you to make relation between User and Collage
class User < ActiveRecord::Base
has_many :educations
has_many :colleges, :through => :educations
has_many :posts
scope :by_college_id, lambda {|cid| where("exists (select educations.id from educations where educations.user_id = users.id AND educations.college_id in (?) limit 1)", Array.wrap(cid)) }
def college_mates
self.class.by_college_id(self.college_ids).where("users.id != ?", id)
end :through => :educations
end
class Education < ActiveRecord::Base
belongs_to :user
belongs_to :college
end
So now in your controller you can write
class WelcomesController < ApplicationController
def index
#posts = #current_user.college_mates.includes(:posts).map(&:posts).flatten
# or
#posts = Post.where(user_id: #current_user.college_mates.map(&:id))
end
end
Second variant generates 3 sql-requests, first variant - only two. But this is same work with data, I think time will be also same. Usually controllers contain only few lines of code, all logic written in models. Ideally controller should contain only Post.by_college_mates_for(#curren_user.id)