phoenix ecto relationships on delete - relationship

There are two models: resource and metadata:
defmodule Myapp.Repo.Migrations.CreateResources do
use Ecto.Migration
def change do
create table(:resources) do
add :name, :string
add :parent_id, references(:resources, on_delete: :delete_all)
timestamps
end
create index(:resources, [:parent_id])
end
end
defmodule Myapp.Repo.Migrations.CreateMetadata do
use Ecto.Migration
def change do
create table(:metadata) do
add :size, :integer
add :resource_id, references(:resources)
timestamps
end
create index(:metadata, [:resource_id])
end
end
schema "resources" do
field :name, :string
belongs_to :parent, Myapp.Resource
has_one :metadata, Myapp.Metadata, on_delete: :delete_all
has_many :childs, Myapp.Resource, foreign_key: :parent_id, on_delete: :delete_all
timestamps()
end
schema "metadata" do
field :size, :integer
belongs_to :resource, Myapp.Resource
timestamps()
end
resource can have children, which parent_id = id.
also has a resource metadata. Table Metadata has resource_id column.
Now, I want to delete the resource with the remove all his children, and all associated metadata.
when I write
Repo.delete resource
I get an error:
[error] #PID<0.441.0> running Myapp.Endpoint terminated
Server: localhost:4000 (http)
Request: POST /resources/%2Fdocs%2Ftest
** (exit) an exception was raised:
** (Postgrex.Error) ERROR (foreign_key_violation): update or delete on table "resources" violates foreign key constraint "metadata_resource_id_fkey" on table "metadata"
table: metadata
constraint: metadata_resource_id_fkey
Key (id)=(3) is still referenced from table "metadata".
that appears only when triggered to remove metadata from a resource children. If I try to delete a resource that has no childrens, then everything is working correctly.
Apparently, does not cause the removal of the children required callbacks removing metadata, so Postgres an error.
Whether it is possible to solve this problem without suffering from resource_id metadata metadata_id resource and without starting a recursive manual removal of all the children of the resource?

:delete_all does not cascade to child records unless set via database migrations. To fix the problem make sure you change your metadata migration script line to
add :resource_id, references(:resources, on_delete: :delete_all)

Related

Django migrate column is not the same data type as referencing column

I have the below model which is an existing DB model and through Django's inspectdb management command the below model is created.
class ExistingLegacyModel(models.Model):
period = models.TextField(db_column="Period", blank=True, null=True)
key = models.AutoField(db_column="OutlookKey", primary_key=True)
class Meta:
managed = False
db_table = "table_name"
and currently, I'm trying to create a model with a field foreign key reference to the existing legacy DB model
class TestModel(models.Model):
period = models.ForeignKey(
ExistingLegacyModel,
on_delete=models.CASCADE,
db_column="OutlookKey",
)
so when I run the makemigrations command the migration file is successfully getting created with no issue. below is the migration file content.
class Migration(migrations.Migration):
initial = True
dependencies = [
('historical', '0011_phoenixcontractprice'),
]
operations = [
migrations.CreateModel(
name='TestModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('period', models.ForeignKey(db_column='OutlookKey', on_delete=django.db.models.deletion.CASCADE, to='app.ExistingLegacyModel')),
],
),
]
so now when i run the migrate command now, it is failing and giving the below error.
django.db.utils.ProgrammingError: ('42000', "[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Column 'table_name.OutlookKey' is not the same data type as referencing column 'version_testmodel.OutlookKey' in foreign key 'version_testmodel_OutlookKey_eb16c31c_fk_table_name_OutlookKey'. (1778) (SQLExecDirectW); [42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Could not create constraint or index. See previous errors. (1750)")
I'm stuck with this issue for the past couple of days and I searched all over the internet but didn't get any resolution. I found a couple of StackOverflow questions that are very similar to my issue, but those questions are also unanswered.
Django - Migration foreign key field type not matching current type
Django 3.2 update AutoField to BigAutoField backward compatibility with foreign key relations
I'm currently using Django 3.2.13 and mssql-django to connect to the MSSQL database.
Any help on this will be highly appreciated! Thank you in advance.
UPDATE 1
I ran the sqlmigrate command for the initial migration. So for the period column, it is creating the table with a foreign key field with big int [OutlookKey] bigint NOT NULL whereas the existing legacy model has a normal integer field.
ALTER TABLE [<app>_<model>] ADD CONSTRAINT [<app>_<model>_OutlookKey_3505d410_fk_<existing_legacy_table>_OutlookKey] FOREIGN KEY ([OutlookKey]) REFERENCES [<existing_legacy_table>] ([OutlookKey]);

