Rails New Field Not Appear - activerecord

I just got an issue after I've done a migration for a table, I've added owner field to the project table, but it is not appear when I request it (project).
Expected response after migration:
{
projects: [
{ id: 1, name: "First proj", owner: 1 },
{ id: 2, name: "Second proj", owner: 1 }
]
}
I got:
{
projects: [
{ id: 1, name: "First proj" },
{ id: 2, name: "Second proj" }
]
}
Here is my migration file
class AddOwnerRefToProjects < ActiveRecord::Migration
def change
add_reference :projects, :owner, index: true
end
end
My projects model
class Project < ActiveRecord::Base
belongs_to :owner, :class_name => :User
end

You need to add the new attribute in your json serializer.

Related

Rails nested attributes validation force save parent firstly

I have 2 models like:
class Father < ActiveRecord::Base
has_many :children
accepts_nested_attributes_for :children
end
class Child < ActiveRecord::Base
belongs_to :father
validate :validate_money
def validate_money
children = Child.where(id: self.father.id)
sum_of_children_pocket_money = my_func # function for getting sum of all pocket money of all children
if sum_of_children_pocket_money > self.father.money
errors.add(:pocket_money, "My error message!!!")
end
end
end
and initially I have:
and when pass passengers_attributes to update customer (id=1) like
{
id: 1,
name: "Father 1",
money: 1000,
children: [
{
name: "Child 1",
id: 1,
pocket_money: 500
}
]
}
and then I am sending for update:
{
id: 1,
name: "Father 1",
money: 2000,
children: [
{
name: "Child 1",
id: 1,
pocket_money: 1500
}
]
}
So I am getting an error in my validation because it starts sum_of_children_pocket_money > self.father.money comparison and sum_of_children_pocket_money is equal to 1500 but self.father.money still 1000. How can I fix this?
Moving the validation logic from Child to Father should solve the problem.
class Father < ActiveRecord::Base
has_many :children
accepts_nested_attributes_for :children
validate :validate_money
def validate_money
children = Child.where(id: id)
sum_of_children_pocket_money = my_func # function for getting sum of all pocket money of all children
if sum_of_children_pocket_money > money
errors.add(:pocket_money, "My error message!!!")
end
end
end
class Child < ActiveRecord::Base
belongs_to :father
end

Get a belongs_to relationship from class Ruby on Rails

Here are my models:
class OrderItem < ApplicationRecord
belongs_to :order
belongs_to :product
end
class Order < ApplicationRecord
has_many :order_items
end
Here is my controller:
def index
orders = Order.where(nil)
render json: {'orders': orders}, include:
['order_items'], status: :ok
end
I want to also include the product in the order_items. How can I achieve this to get the following JSON:
{
"id": 2,
"order_items": [
{
"id": 1,
"product": {
"name": "abc"
},
}
]
},
You can reach this with changing
include: ['order_items']
to
include: ['order_items', 'order_items.product'].
More details you can get here.
I have been able to solve this by changing include: ['order_items'] to include: {'order_items': {include: 'product'}}

How to fix an API using jsonapi-resources that is responding with mismatch type and not allowing me to create records?

