Ecto - Update a Record - undefined function __changeset__/0 - ecto

I'm getting this error when trying to update a record with a changeset:
14:36:29.972 [error] #PID<0.341.0> running Api.Router terminated
Server: 192.168.20.3:4000 (http)
Request: PUT /products/?p_id=11&s_id=11
** (exit) an exception was raised:
** (UndefinedFunctionError) function Ecto.Query.__changeset__/0 is undefined or private
(ecto) Ecto.Query.__changeset__()
(ecto) lib/ecto/changeset.ex:422: Ecto.Changeset.do_cast/4
(api) lib/api/product_shop.ex:17: Api.ProductShop.changeset/2
(api) lib/api/router.ex:168: anonymous fn/1 in Api.Router.do_match/4
(api) lib/api/router.ex:1: Api.Router.plug_builder_call/2
(api) lib/plug/debugger.ex:123: Api.Router.call/2
(plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
(cowboy) /Users/Ben/Development/Projects/vepo/api/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protoco
l.execute/4
code:
pid = conn.query_params["p_id"]
sid = conn.query_params["s_id"]
price = conn.query_params["price"]
query = ProductShop |> Ecto.Query.where(p_id: ^pid)
product_shop = query |> Ecto.Query.where(s_id: ^sid)
changeset2 = Api.ProductShop.changeset(product_shop, %{price: price})
case Api.Repo.update(changeset2) do
{:ok, product_shop} ->
errors = Tuple.append(errors, "Price updated")
{:error, changeset2} ->
errors = Tuple.append(errors, "Price not updated")
end
this is the ProductShop which I want to update:
14:38:56.658 [debug] QUERY OK source="product_shops" db=1.7ms
SELECT p0."id", p0."s_id", p0."p_id", p0."not_in_shop_count", p0."price" FROM "product_shops" AS p0 []
[%Api.ProductShop{__meta__: #Ecto.Schema.Metadata<:loaded, "product_shops">,
id: 11, not_in_shop_count: 0, p_id: 11, price: 5.99, s_id: 11}]
Why am I getting the error?
my ProductShop file with changeset:
defmodule Api.ProductShop do
use Ecto.Schema
import Ecto.Changeset
import Api.Repo
import Ecto.Query
#derive {Poison.Encoder, only: [:s_id, :p_id]}
schema "product_shops" do
field :s_id, :integer
field :p_id, :integer
field :not_in_shop_count, :integer
field :price, :float
end
def changeset(product_shop, params \\ %{}) do
product_shop
|> cast(params, [:s_id, :p_id])
|> validate_required([:s_id, :p_id])
|> unique_constraint(:s_id, name: :unique_product_shop)
end
def insert_product_shop(conn, product_id, shop_id, price) do
changeset = Api.ProductShop.changeset(%Api.ProductShop{p_id: product_id, s_id: shop_id, not_in_shop_count: 0, price: price})
errors = changeset.errors
valid = changeset.valid?
case insert(changeset) do
{:ok, product_shop} ->
{:ok, product_shop}
{:error, changeset} ->
{:error, :failure}
end
end
def delete_all_from_product_shops do
from(Api.ProductShop) |> delete_all
end
def get_product_shops do
Api.ProductShop |> all
end
end
in router.ex
put "/products" do
errors = {}
IO.inspect(conn.body_params)
product = Api.Product |> Api.Repo.get(conn.query_params["p_id"])
shop = Api.Shop |> Api.Repo.get(conn.query_params["s_id"])
params = for key <- ~w(image description), value = conn.body_params[key], into: %{}, do: {key, value}
changeset = Api.Product.changeset(product, params)
case Api.Repo.update(changeset) do
{:ok, product} ->
errors = Tuple.append(errors, "Product updated")
{:error, changeset} ->
errors = Tuple.append(errors, "Product not updated")
end
pid = conn.query_params["p_id"]
sid = conn.query_params["s_id"]
price = conn.query_params["price"]
query = ProductShop |> Ecto.Query.where(p_id: ^pid)
product_shop = query |> Ecto.Query.where(s_id: ^sid)
changeset2 = Api.ProductShop.changeset(product_shop, %{price: price})
case Api.Repo.update(changeset2) do
{:ok, product_shop} ->
errors = Tuple.append(errors, "Price updated")
{:error, changeset2} ->
errors = Tuple.append(errors, "Price not updated")
end
IO.inspect(errors)
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Poison.encode!(%{
successs: "success",
errors: Tuple.to_list(errors)
}))
end

