Rails用户到用户消息

我对rails很新,所以请在回复中详细说明。 我正在构建一个使用设计进行身份validation的Web应用程序。 我现在停留的部分是用户消息系统。 想法是用户A登录到应用程序并可以访问用户B的配置文件,并且在用户B的配置文件上可以单击允许用户A向用户B撰写消息的链接。然后用户B可以登录到应用程序并访问收件箱中将找到用户A的消息。

我相信我在这里定义发件人和收件人角色时遇到了问题,现在我正在尝试显示用户将撰写邮件的表单。有人能看到我在这里做错了吗? 我收到以下错误。 我已经读过要做的事情就是将User_id字段添加到表中,但我希望使用sender_id和recipient_id来链接这些消息,这两个消息都等于user_id(例如,用户1 [发送者]向用户2发送消息[接受者]):

未知属性:user_id

def new @message = current_user.messages.new recipient_id:params [:sender_id] end

此外,对于铁路专家或任何与此类似的人,您能否告知我是否朝着正确的方向前进,或提供任何指导? 我在这里盲目编码,只是在我继续努力的时候试图弥补。 任何指导都会非常感激,为我节省了很多时间。 代码如下:

用户迁移

class DeviseCreateUsers < ActiveRecord::Migration def change create_table(:users) do |t| t.string :first_name t.string :last_name t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" 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.timestamps end add_index :users, :email, unique: true add_index :users, :reset_password_token, unique: true end end 

邮件迁移

 class CreateMessages < ActiveRecord::Migration def change create_table :messages do |t| t.string :content t.integer :sender_id t.integer :recipient_id t.timestamps end end end 

schema.rb

 ActiveRecord::Schema.define(version: 20140909174718) do create_table "messages", force: true do |t| t.string "content" t.integer "sender_id" t.integer "recipient_id" t.datetime "created_at" t.datetime "updated_at" end create_table "users", force: true do |t| t.string "first_name" t.string "last_name" 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" t.datetime "updated_at" t.string "current_industry" t.integer "years_in_current_industry" t.string "hobbies" end add_index "users", ["email"], name: "index_users_on_email", unique: true add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end 

的routes.rb

 Catalyst::Application.routes.draw do devise_for :users, :controllers => { :registrations => "registrations" } devise_scope :user do get 'register', to: 'devise/registrations#new' get 'login', to: 'devise/sessions#new', as: :login get 'logout', to: 'devise/sessions#destroy', as: :logout end resources :users do member do get 'edit_profile' end resources :messages, only: [:new, :create] end resources :messages, only: [:index, :show, :destroy] root to: "home#index" match '/about', to: 'static_pages#about', via: 'get' match '/contact', to: 'static_pages#contact', via: 'get' match '/help', to: 'static_pages#help', via: 'get' match '/legal', to: 'static_pages#legal', via: 'get' end 

users_controller

 class UsersController < ApplicationController before_filter :authenticate_user! def index @users = User.all end def show @user = User.find(params[:id]) end def new end def create end def edit end def update @user = User.find(params[:id]) @user.update!(user_params) redirect_to @user end def destroy end def edit_profile @user = User.find(params[:id]) end def user_params params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation, :current_industry, :years_in_current_industry, :hobbies) end def sender @user = User.find(params[:id]) end def recipient @user = User.find(params[:id]) end end 

messages_controller

 class MessagesController < ApplicationController before_action :set_recipient def new @message = Message.new @recipient = User.find(params[:user_id]) end def create @message = Message.new message_params if @message.save flash[:success] = "Your message has been sent!" redirect_to user_messages_path else flash[:failure] = "Please try again." redirect_to users_path end end private def message_params params.require(:message).permit(:content, :sender_id, :recipient_id) end end 

user.rb

 class User  "sender_id" has_many :to_messages, class_name: 'Message', :foreign_key => "recipient_id" devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :remember_me, :current_industry, :years_in_current_industry, :hobbies end 

message.rb

 class Message < ActiveRecord::Base belongs_to :sender, class_name: "User" belongs_to :recipient, class_name: "User" validates :content, presence: true, length: { maximum: 500 } validates :sender_id, presence: true validates :recipient_id, presence: true end 

消息/ index.html.erb

 

Inbox

消息/ new.html.erb

 

Create Message


耙路线

 user_messages POST /users/:user_id/messages(.:format) messages#create new_user_message GET /users/:user_id/messages/new(.:format) messages#new users GET /users(.:format) users#index POST /users(.:format) users#create new_user GET /users/new(.:format) users#new edit_user GET /users/:id/edit(.:format) users#edit user GET /users/:id(.:format) users#show PATCH /users/:id(.:format) users#update PUT /users/:id(.:format) users#update DELETE /users/:id(.:format) users#destroy messages GET /messages(.:format) messages#index message GET /messages/:id(.:format) messages#show DELETE /messages/:id(.:format) messages#destroy 

楷模

 #app/models/user.rb class User < ActiveRecord::Base has_many :messages, class_name: "Message", foreign_key: "recipient_id" has_many :sent_messages, class_name: "Message", foreign_key: "sender_id" end #app/models/message.rb class Message < ActiveRecord::Base belongs_to :recipient, class_name: "User", foreign_key: "recipient_id" belongs_to :sender, class_name: "User", foreign_key: "sender_id" scope :unread, -> { where read: false } end 

这应该使您能够创建“属于”用户(IE收件人)的邮件,然后您可以将“发件人”配置文件与这些邮件相关联。

