I have a question about storing a value into a integer variable. Is it possible to store different id into the same variable ?
This is my problem, I would like to use a collection_select to save many id's.
My code is actually working for one variable, like this :
My code :
User model :
has_many :pins
scope :artist, -> { where(artist: true) }
Pin model :
belongs_to :user
Pin controller:
def new
#pin = Pin.new
#users = User.all.artist
end
def create
#pin = current_user.pins.build(pin_params)
if #pin.save
redirect_to #pin, notice: "successfully created!"
else
render 'new'
end
end
Pin/new(views):
<div class="form-group">
<%= f.collection_select(:pin_maker, #users, :id, :pseudo) %>
</div>
I would like something like that for my new views :
<div class="form-group">
<%= f.collection_select(:pin_maker, #users, :id, :pseudo, { }, {:multiple => true}) %>
</div>
But the variables are not saving in my sql table.
So my question is : That's possible to store many id in the same variable (:pin_maker) which is an integer ? Or should I create a new table for that ?
pin_maker is user ? if so:
(IMO): you need a n-to-n associations and a table users_pins.
Logic:
One user has many pins
One pin may be made by many users
On Rails:
Model User:
has_many pins, :through => :users_pins
Model Pin:
has_many users, :through => :users_pins
Try has_many :users to involve your associations
Related
I am new to Rails, and trying to learn. In my simple form I have created a drop down select with generated data from a table called professions. This part is working fine, and I can select multiple values. I'm using a mysql database.
When I click the submit button, I need it to save the chosen values to a column called my_professions in another table called users. I can’t figure out how to do this.
I get this error
My form
<%= simple_form_for #user, url: wizard_path, method: :put do |f| %>
<%= collection_select(:f, :professions_id, Profession.where.not(name: nil), :id, :name, {:multiple => true}, {:class=>'js-example-basic-multiple', :id=>'jsmultipleddd'}) %>
<%= f.submit "Save", :class => 'btn blue' %>
<% end %>
I have tried to add this to the user model
user.rb
class User < ApplicationRecord
has_many :professions
accepts_nested_attributes_for :professions
serialize :my_professions, Array
end
And this to the profession model
profession.rb
class Profession < ApplicationRecord
belongs_to :user
end
my params look like this
registration_steps_controller.rb
def user_params
params.require(:user).permit(:gender,:practitioner_website, :public_health_insurance, clinic_images: [], professions: [])
end
application_controller.rb
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:gender, :practitioner_website, :public_health_insurance, clinic_images: [], professions: []])
devise_parameter_sanitizer.permit(:account_update, keys: [:gender, :practitioner_website, :public_health_insurance, clinic_images: [], professions: []])
end
Start by getting rid of accepts_nested_attributes_for :professions. You don't need nested attributes for this.
Then get rid of serialize :my_professions, Array. Serialize is a legacy hack to store complex data in string columns. You don't need or want this (ever), since associations should be stored in join tables in ActiveRecord - not array columns. That's how AR was designed to work and that's how relational databases where designed to work.
Instead what you want is a join model. Which you can generate with:
rails g model user_profession user:belongs_to profession:belongs_to
Run the migration. You then setup the associations between users and professions:
class User < ApplicationRecord
# ...
has_many :user_professions
has_many :professions, through: :user_professions
end
class Profession < ApplicationRecord
# ...
has_many :user_professions
has_many :users, through: :user_professions
end
Now this lets us associate users with professions by passing profession_ids.
In a normal Rails form you would create the input with:
<%= f.collection_select :profession_ids, Profession.all, :id, :name, multiple: true ... %>
In SimpleForm use the association helper:
<%= f.association :professions, ... %>
Then whitelist the correct param:
def user_params
# don't jam this into one super long unreadable line
params.require(:user)
.permit(
:gender, :practitioner_website, :public_health_insurance,
clinic_images: [], profession_ids: []
)
end
I want to make a table called subject_types and populate it with different subjects such as english, spanish, history. And assign the subjects table to each user. I want the user to be able to select multiple subjects with a checkbox and it will save those specific ones and keep checked. each user can save their own preferences/services. Im trying to figure out the best way to approach this.
here is how it would look
in a way the user is editing their settings. I want to be able to take this information and use it later to find results with only those subject types that they select. is there a tutorial or similar reference to this?
-Should I make the subject_types model and populate with these subjects or is there a better approach?
possible solution
could I have an array in a column in my user table? the array would be the ids of the subjects table. How can i do that??
Thank you
Updated:
<%= form_for(#user) do |f| %>
<%= f.collection_check_boxes(:writing_type_ids, WritingType.all, :id, :name) %>
<%= f.submit 'submit' %>
<% end %>
class WritingType < ActiveRecord::Base
has_many :user_writing_types
has_many :users, through: :user_writing_types
end
class UserWritingType < ApplicationRecord
belongs_to :user
belongs_to :writing_type
end
class User < ActiveRecord::Base
has_many :user_writing_types
has_many :writing_types, through: :user_writing_types
end
My migration for the join
create_table :user_writing_types do |t|
t.belongs_to :user, index: true
t.belongs_to :writing_type, index: true
t.boolean :active, default: false
t.timestamps
end
latest update
Im getting my last of errors! when i click submit on that page i get No route matches [PATCH] "/users/51".
I added this <%= f.collection_check_boxes(:writing_type_ids, WritingType.all, :id, :name) %> to my edit form in devise edit.html.erb. The writing_type names populate on the checkboxes, but nothing gets submitted to the database on the user_writing_type table.
Start by creating a many to many relation with a join model by using has_many :through associations:
class User < ApplicationRecord
has_many :user_subjects
has_many :subjects, through: :user_subjects
end
class Subject < ApplicationRecord
has_many :user_subjects
has_many :users, through: :user_subjects
end
# this is the join model
class UserSubject < ApplicationRecord
belongs_to :user
belongs_to :subject
end
You can then create a check box to add subjects to a user with collection_checkboxes:
<%= form_for(#user) do |f| %>
<%= f.collection_checkboxes(:subject_ids, Subject.all, :id, :name) %>
# ...
<% end %>
could I have an array in a column in my user table? the array would be
the ids of the subjects table. How can i do that??
You don't.
Even though Postgres for example allows you to create array type columns
it is not a good solution as thats not how associations work in ActiveRecord.
Its also a crappy relational database design as its messy to write queries with a join though an array column and it does not let you enforce referential integrity with foreign keys or have good indices for performance.
I'm trying to create a form using form_for which will add Employees. For an employee i want to assign multiple specializations such as c#, asp, etc as the values dynamically. I'm using the following code in the form
<%= f.select :specilization, Specialization.all.collect{|p| [p.name, p.id]}, {}, :multiple => true %>
I've also made HABTM between employees and specialization like
Employee.rb
class Employee < ActiveRecord::Base
has_and_belongs_to_many :specializations
end
Specialization.rb
class Specialization < ActiveRecord::Base
has_and_belongs_to_many :employees
end
with these done, im not able to save the selected values in db(MySQl). Would appreciate if anyone could solve my problem or guide me on how to get this right?
Thanks in advance.
I usually solve this by using has_many :through and then in my form the select is a field_for on the join model. Like this:
class Employee < ActiveRecord::Base
has_many :employees_specializations
has_many :specializations, through: :employees_specializations
# we will be creating these join models on the employee form
accepts_nested_attributes_for :employees_specializations
end
class Specialization < ActiveRecord::Base
has_many :employees_specializations
has_many :employees, through: :employees_specializations
end
class EmployeesSpecialization < ActiveRecord::Base
belongs_to :employee
belongs_to :specializations
end
Now it's important to note that with this simplified approach I'm assuming Specializations already exist in the database and we're just selecting them and joining them to the employee we're creating/editing:
# in your controller make sure to build at least 1 new instance, the fields_for needs this
#employee.employees_specializations.build
# remember to add to your strong parameters the new join attributes
params.require(:employee).permit(
# ... other attributes ...
employees_specializations_attributes: [:id, :specialization_id]
)
You need to declare :id, :specialization_id as sub fields since employees_specializations_attributes will be a nested hash with those keys inside.
# now in your form use fields_for
<%= f.fields_for :employees_specializations do |ef| %>
<%= ef.select :specialization_id, Specialization.all.map{|p| [p.name, p.id]}, {}, multiple: true %>
<% end %>
That f.fields_for :employees_specializations will create form fields named employee[employees_specializations_attributes][][specialization_id]. Which basically says we are creating a nested association employees_specializations and setting that nested association's specialization_id (remember employees_specialization belongs_to :specialization) to the selected specialization. Note the [] in the middle of the field name, this means its an array of nested employees_specializations.
Post that and barring any validation errors you should be able to create/edit an employee and set it's specializations by selecting from a list of existing specializations and creating a join model between them.
Further reading:
http://apidock.com/rails/v4.0.2/ActionView/Helpers/FormHelper/fields_for
http://apidock.com/rails/v4.0.2/ActiveRecord/NestedAttributes/ClassMethods/accepts_nested_attributes_for
http://laptite.github.io/blog/2014/02/26/deep-nesting-with-has-many-through-and-a-join-model/
Rails nested form with has_many :through, how to edit attributes of join model?
i got 2 tables connected with each other.
device and push information are my models.
class Device < ActiveRecord::Base
has_one :pushinformation
end
class Pushinformation < ActiveRecord::Base
belongs_to :device
end
these are my 2 model files and their relationships.
and these are my migration files
class CreateDevices < ActiveRecord::Migration
def change
create_table :devices do |t|
#t.integer :id
t.string "token"
t.string "carrier"
t.string "segment"
#t.datetime :created_at
#t.datetime :updated_at
t.timestamps
end
end
end
class CreatePushinformations < ActiveRecord::Migration
def change
create_table :pushinformations do |t|
t.integer "device_id"
#t.string "token"
t.string "first_name"
t.string "last_name"
t.string "nickname"
t.timestamps
end
end
end
now the thing is , i was able to create a relationship successfully in rails console by saying
device.pushinformation=push
and they were associated.
How can i make this process automated, like when i add one device- it will have a push information table filled aswell,
i thought about having the same attribute and relating them might be the solution. In this case its token and its completely unique.
How can i create this relationships? I need to be able to know which device has what kind of first_name
i m a beginner in ruby and this is a newbie question sorry guys :)
I am not sure I understand completely what you ask but my guess is that you could use a callback on create
class Pushinformation < ActiveRecord::Base
belongs_to :device
after_create :create_push_notification
private
def create_push_notification
...
end
end
check the docs
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
xlembouras's answer is right (to a degree), but as you're new, let me explain it for you:
--
Associations
ActiveRecord associations are nothing magical, they're just a way to associate two "objects" using a relational database setup.
ActiveRecord is an ORM -- "object relationship mapper" -- which basically means it just provides a level of abstraction for your ActiveRecord objects to associate with each other
Herein lies the crux of the matter - you need to apprciate how and why your associations will work, and more importantly, how to populate them correctly:
--
Models
Firstly, you need to appreciate the object-orientated nature of Ruby (& by virtue of running on Ruby, Rails). This is where the Models of Rails play such a vital role -- they allow you to build & manage objects from your database
The ActiveRecord associations give you the ability to manage the associations those objects have - maning if you build one, you should be able to build the other:
#app/models/device.rb
Class Device < ActiveRecord::Base
has_one :push_information
before_create :build_push_information #-> creates associative object before creation
end
#app/models/push_information.rb
Class PushInformation < ActiveRecord::Base
belongs_to :device
end
You need to consider the importance of what I've written above.
What you need is to create a push_information object with the same foreign_key as the device object, which can be achieved by using the build method
This will essentially create a "blank" version of your associative object, saving it with the correct foreign key etc
--
Nesting
Further to this, you have to appreciate the idea of "nesting", especially the method accepts_nested_attributes_for
This allows you to create associative / dependent objects based on your "parent" object:
#app/models/device.rb
Class Device < ActiveRecord::Base
has_one :push_information
accepts_nested_attributes_for :push_information
end
#app/models/push_informtion.rb
Class PushInformation < ActiveRecord::Base
belongs_to :device
end
This gives you the ability to do the following:
#app/controllers/devices_controller.rb
Class DevicesController < ApplicationController
def new
#device = Device.new
#device.build_push_information
end
def create
#device = Device.new(device_params)
#device.save
end
private
def device_params
params.require(:device).permit(:your, :device, :params, push_information_attributes: [:push, :information, :attributes])
end
end
This gives you the ability to populate the devices#new form like so:
#app/views/devices/new.html.erb
<%= form_for #device do |f| %>
<%= f.text_field :your_device_attributes %>
<%= f.fields_for :push_information do |p| %>
<%= p.text_field :your_field %>
<% end %>
<%= f.submit %>
<% end %>
Add a create
method to your Devise class. Something like:
def self.create(token, carrier, segment)
device = Device.new(:token => token, :carrier => carrier, :segment => segment)
pi = Pushinformation.create(device.id,..) # other params
end
I have a User that can have many Restaurants. I can also have multiple users.
I'd like to have it so that if User A creates Restaurant A, he should NOT be able to make another restaurant with the same name.
However, if User B goes to create Restaurant A, that should be allowed but still cannot make another Restaurant A afterwards.
I have the following has_many through relationship:
restaurant.rb
has_many :ownerships
has_many :users, :through => :ownerships
# This following ensures uniqueness of the name within the
# Restaurants table regardless of who the User is that created it.
validates :name, presence: true, uniqueness: true
user.rb
has_many :ownerships
has_many :restaurants, :through => :ownerships
ownership.rb
belongs_to :restaurant
belongs_to :user
What I've Tried
1. Adding :uniqu => true
I've tried adding :uniq => true to the restaurant.rb file so it looks like this:
has_many :ownerships
has_many :users, :through => :ownerships, :uniq => true
And removing uniqueness: true from the validation so it looks like this:
validates :name, presence: true
But that doesn't do anything useful.
2. Adding validation within ownership.rb
I've tried adding the validation to the ownership.rb file as such:
validates :restaurant, uniqueness: {:scope => :user}
But I get:
NoMethodError in RestaurantsController#create
undefined method `text?' for nil:NilClass
And I can't seem to tell it to look for the restaurant name within the scope of user either within this validation.
3. Creating before_create callback function
In my restaurant.rb file, I declared the following:
before_create :check_uniqueness
def check_uniqueness?
user = User.find_by_id(self.user_ids)
isUnique = false
user.restaurants.each do |restaurant|
if !Restaurant.find_by_name(self.name).nil? # Restaurant w/ same now found
isUnique = false
else
isUnique = true
end
return isUnique
end
end
My assumption is that before the restaurant record is created, it'll do this check_uniqueness check and if the function returns false, it'll not save.
But I'm getting the following error when I hit the submit button:
NameError in RestaurantsController#create
undefined local variable or method `check_uniqueness' for #<Restaurant:0x007f95a16d10f8>
Working Solution
Thanks to Robert Chuchro's help below, I was able to get the validation to work. Here's what I did:
restaurant.rb
before_create :unique_per_user?
def unique_per_user?
user = User.find_by_id(self.user_ids)
restaurant = user.restaurants.find(:all, :conditions => ["name = ?", self.name])
if restaurant.size > 0
self.errors.add(:name, ": You've already created a restaurant with this name.")
end
return (restaurant.size <= 0)
end
You can try to define a method to do this in your restaurant model
def unique_per_user?
#get user trying to create restaurant, either by paramter or association
#check if any of the user's current restaurant names match this name (return true/false)
end
now whereever you define a new restaurant check if its unique_per_user? before deciding to save it.