Adding Sub-categories in Rails4 - ruby-on-rails

I have a lot of Main Categories and want to add to each sub categories.->
Main Category
-Sub Category
-Sub Category
-Sub Category
Main Category
-Sub Category
-Sub Category
-Sub Category
A lot of people recommend me to use a gem, but as i am fairly new to Rails i rather learn how to do this on my own, to learn all they way through.
Should i start with a Scaffold or simply a Model ?
Could someone explain me how to start (migrations etc.) and how to set it up?
Thank you.

You have to generate a new model with rails g model category, then edit the file generate in db/migrate and write this
class CreateCategories < ActiveRecord::Migration
def change
create_table :categories do |t|
t.belongs_to :category
t.string :name, :null => false
t.timestamps
end
end
end
And edit app/models/category.rb
class Category < ActiveRecord::Base
belongs_to :category
has_many :children, :dependent => :destroy, :class_name => 'Category'
end
And you have to execute rake db:migrate for create the table in your database.
EDIT:
In app/controllers/categories_controller.rb
class CategoriesController < ApplicationController
def index
#categories = Category.all
end
def new
#category = Category.new
end
def edit
#category = Category.find(params[:id])
end
def create
#category = Category.new(params[:category].permit!)
if #category.save
redirect_to categories_url
else
render :new
end
end
def update
#category = Category.find(params[:id])
if #category.update_attributes(params[:category].permit!)
redirect_to categories_url
else
render :edit
end
end
def destroy
Category.destroy(params[:id])
redirect_to categories_url
end
end
And the form for your categories:
<%= form_for #category do |f| %>
<%= f.text_field :name %>
<%= f.select :category_id, options_from_collection_for_select(Category.all, :id, :name, #category.category_id), :include_blank => true %>
<%= f.submit %>
<% end %>

Related

Issues with creating a "many-to-many" relation

I'm trying to do a simple task : I created a "magazine" scaffold, but I want it to have a specific relation : a user can have participate for the creation / redaction of a magazine, and it can take several users to create a magazine.
I checked the APIDock and did the following:
Specified the relation between the magazines and the users
model/magazine.rb
class Magazine < ApplicationRecord
mount_uploader :thumbnail, ThumbnailUploader
has_and_belongs_to_many :users
end
model/user.rb
class User < ApplicationRecord
has_and_belongs_to_many :magazines
# More code...
end
Created a migration to add a table to link both the models
class ManyToMany < ActiveRecord::Migration[5.0]
def change
create_table :magaziness_users, :id => false do |t|
t.integer :user_id
t.integer :magazine_id
end
add_index :magazines_users, [:magazine_id, :user_id]
end
end
Then I ran the migration
Added the list of all users ever recorded to the database to create a dropdown
<div class="field">
<%= f.label :users %>
<%= f.select :users, User.all_except(current_user).collect {|u| [u.username, u]}, {prompt: 'Add a creator?'}, { :multiple => true, :size => 3 } %>
</div>
But, when I'm saving a new magazine, the user doesn't get saved, and the "magazines_user remains empty.
edit 1
This is an auto-generated controller, since I use the scaffold command to create it. I didn't touch anything excepted the set_magazine function, where I added the Friendly_Id
class MagazinesController < ApplicationController
before_action :set_magazine, only: [:show, :edit, :update, :destroy]
def index
#magazines = magazine.all
end
def show
end
def new
#magazine = magazine.new
end
def edit
end
def create
#magazine = magazine.new(magazine_params)
if #magazine.save
redirect_to #magazine, notice: 'magazine was successfully created.'
else
render :new
end
end
def update
if #magazine.update(magazine_params)
redirect_to #magazine, notice: 'magazine was successfully updated.'
else
render :edit
end
end
def destroy
#magazine.destroy
redirect_to magazines_url, notice: 'magazine was successfully destroyed.'
end
private
def set_magazine
#magazine = magazine.friendly.find(params[:id])
end
def magazine_params
params.require(:magazine).permit(:titre, :description, :apercu, :users)
end
end
Did I forget any step?
so here is the answer with my code working:
I did two scaffolds:
rails generate scaffold user username:string email:uniq password:digest
rails generate scaffold magazine title:string description:text preview:string
Then added this to magazine migration:
create_table :magazines_users, id: false do |t|
t.belongs_to :magazine, index: true
t.belongs_to :user, index: true
end
In my form, I added:
<div class="field">
<%= f.label :users %>
<%= f.select :user_ids, User.all.collect { |u| [u.username, u.id] }, {include_blank: true}, {multiple: true} %>
</div>
And in my magazines controller I only modified magazine_params:
def magazine_params
params.require(:magazine).permit(:title, :description, :preview, :user_ids => [])
end
To see that it works, I added this in magazin show view:
<p>
<strong>Users:</strong>
<%= #magazine.users.map(&:username).join(" - ") %>
</p>
Of course I added "has_and_belongs_to_many" as you did in User and Magazine models.
And that's it :)
Tested with Rails 5 and it works just fine. :)
Also I strongly advice you to take a look at the simple_form gem. It has some great methods to handle associations (like has_and_belongs_to_many) easily, like this : <%= f.association :users, collection: User.all_except(current_user).order(:username) %>

