Declaring foreign key in django model - django

I have recently started using django to read out oracle database. I am having a problem with created a model with foreign key.
Here is what I have in Oracle schema:
CREATE TABLE MTRS_CHANNELS(ID int NOT NULL PRIMARY KEY, ALIAS VARCHAR(255) NOT NULL UNIQUE, BOARDNUMBER int, CHIPNUMBER int, CHANNELNUMBER int, TYPEID int, SYSTEMNAME VARCHAR(255));
ALTER TABLE MTRS_CHANNELS ADD CONSTRAINT uniquesensornaming UNIQUE (BOARDNUMBER , CHIPNUMBER , CHANNELNUMBER, SYSTEMNAME);
CREATE TABLE MTRS_DATA(CHANNELID int NOT NULL, FLAG int, UPDATETIME TIMESTAMP, VALUE FLOAT);
ALTER TABLE MTRS_DATA ADD CONSTRAINT CHANNELIDENTIFIER FOREIGN KEY (CHANNEL_ID) REFERENCES MTRS_CHANNELS(ID);
Basically there are 2 tables MTRS_CHANNELS which stores channels identified by unique channelid and MTRS_DATA stores value for a channel, which is referenced by channelid as foreign key.
In my models.py I have :
from django.db import models
class Channel(models.Model):
id = models.IntegerField(primary_key=True)
alias = models.CharField(max_length=256)
boardnumber = models.IntegerField()
chipnumber = models.IntegerField()
channelnumber = models.IntegerField()
typeid = models.IntegerField()
systemname = models.CharField(max_length=255)
class Meta:
db_table = "MTRS_CHANNELS"
managed = False
class values(models.Model):
channel = models.ForeignKey(Channel,on_delete=models.CASCADE)
updatetime = models.DateTimeField()
value = models.FloatField()
class Meta:
db_table = "MTRS_DATA"
managed = False
The problem is that I am trying to load data in view it complains with:
ORA-00904: "MTRS_DATA"."ID": invalid identifier
When I try to modify channel to channel_id in values, I get
ORA-00904: "MTRS_DATA"."CHANNEL_ID_ID": invalid identifier
I might be missing something really obvious but the problem clearly comes from the way Foreign Key is declared.
Thanks and regards,
Ivan

Related

How to remove relation from django model with ManyToMany field?

How to remove relation from model with ManyToMany field?
I've got a model with ManyToManyField relation. I need to remove relation but not data from the following model:
class TxHomes(models.Model):
user = models.ManyToManyField(settings.AUTH_USER_MODEL)
home_id = models.PositiveIntegerField(primary_key=True, unique=True, null=False)
name = models.CharField(max_length=255, null=True)
geo_name = models.CharField(max_length=255, null=True)
payload = models.JSONField()
Django ORM got tables generated:
-- auto-generated definition
create table main_txhomes
(
home_id integer unsigned not null primary key,
name varchar(255),
geo_name varchar(255),
...
);
create table main_txhomes_user
(
id primary key autoincrement,
txhomes_id ...,
user_id ...
);
When I apply to do that with a following code
TxHomes.objects.filter(
home_id__in=TxHomes.objects.filter(user=USER_ID).values('home_id')
,user=USER_ID).delete()
i got entire data deleted from main_txhomes
I want to keep data in main_txhomes table, what i need is to delete relations from main_txhomes_user table. How to do that?
Solution is found:
User.objects.get(id=USER_ID).txhomes_set.clear()
This is how we remove all relation to txhomes table for the user

Django inspectdb not respecting primary key on two columns (postgres)

