Django - How to prevent database foreign key constraint creation - django

I have a model that is backed by a database view.
class OrgCode(models.Model):
org_code = models.CharField(db_column=u'code',max_length=15)
org_description = models.CharField(max_length=250)
org_level_num = models.IntegerField()
class Meta:
db_table = u'view_FSS_ORG_PROFILE'
I need to reference this in another model
class AssessmentLocation(models.Model):
name = models.CharField(max_length=150)
org = models.ForeignKey(OrgCode)
I can't run syncdb because foreign key constraints cannot be created referencing a view.
u"Foreign key 'FK__main_asse__org__1D114BD1'
references object 'view_FSS_ORG_PROFILE'
which is not a user table.", None, 0, -214
7217900), None)
Command:
CREATE TABLE [main_assessmentlocation] (
[id] int IDENTITY (1, 1) NOT NULL PRIMARY KEY,
[name] nvarchar(150) NOT NULL,
[org] int NOT NULL REFERENCES [view_FSS_ORG_PROFILE] ([id]),
)
The workaround is to take out the Meta:db_table pointing to the view and let sync db create the the OrgCode table, then put the Meta:db_table back in after syncdb.
Is there a way to prevent the creation of foreign key constraints for certain models or fields?
Update: I added a static method to the related model indicating it's a view
class OrgCode(models.Model):
org_code = models.CharField(max_length=15)
org_description = models.CharField(max_length=250)
#staticmethod
def is_backend_view():
return True
Then overrode DatabaseCreation.sql_for_inline_foreign_key_references in django_mssql creation.py:
def sql_for_inline_foreign_key_references(self, field, known_models, style):
try:
field.rel.to.is_backend_view()
return "", False
except:
return super(DatabaseCreation,self).sql_for_inline_foreign_key_references(field, known_models, style)
The generated sql from syncdb leaves out the constraint:
CREATE TABLE [main_assessmentlocation] (
[id] int IDENTITY (1, 1) NOT NULL PRIMARY KEY,
[name] nvarchar(150) NOT NULL,
[org] int, -- NO FK CONSTRAINT ANYMORE --
);
It does involve hacking django_mssql so I'm going to keep on trying, maybe hooking into the django.db.backends.signals.connection_created signal will work...

django development version has a db_constraint field for ForeignKey model field - docs.

If you set managed=False (Django docs) in your model's Meta class, Django will not create the table when you run syncdb.
class AssessmentLocation(models.Model):
name = models.CharField(max_length=150)
org = models.ForeignKey(OrgCode)
class Meta:
managed = False
Django has a hook to provide initial sql data. We can (ab?)use this to get Django to create the table immediately after running syncdb.
Create a file myapp/sql/assessmentlocation.sql, containing the create table statement:
CREATE TABLE [main_assessmentlocation] (
[id] int IDENTITY (1, 1) NOT NULL PRIMARY KEY,
[name] nvarchar(150) NOT NULL,
[org] int, -- NO FK CONSTRAINT ANYMORE --
);
If you have other models with foreign keys to the AssessmentLocation model, you may have problems if Django tries to apply the foreign key constraint before executing the custom sql to create the table. Otherwise, I think this approach will work.

Related

Porting (postgresql) SQL into django model classes

