I have a couple models (Site and Server) that are related to eachother via has_many :through. they also both belong_to :user. in my sites/new view, I create a new site, and I create a new server using a nested form.fields_for :servers. Everything works as expected, except for that the server that ends up getting created doesn't have a user_id populated. How do i ensure it is?
My sites_controller new and create methods:
def new
#user = current_user
#site = #user.sites.build
#servers = #user.servers.all
# let there be one server linked
#site.site_servers.build
# #user.servers.build if #user.servers.empty?
#site.servers.build( :user_id => current_user.id ) if #site.servers.empty?
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #site }
end
end
def create
#site = current_user.sites.build(params[:site])
respond_to do |format|
if #site.save
flash[:notice] = 'Site was successfully created.'
format.html { redirect_to(#site) }
format.xml { render :xml => #site, :status => :created, :location => #site }
else
format.html { render :action => "new" }
format.xml { render :xml => #site.errors, :status => :unprocessable_entity }
end
end
end
If you notice the commented lines, those are things I tried that didn't work.
Models:
class User < ActiveRecord::Base
acts_as_authentic
has_many :sites
has_many :servers
end
class Site < ActiveRecord::Base
belongs_to :user
has_many :site_servers
has_many :servers, :through => :site_servers
accepts_nested_attributes_for :site_servers, :allow_destroy => true
accepts_nested_attributes_for :servers, :allow_destroy => true
validates_presence_of :name, :on => :create, :message => "Name is required"
end
class Server < ActiveRecord::Base
attr_encrypted :password, :key => '393b79433f616f445f652a752d', :attribute => 'crypted_password'
belongs_to :user
has_many :site_servers
has_many :sites, :through => :site_servers
validates_presence_of :url, :on => :create, :message => "URL is required."
validates_presence_of :username, :on => :create, :message => "Username is required."
validates_presence_of :password, :on => :create, :message => "Password is required."
def name
username + "#" + url
end
def to_s
name
end
end
class SiteServer < ActiveRecord::Base
belongs_to :site
belongs_to :server
has_one :user, :through => :site
end
And here's my schema:
ActiveRecord::Schema.define(:version => 20091203045550) do
create_table "servers", :force => true do |t|
t.string "url"
t.string "username"
t.string "crypted_password"
t.integer "port"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "site_servers", :force => true do |t|
t.integer "site_id"
t.integer "server_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "sites", :force => true do |t|
t.string "name"
t.string "url"
t.string "path"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", :force => true do |t|
t.string "username"
t.string "email"
t.string "crypted_password"
t.string "password_salt"
t.string "persistence_token"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Do you have a hidden field for the user_id in the server form?
<%= f.hidden_field :user_id %>
If not, the value is not getting passed back, even if you managed to properly set it. The line you have commented out would have worked, if you add a hidden field to the form.
#site.servers.build(:user_id => current_user.id) if #site.servers.empty?
I actually like the idea of setting the user id in the create method better, because otherwise you introduce the possibility of someone crafting up their own form submission and creating things under other people's user ids. I don't know if security is a big deal in your app, but I never trust a user id that is sent from a form.
I'm guessing the problem is in your create action. The new action just builds Ruby objects -- you need to make sure there is similar build code in the create action:
def create
#site = current_user.sites.build(params[:site])
#site.save
end
Related
During a signup process I have a user model and Tenant model. Recently I added a serialized column to the Tenant model and I can update the this column fine. However when creating a new Tenant I have devise creating tenant through nested parameters and I get the following error:
ActiveRecord::SerializationTypeMismatch (Attribute was supposed to be a Hash, but was a String. -- "{}"): Important to note that I don't touch that column during the sign up process I have tried including the column on the sanitizer but it does the same. On the schema there is a default value which is '{}'. Below some of the code:
create_table "tenants", force: :cascade do |t|
t.string "tenant_name"
t.string "tenant_address"
t.string "tenant_city"
t.string "tenant_zip"
t.string "tenant_phone"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "authorized"
t.boolean "trial"
t.string "plan_id"
t.string "plan_name"
t.string "braintree_id"
t.string "subscription_id"
t.jsonb "preferences", default: "{}", null: false
t.string "tenant_state"
t.string "tenant_country"
t.index ["preferences"], name: "index_tenants_on_preferences", using: :gin
end
class Tenant < ApplicationRecord
has_many :users, :dependent => :delete_all
has_many :customers, :dependent => :delete_all
has_many :work_orders, :dependent => :delete_all
has_many :vehicles, :dependent => :delete_all
has_many :suppliers, :dependent => :delete_all
end
serialize :preferences, Hash
store_accessor :preferences, :state_tax, :mun_tax, :welcome_sms, :estimate_sms, :completed_sms, :disclaimer
Here is part of my User controller:
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
# GET /resource/sign_up
def new
build_resource({})
self.resource.tenant = Tenant.new
respond_with self.resource
end
# POST /resource
def create
super
if #user.save
#result = Braintree::Customer.create(
:first_name => #user.name,
:last_name => #user.lastname,
:company => #user.tenant.tenant_name,
:email => #user.email,
:phone => #user.phone
)
if #result.success?
#user.tenant.set_braintree_id(#result.customer.id)
flash[:notice] = 'Thanks you! and Welcome to Autokick.tech enjoy your free 30 days!'
else
flash[:notice] = #result.errors
end
end
end
t.jsonb "preferences", default: "{}", null: false
The default is a string "{}" like the error says.
Change it to default: {} without the quotes.
I've managed to get a few HABTM relationships set up without any problems, but for some reason I can't make a belongs_to/has_many relationship record the values.
An Article belongs_to a Type (news, editorial, chronicle, etc.)
A Type has_many Articles
Schema.db shows the type_id integer column, the models use belongs_to and has_many and a drop down of article types appears in the form on the new/edit article pages.
But on choosing a type from the drop down (e.g. 'chronicle'), it says it creates or edits the article successfully but does not register the link between the article and 'chronicle'. On going back to edit the same article, the drop down just shows the top type ('analysis'), not 'chronicle'.
So not sure where I'm going wrong. Here are all the relevant bits, starting with the database.
From schema.db:
create_table "articles", force: :cascade do |t|
t.string "headline"
t.string "lede"
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "type_id"
end
create_table "types", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Then the models:
class Article < ActiveRecord::Base
has_and_belongs_to_many :categories
has_and_belongs_to_many :regions
has_and_belongs_to_many :stories
belongs_to :type
end
class Type < ActiveRecord::Base
has_many :articles
end
And the articles controller:
# GET /articles/new
def new
#article = Article.new
#regions = Region.all.order(:region)
#categories = Category.all.order(:category)
#stories = Story.all.order(:story)
#types = Type.all.order(:name)
end
# GET /articles/1/edit
def edit
#regions = Region.all.order(:region)
#categories = Category.all.order(:category)
#stories = Story.all.order(:story)
#types = Type.all.order(:name)
end
# POST /articles
# POST /articles.json
def create
#article = Article.new(article_params)
respond_to do |format|
if #article.save
format.html { redirect_to #article, notice: 'Article was successfully created.' }
format.json { render :show, status: :created, location: #article }
else
format.html { render :new }
format.json { render json: #article.errors, status: :unprocessable_entity }
end
end
end
/* …and then at the bottom… */
def article_params
params.require(:article).permit(:headline, :lede, :body, :category_ids => [], :region_ids => [], :story_ids => [], :type_id => [])
end
And finally in the articles form:
<strong>Type:</strong> <%= f.collection_select :type_id, #types, :id, :name %>
Any ideas?
You need to change the article_params to below
def article_params
params.require(:article).permit(:headline, :lede, :body, :type_id, :category_ids => [], :region_ids => [], :story_ids => [])
end
Notice the change :type_id => [] to :type_id
As there will be only one type_id in each articles record, your required params should not contain array of type_id. so change it to only :type_id instead of :type_id => []
def article_params
params.require(:article).permit(:headline, :lede, :body, :type_id, :category_ids => [], :region_ids => [], :story_ids => [])
end
When I try to add a new pin, I can't. Instead, I get this error:
NoMethodError in PinsController#create
undefined method `name' for #<pin:0x000001011b9638>
Application Trace | Framework Trace | Full Trace
app/controllers/pins_controller.rb:48:in `block in create'
app/controllers/pins_controller.rb:47:in `create'
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"n9E2nob/KBzu20PEzYoQWXnibAUR5TH6iPWNd66383k=",
"pin"=>{"description"=>"stea"},
"commit"=>"Create Pin"}
pins_controller.rb
def create
#pin = current_user.pins.new(params[:pin])
respond_to do |format|
if #pin.save
format.html { redirect_to #pin, notice: 'Pin was successfully created.' }
format.json { render json: #pin, status: :created, location: #pin }
else
format.html { render action: "new" }
format.json { render json: #pin.errors, status: :unprocessable_entity }
end
end
end
app/model/user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me, :name
has_many :pins, :dependent => :destroy
end
routes.rb
Omrails::Application.routes.draw do
resources :pins
devise_for :users
root :to => 'pages#home'
get 'about' => 'pages#about'
app/models/pin.rb
class Pin < ActiveRecord::Base
attr_accessible :description
validates :description, presence: true
validates :name, presence: true
belongs_to :user
validates :user_id, presence: true
end
db/migrate/create_pins
class CreatePins < ActiveRecord::Migration
def change
create_table :pins do |t|
t.string :description
t.timestamps
end
end
end
db/migrate/add_user_id_to_pins.rb
class AddUserIdToPins < ActiveRecord::Migration
def change
add_column :pins, :user_id, :integer
add_index :pins, :user_id
end
end
db/migrate/add_name_to_users.rb
class AddNameToUsers < ActiveRecord::Migration
def change
add_column :users, :name, :string
end
end
Any ideas about what has gone wrong?
Not sure if it's relevant, but this used to work. I was able to follow along Mattan Griffel's One Month Rails course -- Add Assoc bt Pins and Users video until 29m50s but then I realized that I had to skip back to Customizing Devise bec I forgot to add simple forms.
Now that simple forms have been added, I am trying to go forward - and getting stuck here :(
UPDATE: I ran migrate redo for creating pins and adding user id to pins. Then I removed the validate name line. Now I get the following error when I create pin
ActiveRecord::UnknownAttributeError in PinsController#new
unknown attribute: user_id
app/controllers/pins_controller.rb:29:in `new'
pins_controller.rb
def new
#pin = current_user.pins.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #pin }
end
end
Many thanks for helping
db/schema.rb
ActiveRecord::Schema.define(:version => 20130828163738) do
create_table "pins", :force => true do |t|
t.string "description"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "users", :force => true do |t|
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
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", :null => false
t.datetime "updated_at", :null => false
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
You are validating the presence of :name in the Pin model but it does not have a :name field. Your User has.
Just remove the validates :name, presence: true from you Pin model (line 5).
What happened is that Rails, when trying to save your Pin model, will run all the validations. When it encounters the presence validation on :name, it will check to see if #pin.name isn't blank. But the thing is that your Pin model does not have a name method. So it raises this error.
If you actually want your Pin model to have a name, add it the to pins table:
$ rails g migration add_name_to_pins name:string
$ rake db:migrate
i have such model:
class ToType < ActiveRecord::Base
attr_accessible :Name, :TYP_CCM, :TYP_CCM_TAX, :TYP_CDS_ID, :TYP_CTM, :TYP_CYLINDERS, :TYP_DOORS, :TYP_HP_FROM, :TYP_HP_UPTO, :TYP_ID, :TYP_KV_ABS_DES_ID, :TYP_KV_ASR_DES_ID, :TYP_KV_AXLE_DES_ID, :TYP_KV_BODY_DES_ID, :TYP_KV_BRAKE_SYST_DES_ID, :TYP_KV_BRAKE_TYPE_DES_ID, :TYP_KV_CATALYST_DES_ID, :TYP_KV_DRIVE_DES_ID, :TYP_KV_ENGINE_DES_ID, :TYP_KV_FUEL_DES_ID, :TYP_KV_FUEL_SUPPLY_DES_ID, :TYP_KV_MODEL_DES_ID, :TYP_KV_STEERING_DES_ID, :TYP_KV_STEERING_SIDE_DES_ID, :TYP_KV_TRANS_DES_ID, :TYP_KV_VOLTAGE_DES_ID, :TYP_KW_FROM, :TYP_KW_UPTO, :TYP_LA_CTM, :TYP_LITRES, :TYP_MAX_WEIGHT, :TYP_MMT_CDS_ID, :TYP_MOD_ID, :TYP_PCON_END, :TYP_PCON_START, :TYP_RT_EXISTS, :TYP_SORT, :TYP_TANK, :TYP_VALVES, :is_in_to
set_primary_key :TYP_ID
belongs_to :to_model
has_many :to_articles, :dependent => :destroy
end
class ToArticle < ActiveRecord::Base
attr_accessible :details, :manufacturer, :name, :oem_number, :only_with_vin, :quantity, :type_id
belongs_to :to_type
end
(some db is converted from big catalog, so rails conventions are a little bit missed)
my show view of to_type is:
part of it:
%td
= link_to "Подробнее", admin_catalog_to_to_article_path(c), :class=>'btn btn-primary'
= link_to "Редактирование", edit_admin_catalog_to_to_type_path(c), :class=>'btn btn-warning'
= link_to "Удалить", admin_catalog_to_to_type_path(c), :confirm => "!!!Тип #{c.Name} будет удалён!!!! Вы уверены?", :method => :delete, :class => "btn btn-danger"
my show action work normally, also controller:
class Admin::Catalog::To::ToTypesController < ApplicationController
respond_to :html
before_filter :auth_user
def auth_user
redirect_to new_admin_session_path unless admin_signed_in?
end
def show
#mod_id = params[:id]
#man = ToType.find(:all, conditions: {:TYP_MOD_ID => #mod_id}, order: "Name ASC")
render :layout => 'admin'
end
def edit
#man = ToType.find(params[:id])
render :layout => 'admin'
end
def update
#man = ToType.find(params[:id])
if #man.update_attributes(params[:to_type])
redirect_to admin_catalog_to_to_type_path(#man.TYP_MOD_ID)
else
render :layout => 'admin'
end
end
def new
#man = ToType.new
#mod_id = params[:mod_id]
render :layout => 'admin'
end
def create
#man = ToType.new(params[:to_type])
#mod_id = params[:mod_id]
#man.TYP_MOD_ID = #mod_id
if #man.save
redirect_to admin_catalog_to_to_type_path(#mod_id)
else
render :layout => 'admin'
end
end
def destroy
#man = ToType.find(params[:id])
if #man.destroy
redirect_to admin_catalog_to_to_type_path(#man.TYP_MOD_ID)
else
render :layout => 'admin'
end
end
end
and route:
namespace :admin do
namespace :catalog do
namespace :to do
resources :to_manufacturers,
:to_models,
:to_types,
:to_articles
end
end
end
but when i try to call destroy method i get:
ActiveRecord::StatementInvalid in Admin::Catalog::To::ToTypesController#destroy
Mysql2::Error: Unknown column 'to_articles.to_type_id' in 'where clause': SELECT `to_articles`.* FROM `to_articles` WHERE `to_articles`.`to_type_id` = 26923
also when i try edit or create i get:
undefined method `model_name' for NilClass:Class
i think that something is bad with connection with model: with update and create it didn't initialize object.
With destroy it use other! db. What happens?
Also i try to recreate it all and rename, nothing... Could understand what wrong... Also when in model i write which db table to use same errors appear.
when i try to add new object via console all is ok.
upd:
class CreateToTypes < ActiveRecord::Migration
def change
create_table :to_types, :primary_key => :TYP_ID do |t|
t.integer :TYP_ID
t.integer :TYP_CDS_ID
t.integer :TYP_MMT_CDS_ID
t.integer :TYP_MOD_ID
t.binary :TYP_CTM
t.binary :TYP_LA_CTM
t.integer :TYP_SORT
t.integer :TYP_PCON_START
t.integer :TYP_PCON_END
t.integer :TYP_KW_FROM
t.integer :TYP_KW_UPTO
t.integer :TYP_HP_FROM
t.integer :TYP_HP_UPTO
t.integer :TYP_CCM
t.integer :TYP_CYLINDERS
t.integer :TYP_DOORS
t.integer :TYP_TANK
t.integer :TYP_KV_VOLTAGE_DES_ID
t.integer :TYP_KV_ABS_DES_ID
t.integer :TYP_KV_ASR_DES_ID
t.integer :TYP_KV_ENGINE_DES_ID
t.integer :TYP_KV_BRAKE_TYPE_DES_ID
t.integer :TYP_KV_BRAKE_SYST_DES_ID
t.integer :TYP_KV_FUEL_DES_ID
t.integer :TYP_KV_CATALYST_DES_ID
t.integer :TYP_KV_BODY_DES_ID
t.integer :TYP_KV_STEERING_DES_ID
t.integer :TYP_KV_STEERING_SIDE_DES_ID
t.float :TYP_MAX_WEIGHT
t.integer :TYP_KV_MODEL_DES_ID
t.integer :TYP_KV_AXLE_DES_ID
t.integer :TYP_CCM_TAX
t.float :TYP_LITRES
t.integer :TYP_KV_DRIVE_DES_ID
t.integer :TYP_KV_TRANS_DES_ID
t.integer :TYP_KV_FUEL_SUPPLY_DES_ID
t.integer :TYP_VALVES
t.integer :TYP_RT_EXISTS
t.string :Name
t.boolean :is_in_to
t.string :fuel_type
end
end
end
class CreateToArticles < ActiveRecord::Migration
def change
create_table :to_articles do |t|
t.string :oem_number
t.string :manufacturer
t.text :name
t.integer :quantity
t.text :details
t.boolean :only_with_vin
end
end
end
you don't have relationship between ToArticle and ToType in database.
use belongs_to in ToArticle migration
check rails guide on associations
I want to get user info like usernaem or first name who create roles in my ROR application. There are many to many association between users and roles by having role_users as join table. I am able to crate roles and save them. But i have no idea how to get user who creates role. For example,if I am Admin, I can create new roles in application. While creating role, I need to get user who is creating that role in roles_controller.rb.
roles_controller.rb
class RolesController < ApplicationController
before_filter :authorize_admin!
def index
#roles = Role.all
end
def new
#role = Role.new
end
def create
#role = Role.new(params[:role])
# #article.user_id = current_user.id
#role_user.user_id = current_user.id
if #role.save
flash[:success] = "role created!"
redirect_to roles_path(#role)
else
render 'new'
end
end
def show
#role = Role.find(params[:id])
end
def edit
#role = Role.find(params[:id])
end
def update
#role = Role.find(params[:id])
if #role.update_attributes(params[:role])
flash.notice = "Role #{#role.name} has been updated"
redirect_to role_path(#role)
else
render 'edit'
end
end
def destroy
#role = Role.find(params[:id])
#role.destroy
redirect_to action: 'index'
end
end
users_controller.rb
class Admin::UsersController < Admin::BaseController
before_filter :find_user, :only => [:show, :edit, :update, :destroy]
def index
#users = User.all( :order => :email )
#roles = Role.all
end
def show
end
def new
#user = User.new
end
def create
is_admin = params[:user].delete(:is_admin) == "1"
#user = User.new(params[:user])
#user.save
#user_role = RoleUser.new({:user_id => #user.id, :role_id => params[:role_id]})
#user_role.role_id = params[:role_id]
#user_role.save
#user.is_admin = is_admin
if #user.save
flash[:notice] = "User has been created."
redirect_to admin_users_path
else
flash[:alert] = "User has not been created."
render :action => :new
end
end
def edit
end
def update
if params[:user][:password].empty?
params[:user].delete(:password)
end
set_admin
if #user.update_attributes(params[:user])
flash[:notice] = "User has been updated."
redirect_to admin_users_path
else
flash[:alert] = "User has not been updated."
render :action => :new
end
end
def destroy
if #user == current_user
flash[:alert] = "You cannot delete yourself!"
else
#user.destroy
flash[:notice] = "User has been deleted."
end
redirect_to admin_users_path
end
private
def find_user
#user = User.find(params[:id])
end
def set_admin
is_admin = params[:user].delete(:is_admin) == "1"
#user.is_admin = true
end
end
user.rb
class User < ActiveRecord::Base
has_many :roles, through: :role_users
has_many :role_users
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :username, :email, :password, :password_confirmation,
:remember_me, :first_name, :last_name, :is_admin, :contact_no, :birth_date,
:joining_date, :is_active, :is_hr, :is_manager, :user_code, :designation
# attr_accessible :title, :body
end
role_user.rb
class RoleUser < ActiveRecord::Base
belongs_to :user
belongs_to :role
attr_accessible :role_id, :user_id
end
role.rb
class Role < ActiveRecord::Base
attr_accessible :name
has_many :users, through: :role_users
has_many :role_users
end
ActiveRecord::Schema.define(:version => 20130601093644) do
create_table "role_users", :force => true do |t|
t.integer "role_id"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "roles", :force => true do |t|
t.string "name"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "users", :force => true do |t|
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
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", :null => false
t.datetime "updated_at", :null => false
t.boolean "is_admin"
t.string "username"
t.string "first_name"
t.string "last_name"
t.string "contact_no"
t.date "birth_date"
t.boolean "is_active"
t.date "joining_date"
t.string "avatar_url"
t.boolean "is_hr"
t.boolean "is_manager"
t.string "designation"
t.string "user_code"
t.string "user_role"
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
Pass user_id as a params via the form:
#_form.html.haml
%input{:name => "user_id, :value => current_user.id, :type => "hidden"}
#controller:
creating_user = User.find(params[:role][:user_id])
But I don't think I'm following you completely. Is this what you're after?
If you want to get user object from.role just do #role.user. it will execute sql query which will join all 3 tables and return you a user
As per my understanding you need to add one more column into roles model i.e user_id. Make proper association between them like. User can have many roles and role belongs to User.
While creating a new role you need to pass the user_id also or use association for this will save you from passing explicitly user_id value.
ex:-
#current_user.roles.new(params[:new_role])
when you will role_user object you can fetch role owner data with proper association between there models i.e User, Role, RoleUser.
ex:-
#role_user.role.user