控制器

这将使您能够调用以下内容:

 #app/controllers/messages_controller.rb class MessagesController < ApplicationController before_action :set_recipient, only: [:new, :create] def new @message = current_user.sent_messages.new end def create @message = current_user.sent_messages.new message_params @message.recipient_id = @recipient.id @message.save end def index @messages = current_user.messages end def destroy @message = current_user.messages.destroy params[:id] end def show @message = current_user.messages.find params[:id] end private def message_params params.require(:message).permit(:content, :recipient_id, :sender_id) end def set_recipient @recipient = User.find params[:user_id] end end 

-

路线

 #config/routes.rb devise_for :users, path: "", controllers: { :registrations => "registrations" }, path_names: {sign_up: "register", sign_in: "login", sign_out: "logout"} resources :users do get :profile resources :messages, only: [:new, :create] #-> domain.com/users/:user_id/messages/new end resources :messages, only: [:index, :show, :destroy] #-> domain.com/messages/:id 

-

查看

这将使您能够使用以下链接:

 #app/views/users/show.html.erb (user to send message to) <%= link_to "Send Message", user_messages_path(@user.id) %> #app/views/messages/new.html.erb <%= form_for [@recipient, @user] do |f| %> <%= f.text_field :content %> <%= f.submit %> <% end %> #app/views/messages/index.html.erb 

Inbox

<% @messages.each do |message| %> <%= message.content %> <% end %>

-

固定

我已经读过要做的事情就是将User_id字段添加到表中,但我希望使用sender_id和recipient_id来链接这些消息,这两个消息都等于user_id(例如,用户1 [发送者]向用户2发送消息[接受者])

您不需要将user_id添加到表中。 user_id只是一个foreign_key ,您已在models重写。

您需要做的就是设置我们在create方法中执行的recipient_idsender_id

 def create @message = current_user.message.new message_params @message.recipient_id = @recipient.id @message.save end 

你在这里做了一些非常聪明的事情。

首先,您通过调用current_user.messages隐式设置sender_id外键。 如果您调用了Message.new ,那将是一个完全不同的故事(必须设置sender_id

其次,因为您正在使用嵌套路由,所以您将能够使用您在before_action方法中设置的@recipient变量来为我们提供recipient_idid

这应该适合你。 除非您尝试访问子/嵌套模型中的“父”模型数据,否则不需要使用inverse_of


建议

你正在做的是完全有效的

核心技巧是确保您的Message模型与User完全独立且独立。 这是通过您的设置实现的,允许您创建所需的各种对象。

您需要考虑的另一个方面是如何确保您能够为用户提供具有“线程”消息的能力。 您将使用其中一个层次结构gem( AncestryClosure_Tree实现此Closure_Tree

添加此function将更加深入。 如果您需要,我可以提供信息(只需发表评论)


穿线

层次结构gem实际上相对简单易用。

“踩踏”你的消息的诀窍是使用其中一个gem( AncestryClosure_Tree ),因为它们为你提供了可以调用你的物品的“方法”。 它们通过在数据库中创建多个列来工作,在保存/创建所需对象时填充它们

“线程”问题是一个很大的问题,因为没有“层次结构”gem,您将无法调用所需记录的“子”对象,从而防止线程发生。 这是一个很好的Railscast如何实现它:

在此处输入图像描述

这个的诀窍是使用一种叫做“递归”的东西

递归是您创建“无限期”循环的地方,就数据的“递归”方式而言。 EG如果你有一个带孩子的物体,你必须递归地遍历孩子们,然后是孩子们的孩子,直到你达到显示所有数据的程度:

递归是以自相似的方式重复项目的过程。 例如,当两个镜子的表面彼此完全平行时,出现的嵌套图像是无限递归的forms。

因此,这是你如何做到这一点:

  1. 确保使用正确的父项保存对象
  2. 要显示“线程”对话,请循环访问这些父对象
  3. 使用递归循环遍历其子项

我们使用ancestry gem,它存储的层次结构与我们发现的closure_tree gem略有不同(打算很快使用closure tree gem)。

因此,您首先必须自己保存任何层次结构:

在此处输入图像描述

这将允许您保存该对象的各种“父”。 这意味着当您加载对象并希望循环其后代时,您将能够使用Ancestry对象方法 :

在此处输入图像描述

这意味着您将能够使用以下内容:

 #app/views/comments/index.html.erb <%= render partial: "comments", locals: { collection: @comments } %> #app/comments/_comments.html.erb <% collection.arrange.each do |comment, sub_item| %> <%= link_to comment.title, comment_path(comment) %> <% if category.has_children? %> <%= render partial: "category", locals: { collection: category.children } %> <% end %> <% end %> 

要解决您的错误,请尝试在模型类中设置has_manybelongs_to语句的:inverse_of属性。 你最终可能有两个has_many – 每个belongs_to反向:

 user.rb: has_many :from_messages, :class_name => 'Message', :foreign_key => "sender_id", :inverse_of => :sender has_many :to_messages, :class_name => 'Message', :foreign_key => "to_id", :inverse_of => :recipient message.rb: belongs_to :sender, :class_name => 'User', :inverse_of => :from_messages belongs_to :recipient, :class_name => 'User',:inverse_of => :to_messages 

总的来说,我认为您的方法是消息传递系统的良好起点。 您可以尝试将代码发布到https://codereview.stackexchange.com/进行详细审核。