I have a problem with Mismatch types in my API everytime I want to create a new item.
It happens in 2 places:
when I try to POST a new item
POST http://localhost:8060/datasets/
{
"data": {
"type": "datasets",
"attributes": {
"doi": "10.5259/2008120816KAKA",
"version": 0,
"is-active": "1",
"datacentre": 201
}
}
Response
"errors": [
{
"title": "Internal Server Error",
"detail": "Internal Server Error",
"code": "500",
"status": "500",
"meta": {
"exception": "Datacentre(#23863440) expected, got 201 which is an instance of Fixnum(#4211740)",
"backtrace": [
"/usr/local/rvm/gems/ruby-2.3.3/gems/activerecord-5.1.1/lib/active_record/associations/association.rb:239:in `raise_on_type_mismatch!'",
"/usr/local/rvm/gems/ruby-2.3.3/gems/activerecord-5.1.1/lib/active_record/associations/belongs_to_association.rb:11:in `replace'",
"/usr/local/rvm/gems/ruby-2.3.3/gems/activerecord-5.1.1/lib/active_record/associations/singular_association.rb:15:in `writer'",
"/usr/local/rvm/gems/ruby-2.3.3/gems/activerecord-5.1.1/lib/active_record/associations/builder/association.rb:119:in `datacentre='"
I can see it happening when accessing the relationships of an item. For example this is a a GET response:
GET http://localhost:8060/datasets/
{
"data": [
{
"id": "5",
"type": "datasets",
"links": {
"self": "http://localhost:8060/datasets/5"
},
"attributes": {
"created": "2011-03-02T16:41:20.000Z",
"doi": "10.5259/20070410230000/HTTP://www.STAFFSPASTTRACK.ORG.UK/EXHIBIT/CRIMEANDPUNISHMENT/IMAGEPAGE/ZERO.HTM",
"version": 0,
"is-active": "",
"updated": "2011-03-02T16:41:20.000Z",
"datacentre": "#<Datacentre:0x000000033389b8>",
"deposited": "2012-07-31T09:12:37.000Z"
},
"relationships": {
"datacentre": {
"links": {
"self": "http://localhost:8060/datasets/5/relationships/datacentre",
"related": "http://localhost:8060/datasets/5/datacentre"
}
}
}
}
You can see how the datacentre (foreign_key) attribute is presented as an object type of the Model Datacentre. If I try to access the relantship to that model http://localhost:8060/datasets/5/datacentre I get the following:
{
* errors: [
* {
* title: "Internal Server Error",
* detail: "Internal Server Error",
* code: "500",
* status: "500",
* meta: {
* exception: "Unknown source type #<Datacentre id: 10015, comments: "", contact_email: "andrew.jackson#bl.uk", contact_name: "Andy Jackson", created: "2010-12-14 22:05:32", doi_quota_allowed: 50000, doi_quota_used: 17, domains: "webarchive.org.uk", is_active: "\x01", name: "Web Archive Programme at BL", password: "98583c1bf114bfe80105f906d800e05325307f06b93ccee6b6...", role_name: "ROLE_DATACENTRE", symbol: "BL.WAP", updated: "2012-02-17 11:49:01", version: 2, allocator: 106, experiments: nil>",
* backtrace: [
* "/usr/local/rvm/gems/ruby-2.3.3/gems/jsonapi-resources-0.9.0/lib/jsonapi/resource_serializer.rb:267:in `top_level_source_key'",
* "/usr/local/rvm/gems/ruby-2.3.3/gems/jsonapi-resources-0.9.0/lib/jsonapi/resource_serializer.rb:47:in `block in serialize_to_hash'",
* "/usr/local/rvm/gems/ruby-2.3.3/gems/jsonapi-resources-0.9.0/lib/jsonapi/resource_serializer.rb:47:in `map'",
* "/usr/local/rvm/gems/ruby-2.3.3/gems/jsonapi-resources-0.9.0/lib/jsonapi/resource_serializer.rb:47:in `serialize_to_hash'",
* "/usr/local/rvm/gems/ruby-2.3.3/gems/jsonapi-resources-0.9.0/lib/jsonapi/response_document.rb:109:in `results_to_hash'",
* "/usr/local/rvm/gems/ruby-2.3.3/gems/jsonapi-resources-0.9.0/lib/jsonapi/response_document.rb:12:in `contents’”,
I think the problem is that the API is expecting the wrong type of object. It’s expecting Datacentre when it should be expecting DatacentreResource.
My setup is as follows:
I have a Legacy database that doesn’t follow the ActiveRecord conventions for naming tables and foreign_keys.
Tables are singular and foreign_keys do not have the _id suffix.
The tables/models in which I have the problem have one_to_many relationship.
The relationship being a datacentre has_many datasets.
I am using jsonapi-resources and rails 5 api-only.
Datacentre Model
class Datacentre < ApplicationRecord
self.table_name = "datacentre"
alias_attribute :allocator_id, :allocator
has_and_belongs_to_many :prefixes, class_name: 'Prefix', join_table: "datacentre_prefixes", foreign_key: :prefixes, association_foreign_key: :datacentre
belongs_to :allocator, class_name: 'Allocator', foreign_key: :allocator
has_many :datasets
end
Dataset Model
class Dataset < ApplicationRecord
self.table_name = "dataset"
alias_attribute :datacentre_id, :datacentre
belongs_to :datacentre, class_name: 'Datacentre', foreign_key: :datacentre
end
Datacentre Resource
class DatacentreResource < JSONAPI::Resource
model_name 'Datacentre'
model_hint model: Datacentre
attributes :comments, :contact_email, :contact_name, :created, :doi_quota_allowed, :doi_quota_used, :domains, :is_active, :name, :password, :role_name, :symbol,
:updated, :version, :experiments, :allocator
has_many :datasets
has_many :prefixes
has_one :allocator, class_name: 'Allocator', foreign_key: :allocator
end
Dataset Resource
class DatasetResource < JSONAPI::Resource
model_name 'Dataset'
model_hint model: Dataset
attributes :created, :doi, :version, :is_active, :updated, :datacentre
attribute :deposited
has_one :datacentre, class_name: "Datacentre", foreign_key: :datacentre
end
So far I have got around the first problem (i.e. accessing the relationship) by modifying the methods for datacentre and its alias datacentre_id in the Dataset Resource
def datacentre(context=nil)
DatacentreResource.find_by_key(#model.datacentre.id)
end
def datacentre_id()
#model.datacentre.id
end
But this doesn’t solve the POST problem.
I think this is the thing that cause the error
in
class Dataset < ApplicationRecord
self.table_name = "dataset"
alias_attribute :datacentre_id, :datacentre
belongs_to :datacentre, class_name: 'Datacentre', foreign_key: :datacentre
end
datacentre_id and datacentre are aliased so it gives back and object datacentre back in response instead of datacentre_id
probably it will wokr if you remove this line

How to populate the response model for http_code 400/401 in swagger ui using ruby?

How to populate the response model for http_code 400/401 in swagger ui using ruby? I want to add the response model for 401 error.My code looks something like this:
user_entity.rb:
module something
module V1
class UserEntity < Grape::Entity
expose :id, documentation: { type: "String", desc: "User id" }
expose :phone, documentation: { type: "String", desc: "Registered phone number" }
expose :email, documentation: { type: "String", desc: "Email" }
expose :created_at, documentation: { type: "String", desc: "format:yyyy-mm-ddThh:mm:ss.364+(gmt) for eg:\"2015-10-04T15:33:39.364+04:00\"" }
expose :updated_at, documentation: { type: "String", desc: "format:yyyy-mm-ddThh:mm:ss.364+(gmt) for eg:\"2015-10-04T15:33:39.364+04:00\"" }
expose :access_token, if: lambda { |object, options| options[:show_access_token] == true }
expose :access_token_expires, if: lambda { |object, options| options[:show_access_token] == true }
private
def id
object.id.to_s
end
end
end
end
user_resource.rb:
module something
module V1
class UsersResource < Grape::API
include something::V1::Defaults
resource :users, desc: 'Operations about users' do
desc "Returns all users", {
headers: {
"Authorization" => {description: "pass the access token as Bearer",
required: true
}
},
http_codes: [
[401, 'Not authorized: The access token does not exist or is invalid'],
[200, 'Success',UserEntity]
],
detail: 'This endpoint returns all the users that have successfully registered with a valid phone number.'
}
get do
User.all.to_a
end
.....
I figured it out myself. I just added all the error codes i needed in my defaults.rb and added it to my http_codes

elastic search object association querying through params

I'm having some difficulty with Elastic Search and Tire not returning any results. I'm using Ruby 1.9.3 and Rails 3.2.11.
In my controller I'm calling:
#location_id = 1
#listings = Listing.search(params.merge!(location_id: #location_id))
In my listing model I have
mapping do
indexes :id, type: 'integer'
...
indexes :author do
indexes :location_id, :type => 'integer', :index => :not_analyzed
...
end
def self.search(params={})
tire.search(load: true, page: params[:page], per_page: 20) do |search|
search.query { string params[:query], :default_operator => "AND" } if params[:query].present?
search.filter :range, posted_at: {lte: DateTime.now}
search.filter :term, "author.location_id" => params[:location_id]
end
I have 300 results which all have the location_id of 1 in the database so I can't seem to figure out why it's returning a nil set? If I comment out the author.location_id search filter line it returns all other results as expected?
There are several things which needs to be adressed in a situation like yours. Let's start with a fully working code:
require 'active_record'
require 'tire'
require 'logger'
# Tire.configure { logger STDERR }
# ActiveRecord::Base.logger = Logger.new(STDERR)
Tire.index('articles').delete
ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: ":memory:" )
ActiveRecord::Schema.define(version: 1) do
create_table :articles do |t|
t.string :title
t.integer :author_id
t.date :posted_at
t.timestamps
end
create_table :authors do |t|
t.string :name
t.integer :number, :location_id
t.timestamps
end
add_index(:articles, :author_id)
add_index(:authors, :location_id)
end
class Article < ActiveRecord::Base
belongs_to :author, touch: true
self.include_root_in_json = false
include Tire::Model::Search
include Tire::Model::Callbacks
mapping do
indexes :title
indexes :author do
indexes :location_id, type: 'integer'
end
end
def self.search(params={})
tire.search load: {include: 'author'} do |search|
search.query do |query|
query.filtered do |f|
f.query { params[:query].present? ? match([:title], params[:query], operator: 'and') : match_all }
f.filter :range, 'posted_at' => { lte: DateTime.now }
f.filter :term, 'author.location_id' => params[:location_id]
end
end
end
end
def to_indexed_json
to_json( only: ['title', 'posted_at'], include: { author: { only: [:location_id] } } )
end
end
class Author < ActiveRecord::Base
has_many :articles
after_touch do
articles.each { |a| a.tire.update_index }
end
end
# -----
Author.create id: 1, name: 'John', location_id: 1
Author.create id: 2, name: 'Mary', location_id: 1
Author.create id: 3, name: 'Abby', location_id: 2
Article.create title: 'Test A', author: Author.find(1), posted_at: 2.days.ago
Article.create title: 'Test B', author: Author.find(2), posted_at: 1.day.ago
Article.create title: 'Test C', author: Author.find(3), posted_at: 1.day.ago
Article.create title: 'Test D', author: Author.find(3), posted_at: 1.day.from_now
Article.index.refresh
# -----
articles = Article.search query: 'test', location_id: 1
puts "", "Documents with location:1", '-'*80
articles.results.each { |a| puts "* TITLE: #{a.title}, LOCATION: #{a.author.location_id}, DATE: #{a.posted_at}" }
articles = Article.search query: 'test', location_id: 2
puts "", "Documents with location:2", '-'*80
articles.results.each { |a| puts "* TITLE: #{a.title}, LOCATION: #{a.author.location_id}, DATE: #{a.posted_at}" }
puts "(NOTE: 'D' is missing, because is not yet posted)"
articles = Article.search query: 'test b', location_id: 1
puts "", "Documents with query:B and location:1", '-'*80
articles.results.each { |a| puts "* TITLE: #{a.title}, LOCATION: #{a.author.location_id}, DATE: #{a.posted_at}" }
First, it's usually a good idea to create an isolated, extracted case like this.
In your example code, I assume you have a relationship Listing belongs_to :author. You need to properly define the mapping and serialization, which I again assume you did.
As for the query itself:
Unless you're using faceted navigation, use the filtered query, not top level filters, as in my example code.
Do not use the string query, unless you really want to expose all the power (and fragility!) of the Lucene query string query to your users.
Use the match query, as your "generic purpose" query -- Tire sprinkles some sugar on top of it, allowing to easily create multi_match queries, etc
The filter syntax in your example is correct. When the filter method is called multiple times in Tire, it creates and and filter.
Uncomment the Tire logging configuration (and possibly also the ActiveRecord logging), to see what the code is doing.

Resources