I want to have URL like www.mydomain.com/category/parent/child so my routes are get '/category/:name(/:name)' => 'categories#show'
I got a table 'categories' like
> desc categories;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| public_id | int(11) | YES | | NULL | |
| name | varchar(255) | YES | | NULL | |
| root | int(11) | YES | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
I populate the database like this
Category.create(:public_id => 60, :name => "nues", :root => 0)
Category.create(:public_id => 61, :name => "artistiques", :root => 60)
Category.create(:public_id => 62, :name => "glamours", :root => 60)
Category.create(:public_id => 63, :name => "fetichiste", :root => 60)
Category.create(:public_id => 69, :name => "autres", :root => 60)
So if root = 0 so it's a parent category. If root > 0the category is a child from the root value
My Categorie_Controller show action. The problem is that categories got the same name like 'Nature -> Others' and 'Portrait -> Others'. So my find_by_name doesn't work properly
def show
if request.get?
#photographs = Category.find_by_name(params[:name]).photographs
end
end
Is-it possible to do what I want with my routes system ?
Don't pick the same name for the param variables.
# config/routes.rb
get '/category/:name(/:child_name)', to: 'categories#show'
# app/controllers/categories_controller.rb
def show
# Now params[:name] and params[:child_name] are available
# BTW: Only get requests will be routed anyways with your routes definition
#photographs = if params[:child_name].present?
parent = Category.find_by!(name: params[:name])
Category.find_by!(name: params[:child_name], root: parent.id)
else
Category.find_by!(name: params[:name])
end
end
Hope these hints help.
Additionally, you can see your incoming params by raising them in the controller action:
raise params.inspect
Related
I have 2 models which have 1 to 1 relationship, namely Project and Inspection, the structure is something like,
class Project < ApplicationRecord
has_one :inspection
def inspection_required?
(inspection || build_inspection).required?
end
end
class Inspection < ApplicationRecord
belongs_to :project
def required?
required_saved || API.check({ id: id})
end
end
As you see above, for a Project if we don't have an Inspection it does build_inspection. I looked at the database and it seems like the inspection record id skips the sequence as shown below,
ID | PROJECT_ID | VENDOR_TYPE | REQUIRED_SAVED | CREATED_AT | UPDATED_AT |
---|--------------|-------------|----------------|---------------------|----------------------|
1 | 7983f1a33d55 | vendor1 | false | 2020-09-12 00:43:52 | 2020-09-12 00:43:52 |
3 | asdereww224f | vendor2 | true | 2020-09-12 00:48:51 | 2020-09-12 00:48:51 |
4 | 3q3332reere1 | vendor1 | false | 2020-09-13 01:02:53 | 2020-09-13 01:02:53 |
7 | 1df343qe233a | vendor2 | true | 2020-09-14 02:43:51 | 2020-09-14 02:43:51 |
The ID column is skipping sequence.
So my question is, does build_inpsection is using the Sequence generated by Postgres SQL even when we don't save the record?
Rails version: Rails 5.2.3
Postgres version: 9.6
I want to call this in my console (ap is the awesome print gem):
ap Purchase.last(10)
but I get this error:
ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
It works like this:
irb(main):020:0> ap Purchase.last
#<Purchase:0x00007f86b792a320> {
:id => 28445,
:user_id => 10177,
:product_id => nil,
:product_type => nil,
:price => 9.0,
:gateway_code => nil,
:gateway_msg => nil,
:gateway_response => nil,
:created_at => Fri, 18 May 2018 22:20:10 UTC +00:00,
:updated_at => Fri, 18 May 2018 22:20:10 UTC +00:00,
:checkout_total => 9.0,
:successful => true,
:cart_id => 17242,
:report_errors => nil,
:transacted_value_of_products => 9.0,
:comp_credits_applied => 0.0
}
And without ap like this:
irb(main):022:0> Purchase.last(10)
D, [2018-05-25T20:58:54.692575 #70552] DEBUG -- : Purchase Load (0.5ms) SELECT "purchases".* FROM "purchases" ORDER BY "purchases"."id" DESC LIMIT $1 [["LIMIT", 10]]
+-------+---------+------------+-------------+-------+-------------+-------------+-------------+-------------+--------------+-------------+------------+---------+-------------+-------------+-------------+
| id | user_id | product_id | product_... | price | gateway_... | gateway_msg | gateway_... | created_at | updated_at | checkout... | successful | cart_id | report_e... | transact... | comp_cre... |
+-------+---------+------------+-------------+-------+-------------+-------------+-------------+-------------+--------------+-------------+------------+---------+-------------+-------------+-------------+
| 28436 | 10471 | | | 5.0 | | Completed | {"mc_gro... | 2018-05-... | 2018-05-1... | 5.0 | true | 17228 | {} | 5.0 | 0.0 |
| 28437 | 9754 | | | 1.99 | | Completed | {"mc_gro... | 2018-05-... | 2018-05-1... | 2.48 | true | 15273 | {} | 1.99 | 0.0 |
| 28438 | 10472 | | | 9.0 | | | {\n "id... | 2018-05-... | 2018-05-1... | 9.0 | true | 17231 | {} | 9.0 | 0.0 |
| 28439 | 10348 | | | 9.0 | | | | 2018-05-... | 2018-05-1... | 9.0 | true | 17235 | | 9.0 | 0.0 |
But not with an argument and ap
irb(main):021:0> ap Purchase.last(3)
ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
from (irb):21
It turns out I can't do much of anything:
irb(main):023:0> ap Purchase.find(28444)
ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
from (irb):23
irb(main):024:0> ap Purchase.find(28444).gateway_response
ActionController::UnfilteredParameters: unable to convert unpermitted parameters to hash
from (irb):24
What's going on?
What's Happening and Why
ActionController::Parameters (which is what params works with in controllers) used to inherit from HashWithIndifferentAccess which inherits from Hash. So ActionController::Parameters < Hash used to be true as would something like:
params.require(:x).permit(some_hash: %i[key1 key2]).is_a? Hash
If you were digging a Hash out of params:
some_hash = params.require(:x).permit(some_hash: ...)
and serializing that in a model:
class M < ApplicationRecord # Or ActiveRecord::Base in the past
serialize :h, Hash
end
#...
m.h = some_hash
you could end up with some YAML like this in your database:
--- !ruby/object:ActionController::Parameters
...
rather than the expected plain YAMLized hash.
But then Rails5 comes along and ActionController::Parameters no longer inherits from Hash:
Make ActionController::Parameters no longer inherits from HashWithIndifferentAccess.
and calling to_h or to_hash on an ActionController::Parameters now raises an exception.
If you upgrade your code and try to load a model with serialized data in it:
serialize :h, Hash
then the model will load the text from h, parse the YAML to get an ActionController::Parameters instance, and call to_h on it to make sure it has a Hash, and you get an exception.
What To Do About It
There are a couple things you need to do:
Fix your controllers to make sure they get real hashes out of params.
Fix your data so that you have serialized hashes rather than ActionController::Parameters instances.
Fixing the controllers is a simple matter of calling to_unsafe_h on the parameters that aren't yet real hashes.
Fixing the data is uglier. I'd probably go through the tables using the low level database interface (i.e. no ActiveRecord anywhere), read the YAML out from each row, YAML.load it, convert it to a hash by calling to_unsafe_h on it, and then writing back the_real_hash.to_yaml text. You could use a like '--- !ruby/object:ActionController::Parameters%' filter in the WHERE clause to only deal with the broken rows.
I'd also strongly recommend that you stop using serialize while you're there. serialize is a bit of a kludge and there's no sane way to work with YAML inside the database; there's also little need for it now that both PostgreSQL and MySQL have native JSON support (but I'm not sure how well ActiveRecord supports MySQL's JSON).
By default, the resources keyword of Rails routing creates 7 actions.
For example with resources :foos:
-----------------------
| Verb | Action |
-----------------------
| GET | index |
| GET | show |
| GET | edit |
| GET | new |
| PUT/PATCH | update |
| POST | create |
| DELETE | destroy |
-----------------------
How can we add inside this list the OPTIONS verb such as:
--------------------------------
| Verb | Action |
--------------------------------
| ... | ... |
| OPTIONS | member_options |
| OPTIONS | collection_options |
--------------------------------
In other words, by default, for each resource, we have to use something like this:
match '/foos/', via: :options, controller: 'foos', action: 'collection_options'
match '/foos/:id/', via: :options, controller: 'foos', action: 'member_options', as: :foo
resources :foos
Instead, I would prefer a custom settings in order to just do this:
resources :foos
To avoid listing every path for the OPTIONS verb, you could add a catch all route, e.g.
match '*path', to: 'application#cors_preflight_check', via: [:options]
I need to get all of the users from groups including subgroups:
app/indices/user_index.rb
ThinkingSphinx::Index.define :user, with: :active_record, delta: ThinkingSphinx::Deltas::SidekiqDelta do
has groups.id
has "CONCAT_WS('/',groups.id,groups.ancestry)", as: :group_ids, type: :integer, multi: true
end
But when i'm trying to search:
User.search_for_ids(with_all: { group_ids: [3] })
It returns all of the users from subgroups, but without users from group with id 3
Sphinx 2.1.7 running undex arch linux
Thinking sphinx v3.1.1
Finally found out the problem:
has "CONCAT_WS('/',groups.id,groups.ancestry)", as: :group_ids, type: :integer, multi: true
was returning only 1 id or ancestry per group, meaning if user has few root groups, e.g 3,5
the expression above will return only 1 group:
+----+--------+-----------+
| id | groups | group_ids |
+----+--------+-----------+
| 39 | 5 | 5/3 |
| 40 | 245,3 | 245 |
| 42 | 5 | 5/3 |
| 43 | 234 | 234/3/5 |
and the user with id 40 was was not finding. But, if you notice, everything works fine for groups column. The solution is to use group concat:
has "CONCAT_WS('/',GROUP_CONCAT(DISTINCT groups.`id` SEPARATOR '/'), GROUP_CONCAT(groups.`ancestry` SEPARATOR '/') )", as: :group_ids, type: :integer, multi: true
Result:
+----+--------+-----------+
| id | groups | group_ids |
+----+--------+-----------+
| 39 | 5 | 5/3 |
| 40 | 245,3 | 245/3 |
| 42 | 5 | 5/3 |
| 43 | 234 | 234/3/5 |
Also, it works fine with "/" separator.
I want to update values taken from a single form in two different model at the same time, I have written the following code to do so :-
if #mess.update_attributes!(:mess_name => params[:mess_name_update], :mess_capacity => params[:mess_capacity_update]) && #mess_price.update_attributes!(:breakfast_charge => params[:mess_breakfast_charge_update], :lunch_charge => params[:mess_lunch_charge_update], :dinner_charge => params[:mess_dinner_charge_update], :monthly_charge => params[:mess_monthly_charge_update], :semesterly_charge => params[:mess_semesterly_charge_update], :start_date => params[:start_date_update], :end_date => params[:end_date_update], :rebate => params[:rebate_update])
flash[:success] = "Mess Details Updated Successfully!!"
else
flash[:error] = "Some Error! Please Try Again!"
end
But the above code is giving following error
ActiveRecord::RecordInvalid
Validation failed: Start date can't be blank
Following is the two schema I am using, for #mess its MessType model and for #mess_price its MessPrice model:
MessType
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| mess_id | int(11) | NO | PRI | NULL | auto_increment |
| mess_name | varchar(255) | NO | | NULL | |
| mess_capacity | int(11) | NO | | NULL | |
| start_date | date | No | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
MessPrice
+-------------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| breakfast_charge | float | YES | | NULL | |
| lunch_charge | float | YES | | NULL | |
| dinner_charge | float | YES | | NULL | |
| monthly_charge | float | YES | | NULL | |
| semesterly_charge | float | YES | | NULL | |
| rebate | float | YES | | NULL | |
| start_date | date | YES | | NULL | |
| end_date | date | YES | | NULL | |
| mess_id | int(11) | YES | MUL | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+-------------------+----------+------+-----+---------+----------------+
I recommend installing the awesome_print gem - https://github.com/michaeldv/awesome_print
# add to Gemfile
gem 'awesome_print'
# install
bundle
Then the first thing in your controller action do
logger.debug " -----------"
logger.ap params
logger.debug " -----------"
Check your log file log/development.log for the output, it could be the params are coming across correctly but not what you expect?
some of the attributes might be nested in another hash key and need to be accessed via params[:something][:xyz]
I would also recommend making the code more readable and running it in a transaction
#mess.mess_name = params[:mess_name_update]
#mess.mess_capacity = params[:mess_capacity_update]
#mess_price.breakfast_charge = params[:mess_breakfast_charge_update]
#mess_price.lunch_charge = params[:mess_lunch_charge_update]
#mess_price.dinner_charge = params[:mess_dinner_charge_update]
#mess_price.monthly_charge = params[:mess_monthly_charge_update]
#mess_price.semesterly_charge = params[:mess_semesterly_charge_update]
#mess_price.start_date = params[:start_date_update]
#mess_price.end_date = params[:end_date_update]
#mess_price.rebate = params[:rebate_update]
# NOTE: an alternative to above is to name your html input fields the rails way
# so that params are sent in a nested hash, i.e. "mess_price[start_date]" -> params[:mess_price][:start_date]
# then you can do #mess_price.update_attributes(params[:mess_price])
# using form_for html helper will automatically apply this style of naming to html input fields
Mess.transaction do
# you might want save! vs save - depends if you show validation error in the UI or not
if #mess.save && #mess_price.save
flash[:success] = "Mess Details Updated Successfully!!"
else
# don't save changes, show validation errors
raise ActiveRecord::Rollback
flash[:error] = "Some Error! Please Try Again!"
end
end