Junction table referencing non existent entity - django

I have the following models in django
class WorkSession(models.Model):
pass
class Invoice(models.Model):
work_sessions = models.ManyToManyField(WorkSession, blank=True)
what I noticed is that when i do the following:
invoice = Invoice()
session = WorkSession(a=a, b=b)
invoiceo.work_sessions.set([session])
The invoice_worksession junction table gets populated with a relation, even though I haven't saved invoice yet.
Meaning that the invoices table, there's no row, but in the junction table, there's a row that references an invoice that doesn't exist yet.
Is this normal ?
Because this is causing an integrity error on fixture teardown since the invoice doesn't exist and yet, there's a refrence to
an invoice id in the junction table
EDIT
The following is a better explanation of what I'm trying to do and the problem itself
Here are my tables
CREATE TABLE "drscm_worksession" (
"id" char(32) NOT NULL,
"start_timestamp" integer NOT NULL,
"end_timestamp" integer NOT NULL,
PRIMARY KEY("id"),
);
CREATE TABLE "drscm_invoice" (
"id" char(32) NOT NULL,
PRIMARY KEY("id"),
);
and the junction table
CREATE TABLE "drscm_invoice_work_sessions" (
"id" integer NOT NULL,
"invoice_id" char(32) NOT NULL,
"worksession_id" char(32) NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT),
FOREIGN KEY("invoice_id") REFERENCES "drscm_invoice"("id") DEFERRABLE INITIALLY DEFERRED,
FOREIGN KEY("worksession_id") REFERENCES "drscm_worksession"("id") DEFERRABLE INITIALLY DEFERRED
);
The objective is to create an invoice in the database with work_sessions via an api call.
The data i would need to send over, using my api client is this:
{work_sessions: [uuid1, uuid2] }
so the code would be
url = "/invoices/"
ws1 = WorkSession().save()
ws2 = WorkSession().save()
data = {'work_sessions': [ws1.id, ws2.id] }
self.client.post(path=url, data=data)
This works PERFECTLY.
BUT,
I thought, I don't want to have to write these objects manually as they get bigger in the real test.
So the approach was to do this:
invoice = Invoice()
invoice.work_sessions.set([ws1, ws2])
data = InvoiceSerializer(instance=invoice).data
self.client.post(path=url, data=data)
This does create the invoice with the according sessions BUT throws an IntegrityError exception during the teardown phase of the test
Here's why it throws the exception:
Invoice() creates an instance of the object and that instance has an id, but the invoice is NOT in the database
invoice.work_sessions.set([ws1, ws2]) creates 2 rows in the drscm_invoice_work_sessions table (junction table), and the invoice_id column takes the id of the NOT CREATED invoice instance. ( Which is the bug here)
During teardown, it tries do delete the relations in the junction table --> looks for an invoice with id: invoice.id in the drscm_invoices table, but it doesn't exit.
--> Throws an exception

You need to save the objects first to the database, such that these have a primary key, so:
invoice = Invoice.objects.create()
session = WorkSession.objects.create(a=a, b=b)
invoice.work_sessions.add(session)

Related

Django isn't creating db constraints for foreign keys with SQLite

I'm using Django 3.0.6
I'm adding ForeignKey and ManyToManyField to my models, but I've noticed that django creates the INDEX, but not the actual FOREIGN KEY constraints in the db.
I've tried to explicitly set db_constraint=True but as expected is useless, since it's True by default.
I've found so many answers explaining this, but only for very old Django versions, doing tricks for enabling it when it was otherwise disabled. Now instead it should just work out of the box. Couldn't find anything AT ALL regarding Django 3.
Code
class Token (models.Model):
owner = models.ForeignKey(Chiefdom, on_delete=models.CASCADE, db_constraint=True)
county = models.ManyToManyField(County, db_constraint=True)
amount = models.PositiveSmallIntegerField()
SQLite
CREATE TABLE IF NOT EXISTS Piece_token (
id integer PRIMARY KEY AUTOINCREMENT NOT NULL,
amount smallint unsigned NOT NULL,
owner_id integer NOT NULL
);
CREATE INDEX IF NOT EXISTS Piece_token_owner_id_d27c77f0 ON Piece_token (owner_id);
CREATE TABLE IF NOT EXISTS Piece_token_county (
id integer PRIMARY KEY AUTOINCREMENT NOT NULL,
token_id integer NOT NULL,
county_id integer NOT NULL
);
CREATE INDEX IF NOT EXISTS Piece_token_county_county_id_57802417 ON Piece_token_county (county_id);
CREATE INDEX IF NOT EXISTS Piece_token_county_token_id_e7798ae9 ON Piece_token_county (token_id);
CREATE UNIQUE INDEX IF NOT EXISTS Piece_token_county_token_id_county_id_b06b16cc_uniq ON Piece_token_county (token_id, county_id);
I have checked now with same version of Django and SQLite there are all foreign keys present
For example
SELECT * FROM pragma_foreign_key_list('auth_user_groups');
Note all foreign keys are deferred and checked from Django -> source