I am hooking up to an existing Postgres database with Django, using inspectdb to generate my models.py file.
The tables in the Postgres were created like this:
CREATE TABLE "a"
(
"id" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'::uuid,
CONSTRAINT "PK.a" PRIMARY KEY ("id")
)
CREATE TABLE "c"
(
"id" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'::uuid,
CONSTRAINT "PK.c" PRIMARY KEY ("id")
)
CREATE TABLE "b"
(
"aid" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'::uuid,
"cid" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'::uuid,
CONSTRAINT "PK.b" PRIMARY KEY ("aid","cid"),
CONSTRAINT "FK_a" FOREIGN KEY ("aid")
REFERENCES "a" ("id") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT "FK_c" FOREIGN KEY ("cid")
REFERENCES "c" ("id") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE
)
I then run python manage.py inspectdb --database primary > models_test.py, which results in the following models_test.py.
class A(models.Model):
id = models.UUIDField(primary_key=True)
class Meta:
managed = False
db_table = 'a'
class B(models.Model):
aid = models.OneToOneField(A, models.DO_NOTHING, db_column='aid', primary_key=True)
cid = models.ForeignKey('C', models.DO_NOTHING, db_column='cid')
class Meta:
managed = False
db_table = 'b'
unique_together = (('aid', 'cid'),)
class C(models.Model):
id = models.UUIDField(primary_key=True)
class Meta:
managed = False
db_table = 'c'
note the OneToOneField defined on aid.
If I instead create table b as:
CREATE TABLE "b"
(
"aid" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'::uuid,
"cid" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'::uuid,
CONSTRAINT "PK.b" PRIMARY KEY ("cid","aid"),
CONSTRAINT "FK_a" FOREIGN KEY ("aid")
REFERENCES "a" ("id") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE,
CONSTRAINT "FK_c" FOREIGN KEY ("cid")
REFERENCES "c" ("id") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE CASCADE
)
then rerun inspectdb I get the following output:
class A(models.Model):
id = models.UUIDField(primary_key=True)
class Meta:
managed = False
db_table = 'a'
class B(models.Model):
aid = models.ForeignKey(A, models.DO_NOTHING, db_column='aid')
cid = models.OneToOneField('C', models.DO_NOTHING, db_column='cid', primary_key=True)
class Meta:
managed = False
db_table = 'b'
unique_together = (('cid', 'aid'),)
class C(models.Model):
id = models.UUIDField(primary_key=True)
class Meta:
managed = False
db_table = 'c'
note the OneToOneField is now on cid. I suspect this is a bug, but I am inexperienced so wanted to ask here before reporting.
Secondary question: if this is a bug, is it worth reporting? Maybe the database design is very poor or uncommon?
I suspect this is a bug, but I am inexperienced so wanted to ask here before reporting.
Not really. Django does not work with primary keys that span over two or more columns, or at least not at the moment of writing. It has been requested, for example by ticket #373, but the designers decided to set this on "wontfix".
The documentation furthermore explains this:
Do Django models support multiple-column primary keys?
No. Only single-column primary keys are supported.
But this isn’t an issue in practice, because there’s nothing stopping
you from adding other constraints (using the unique_together model
option or creating the constraint directly in your database), and
enforcing the uniqueness at that level. Single-column primary keys are
needed for things such as the admin interface to work; e.g., you need
a single value to specify an object to edit or delete.
Likely you will not be able to do that in the (near) future, since a lot of Django tooling is built with the assumption that primary keys are "scalar" entities.
You thus likely should slightly redesign your existing database, and try to run inspectdb after that.

django throws error: Invalid column name 'id' (SQL SERVER)