How to make the 'public' schema default in a Scala Play project that uses PostgreSQL?

I am not sure if my issue connecting to the Scala Play 2.5.x Framework or to PostgreSQL so I am going to describe my setup.
I am using the Play 2.5.6 with Scala and PostgreSQL 9.5.4-2 from the BigSQL Sandboxes. I use the Play Framework default evolution package to manage the DB versions.
I created a new database in BigSQL Sandbox's PGSQL and PGSQL created a default schema called public. I use this schema for development.
I would like to create a table with the following script (1.sql in DB evolution config):
# Initialize the database
# --- !Ups
CREATE TABLE user (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL,
creation_date TIMESTAMP NOT NULL
);
# --- !Downs
DROP TABLE user;
Besides that I would like to read the table with a code like this:
val resultSet = statement.executeQuery("SELECT id, name, email FROM public.user WHERE id=" + id.toString)
I got an error if I would like to execute any of the mentioned code or even if I use the CREATE TABLE... code in pgadmin. The issue is with the user table name. If I prefix it with public (i.e. public.user) everything works fine.
My questions are:
Is it normal to prefix the table name with the schema name every time? It seems to odd to me.
How can I make the public schema a default option so I do not have to qualify the table name? (e.g. CREATE TABLE user (...); will not throw an error)
I tried the following:
I set the search_path for my user: ALTER USER my_user SET search_path to public;
I set the search_path for my database: ALTER database "my_database" SET search_path TO my_schema;
search_path correctly shows this: "$user",public
I got the following errors:
In Play: p.a.d.e.DefaultEvolutionsApi - ERROR: syntax error at or near "user"
In pgadmin:
ERROR: syntax error at or near "user"
LINE 1: CREATE TABLE user (
********** Error **********
ERROR: syntax error at or near "user"
SQL state: 42601
Character: 14
This has nothing to do with the default schema. user is a reserved word.
You need to use double quotes to be able to create such a table:
CREATE TABLE "user" (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL,
creation_date TIMESTAMP NOT NULL
);
But I strongly recommend not doing that. Find a different name that does not require a quoted identifier.

Weird behavior with web2py DAL on table definition

Hi getting behaviour I don't understand with web2py
In [50]: db = DAL('sqlite://deposit/sample.sqlite')
In [51]: db.define_table('customer',Field('name','string',required=True),
Field('nric','string',required=True),
Field('address','string'),
Field('phone','integer'),
primarykey=['name'])
Out[51]: <Table customer (name,nric,address,phone)>
works as expected.
I then do
In [53]: db.define_table('check',
Field('nric', db.customer.nric, required=True),
Field('clear','string'))
which gets me the message
AttributeError: 'DAL' object has no attribute 'customer.nric'
So thinking this may be an issue of not having committed customer to the database
so I do a db.commit() and then try again
In [56]: db.define_table('check',Field('nric', db.customer.nric, required=True), Field('clear','string'))
File "<string>", line unknown
SyntaxError: table already defined: check
Not sure why .. but anyway I try and drop the table
In [59]: db['check'].drop()
and get the following weird traceback
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-59-998297b798f5> in <module>()
----> 1 db['check'].drop()
/home/tahnoon/.dropbox-cyn/Dropbox (TIS Group)/Cynopsis/Builds/Apollo/Code Src/web2py/gluon/dal.pyc in drop(self, mode)
9225
9226 def drop(self, mode=''):
-> 9227 return self._db._adapter.drop(self, mode)
9228
9229 def _listify(self, fields, update=False):
/home/tahnoon/.dropbox-cyn/Dropbox (TIS Group)/Cynopsis/Builds/Apollo/Code Src/web2py/gluon/dal.pyc in drop(self, table, mode)
1328 queries = self._drop(table, mode)
1329 for query in queries:
-> 1330 if table._dbt:
1331 self.log(query + '\n', table)
1332 self.execute(query)
/home/tahnoon/.dropbox-cyn/Dropbox (TIS Group)/Cynopsis/Builds/Apollo/Code Src/web2py/gluon/dal.pyc in __getitem__(self, key)
9108 return self._db(self._id == key).select(limitby=(0, 1), orderby_on_limitby=False).first()
9109 elif key:
-> 9110 return ogetattr(self, str(key))
9111
9112 def __call__(self, key=DEFAULT, **kwargs):
AttributeError: 'Table' object has no attribute '_dbt'
Checking tables shows
In [61]: db.tables()
Out[61]: ['customer']
Is this expected behaviour? If so how do I drop/create a table after a syntax error? thanks
Since db.customer is a keyed table (i.e., you have defined a primarykey attribute rather than relying on the default autoincrement integer ID field as the primary key), it can only be referenced by other keyed tables.
Also, when creating reference fields for keyed tables, use the following syntax:
Field('nric', 'reference customer.nric', required=True)
However, I don't think keyed tables are supported for SQLite (the docs say only DB2, MS-SQL, Ingres, and Informix are supported). Anyway, if you are creating a new table in SQLite, there is no reason to use a keyed table (that functionality was added primarily to enable access to legacy databases that lack autoincrement integer primary key fields).
Finally, dropping a table does not remove the model from the db DAL instance -- rather, that operation drops the table from the database itself. If you want to redefine a model within a shell session, you should use the "redefine" argument:
db.define_table(..., redefine=True)

Cayenne, Postgres: primary key generation

I'm using Cayenne 3.2M1 and Postgres 9.0.1 to create a database. Right now I'm having problems with the primary key generation of Cayenne since I have tables with more than one primary key and as far as I've read Cayenne cant generate more that one primary key per table. So I want the Postgres to do that work.
I have this table:
CREATE TABLE telefonocliente
(
cod_cliente integer NOT NULL DEFAULT currval('cliente_serial'::regclass),
cod_telefono integer NOT NULL DEFAULT nextval('telefonocliente_serial'::regclass),
fijo integer,
CONSTRAINT telefonocliente_pkey PRIMARY KEY (cod_cliente, cod_telefono)
)
WITH (
OIDS=FALSE
);
TelefonoCliente telefono = context.newObject(TelefonoCliente.class);
telefono.setFijo(4999000);
context.commitChanges();
and this is the error I get:
INFO: --- transaction started.
19/11/2013 22:46:17 org.apache.cayenne.access.dbsync.CreateIfNoSchemaStrategy processSchemaUpdate
INFO: Full or partial schema detected, skipping tables creation
19/11/2013 22:46:17 org.apache.cayenne.log.CommonsJdbcEventLogger logQuery
INFO: SELECT nextval('pk_telefonocliente')
Exception in thread "main" org.apache.cayenne.CayenneRuntimeException: [v.3.2M1 Jul 07 2013 16:23:58] Commit Exception
at org.apache.cayenne.access.DataContext.flushToParent(DataContext.java:759)
at org.apache.cayenne.access.DataContext.commitChanges(DataContext.java:676)
at org.example.cayenne.Main.main(Main.java:45)
Caused by: org.postgresql.util.PSQLException: ERROR: no existe la relaci?n ≪pk_telefonocliente≫
Position: 16
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2102)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1835)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:257)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:500)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:374)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:254)
at org.apache.cayenne.dba.postgres.PostgresPkGenerator.longPkFromDatabase(PostgresPkGenerator.java:79)
at org.apache.cayenne.dba.JdbcPkGenerator.generatePk(JdbcPkGenerator.java:272)
at org.apache.cayenne.access.DataDomainInsertBucket.createPermIds(DataDomainInsertBucket.java:171)
at org.apache.cayenne.access.DataDomainInsertBucket.appendQueriesInternal(DataDomainInsertBucket.java:76)
at org.apache.cayenne.access.DataDomainSyncBucket.appendQueries(DataDomainSyncBucket.java:78)
at org.apache.cayenne.access.DataDomainFlushAction.preprocess(DataDomainFlushAction.java:188)
at org.apache.cayenne.access.DataDomainFlushAction.flush(DataDomainFlushAction.java:144)
at org.apache.cayenne.access.DataDomain.onSyncFlush(DataDomain.java:685)
at org.apache.cayenne.access.DataDomain$2.transform(DataDomain.java:651)
at org.apache.cayenne.access.DataDomain.runInTransaction(DataDomain.java:712)
at org.apache.cayenne.access.DataDomain.onSyncNoFilters(DataDomain.java:648)
at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.onSync(DataDomain.java:852)
at org.apache.cayenne.access.DataDomain.onSync(DataDomain.java:629)
at org.apache.cayenne.access.DataContext.flushToParent(DataContext.java:727)
... 2 more
I've been trying the suggestions on Cayenne tutorial "generated columns", "primary key support" but I seems to always get some error.
INFO: SELECT nextval('pk_telefonocliente')
Exception in thread "main" org.apache.cayenne.CayenneRuntimeException: [v.3.2M1 Jul 07 2013 16:23:58] Primary Key autogeneration only works for a single attribute.
I want to know how to solve this.
Thanks in advance
From your description in comments, out of 2 columns comprising the PK of 'telefonocliente', only one is truly independent - 'cod_telefono'. This will be what Cayenne will generate. In case of PosgreSQL, you will need the following sequence in DB for this to happen:
CREATE SEQUENCE pk_telefonocliente INCREMENT 20 START 200;
Now, where does the second PK 'cod_cliente' come from? Since it is also FK to another table, it means it is a "dependent" PK, and must come from a relationship. So first you need to map a many-to-one relationship between 'telefonocliente' and 'cliente'. Check "To Dep Pk" checkbox on the 'telefonocliente' side. Generate a matching ObjRelationship for your Java objects. Now you can use it in your code:
Cliente c = .. // get a hold of this object somehow
TelefonoCliente telefono = context.newObject(TelefonoCliente.class);
telefono.setFijo(4999000);
telefono.setCliente(c); // this line is what will populate 'cod_cliente' PK/FK
That should be it.
The primary key is allowed just to be one per a table! In your case you create a primary key on two columns, that is right, it is defined in an SQL standard and Postgres supports it well.
However there is a not in Cayenne documentation:
Cayenne only supports automatic PK generation for a single column per table.
see http://cayenne.apache.org/docs/3.0/primary-key-generation.html at the bottom of the page.
Probably they can fix it in a newer version or you can put a request to the Cayenne community.