django: how to create a column whose default value is now()?

I am using django model to create a table. And the table has a inserted_time column, and I am using plain sql to insert the data, so in the insert sql I don't want to care about this column since I expected database should auto fill now() to the column (using mysql). But how to create such column whose default value is now() in django model. I am using auto-now, but it doesn't work.
updated:
I created a model as:
class TestDaniel(models.Model):
inserted_time = models.DateTimeField(db_index = True,auto_now_add=True)
Then I checked the mysql database after migration, the definition of the table is:
CREATE TABLE `orajob_testdaniel` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`inserted_time` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `orajob_testdaniel_e69592ad` (`inserted_time`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
From the table definition, inserted_time doesn't have a default value, what I want is to have a default value of now() for column of inserted_time, So what I inserted data using SQL (not django model), it will auto populate that column as now()
Well, usage of default values for DateField is clearly declared in the Django documentation. Django DateField Documentation
According to the docs, you can do the following:
inserted_time = models.DateTimeField(auto_now_add=True)
This will set current datetime to this field. This value will be by default and will be set once you create an object. Warning: now, even if you set custom datetime to this field, it will be ignored!
It will not work if you create your object using plain SQL. Django should use its own ORM in order to set default values.

How to relationship with mysql table in QT QSqlRelationalTableModel?

I am trying to work with QSqlRelationalTableModel of QT. I am new to MySQL table relationship but still I tried and can't make it work properly in QT.
I can get the result from MySQL:
create table stu(idd int auto_increment primary key,stu_name varchar(60),stu_age int);
create table stuInfo(idd int auto_increment primary key,stu_city varchar(60),stu_sub varchar(100), foreign key(id) references stu(id));
select stu.stu_name,stuInfo.stu_city from stu inner join stuInfo on stu.id=stuInfo.id;
To retrieve data from MySQL :
select stu.stu_name,stuInfo.stu_city from stu inner join stuInfo on stu.id=stuInfo.id;
In QT I can't make it work. I am getting confused with setRelation() and QSqlRelation() . I am not exactly understanding that how I can execute the same query in QT, I tried it in various way but sometime I get blank data, ugly header, errors etc.
Here is my learning code:
model = new QSqlRelationalTableModel();
model->setTable("stu");
model->setRelation(0,QSqlRelation("stu","id","stu_name","stu_age"));
model->setRelation(0,QSqlRelation("stuInfo","id","stu_city","stu_sub"));
model->select();
ui->tableView->setModel(model);
A QSqlRelation replaces the value of a field by the value of the other field in the relation, the replaced field won't appear in the query anymore, so you can't have 2 relations assigned to the same column, and you can't assign a relation to the primary key (as stated in the documentation of setRelation).
Basically the structure for which QSqlRelationalTableModel should be used would be a main table which would have 1 or more foreign index fields, and each of these fields could be replaced by the value of a chosen field in the tables from which the foreign indexes comes from (e.g.: to replace a "city_id" numerical field in the main table by the name of the city coming from another table for which that "city_id" is the primary key).
For what you want to do, you should use QSqlQueryModel with a manually constructed query instead of QSqlRelationalTableModel.
The problem is that your code does not really express the model you described.
You have a primary table called stuInfo, which references another table called stu.
To do this in Qt, you should create a table based on "stuInfo" (and not "stu"!):
model=new QSqlRelationalTableModel();
model->setTable("stuInfo");
Then you can implement your foreign key, as a relation:
model->setRelation(3,QSqlRelation("stu","id","stu_name"));
You need to point to index "3", which is the position of the reference field "id", on stuInfo table (0 will point to the primary key, which is not what you want!). The parameters of the QsqlRelation are the reference table name ("stu") the primary field name ("id") and the reference table field to which you want to point: in this case I am pointing to "stu_name"; if I wanted to point to the age, I could do something like this instead:
model->setRelation(3,QSqlRelation("stu","id","stu_age"));
After this code:
model->select();
ui->tableView->setModel(model);
you should have a view that shows you all the fields on stuInfo, and whose last field ("id") is mapped to the name (or age) on the "stu" table;

Open JPA how do I get back results from foreign key relations

Good morning. I have been looking all over trying to answer this question.
If you have a table that has foreign keys to another table, and you want results from both tables, using basic sql you would do an inner join on the foreign key and you would get all the resulting information that you requested. When you generate your JPA entities on your foreign keys you get a #oneToone annotation, #oneToMany, #ManyToMany, #ManyToOne, etc over your foreign key columns. I have #oneToMany over the foreign keys and a corresponding #ManyToOne over the primary key in the related table column I also have a #joinedON annotation over the correct column... I also have a basic named query that will select everything from the first table. Will I need to do a join to get the information from both tables like I would need to do in basic sql? Or will the fact that I have those annotations pull those records back for me? To be clear if I have table A which is related to Table B based on a foreign key relationship and I want the records from both tables I would join table A to B based on the foreign key or
Select * From A inner Join B on A.column2 = B.column1
Or other some-such non-sense (Pardon my sql if it is not exactly correct, but you get the idea)...
That query would have selected all column froms A and B where those two selected column...
Here is my named query that I am using....
#NamedQuery(name="getQuickLaunch", query = "SELECT q FROM QuickLaunch q")
This is how I am calling that in my stateless session bean...
try
{
System.out.println("testing 1..2..3");
listQL = emf.createNamedQuery("getQuickLaunch").getResultList();
System.out.println("What is the size of this list: number "+listQL.size());
qLaunchArr = listQL.toArray(new QuickLaunch[listQL.size()]);
}
Now that call returns all the columns of table A, but it lack's the column's of table B. My first instinct would be to change the query to join the two tables... But that kind of makes me think what is the point of using JPA then if I am just writing the same queries that I would be writing anyway, just in a different place. Plus, I don't want to overlook something simple. So what say you stack overflow enthusiasts? How does one get back all the data of joined query using JPA?
Suppose you have a Person entity with a OneToMany association to the Contact entity.
When you get a Person from the entityManager, calling any method on its collection of contacts will lazily load the list of contacts of that person:
person.getContacts().size();
// triggers a query select * from contact c where c.personId = ?
If you want to use a single query to load a person and all its contacts, you need a fetch in the SQL query:
select p from Person p
left join fetch p.contacts
where ...
You can also mark the association itself as eager-loaded, using #OneToMany(lazy = false), but then every time a person is loaded (vie em.find() or any query), its contacts will also be loaded.

Django AutoField not returning new primary_key

We've got a small problem with a Django project we're working on and our postgresql database.
The project we're working on is a site/db conversion from a PHP site to a django site. So we used inspect db to generate the models from the current PHP backend.
It gave us this and we added the primary_key and unique equals True:
class Company(models.Model):
companyid = models.IntegerField(primary_key=True,unique=True)
...
...
That didn't seem to be working when we finally got to saving a new Company entry. It would return a not-null constraint error, so we migrated to an AutoField like below:
class Company(models.Model):
companyid = models.AutoField(primary_key=True)
...
...
This saves the Company entry fine but the problem is when we do
result = form.save()
We can't do
result.pk or result.companyid
to get the newly given Primary Key in the database (yet we can see that it has been given a proper companyid in the database.
We are at a loss for what is happening. Any ideas or answers would be greatly appreciated, thanks!
I just ran into the same thing, but during a django upgrade of a project with a lot of history. What a pain...
Anyway, the problem seems to result from the way django's postgresql backend gets the primary key for a newly created object: it uses pg_get_serial_sequence to resolve the sequence for a table's primary key. In my case, the id column wasn't created with a serial type, but rather with an integer, which means that my sequence isn't properly connected to the table.column.
The following is based on a table with the create statement, you'll have to adjust your table names, columns and sequence names according to your situation:
CREATE TABLE "mike_test" (
"id" integer NOT NULL PRIMARY KEY,
"somefield" varchar(30) NOT NULL UNIQUE
);
The solution if you're using postgresql 8.3 or later is pretty easy:
ALTER SEQUENCE mike_test_id_seq OWNED BY mike_test.id;
If you're using 8.1 though, things are a little muckier. I recreated my column with the following (simplest) case:
ALTER TABLE mike_test ADD COLUMN temp_id serial NOT NULL;
UPDATE mike_test SET temp_id = id;
ALTER TABLE mike_test DROP COLUMN id;
ALTER TABLE mike_test ADD COLUMN id serial NOT NULL PRIMARY KEY;
UPDATE mike_test SET id = temp_id;
ALTER TABLE mike_test DROP COLUMN temp_id;
SELECT setval('mike_test_id_seq', (SELECT MAX(id) FROM mike_test));
If your column is involved in any other constraints, you'll have even more fun with it.