rails PG undefined column does not exist for index creation - postgresql

I'm trying to add an index for a for column but I'm getting the error:
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column "contact" does not exist
: CREATE UNIQUE INDEX "index_users_on_contact" ON "users" ("contact")
which is strange because as you an see I'm creating the column before I try to index it:
class AddContactToUser < ActiveRecord::Migration[5.1]
def change
add_reference :users, :contact, foreign_key: true
add_index :users, :contact, unique: true
end
end
Why am I getting this error?
In case your wondering why I'm doing a separate contact model, it's because all users will have a contact but not all contacts will have a user.

add_reference :users, :contact, foreign_key: true
Creates a column named contact_id. So you're index needs to be
add_index :users, :contact_id, unique: true

Related

How to add indexes to already created table in Liquibase

There is already created table 'A'
'id' 'b_id' 'name'
1 2 someName
Now I want to add unique index to id and b_id columns
How to do it in yml format
based on documentation, something like this:
changeSet:
id: addUniqueConstraint-example
author: liquibase-docs
changes:
- addUniqueConstraint:
catalogName: cat
clustered: false
columnNames: id, b_id
constraintName: const_name
deferrable: true
disabled: false
forIndexName:
initiallyDeferred: true
schemaName: [yourschema]
tableName: [yourtablename]
tablespace:
validate: true

Ecto will not insert a :date field into the database - why?

I am facing an issue while learning Elixir & Ecto. The idea is to build a standard posts/comments page to understand how the basics work. I am at a point where I have schemas defined, a migration written and encounter an error when trying to insert data into the database (PostgreSQL) via the Repo. I have done a fair deal of web searching and documentation reading, which leads me to believe it's a scenario that should just work and I am making a stupid mistake somewhere, which I just can't see.
They are defined as follows:
lib/hello/schemas.ex
defmodule Hello.PostAuthor do
use Ecto.Schema
schema "post_authors" do
field :name, :string
end
end
defmodule Hello.CommentAuthor do
use Ecto.Schema
schema "comment_authors" do
field :name, :string
end
end
defmodule Hello.Comment do
use Ecto.Schema
schema "comments" do
has_one :author, Hello.CommentAuthor
field :date, :date
field :body, :string
end
end
defmodule Hello.Post do
use Ecto.Schema
schema "posts" do
has_one :author, Hello.PostAuthor
field :date, :date
field :body, :string
has_many :comments, Hello.Comment
end
end
as you can see, I have two fields with :date type - on post and comment schemas. The corresponding migration is as follows:
defmodule Hello.Repo.Migrations.CreatePosts do
use Ecto.Migration
def change do
create table(:post_authors) do
add :name, :string
end
create table(:comment_authors) do
add :name, :string
end
create table(:comments) do
add :author, references(:comment_authors)
add :date, :date
add :body, :string
end
create table(:posts) do
add :author, references(:post_authors), null: false
add :date, :date
add :body, :string
add :comments, references(:comments)
timestamps()
end
end
end
Now, when I start iex -S mix I can successfully create all structs:
iex(1)> post_author = %Hello.PostAuthor{name: "John"}
%Hello.PostAuthor{
__meta__: #Ecto.Schema.Metadata<:built, "post_authors">,
id: nil,
name: "John"
}
iex(2)> comment_author = %Hello.PostAuthor{name: "Adam"}
%Hello.PostAuthor{
__meta__: #Ecto.Schema.Metadata<:built, "post_authors">,
id: nil,
name: "Adam"
}
iex(3)> comment = %Hello.Comment{author: comment_author, date: ~D[2019-01-01], body: "this is a comment"}
%Hello.Comment{
__meta__: #Ecto.Schema.Metadata<:built, "comments">,
author: %Hello.PostAuthor{
__meta__: #Ecto.Schema.Metadata<:built, "post_authors">,
id: nil,
name: "Adam"
},
body: "this is a comment",
date: ~D[2019-01-01],
id: nil
}
iex(4)> post = %Hello.Post{author: post_author, date: ~D[2019-01-01], body: "this is a post", comments: [comment]}
%Hello.Post{
__meta__: #Ecto.Schema.Metadata<:built, "posts">,
author: %Hello.PostAuthor{
__meta__: #Ecto.Schema.Metadata<:built, "post_authors">,
id: nil,
name: "John"
},
body: "this is a post",
comments: [%Hello.Comment{
__meta__: #Ecto.Schema.Metadata<:built, "comments">,
author: %Hello.PostAuthor{
__meta__: #Ecto.Schema.Metadata<:built, "post_authors">,
id: nil,
name: "Adam"
},
body: "this is a comment",
date: ~D[2019-01-01],
id: nil
}],
date: ~D[2019-01-01],
id: nil
}
The problem arises when I call Hello.Repo.insert(post) (where post is the struct representing the Hello.Post schema). I receive what looks like serialization error:
iex(8)> Hello.Repo.insert(post) [debug] QUERY OK db=0.1ms
begin []
[debug] QUERY ERROR db=1.6ms
INSERT INTO "posts" ("body","date") VALUES ($1,$2) RETURNING "id" ["this is a post", ~D[2019-01-01]]
[debug] QUERY OK db=0.1ms
rollback []
** (DBConnection.EncodeError) Postgrex expected a binary, got ~D[2019-01-01]. Please make sure the value you are passing matches the definition in your table or in your query or convert the value accordingly.
(postgrex) lib/postgrex/type_module.ex:897: Postgrex.DefaultTypes.encode_params/3
(postgrex) lib/postgrex/query.ex:75: DBConnection.Query.Postgrex.Query.encode/3
(db_connection) lib/db_connection.ex:1148: DBConnection.encode/5
(db_connection) lib/db_connection.ex:1246: DBConnection.run_prepare_execute/5
(db_connection) lib/db_connection.ex:540: DBConnection.parsed_prepare_execute/5
(db_connection) lib/db_connection.ex:533: DBConnection.prepare_execute/4
(postgrex) lib/postgrex.ex:198: Postgrex.query/4
(ecto_sql) lib/ecto/adapters/sql.ex:666: Ecto.Adapters.SQL.struct/10
(ecto) lib/ecto/repo/schema.ex:651: Ecto.Repo.Schema.apply/4
(ecto) lib/ecto/repo/schema.ex:262: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
(ecto) lib/ecto/repo/schema.ex:916: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/6
(ecto_sql) lib/ecto/adapters/sql.ex:898: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
(db_connection) lib/db_connection.ex:1415: DBConnection.run_transaction/4
This is where I am lost. Both the schema and the migration are expecting a :date . I believe that ~D[2019-01-01] is a date. PostgreSQL defines date as a 4 byte binary value. I am expecting Ecto.Adapters.Postgres to translate elixir date struct into the Postgres binary value. This is not happening. Why?
Struct itself is just raw data. You should go through Ecto.Changeset as shown in the documentation, specifically to all types to be cast to the respective DB types with Ecto.Changeset.cast/4.
The conversion will be done automagically, but you need to explicitly call cast/4 (hence the Changeset,) otherwise the adapter has no idea of how to convert your ecto types.