Ecto's changeset function that you write in schema, by default works for Ecto.Schema, which means that it works for modules with defined schemas in them. After using cast it deals with Ecto.Changeset struct.
Your code tries to work with Ecto.Query in changeset function, namely here:
product_shop = query |> Ecto.Query.where(s_id: ^sid)
You should use Repo.one() at the end to have valid ProductShop struct and then you can use it in ProductShop.changeset function.
Also consider rewriting how you want to retrieve this product_shop. Please use Repo.get_by:
Repo.get_by(ProductShop, s_id: s_id, p_id: p_id)

Related

Couchbase - Case with subquery

I am going to write a case which has subquery, something like below, But I cannot get neither out put nor error:
select
case
when f.resGeo.isRural = true
then (select g.ID as geo_id
from bktsample.scpPC.GeoInfo g
where g.PROVINCE_ID = f.resGeo.province.id
and g.CITYES_ID = f.resGeo.countie.id
and g.PART_ID = f.resGeo.part.id
and g.CITYORCOUNTRY_ID = f.resGeo.countie.id
and g.VILLAGE_ID = f.resGeo.village.id)
when f.resGeo.isRural = false
then (select g.ID
from bktsample.scpPC.GeoInfo g
where g.PROVINCE_ID = f.resGeo.province.id
and g.CITYES_ID = f.resGeo.countie.id
and g.PART_ID = f.resGeo.part.id
and g.CITYORCOUNTRY_ID = f.resGeo.countie.id)
end as geo_id
from bktsample.scpPC.Family f;
PS: GEO is my collection, scpPC is my scope and bktsample is my bucket.
Each document in the Family collection should have returned one document of geo_id (either empty array or objects of ID)
Small change to your query:
CREATE INDEX ix1 ON bktsample.scpPC.Family(resGeo.province.id, resGeo.countie.id, resGeo.part.id, resGeo.countie.id, resGeo.village.id, resGeo.isRural);
CREATE INDEX ix2 ON bktsample.scpPC.GeoInfo(PROVINCE_ID, CITYES_ID, PART_ID, CITYORCOUNTRY_ID, VILLAGE_ID, ID);
SELECT
(SELECT g.ID AS geo_id
FROM bktsample.scpPC.GeoInfo AS g
WHERE g.PROVINCE_ID = f.resGeo.province.id
AND g.CITYES_ID = f.resGeo.countie.id
AND g.PART_ID = f.resGeo.part.id
AND g.CITYORCOUNTRY_ID = f.resGeo.countie.id
AND (f.resGeo.isRural == false OR g.VILLAGE_ID = f.resGeo.village.id))
) AS geo_ids
FROM bktsample.scpPC.Family f
WHERE f.resGeo.province.id IS NOT NULL;

Netsuite get "Currency Revaluation (Unrealized Gain/Loss)" Table

I am new to netsuite,
Is it possible to get the table "Currency Revaluation (Unrealized Gain/Loss)" using suitescript QL.
I would like to use it in Suite QL
here is an sample:
SELECT
NT .*
FROM
NextTransactionLineLink AS NTLL
INNER JOIN Transaction AS NT ON (NT.ID = NTLL.NextDoc)
INNER JOIN Transaction ON (Transaction.ID = NTLL.PreviousDoc)
inner join transactionline tl ON (tl.TRANSACTION = Transaction.id)
inner join subsidiary sb ON sb.id = tl.subsidiary
inner join accountingperiod ap ON (
(ap.id = Transaction.postingperiod)
AND ap.isposting = 'T'
)
inner join accountingperiod pap ON (
(pap.id = NT.postingperiod)
AND pap.isposting = 'T'
)
inner join currencyrate cr1 ON (
cr1.basecurrency = sb.currency
AND cr1.transactioncurrency = Transaction.currency
AND (
cr1.effectivedate = To_date(
ap.startdate,
'MM/DD/YYYY'
)
)
)
inner join consolidatedexchangerate cexr ON (
cexr.postingperiod = Transaction.postingperiod
AND cexr.fromsubsidiary = tl.subsidiary
AND cexr.tosubsidiary = 1
)
WHERE
(NTLL.NextDoc = 212328)
Thanks in Advance
By "getting", I am assuming you want to load this record and the fetch/update the values within.
Simply use &xml=t in the url, after the URL of the record
Something like this -
https://342xxxx-sb1.app.netsuite.com/app/accounting/transactions/vendbill.nl?id=00000&whence=&xml=t
You will get the record type.
<record recordType="vendorbill"
Use this record type in your Script.
var objRecord = record.load({
type: record.Type.VENDOR_BILL,
id: 157,
isDynamic: true,
});
If in case the record is a Custom Record, use this
var newFeatureRecord = record.load({
type: 'customrecord_feature',
id: 1,
isDynamic: true
});
Let me know in case of issues in the comments below.