I am porting a database schema (by hand), from (postgresql) SQL to django (1.10) model.
Here is my SQL:
CREATE TABLE ref_continent (
id SERIAL PRIMARY KEY,
name VARCHAR(64)
);
CREATE UNIQUE INDEX idxu_continent_nm ON ref_continent (name);
CREATE TABLE ref_geographic_region (
id SERIAL PRIMARY KEY,
continent_id INTEGER REFERENCES ref_continent(id) ON DELETE RESTRICT ON UPDATE CASCADE,
name VARCHAR(256)
);
CREATE UNIQUE INDEX idxu_geogreg_nm ON ref_geographic_region (name);
I am particularly interested in how to create the UNIQUE INDEX and how to link the FK to the PKey, since (AFAIK), django creates the primary key id behind the scenes?
According to docs
Note that when unique is True, you don’t need to specify db_index,
because unique implies the creation of an index.
Well, when you link a model as a foreign key, it links to default FK(that's id by default).
models.ForeignKey('app_name.ref_continent', )
there is another class class META that is used to fix these things. You don't have to almost ever specify to link FK to PK of other table, this is all done by Django. Have a at source code
models.py
class ref_continent(models.Model):
# Fields
name = models.CharField(max_length=64,unique=True)#making unique=True will generate query that will have UNIQUE INDEX both in PLSQL
def __unicode__(self):
return u'%s' % self.pk
class ref_geographic_region(models.Model):
# Fields
name = models.CharField(max_length=64,unique=True)
# Relationship Fields
continent_id = models.ForeignKey('app_name.ref_continent', )
def __unicode__(self):
return u'%s' % self.pk

Django Custom Primary Key Tries to insert Null

There are some constraints to this problem. We currently use a production database, with live Virtual Machine Statistics. We are trying to create a django interface, that interfaces with the tables we want our administrators to be able to edit. Thus, migrations are out of the question, for unless I have come to understand migrations wrong it will affect the current database structure and or data.
I matched the database structure exactly in my models.py file. However I have run into a few issues. One of the issues I have run into is when I try to add a new item under the admin control panel it will give me an integrity error as it is attempting to insert a null value for the field I have set as the primary key in the models.py file.
We are currently using an oracle database.
My Models.py not all of it but a sample of it.
class License(models.Model):
license_id = models.AutoField(primary_key = True, editable = False, db_column='license_id')
license_authority_id = models.ForeignKey(License_authoritie, on_delete = models.PROTECT, db_column='license_authority_id')
product = models.CharField(max_length = 20)
class Meta:
managed = False
db_table = 'licenses'
ordering = ['product']
def __unicode__(self): # Python 3: def __str__(self):
return self.product
class Vm_license(models.Model):
vm_license_id = models.AutoField(primary_key = True, db_column='vm_license_id')
vm_id = models.ForeignKey(Vm, on_delete = models.PROTECT, db_column='vm_id')
license = models.ManyToManyField(License)
class Meta:
managed = False
db_table = 'vm_licenses'
The error I get:
Request Method: POST
Request URL: http://127.0.0.1:8000/admin/portal/vm_license/add/
Django Version: 1.6.5
Exception Type: IntegrityError
Exception Value:
ORA-01400: cannot insert NULL into ("DEV"."VM_LICENSES"."VM_LICENSE_ID")
On top of that I have run into another problem.
For these two tables, under the vm_licenses section in the admin panel which is a table that holds all VM's and their assigned licenses. I need the ability to select multiple licenses at a time for each vm_id under the add section of the admin panel but i'm not quite sure how to do this.
admin.py code
class vm_license_admin(admin.ModelAdmin):
#list_display = ('vm_id', 'license_id')
list_display = ('vm_id',)
search_fields = ('vm_id__vm_name',)
ordering = ('vm_id',)
filter_horizontal = ('license',)
admin.site.register(Vm_license, vm_license_admin)
I also made an oracle trigger to auto increment a primary key if there is none, but im still getting the same error.
CREATE OR REPLACE TRIGGER license_trigger
BEFORE INSERT ON vm_licenses
FOR EACH ROW
BEGIN
SELECT vm_license_seq.nextval
INTO :new.vm_license_id
FROM dual;
END;
to be more percise I am using a manytomany field and it displays correctly when I goto add a new item before clicking save and getting the null error, however if I goto an existing item it will say table or view doesnt exist.
I was going to comment on your question, but I do not have the reputation yet...
but can I suggest you post your relevant admin.py code? Perhaps there is something within it relating to the Null PK error.
With regards to the second part, a ManyToManyField sounds more suitable.

Django ManyToManyField not present in Sqlite3

I'm new to Django and I have some issues with a ManyToMany relationship.
I work on a blastn automatisation and here are my classes:
class Annotation(models.Model):
sequence = models.IntegerField()
annotation = models.TextField()
start = models.IntegerField()
end = models.IntegerField()
class Blast(models.Model):
sequence = models.ManyToManyField(Annotation, through="AnnotBlast")
expectValue = models.IntegerField()
class AnnotBlast(models.Model):
id_blast = models.ForeignKey(Blast, to_field="id")
id_annot = models.ForeignKey(Annotation, to_field="id")
class Hit(models.Model):
id_hit = models.ForeignKey(Blast, to_field="id")
length = models.IntegerField()
evalue = models.IntegerField()
start_seq = models.IntegerField()
end_seq = models.IntegerField()
In a view, I want to access to Annotation's data from the rest of the model via this many to many field and then apply filters based on a form. But when I do a syncdb , the "sequence" field of the Blast class disappear :
In Sqlite3 :
.schema myApp_blast
CREATE TABLE "myApp_blast" (
"id" integer not null primary key,
"expectValue" integer not null
);
So I can't load data in this table as I want. I don't understand why this field disappear during the syncdb. How can I do to link the first class to the others (and then be able to merge data in a template) ?
A ManyToManyField isn't itself a column in the database. It's represented only by an element in the joining table, which you have here defined explicitly as AnnotBlast (note that since you're not defining any extra fields on the relationship, you didn't actually need to define a through table - Django would have done it automatically if you hadn't).
So to add data to your models, you add data to the AnnotBlast table pointing at the relevant Blast and Annotation rows.
For a many-to-many relationship an intermediate join table is created, see documentation here: https://docs.djangoproject.com/en/1.2/ref/models/fields/#id1

Adding values in my database via a ManyToMany relationship represented in admin.py

I've got a tiny little problem that, unfortunately, is taking all my time.
It is really simple, I already have my database and I created then modified models.py, and admin.py. Some staff users, who will need to enter values in my database, need the simpliest form to do so.
Here is my database :
-- Table NGSdb.line
CREATE TABLE IF NOT EXISTS `NGSdb`.`line` (
`id` INT NOT NULL AUTO_INCREMENT ,
`value` INT NOT NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
CREATE UNIQUE INDEX `value_UNIQUE` ON `NGSdb`.`line` (`value` ASC) ;
-- Table NGSdb.run_has_sample_lines
CREATE TABLE IF NOT EXISTS `NGSdb`.`run_has_sample_lines` (
`line_id` INT NOT NULL ,
`runhassample_id` INT NOT NULL ,
PRIMARY KEY (`line_id`, `runhassample_id`) ,
CONSTRAINT `fk_sample_has_line_line1`
FOREIGN KEY (`line_id` )
REFERENCES `NGSdb`.`line` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_sample_has_line_run_has_sample1`
FOREIGN KEY (`runhassample_id` )
REFERENCES `NGSdb`.`run_has_sample` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
-- Table NGSdb.run_has_sample
CREATE TABLE IF NOT EXISTS `NGSdb`.`run_has_sample` (
`id` INT NOT NULL AUTO_INCREMENT ,
`run_id` INT NOT NULL ,
`sample_id` INT NOT NULL ,
`dna_quantification_ng_per_ul` FLOAT NULL ,
PRIMARY KEY (`id`, `run_id`, `sample_id`) ,
CONSTRAINT `fk_run_has_sample_run1`
FOREIGN KEY (`run_id` )
REFERENCES `NGSdb`.`run` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_run_has_sample_sample1`
FOREIGN KEY (`sample_id` )
REFERENCES `NGSdb`.`sample` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
Here is my models.py :
class Run(models.Model):
id = models.AutoField(primary_key=True)
start_date = models.DateField(null=True, blank=True, verbose_name='start date')
end_date = models.DateField(null=True, blank=True, verbose_name='end date')
project = models.ForeignKey(Project)
sequencing_type = models.ForeignKey(SequencingType)
def __unicode__(self):
return u"run started %s from the project %s" % (self.start_date,self.project)
class Line(models.Model):
id = models.AutoField(primary_key=True)
value = models.IntegerField()
def __unicode__(self):
return u"%s" % str(self.value)
class RunHasSample(models.Model):
id = models.AutoField(primary_key=True)
run = models.ForeignKey(Run)
sample = models.ForeignKey(Sample)
dna_quantification_ng_per_ul = models.FloatField(null=True, blank=True)
lines = models.ManyToManyField(Line)
def __unicode__(self):
return u"Sample %s from run %s" % (self.sample, self.run)
And here is my admin.py :
class RunHasSamplesInLine(admin.TabularInline):
model = RunHasSample
fields = ['sample', 'dna_quantification_ng_per_ul', 'lines']
extra = 6
class RunAdmin(admin.ModelAdmin):
fields = ['project', 'start_date', 'end_date', 'sequencing_type']
inlines = [RunHasSamplesInLine]
list_display = ('project', 'start_date', 'end_date', 'sequencing_type')
As you can see, my samples are displayed in lines in the run form so that the staff can easily fullfill the database.
When I try to fill the database I have this error :
(1054, "Unknown column 'run_has_sample_lines.id' in 'field list'")
Of course, there are no field "lines" in my database ! It is a many to many field so I already created my intermediate table !
Okay okay ! So I tried to create the model for the intermediate table (run_has_sample_lines) and add a "through" to the ManyToManyField in the RunHasSample model. But, as I add manually the "through", I cannot use the ManyToMany field. The only way to add lines to the admin view is to stack them in lines... As you can see the samples are already in lines, it is impossible to put a new "inlines" in the already in lines samples...
Finally, I just tried to see what django had created with the manage.py sqlall.
I see that :
CREATE TABLE `run_has_sample_lines` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`runhassample_id` integer NOT NULL,
`line_id` integer NOT NULL,
UNIQUE (`runhassample_id`, `line_id`)
)
;
ALTER TABLE `run_has_sample_lines` ADD CONSTRAINT `line_id_refs_id_4f0766aa` FOREIGN KEY (`line_id`) REFERENCES `line` (`id`);
It seems that there are no foreign key on the run_has_sample table whereas I created it in the database in the first place. I guess that the problem is coming from here but I cannot resolve it and I really hope that you can...
Thank you very much !
you may wish to try a 'through' attribute on the many-to-many relationship and declare your intermediate table in Django.
I found where the problem is...
It is not a problem in the ManyToManyField but in the intermediate table. Django refused that my intermediate table doesn't have an unique id !
So, in the sql which created django, it created automatically an unique id named "id", but in my database I didn't create one (because the couple of two foreign key is usually enough).
Next time, I'll be more carefull.

Django: ManyToManyField creates duplicate column name

I have two types of users in my application: auth.User, which comes from django.contrib.auth (the standard Django authentication module) and mysql.User, which is in my own module. In addition, mysql.User inherits from an abstract model. The whole thing looks similar to this (some fields were omitted for brevity):
class Resource(Model):
class Meta:
abstract = True
owners = ManyToManyField('auth.User', related_name='%(class)s_owners')
class User(Resource):
name = CharField(max_length=16)
host = CharField(max_length=64)
class Database(Resource):
name = CharField(max_length=64)
As you can see, I want to make it so that multiple auth.Users may "own" a given mysql.User and a given mysql.Database, hence the ManyToManyFields. However, when I go to run ./manage.py syncdb I get the error:
_mysql_exceptions.OperationalError: (1060, "Duplicate column name 'user_id'")
Indeed, ./manage.py sql mysql shows the source of the error (again, some columns and ALTER TABLE statements omitted for brevity):
CREATE TABLE `mysql_database` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`name` varchar(64) NOT NULL,
UNIQUE (`server_id`, `name`)
);
CREATE TABLE `mysql_user` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`name` varchar(16) NOT NULL,
`host` varchar(64) NOT NULL,
UNIQUE (`server_id`, `name`, `host`)
);
CREATE TABLE `mysql_database_owners` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`database_id` integer NOT NULL,
`user_id` integer NOT NULL,
UNIQUE (`database_id`, `user_id`)
);
CREATE TABLE `mysql_user_owners` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`user_id` integer NOT NULL,
`user_id` integer NOT NULL, -- <<<<< here is the conflict >>>>>
UNIQUE (`user_id`, `user_id`)
);
Notice how the intermediate table for Database is created without a naming conflict but the table for User has a conflict. I don't see where a ManyToManyField provides a way for one to provide column names in the intermediate table, but unfortunately I think that's what I need.
Another method I tried was to explicitly create the intermediate table and use the through option of ManyToManyField, like so:
class Resource(models.Model):
class Meta:
abstract = True
owners = models.ManyToManyField('auth.User', related_name='%(class)s_owners', through='Owner')
class Owner(models.Model):
user = models.ForeignKey('auth.User', related_name='Resource_owners')
resource = models.ForeignKey(Resource)
But then I get this error:
AssertionError: ForeignKey cannot define a relation with abstract class Resource
Which is to be expected with Django.
So, short of renaming mysql.User to something like mysql.DBUser, is there any way to avoid the naming conflict created by Django?
How about creating the many to many table separately, avoiding the use of the ManyToMany field? You could use a manager or method to return a list of users for a given resource and visa versa.
class Resource(models.Model):
...
class Meta:
abstract = True
def owners(self):
return ResourceOwner.objects.filter(resource=self)
class ResourceOwners(models.Model):
resource = models.ForeignKey(Resource)
owner = models.ForeignKey(User)
class Meta:
abstract = True