invalid or unknown type App.ImageFile.Type for field :filename Phoneix App

I'm attempting to use Arc to allow for uploading images to Google Cloud Storage, (following this - https://maartenvanvliet.nl/2017/08/05/arc_uploads_to_google_cloud_storage/) however, I'm getting a compile error when running mix ecto.migrate:
== Compilation error on file lib/codsonline/assets/image.ex ==
** (ArgumentError) invalid or unknown type App.ImageFile.Type for field :filename
lib/ecto/schema.ex:1886: Ecto.Schema.check_type!/3
lib/ecto/schema.ex:1507: Ecto.Schema.__field__/4
lib/codsonline/assets/image.ex:8: (module)
(stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
(elixir) lib/kernel/parallel_compiler.ex:117: anonymous fn/4 in
Kernel.ParallelCompiler.spawn_compilers/1
I've changed my schema from:
schema "images" do
field :filename, :string
field :name, :string
timestamps()
end
to:
schema "images" do
field :filename, App.ImageFile.Type
field :name, :string
timestamps()
end
My uploaders/image_file.ex:
defmodule Codsonline.ImageFile do
use Arc.Definition
# Include ecto support (requires package arc_ecto installed):
use Arc.Ecto.Definition
#versions [:original]
def __storage, do: Arc.Storage.GCS
def gcs_object_headers(:original, {file, _scope}) do
[content_type: MIME.from_path(file.file_name)]
end
end
Have I got the syntax wrong somewhere?
Update
#dogbert pointed out I wasn't Using Codsonline instead of App, the below now works:
schema "images" do
field :filename, Codsonline.ImageFile.Type
field :name, :string
timestamps()
end

What's the proper way to add an index to a table in a Rails migration?

I'm using Rails 5 with PostGres 9.5 . I have the following migration
class CreateCryptoIndexCurrencies < ActiveRecord::Migration[5.0]
def change
create_table :crypto_index_currencies do |t|
t.references :crypto_currency, foreign_key: true
t.date :join_date, :null => false, :default => Time.now
t.timestamps
end
add_index :crypto_index_currencies, :crypto_currency, unique: true
end
end
Upon running the migration, it is dying with this error
PG::UndefinedColumn: ERROR: column "crypto_currency" does not exist
What is the proper way to add the index? The table name that I want to reference is called "crypto_currencies".
add_index 'crypto_index_currencies', ['crypto_currency'], name: "index_crypto_index_currencies_on_crypto_currency", unique: true, using: :btree
using: :btree its optional.
This is the syntax to add it inside the create_table block
class CreateCryptoIndexCurrencies < ActiveRecord::Migration[5.0]
def change
create_table :crypto_index_currencies do |t|
t.references :crypto_currency, foreign_key: true
t.date :join_date, :null => false, :default => Time.now
t.index :crypto_currency, unique: true
end
end
end

Rails 4, Rspec, FactoryGirl

I am having trouble having an id column in a database that is not rails automagic. Basically the situation is the users table has to be between 5000 and 7000 and there are other external stipulations that don't allow this to auto-increment Here is the migration (which works magically in the console):
class CreateUsers < ActiveRecord::Migration
def change
create_table :users, id: false do |t|
t.integer :id, null: false
t.string :firstname, null: false
t.string :lastname, null: false
t.string :username, null: false
t.boolean :active, null: false, default: true
t.date :hiredate, null: false
t.date :termdate
t.timestamps
end
add_index :users, [:id], :unique => true
execute "ALTER TABLE users ADD PRIMARY KEY (id);"
end
end
The problem I'm having is with testing in order to create a user with the factory I have to have a begin rescue block. The first time it fails and the second time it works perfectly. I have no idea why this happens. Here is the first part of users_spec.rb:
describe User do
before(:all)do
begin
#user = FactoryGirl.create(:user)
rescue
#user = FactoryGirl.create(:user)
end
end
end
I'm contemplating just having the id column perform as it should in a rails app and making a new extensions column, but I would appreciate someone helping me with this issue. Thank you for the help!