psycopg2 string composition error

I am doing an upsert.
owner.sq_properties = ['name', 'place', 'email_address', 'preferred_transport', 'note', 'address_line_one', 'address_line_two', 'address_line_three', 'contact_one', 'contact_two', 'contact_three', 'gst_number', 'nickname']
statement:
sq = sql.SQL("insert into master.customer select * from public.customer on conflict (id) do update set({})=({})").format(sql.SQL(',').join(sql.Identifier(n) for n in owner.sq_properties),sql.SQL(',').join(sql.Identifier("excluded."+n) for n in owner.sq_properties))
when I execute:
print(sq.as_string(cursor))
output is :
insert into master.customer select * from public.customer on conflict (id) do update set ("name", "place", "email_address", "preferred_transport", "note", "address_line_one", "address_line_two", "address_line_three", "contact_one", "contact_two", "contact_three", "gst_number", "nickname") = ("excluded.name", "excluded.place", "excluded.email_address", "excluded.preferred_transport", "excluded.note", "excluded.address_line_one", "excluded.address_line_two", "excluded.address_line_three", "excluded.contact_one", "excluded.contact_two", "excluded.contact_three", "excluded.gst_number", "excluded.nickname")
error:
cursor.execute(sq)
psycopg2.ProgrammingError: column "excluded.name" does not exist
LINE 1: ...o", "contact_three", "gst_number", "nickname") = ("excluded..
What am I doing wrong?
Figured it out. excluded. should be an sql identifier.
joined =sql.SQL(',').join(sql.SQL('excluded.')+sql.Identifier(n) for n in owner.sq_properties)
corrected statement:
sql.SQL("insert into master.customer select * from public.customer on conflict (id) do update set ({}) = ({}) returning id").format(sql.SQL(',').join(sql.Identifier(n) for n in owner.sq_properties), joined)

JSON encode GEO.Point from geo library as human readable form

I have this schema which has a Geo Geo.Point:
defmodule Api.Shop do
use Ecto.Schema
import Ecto.Changeset
import Api.Repo
import Ecto.Query
#derive {Poison.Encoder, only: [:name, :place_id, :geo_json, :distance]}
schema "shops" do
field :name, :string
field :place_id, :string
field :point, Geo.Point
field :geo_json, :string, virtual: true
field :distance, :float, virtual: true
timestamps()
end
def encode_model(shop) do
%Api.Shop{shop | geo_json: Geo.JSON.encode(shop.point) }
end
defimpl Poison.Encoder, for: Api.Shop do
def encode(shop, options) do
shop = Api.Shop.encode_model(shop)
Poison.Encoder.Map.encode(Map.take(shop, [:id, :name, :geo_json]), options)
end
end
def changeset(shop, params \\ %{}) do
shop
|> cast(params, [:name, :place_id, :point])
|> validate_required([:name, :place_id, :point])
|> unique_constraint(:place_id)
end......
end
And when I return the shop.point field in a query:
def create_query_no_keyword(categories, shop_ids) do
products_shops_categories = from p in Product,
distinct: p.id,
join: ps in ProductShop, on: p.id == ps.p_id,
join: s in Shop, on: s.id == ps.s_id,
join: pc in ProductCategory, on: p.id == pc.p_id,
join: c in Subcategory, on: c.id == pc.c_id,
where: c.id in ^categories,
where: s.id in ^shop_ids,
group_by: [p.id, p.name, p.brand],
select: %{product: p, categories: fragment("json_agg( DISTINCT (?, ?)) AS category", c.id, c.name), shops: fragment("json_agg( DISTINCT (?, ?, ?)) AS shop", s.id, s.name, s.point)}
end
What gets returned is actually 0101000020E6100000A3BDB0EB0DD9654030AC2C1BE76D42C0 which is the wrong format - WKB. I'm looking to encode as WKT which has readable coordinates.
How do I get s.point to be WKT format and thus have coordinates, when it is returned by the query?
I found this Stack Exchange GIS answer to be the solution:
use this for point object:
SELECT ST_AsText(the_geom)
FROM myTable; and viewing X,Y and geom object:
SELECT ST_X(the_geom), ST_Y(the_geom), ST_AsText(the_geom)
FROM myTable;
The Geo library is using PostGIS and the solution was PostGIS specific. You need to select the column using ST_AsText, or ST_X and ST_Y from PostGIS.
My select statement changed to this:
select: %{product: p, categories: fragment("json_agg( DISTINCT (?, ?)) AS category", c.id, c.name), shops: fragment("json_agg( DISTINCT (?, ?, ST_X(?), ST_Y(?))) AS shop", s.id, s.name, s.point, s.point)}