Uninitialized constant many_to_many association in Rails4

I have 2 model with the association many_to_many. When creating a brand I have error:
NameError in Admin::Brands#new
Showing /.../app/views/admin/brands/_form.html.slim where line #3 raised:
uninitialized constant Brand::BrandCatalog
What am I doing wrong?
#app/models/category.rb
class Category < ActiveRecord::Base
has_many :brand_catalogs
has_many :brands, through: :brand_catalogs
end
#app/models/brand.rb
class Brand < ActiveRecord::Base
has_many :brand_catalogs
has_many :categories, through: :brand_catalogs
end
#app/models/brandcatalog.rb
class BrandCatalog < ActiveRecord::Base
belongs_to :category
belongs_to :brand
end
migration
#db/migrate/20151230092013_create_brand_catalogs.rb
class CreateBrandCatalogs < ActiveRecord::Migration
def change
create_table :brand_catalogs, id: false do |t|
t.integer :category_id
t.integer :brand_id
end
add_index :brand_catalogs, [:category_id, :brand_id]
end
end
brands controller
#app/controllers/admin/brands_controller.rb
class Admin::BrandsController < Admin::BaseController
before_action :require_login
load_and_authorize_resource
before_action :load_brand, only: [:edit, :update, :destroy]
def index
#brands = Brand.all
end
def new
#brand = Brand.new
end
def edit
end
def create
#brand = Brand.create(brand_params)
if #brand.save
redirect_to admin_brands_path, notice: 'Brand was successfully created.'
else
render :new, notice: 'Something wrong!'
end
def update
end
def destroy
end
private
def load_brand
#brand = Brand.find(params[:id])
end
def brand_params
params.require(:brand).permit(:title, {category_ids: []})
end
end
brands form
# views/admin/brands/_form.html.slim
= bootstrap_form_for [:admin, #brand] do |f|
div class='form-group'
= f.collection_check_boxes(:category_ids, Category.all, :id, :title)
div class='form-group'
= f.text_field :title, class: 'form-control'
= f.submit 'Save', class: 'btn btn-success'
Try renaming your #app/models/brandcatalog.rb file to brand_catalog.rb. Modelnames needs to have matching filenames but with underscore instead of camelcase.
For example a model called ThisIsMyModel should have its file named this_is_my_model.rb

Dealing With Multiple Objects

I am new to Rails and currently trying to add a patient to an existing dentist appointment. I am having difficulty setting up my views and controllers properly. How can I properly accomplish this?
Note: With the way I have set things up, I can create an appointment and tie it to a dentist. Of course, the patient_id is missing.
Models:
class Dentist < ActiveRecord::Base
has_many :appointments
has_many :patients, :through => :appointments
end
class Appointment < ActiveRecord::Base
belongs_to :dentists
belongs_to :patients
end
class Patient < ActiveRecord::Base
has_many :appointments
has_many :dentists, :through => :appointments
end
Schema:
ActiveRecord::Schema.define(version: 20151107052115) do
create_table "appointments", force: :cascade do |t|
t.integer "dentist_id"
t.integer "patient_id"
t.datetime "appt_date"
end
create_table "dentists", force: :cascade do |t|
t.string "name"
end
create_table "patients", force: :cascade do |t|
t.string "name"
end
end
Routes:
Rails.application.routes.draw do
concern :commentable do
resources :appointments
end
resources :dentists, concerns: :commentable
resources :patients, concerns: :commentable
end
Dentists Controller:
class DentistsController < ApplicationController
def new
#dentist = Dentist.new
end
def create
#dentist = Dentist.new(dentist_params)
if #dentist.save
redirect_to dentists_path
else
render :new
end
end
...
end
Appointments Controller:
class AppointmentsController < ApplicationController
def new
#dentist = Dentist.find(params[:dentist_id])
#appointment = #dentist.appointments.new
end
def create
#dentist = Dentist.find(params[:dentist_id])
#appointment = #dentist.appointments.new(appt_params)
if Appointment.exists?(:appt_date => #appointment.appt_date)
render :new
else
#appointment.save
redirect_to dentist_path(#dentist)
end
end
...
end
Patients Controller:
TBD
Dentists View (Show):
<p><%= #dentist.name %> DDS</p>
<% if #dentist.appointments.any? %>
<% #dentist.appointments.each do |appt| %>
<ul>
<li><%= appt.appt_date %></li>
<p><%= link_to "Edit", edit_dentist_appointment_path(#dentist, appt) %> |
<%= link_to 'Delete', dentist_appointment_path(#dentist, appt), :method => :delete,
data: {:confirm => 'Are you sure you want to delete this record?'} %> |
<%= link_to 'Add Patient', new_patient_path %></p>
</ul>
<% end %>
<% else %>
<p>There are currently no appointments scheduled</p>
<% end %>
<p><%= link_to 'Delete Dentist', dentist_path(#dentist), :method => :delete,
data: {:confirm => 'Are you sure you want to delete this record?'} %></p>
<p><%= link_to 'Create an appointment', new_dentist_appointment_path(#dentist) %></p>
<p><%= link_to 'Return to list', root_path %></p>
I am new to Rails
Welcome!
You need to change your belongs_to references to be singular:
class Appointment < ActiveRecord::Base
belongs_to :dentist
belongs_to :patient
end
--
Because I can't see where you're trying to achieve this functionality, I'll show you what I'd do (using the appointment#edit action):
#app/controllers/appointments_controller.rb
class AppointmentsController < ApplicationController
def edit
#appointment = Appointment.find params[:id]
end
def update
#appointment = Appointment.find params[:id]
#appointment.save appointment_params
end
private
def appointment_params
params.require(:appointment).permit(:dentist_id, :patient_id, :appt_date)
end
end
#app/views/appointments/edit.html.erb
<%= form_for #appointment do |f| %>
<%= f.collection_select :patient_id, Patient.all, :id, :name %>
<%= f.submit %>
<% end %>
--
If you're trying to set the patient from your appointments#create method, you'll be best doing this:
#app/controllers/appointments_controller.rb
class AppointmentsController < ApplicationController
def new
#dentist = Dentist.find params[:id]
#appointment = #dentist.appointments.new
end
def create
#dentist = Dentist.find params[:id]
#appointment = #dentist.appointments.new appointment_params
end
private
def appointment_params
params.require(:appointment).permit(:dentist_id, :patient_id, :appt_date)
end
end
#app/views/appointments/new.html.erb
<%= form_for #appointment do |f| %>
<%= f.collection_select :patient_id, Patient.all, :id, :name %>
<%= f.submit %>
<% end %>
I think what you are asking, is can you create an appointment through both the Dentist model and the Patient model at the same time eg. #dentist.#patient.apointment.new
you cannot do that. Based on the relationships you have set up, you will either want to create the appt from the Dentist, like you have now, and pass in the patient ID as an attribute, or vice-versa. OR, create through your Appointment model eg. Appointment.new(dentist: #dentist, patient: #patient, ...)

Rails: Globalize3 and batch_translations

I'm using rails 3.2.8, globalize3 and batch_translations to translate specific content for an little cms and shop system.
I integrated it without problems for one translation on one model. So everthings works fine. I started adding this functionality for my other models and..shhhhh weird things happen.
Status now: I can create new content with translations. Everything ok. But if I try to edit/update the values in the translations tables nothing happen! Maybe there is a wrong parameter path in batch_translations or something...
Here an example for categories!
migration_file
class CreateCategories < ActiveRecord::Migration
def self.up
create_table :categories do |t|
t.timestamps
end
Category.create_translation_table! :category_name => :string, :description => :string
end
def self.down
Category.drop_translation_table!
drop_table :categories
end
end
model:
class Category < ActiveRecord::Base
attr_accessible :category_name, :description
attr_accessible :translations_attributes
translates :category_name, :description
has_many :translations
accepts_nested_attributes_for :translations
class Translation
attr_accessible :locale, :category_name, :description
end
end
this weird class Translation I wrote because I got mass-assignemnt errors for locale, etc...
form:
<div>
<%= form_for #category, html: { :multipart => true } do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= build_translation_text_field f, :category_name, :description %>
<%= f.submit (t ".save"), class: "btn btn-large btn-primary" %>
<% end %>
</div>
helper for my translation form:
def build_translation_text_field(f, *atts)
tag = content_tag(:h1, "Test")
I18n.available_locales.each do |l|
f.globalize_fields_for l do |g|
atts.each do |a|
tag += content_tag(:div, content_tag(:h4, t(a)+":"))
tag += (g.text_field a, class: l)
end
end
end
tag
end
categories_controller update method:
def create
#category = Category.new(params[:category])
if #category.save
#categories = Category.all
flash[:success] = t(:category_created)
respond_to do |format|
format.html {render 'index'}
format.js
end
else
flash[:error] = #category.errors.full_messages.each {|msg| p msg}
#categories = Category.all
respond_to do |format|
format.html {render 'new'}
format.js
end
end
end
def update
#category = Category.find(params[:id])
if #category.update_attributes(params[:category])
#categories = Category.all
flash[:success] = t(:category_updated)
respond_to do |format|
format.html {render 'index'}
format.js
end
else
flash[:error] = #category.errors.full_messages.each {|msg| p msg}
#categories = Category.all
respond_to do |format|
format.html {render 'edit'}
format.js
end
end
end
anyone an idea or have an working example with two models whit one or more translated attributes?
My fault:
Update for the model:
class Category < ActiveRecord::Base
attr_accessible :category_name, :description
attr_accessible :translations_attributes
translates :category_name, :description
# has_many :translations <-- delete this
accepts_nested_attributes_for :translations
class Translation
attr_accessible :locale, :category_name, :description
end
end

Using rails3-jquery-autocomplete to avoid duplicate entries?

I'm using the rails3-jquery-autocomplete gem my artist field on a releases form and want to make sure any new releases use the id of an existing artist if found via autocomplete or create a new artist if not found. At the moment the autocomplete gem returns artists as expected but created a new entry even if that artist exists.
My models are as follows:
class Artist < ActiveRecord::Base
has_and_belongs_to_many :releases
end
class Release < ActiveRecord::Base
has_and_belongs_to_many :artists
accepts_nested_attributes_for :artists, :reject_if => lambda { |a| a[:name].blank? }
end
And controllers:
class ArtistsController < ApplicationController
def index
#artists = Artist.find(:all, :order => :name, :group => :name)
end
def create
#release = Release.find(params[:release_id])
#artist = #release.artists.create(params[:artist])
redirect_to release_path(#release)
end
def destroy
#release = Release.find(params[:release_id])
#artist = #release.artists.find(params[:id])
#artist.destroy
redirect_to release_path(#release)
end
end
class ReleasesController < ApplicationController
autocomplete :artist, :name
def new
#release = Release.new
#release.artists.build
end
def create
#release = Release.new(params[:release])
#release.user_id = current_user.id
end
end
My routes contains:
resources :releases do
get :autocomplete_artist_name, :on => :collection
end
And finally the artist fields:
<%= f.fields_for :artists do |builder| %>
<%= render 'artist_fields', :f => builder %>
<% end %>
(The rendered fields)
<%= f.label :name, "Artist" %><br />
<%= f.autocomplete_field :name, autocomplete_artist_name_releases_path, :class => "text" %>
I suggest you watch this railscast on autcomplete associations
It clearly does what you need.
I don't want to spoil your viewing pleasure, but it all revolves around find_or_create_by
Why don't you fetch the id of the artist with
:id_element => '#artist_id' on the f.autocomplete_field?
If the artist_id param does not come back, you can create it

Resources