I try to create a web app using django and connecting to a SQL Server Database. The table that I use to display the data in a django form consists of 2 columns. Both of them being a foreign key and both of them together building the primary key of the table
CREATE TABLE [dbo].[MyTable]( [ID_Field1] [int] NOT NULL,
[ID_Field2] [int] NOT NULL, CONSTRAINT [PK_Movies2Genres] PRIMARY
KEY CLUSTERED ( [ID_Field1] ASC, [ID_Field2] ASC )WITH (PAD_INDEX =
OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON
[PRIMARY]
ALTER TABLE [dbo].[MyTable] WITH CHECK ADD CONSTRAINT [FK_Field2]
FOREIGN KEY([ID_Field2]) REFERENCES [dbo].[Table2] ([ID_Field2])
ALTER TABLE [dbo].[MyTable] CHECK CONSTRAINT [FK_Field2]
ALTER TABLE [dbo].[MyTable] WITH CHECK ADD CONSTRAINT [FK_Field1]
FOREIGN KEY([ID_Field1]) REFERENCES [dbo].[Table1] ([ID_Movie])
ALTER TABLE [dbo].[MyTable] CHECK CONSTRAINT [FK_Field1]
Now, django apperantly cannot create a model corresponding to this kind of sql table structure, i.e. it cannot create a primary key consisting of more than one field. Instead, it sets the primary key on one of the 2 columns and in the meta section of the model class it sets
unique_together = (('id_field1', 'id_field2'),)
The complete model:
class MyTable(models.Model):
id_field1 = models.ForeignKey(Table1, on_delete=models.DO_NOTHING, db_column='ID_Field1')
id_field2 = models.ForeignKey(Table2, on_delete=models.DO_NOTHING, db_column='ID_Field2')
class Meta:
managed = False
db_table = 'MyTable'
unique_together = (('id_field1', 'id_field2'),)
However, this is what django inspectdb tells me to do. I know that django creates automatically a field called id when there is no primary key defined. This seems to be the case here, although there is a primary key defined. Any idea how to deal with that problem?
use: https://github.com/onysos/django-composite-foreignkey
class Customer(models.Model):
company = models.IntegerField()
customer_id = models.IntegerField()
name = models.CharField(max_length=255)
address = CompositeForeignKey(Address, on_delete=CASCADE, to_fields={
"tiers_id": "customer_id",
"company": LocalFieldValue("company"),
"type_tiers": RawFieldValue("C")
})
class Meta(object):
unique_together = [
("company", "customer_id"),
]
class Contact(models.Model):
company_code = models.IntegerField()
customer_code = models.IntegerField()
surname = models.CharField(max_length=255)
# virtual field
customer = CompositeForeignKey(Customer, on_delete=CASCADE, related_name='contacts', to_fields={
"customer_id": "customer_code",
"company": "company_code"
})

m2m for existing table (through/unique_together)

I have found in internet different examples on how to handle m2m relations with existing DB models, such as ex1 or here ex2, however I'm still not able to solve the error I get.
My models are depicted below. Basically, all the tables where created manually.
I got the following error message:
OperationalError: (1054, "Unknown column 'supervisor_project.id' in 'field list'").
I'm still a bit confused on when to use unique_together with through. Do you see any errors in the model below? The table supervisor_project has no id field and its PK is composed actually of two FK's, i.e. surrogate PK.
class Supervisor(models.Model):
name = models.CharField(max_length=45, blank=True, null=True, help_text="Name, e.g. John Smith")
class Meta:
managed = False
db_table = 'supervisor'
def __unicode__(self):
return self.name
class Project(models.Model):
title = models.CharField(max_length=45, blank=True, null=True)
supervisors = models.ManyToManyField(Supervisor, through='SupervisorProject', through_fields=('project', 'supervisor'))
class SupervisorProject(models.Model):
supervisor = models.ForeignKey('Supervisor', on_delete=models.CASCADE)
project = models.ForeignKey('Project', on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'supervisor_project'
unique_together = (('supervisor', 'project'),)
Django requires each model to have exactly one primary key field. It doesn't support multiple-column primary keys yet.
Since you haven't explicitly defined a primary key on the SupervisorProject model, Django assumes that there is an automatic primary key field id. When it includes the id field in a query, you get the error because it doesn't exist.
If possible, I would add an auto-incrementing id column to each intermediate table. There isn't a way to get Django to add the column to the tables automatically. You have set managed=False, so Django expects you to manage the database table.

Many to Many relation with Model inheritance in Django

I have an abstract object called parameter. parameter can be of several different types. For example - Numeric Parameter, Constant parameter, MultiValue parameter etc.
Each parameter can be related to many parameter of different types and vise versa.
After reviewing Django's doc on Model inheritance I decided what I need is a simple abstract base class. An example of a many to many relation from the base class can be found later on in the docs.
class ParameterBase(models.Model):
id = models.AutoField(primary_key=True)
description = models.CharField(max_length=200)
sort_order = models.DecimalField(null=False, max_digits=6, decimal_places=4)
m2m = models.ManyToManyField('self',related_name='dependent_on')
class Meta:
abstract = True
class ParameterConstant(ParameterBase):
value = models.DecimalField(null=False, blank=False, max_digits=20 , decimal_places=4)
class ParameterNumeric(ParameterBase):
minimum = models.DecimalField(null=True, blank=True, max_digits=20 , decimal_places=4)
maximum = models.DecimalField(null=True, blank=True, max_digits=20 , decimal_places=4)
So after syncing i could see that django created 4 tables -
CREATE TABLE "calc_parameterconstant_m2m" (
"id" serial NOT NULL PRIMARY KEY,
"from_parameterconstant_id" integer NOT NULL,
"to_parameterconstant_id" integer NOT NULL,
UNIQUE ("from_parameterconstant_id", "to_parameterconstant_id")
)
;
CREATE TABLE "calc_parameterconstant" (
"id" serial NOT NULL PRIMARY KEY,
"description" varchar(200) NOT NULL,
"sort_order" numeric(6, 4) NOT NULL,
"value" numeric(20, 4) NOT NULL
)
;
ALTER TABLE "calc_parameterconstant_m2m" ADD CONSTRAINT "from_parameterconstant_id_refs_id_f893bb67" FOREIGN KEY ("from_parameterconstant_id") REFERENCES "calc_parameterconstant" ("id") DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE "calc_parameterconstant_m2m" ADD CONSTRAINT "to_parameterconstant_id_refs_id_f893bb67" FOREIGN KEY ("to_parameterconstant_id") REFERENCES "calc_parameterconstant" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE TABLE "calc_parameternumeric_m2m" (
"id" serial NOT NULL PRIMARY KEY,
"from_parameternumeric_id" integer NOT NULL,
"to_parameternumeric_id" integer NOT NULL,
UNIQUE ("from_parameternumeric_id", "to_parameternumeric_id")
)
;
CREATE TABLE "calc_parameternumeric" (
"id" serial NOT NULL PRIMARY KEY,
"description" varchar(200) NOT NULL,
"sort_order" numeric(6, 4) NOT NULL,
"minimum" numeric(20, 4),
"maximum" numeric(20, 4)
)
Now, This is obviously was not my intention - I want to be able to connect a parameter of each type to parameters of other types as well. Is there any way to achieve this goal using Django ORM and model inheritance ?
If the base parameter model was a table on it's own with Many to Many relation to it self and the sub tables were connected with an unbinding one to one relation this might be a good solution here database wise.
I think multi-table inheritance will solve your problem. To use it, just remove abstract = True from the ParameterBase.Meta subclass (or remove that subclass completely).
When using multi-table inheritance, to access attributes of a particular subclass, Django has to know that you're dealing with that subclass.
For example, this will fail:
p = ParameterBase.objects.get(...) # get a ParameterBase that is a ParameterConstant
print p.value
Instead, you would have to do this:
p = ParameterConstant.objects.get(...)
print p.value
Or this:
p = ParameterBase.objects.get(...) # get a ParameterBase that is a ParameterConstant
print p.paramaterconstant.value
One way to solve this would be to replace
m2m = models.ManyToManyField('self',related_name='dependent_on')
with
m2m = models.ManyToManyField('ParameterBase', related_name='dependent_on')
but Django won't let you create an m2m field that points to a model that hasn't been installed yet or one that is abstract. At this point ParameterBase is both of those.
I'd do it like this
class Parameter(models.Model):
id = models.AutoField(primary_key=True)
description = models.CharField(max_length=200)
sort_order = models.DecimalField(null=False, max_digits=6, decimal_places=4)
class ParameterType(models.Model):
parameter = models.ForeignKey(Parameter)
related_parameters = models.ManyToManyField(Parameter,related_name='dependent_on')
class Meta:
abstract = True
class ParameterConstant(ParameterType):
value = models.DecimalField(null=False, blank=False, max_digits=20 , decimal_places=4)
class ParameterNumeric(ParameterType):
minimum = models.DecimalField(null=True, blank=True, max_digits=20 , decimal_places=4)
maximum = models.DecimalField(null=True, blank=True, max_digits=20 , decimal_places=4)
If you use a ManyToMany field in an abstract parent class, you should provide a unique related_name as stated in the docs... in your case, this should go fine:
m2m = models.ManyToManyField('self',related_name='%(app_label)s_%(class)s_m2m')