想問看看 devise 擴展


#1

找了蠻多的資料 蠻多人建議把註冊的帳密 與個人資料分開

因為rails的相關動作會常常去使用user表單

但如果個人資料也寫再一起 除了效能 安全上也可能會有問題

但是用devise 這個用戶註冊 gem 寫得非常高端 會有人推薦我自幹嗎 感覺就是後遺症更多

而我自認還沒這個辦法去處理 目前想到的 有幾種方式

1.在註冊的時候 利用步驟1 步驟2 換頁方式 將註冊以及個人資料頁面分開存放 基本註冊就維持使用devise原本的 個人資料就獨立存到其他資料表做關連集可

缺點 如果會員有註冊沒填寫步驟2後面的個人資料會有很多麻煩

2.在devise框架內 註冊的頁面另外再多寫入基本資料到其他的table 等於 user資料表不新增其他欄位 可是devise 資料寫入是在 application_controller裡面 也不知道跟怎麼動他

其他功能通通沒要改 只是要把個人資料另外在存到別的表 也有利後端再處理其他作業

不知道有沒有大大有沒有處理過相似的做法 或想法


#2

我不建議自己製作 devise 相關東西,因為裡面包含的安全機制非常非常的多,自己應該沒辦法那麼透徹,且讓別人檢視你的語法是否有漏洞,我隨便列幾個給你就好

  1. 密碼用HMAC加密,基本上就類似 sha1 + slat 製法,且和 session 連動
  2. 註冊機制,忘記密碼,驗證,增加 OAuth2 的擴充性
  3. 多重身份支援
  4. 使用者鎖定,寄發EMail,IP追蹤…

okay,如果你有自信當然可以幹啦,不過請把這先都先想進去,然後再評估會花多少時間之類的

再來剩下的,其實就是你 Rails 基本工的問題了…別用 scaffold 然後被 scaffold 的規則綁死,那樣很蠢,你可以自己打個 form_tag 然後送到 Rails 去然後在 action 內 1 : 1 的吃 params,沒叫你這樣做,單純給你想起有這種自由度,工具是給人用的,而不是人被工具綁死|||

User 和 Profile 切離成 1 : 1 relation 是很正常的,因為你不會希望每次 SELECT users 的時候都把地址之類的資訊都 select 出來,因為這頻率很高,而這樣做很蠢(累死你家 mysql),而在我們家的打法,Profile 沒有 id,只有 user_id,且 add index unique => true,這樣就很漂亮,create User 的時候用 callback 順便把空的 Profile 一起生出來即可

再來, User 儲存一個 column 叫做 is_profile 來存 Profile 是否有填過完整資料,在必要時才阻擋或提醒需要去完成即可,這邊在 Profile 增加一個 after_save 的 callback 來檢查 profile 的完整性,ok 時修改 user 即可,而以使用者便利的因素,是不會在註冊時就要他填入過多資料就是了

okay,有問題再提出,以上


#3

我設了一個User has one Profile跟Profile belongs_to User。
也將Profile設作沒有 id,只有 user_id,且 add index, unique => true。

後來在edit -> update的過程中發生了錯誤:nil is not a symbol nor a string

終於找到這兩篇,所以問題又解決了。
http://railsproblems.blogspot.tw/2015/02/updating-rails-model-with-non-default.html
http://ruby-journal.com/how-to-override-default-primary-key-id-in-rails/

不過,當我試著在console找到Profile.find_by(:user_id => 3),要去把它刪除的時候:
用Profile.find_by(:user_id => 3).delete的時候,他會出現:
NoMethodError: undefined methodto_sym’ for nil:NilClass用Profile.find_by(:user_id => 3).destroy的時候,他會出現:ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: profiles.: DELETE FROM “profiles” WHERE “profiles”."" = ?`

請問大大這又該怎麼處理?


#4

應該是model 的primary_key 沒改吧

class Profile < ActiveRecord::base 
  self.primary_key ='user_id'
end

#5

改過啦,所以才能夠update。可是現在變成不能delete or destroy了。


#6

… 你在做啥

Profile.find_by(:user_id => 3).destroy

全等於

profile = Profile.find(3) #可能是 nil
#profile.before_delete  # 執行 callback,如果有profile
Profile.where(:user_id => profile.id).delete_all
#當 nil 時你就找不到該東西了,再看一次你的錯誤訊息
#profile.after_delete

以上動作是 SELECT + DELETE 兩個 SQL,所以如果沒有要 callback 檢查的話就用

Profile.where(:user_id => 3).delete_all

即可,只有一個 SQL 動作,update_all / delete_all 之類的語法因為是全域 SQL 操作,所以沒有 callback 且無多一個 SELECT 進行檢查,不然就改用

profile = Profile.where(:user_id => 3).first
profile.destroy if profile

這樣就會有 callback 執行(before_delete / after_delete)

Rails 內只有 where / joins / group 之類的串接 SQL 系列才有 lazy loading(先等待收集資訊,等必要時才執行轉為 SQL),否則順序都會是

(Profile.find_by(:user_id => 3)).destroy
#先執行 SELECT 變成暫存變數後再直接執行 destory

尤其是 first / find 系列,會先執行後才拋出,才是你的下一個動作,看懂意思了嗎?不是你腦中或寫法串在一起他們就全部串在一起的哩|||

最後貼上 doc 你可以看出其中的差異

http://api.rubyonrails.org/classes/ActiveRecord/Relation.html

delete_all / update_all 直接呼叫 SQL 的 DELETE / UPDATE,沒有 callback

http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html

尋找後直接丟出

http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html

這一系列隨便你串,類似 where / from / joins …

其中 Query 系列是限制域,所以末端可以串另外兩個,但另外兩個無法交互使用,因為"語意"上也不允許就是(find 系列語意接近一定要取得,沒有時會直接拋 404,否則改用 where.first )

anyway 上面的三份 doc 多看看,熟悉後應該就不會被混淆到就是,以上


#7

奇怪了,我剛剛把我昨天寫的code複製貼上,就沒有出現錯誤訊息了,順利的把資料刪掉了…可能昨天鬼打牆吧。
不過感謝JC大,我會把連結看完的。


#8

墨菲定律:只要會發生的事情就一定會發生,如果你技術債欠太多,遇到問題的鬼事自然很多

所以希望你真的瞭解上面的 code 與其"真義"就是


#9

有東西刪的時候不會噴,沒有東西刪的時候會噴。


#10

Profile.find_by(:user_id => 3),這個確定有東西之後,
才加上delete跟destroy,不過還是噴了。
應該是技術債欠太多了


#11

用 find 找不到就會立刻噴錯
如果用 profiles = Profile.where(“user_id = ?”, 3) ,這樣取出來會是一個陣列,因為符合的可能有很多筆。
判斷取出來的陣列 profiles.size > 0 再執行刪除命令
你應該用 binding.pry 進入測試環境,然後把 controller 的 code 分別貼上去測試
這樣就知道哪邊是錯誤的原因