Look for help on Ruby on Rails - ruby

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>

Related

rails 4 multiple autocomplete with nested attributes

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.

NoMethodError - undefined method for a nested form from a join table using devise registration

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.

Set user_id for nested_attribute in devise registration before user exists

The idea is simple. I require a nested attribute tag for user registration. tag requires a user_id.
the view
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: {role: :form}) do |f| %>
<%= f.fields_for :tags, resource.tags.build do |a| %>
<%= a.text_field :tagname %>
<% end %>
<% end %>
tag migration
class CreateTags < ActiveRecord::Migration
def change
create_table :tags do |t|
t.string :tagname
t.references :user, index: true
t.timestamps
end
add_index :tags, :tagname, unique: true
end
end
the tag model
class Tag < ActiveRecord::Base
belongs_to :user
validates_presence_of :user_id
validates_uniqueness_of :tagname
end
the user model
class User < ActiveRecord::Base
has_many :tags, autosave: true, dependent: :destroy
accepts_nested_attributes_for :tags
end
strong parameters
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(
:tags_attributes => [:id, :user_id, :tagname]
) }
The save feature is untouched at the moment. The form passes the nested attribute :tagname just fine. But I've been unable to get the "would be" user_id from resource.
I've already looked for hours online for any answer to this. None has appeared, but the idea that the nested attribute should be saved after the initial user object is saved sounds like a workable solution. But then it's no longer handled as a nested attribute.
Help is appreciated! Thanks!
Solution
Alright I solved it. I bastardised my devise registrations controller a bit, but it works.
First I removed user_id verification from my tag model.
class Tag < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :tagname
end
I then proceeded with modifying the devise registration controller.
def create
build_resource(sign_up_params)
# added code begins here
#tag = nil
#tag.define_singleton_method(:valid?) {false}
if params["user"].has_key? "tags_attributes"
#tag = Tag.new(params["user"].delete("tags_attributes").values.first)
if #tag.valid?
resource_saved = resource.save # original line of code
else
resource.errors.add(*#tag.errors.first)
flash.delete :tagname_error
set_flash_message :alert, :tag_taken if is_flashing_format?
end
else
set_flash_message :alert, :need_tag if is_flashing_format?
end
# end added code
yield resource if block_given?
if resource_saved
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_flashing_format?
sign_up(resource_name, resource)
# This is where current_user has been created and resource.id == current_user.id
# begin added code
#tag.user_id= resource.id
# end added code
respond_with resource, location: after_sign_up_path_for(resource)
else
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format?
expire_data_after_sign_in!
respond_with resource, location: after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
#validatable = devise_mapping.validatable?
if #validatable
#minimum_password_length = resource_class.password_length.min
end
respond_with resource
end
end
I've defined :tag_taken and :need_tag in my devise locales file for translations of the error string.
And everything works!
For the record this is in addition to the existing code in the question.

Undefined method 'menu'

hi im new in ROR development im just wondering why my app rise a
"undefined method `menu'
i seems to associate my models right
i would like to show a menu that the reservation reserverd and show its recipes inside that menu but it rises undefiend method 'menu'
package_line_item.rb
belongs_to :menu
belongs_to :reservation
reservation.rb
has_one :reservation_package
belongs_to :service
has_many :reservation_function_rooms
has_many :package_line_items
has_many :menus , :through => :package_line_items, :uniq => true
has_many :function_rooms, :through =>:reservation_function_rooms
menu.rb
has_many :package_line_items
has_many :menu_recipes
has_many :recipes, :through => :menu_recipes, :uniq => true
belongs_to :menu_category
package_line_item_controller.rb
def index
#package_line_items = PackageLineItems.all
end
def show
#reservation = Reservation.includes(:package_line_items => :menu).find(params[:id])
end
def new
#reservation = Reservation.find(params[:reservation_id])
#package_line_item = #reservation.package_line_items.build
end
def create
#reservation = Reservation.find(params[:reservation_id])
#reservation.package_line_items.build(params[:package_line_item])
if #package_line_item.save
redirect_to #reservation ,:notice => "added menu"
end
routes.rb
resources :services
resources :reservations do
resources :reservation_packages
resources :reservation_function_rooms
resources :packages
resources :package_line_items
resources :package_crews
end
resources :function_rooms
resources :crews
resources :menu_categories
resources :menus do
resources :menu_recipes
end
ActiveAdmin.routes(self)
devise_for :admin_users, ActiveAdmin::Devise.config
resources :recipe_categories
resources :recipes
package_line_item/show.html.erb
<p id="notice"><%= notice %></p>
<%= #reservation.package_line_items.menu.name%>
if other file is needed feel free to ask me thank you more power to us thanks
It might be because "menu" is a method in the ActiveAdmin DSL. I had a problem with a model called "Page" precisely for this reason. Try renaming your model and see what happens.

Moving Query from View to Model

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.

Resources