I have two non-attached models - milestones and users. (My milestones actually belong to companies and companies have many milestones.)
Each milestone has a user who'd responsible for it - in my milestone form, I'm using the following to find and select users:
<%= f.input :milestone_user, :as => :select, :collection => User.find(:all, :order => "name ASC") %>
This gives me a user_id which I've converted in one view to a name as follows:
<%= User.find(milestone.milestone_user).name %>
This works fine but I want to use this in a few views now and don't like having the query in my views.
I have tried to move it into my User model but I don't know how to go about it. I've tried this in the model:
scope :username, lambda { where("id = milestone_user")}
And this in my view:
<%= User.username.first_name %>
But it complains about an undefined method for first_name..
Thanks in advance
Add this to your Milestone model:
def username
User.find(milestone_user).try :name
end
and use it like this in your view:
<%= milestone.username %>
But this isn't efficient, the better way would be to stick belongs_to :user, :foreign_key => "milestone_user", :class_name => "User" in your Milestone model. You won't have to fill the association, you still could create milestones without an assigned user.
The belongs_to would also allow you to use code like this in your views:
<%= milestone.user.name %>
without having to create additional methods for every user property that you'd like to access.
Move the query in the action of the related controller:
#milestone_user=User.find(milestone.milestone_user)
And then use try in the view
<%= #milestone_user.try(:name) %>
set the model to have a default_scope => :order('name asc')
In your controllers do #users = User.all
In your views use collection_select with #users
e.g. collection_select(:milestone, :user_id, #users, :id, :name )
See http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_select for more info about
collection_select, grouped_collection_select, grouped_options_for_select and other helpers which have options like
'grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})'
I think associations should be like this:
class Milestone < ActiveRecord::Base
belongs_to :company
has_one :user
end
class Company < ActiveRecord::Base
has_many :milestones
end
class User < ActiveRecord::Base
has_one :milestone
end
if you make association like above one then on the view side you can simply write
<%= #milestone.user.firstname%>
otherwise you can just modify your scope:
scope :milestone_user, lambda { |user_id|
where("id = ?",user_id)
}
Here I have just modify the name of scope.
and call from view through passing the parameter.
<%= User.milestone_user(milestone.milestone_user).first_name %>
Also you can use delegate methods.
Related
Issue
User can choose languages
Models
User
has_many :fluent_languages, foreign_key: 'user_id'
accepts_nested_attributes_for :fluent_languages,
allow_destroy: true,
reject_if: proc { |attributes| attributes['user_id'].blank? || attributes['language_id'].blank? }
FluentLanguage
belongs_to :user
belongs_to :language
Language
has_many :fluent_languages, foreign_key: 'language_id'
I wish select look like this
https://select2.github.io/examples.html#multiple
My try:
<%= f.fields_for :fluent_languages do |fluent_language| %>
<%= fluent_language.text_field :user_id, :type => 'hidden' %>
<%= fluent_language.autocomplete_field :language_id, autocomplete_language_language_users_path, 'data-delimiter' => ',', :multiple => true, :placeholder => 'Choose languages' %>
<% end %>
It's record to params next:
"7"=>{"user_id"=>"15", "language_id"=>["English,"]}}
but correct data look like this:
"4"=>{"user_id"=>"15", "language_id"=>["1995"], "id"=>"1"}
Another problem in my code - multiselect with autocomplete render for each record in FluentLanguage. And another one - default data is languages ids from FluentLanguage instead language title from languages table.
What design for this issue are you advise?
You've asked several questions, I will answer the most important one regarding design. Once you have a good data model in place, the solutions for getting the form to do what you want will be easier.
It appears that you have a has_many_through relationship between User and Language with FluentLanguage as the through table, which needs to be defined.
class User
has_many :fluent_languages
has_many :languages, through: fluent_languages
end
class Language
has_many :fluent_languages
has_many :users, through: fluent_languages
end
class FluentLanguage
belongs_to :language
belongs_to :user
end
You will need to run migrations to update your schema. The rails guide is a good source for how a has_many_through relationship works and how to build it. The api docs are also very useful.
I'm following this tutorial on how to nest other Models in my Devise registration form. I'm getting an error in my New controller:
'NoMethodError in Users::RegistrationsController#new undefined method `languages_user=' for #'.
Languages_Users is a join table, and I'm wondering if this is the reason it isn't working, but I don't understand what the solution is. I want to add 2 different records of Languages_Users when the user signs up.
Models:
User.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :role
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100#" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
validates_presence_of :first_name, :last_name, :location, :nationality, :bio
before_save :assign_role
def assign_role
self.role = Role.find_by name: "user" if self.role.nil?
end
has_many :languages_users
has_many :languages, :through => :languages_users
accepts_nested_attributes_for :languages_users
Language.rb
class Language < ActiveRecord::Base
has_many :languages_users
has_many :users, :through => :languages_users
end
Langauges_user.rb
class LanguagesUser < ActiveRecord::Base
belongs_to :user
belongs_to :language
validates_presence_of :user_id, :language_id, :level
validates :user_id, :uniqueness => {:scope => :language_id, :message => 'can only delcare each language once. Please change the level of the language in Manage Languages.'}
end
Controllers:
registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
def new
build_resource({})
self.resource.languages_user = LanguagesUser.new[sign_up_params]
respond_with self.resource
end
def create
#user_id = current_user.id
super
end
def sign_up_params
allow = [:email, :password, :password_confirmation, [languages_user_attributes: [:language_id, :user_id, :level]]]
end
end
Relevant sections of User's new.html.erb
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<%= f.fields_for :langauges_user do |lu| %>
<%#= lu.text_field :language_id %>
<%= lu.collection_select(:language_id, Language.order('language ASC').all, :id, :language) %><br>
<%= lu.hidden_field languages_user[level], value: 1 %>
<% end %>
<%= f.submit "Sign up" %>
<% end %>
Relevant routes
Rails.application.routes.draw do
resources :languages_users
devise_for :users, controllers: { registrations: "users/registrations" }
resources :users
get 'languages_users/:id/sign_up', to: 'languages_users#sign_up', as: 'sign_up'
end
I'm still learning - so please let me know if you need to see anything else. Thanks!
I'm not that up to speed on Devise as I only recently started using it myself, but if I understand correctly it's not a Devise related problem - just harder to get a fix on because of Devise's self.resource abstraction.
You've deviated from your tutorial in an important respect: in the tutorial a User creates a Company, but the Company has_many :users. In your case the User creates a LanguagesUser, but here, the User has_many :languages_users. This means new syntax. This line, that's causing your error currently:
self.resource.languages_user = LanguagesUser.new[sign_up_params]
Needs to be along the lines of:
self.resource.languages_users.build(sign_up_params) #but see below re sign_up_params
Or if you want to save the associated resource right off the bat (I assume not, since you're not at the moment), you can use create or create! instead of build.
Aside
You may run into different trouble with your sign_up_params method, which also appears to have deviated from the tutorial - it doesn't actually use the allow array to whitelist any params, at least as written in your question. In any case, note they didn't use it when instantiating the Company, so it may not be fit for purpose when building your LanguagesUser, either.
A simple call to sign_up_params[:languages_user_attributes] should get you over the line, once you've fixed the sign_up_params method. Or you can set the nested object up with its own params whitelist.
I have scaffold student name:string is_active:boolean and scaffold attendance student_id:integer event_id:integer
Student has_many :attendances
Attendance belongs_to :student
Attendances/_form.html.haml:
= simple_form_for(#attendance) do |f|
= f.error_notification
.form-inputs
= f.association :student
= f.association :event
.form-actions
= f.button :submit
How to edit the association dropdown to see there only students that have is_active : true?
You will want to separate your logic from your views. So, in the controller method that uses the form above you can define what you want to see in your drop down menu:
def controller_method
#active_students = Student.where(is_active: true)
end
and in your association in your form you can specify the drop down menu collection to be equal to #active_students:
f.association :student, collection: #active_students
Alternatively, in one line:
f.association :student, collection: Student.where(is_active: true)
I'm trying to save a parent object (Report) and the associated polymorphic child (Attachment) in one go - the association is a 'has_one'.
I'm using the '.create' method on the Report with a params hash containing the nested contents for the child, but I'm getting the error 'Validation failed: Attachment attachable can't be blank'.
What I have is (simplyfied):
Parent model:
Class Report
has_one :attachment, as: :attachable, :dependent => :destroy
attr_accessible :refdate, :link_name, :type_name, :attachment_attributes
accepts_nested_attributes_for :attachment
Child model:
Class Attachment
belongs_to :attachable, polymorphic: true
attr_accessible :file
validates_presence_of :attachable
validates_presence_of :file
Controller:
ReportsController
def create
#report = Report.create!(params[:report])
end
View (haml):
= form_for #report, html: { multipart: true } do |f|
= f.select :type_name
= f.text_field :link_name
= f.text_field :refdate
= f.fields_for :attachment_attributes, html: { multipart: true } do |p|
= p.file_field :file
= f.submit
Adapting the controller I can achieve that first the parent is saved to the db and afterwards the attachment is saved (here attachable is filled automatically by Rails), but I'd like to avoid this two-step save process, to make sure that either both are saved, or none of the two.
Does anyone have an idea?
Thanks!
I am new with Ruby on Rails. I just build web application on the existing database. I use rails to generate 2 scaffolds for restaurant and location tables. After that I set relationship for these two tables:
class Restaurant < ActiveRecord::Base
attr_accessible :created, :cuisine_fk, :dish_keywords, :dish_names, :factual_id, :first_name, :last_name, :name, :status
has_many :locations
end
class Location < ActiveRecord::Base
attr_accessible :address1, :address2, :city, :created, :latitude, :longitude, :phone, :restaurant_fk, :state, :status, :url, :zip
belongs_to :restaurant
end
I didn't use "rake db:migrate" after I set up this relationship for these tables, because I was afraid that this action would make changes the existing tables.
When I run this command line
<%= restaurant.location.address1%>
it shows error:
undefined method `location'
" NoMethodError in Restaurants#index
Showing C:/Sites/Dishclips/app/views/restaurants/index.html.erb where line #52 raised:
undefined method `location' for #<Restaurant:0x5853bb8> "
After that I tried to set foreign key for the file:
class Location < ActiveRecord::Base
attr_accessible :address1, :address2, :city, :created, :latitude, :longitude, :phone, :restaurant_fk, :state, :status, :url, :zip
belongs_to :restaurant, :class_name => "Restaurant", :foreign_key => 'restaurant_fk'
end
but it still doen't work.
Is there any way that we can set foreign keys in stead of using "rails db:migrate" after we set up the relationships for tables ? I appreciate your help a lot.
The problem is that you are using location wrongly.
Since the restaurant has_many locations you can't use it the way you mentioned. Because you have an array of locations, actually is a ActiveRecord relationship, so in order to access one of the items assciated you'll have to execute the query and get one of the elements. Here is an example of how to get the first element.
restaurant.locations.first.address1
If the restaurant have only one location, than you should change your model to
class Restaurant < ActiveRecord::Base
attr_accessible :created, :cuisine_fk, :dish_keywords, :dish_names, :factual_id, :first_name, :last_name, :name, :status
has_one :locations
end
and access the property as you are doing:
restaurant.location.address1
Also I'm assuming that your database have the columns you specified, otherwise you'll have to run the migrations.
Regards!
Rails associations are covered very well here in the Rails Guides.
I'll walk you through a basic setup here.
$ rails generate model Restaurant name owner ...
$ rails generate model Location restaurant_id:integer city ...
You then need to migrate your database with rake db:migrate for the database table changes to become effective.
The restaurant_id allows us to set the associations in our models as follows
class Restaurant < ActiveRecord::Base
has_many :locations, dependent: :destroy
attr_accessible :name, :owner
end
class Location < ActiveRecord::Base
belongs_to :restaurant
attr_accessible :city # no restaurant_id here
end
Now you can access your restaurants location as follows.
r = Restaurant.create!(name: '...')
l = Location.create!(city: '...')
# Add association
r.locations << l
r.locations will now return an Array with l in it
l.restaurant will return r
Try to play a little with the different styles of associations, for example by creating new Rails apps quickly and just trying some kind of associations, also some that require a join model.
Now I try this way, then it works. Thank you very much.
<td>
<% restaurant.locations.search(params[:restaurant_fk]).each do |location| %>
<!--address = <%= location.address1 %> + " " + <%= location.address2 %>-->
<%= location.address1 %>
<%= location.address2 %> ,
<%= location.city %> ,
<%= location.state %> ,
<%= location.zip %>
<% end %>
</td>