Ecto join query causing (Poison.EncodeError) unable to encode value

I have 3 tables:
categories
subcategories
categorySubcategories
1 category has 0 to many subcategories.
I have this function which gets all entries from categories table and only gets the entries of the subcategories table that belong to the category which has an id of 1:
def getCategories(conn) do
categories = all Api.Category
groceryItemSubcategories = from s in Api.Subcategory,
join: cs in Api.CategorySubcategory, on: cs.c_id == 1,
select: %{name: s.name, foo: cs.c_id}
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Poison.encode!(%{categories: categories, groceryItemSubcategories: groceryItemSubcategories}))
end
giving this error:
23:10:27.169 [error] #PID<0.339.0> running Api.Router terminated
Server: localhost:4000 (http)
Request: GET /categories
** (exit) an exception was raised:
** (Poison.EncodeError) unable to encode value: {"subcategories", Api.Subcategory}
(poison) lib/poison/encoder.ex:383: Poison.Encoder.Any.encode/2
(poison) lib/poison/encoder.ex:227: anonymous fn/4 in Poison.Encoder.Map.encode/3
(poison) lib/poison/encoder.ex:228: Poison.Encoder.Map."-encode/3-lists^foldl/2-0-"/3
(poison) lib/poison/encoder.ex:228: Poison.Encoder.Map.encode/3
(poison) lib/poison/encoder.ex:227: anonymous fn/4 in Poison.Encoder.Map.encode/3
(poison) lib/poison/encoder.ex:228: Poison.Encoder.Map."-encode/3-lists^foldl/2-0-"/3
(poison) lib/poison/encoder.ex:228: Poison.Encoder.Map.encode/3
(poison) lib/poison.ex:41: Poison.encode!/2
The final answer is this:
def getCategories(conn) do
categories = all Api.Category
groceryItemSubcategories = Api.Repo.all(from s in Api.Subcategory,
join: cs in Api.CategorySubcategory, on: cs.s_id == s.id,
join: c in Api.Category, on: c.id == cs.c_id,
where: c.id == 1,
select: %{name: s.name, foo: cs.c_id}
)
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Poison.encode!(%{categories: categories, groceryItemSubcategories: groceryItemSubcategories}))
end
The part that makes the error go away is wrapping the statement in Api.Repo.All(). Dogbert was really the one who answered it so I'm not wanting to answer this.
There are two main issues with the original code:
You forgot to call Repo.all in the second query.
You're selecting a tuple in the query and then encoding it to JSON. Poison does handle encoding tuples to JSON. You can select a list or map instead, depending on what data structure you want. Here's how to select a map:
groceryItemSubcategories = Api.Repo.all(from s in Api.Subcategory,
join: cs in Api.CategorySubcategory, on: cs.s_id == s.id,
join: c in Api.Category, on: c.id == cs.c_id,
where: c.id == 1,
select: %{name: s.name, c_id: cs.c_id})

Resources