PostgreSQL JPA cascade partially working

Again we probably have a very simple problem;
our database look like:
CREATE TABLE Question (
idQuestion SERIAL,
questionContent VARCHAR,
CONSTRAINT Question_idQuestion_PK PRIMARY KEY (idQuestion),
);
CREATE TABLE Answer (
idAnswer SERIAL,
answerContent VARCHAR,
idQuestion INTEGER,
CONSTRAINT Answer_idAnswer_PK PRIMARY KEY (idAnswer),
CONSTRAINT Answer_idQuestion_FK FOREIGN KEY (idQuestion) REFERENCES Question(idQuestion),
);
So a Question have many Answers.
Following in entity generated by Netbeans 7.1.2 we have field:
#OneToMany(mappedBy = "idquestion", orphanRemoval=true, cascade= CascadeType.ALL, fetch= FetchType.EAGER)
private Collection<Answer> answerCollection;
As you can see I've already added all possible orphan removal and cascade instructions for cascade removal of collection. And it's working fine but for one moment:
You can delete a Question and connected Answers only if they were created in previous 'instance' of our Application. If I first create a new Question and even one Answer and then go straight and delete it we got an error like:
root cause
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: update or delete on table "question" violates foreign key constraint "answer_idquestion_fk" on table "answer"
Detail: Key (idquestion)=(30) is still referenced from table "answer".
Error Code: 0
Call: DELETE FROM question WHERE ((idquestion = ?) AND (version = ?))
bind => [2 parameters bound]
Query: DeleteObjectQuery(com.accenture.androidwebapp.entities.Question[ idquestion=30 ])
root cause
org.postgresql.util.PSQLException: ERROR: update or delete on table "question" violates foreign key constraint "answer_idquestion_fk" on table "answer"
Detail: Key (idquestion)=(30) is still referenced from table "answer".
If I restart (rebuild, redeploy) the application it's working though.. why? Thanks!
Try adding the EclipseLink #PrivateOwned extension annotation to your collection mapping.
As for the issue with deletion not working until you restart the app, two things come to mind:
You might be using very long EntityManager sessions where everything stays attached to the EnitityManager. If that's the case, it's the change of EntityManager session forced by a restart that's helping. Consider using shorter EntityManager sessions by calling EntityManager.close() when you're done with a session. Work with detached entities and EntityManager.merge() state back in when you want to modify it.
The second level cache, if any, will be cleared by a redeploy. Try disabling the second level cache and see if that helps.

Resources