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
Related
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
I have two models:
class StateTax(models.Model):
name = models.CharField(max_length=256)
abbr = models.CharField(max_length=64, primary_key=True)
rate = models.IntegerField(default=0)
class Account(models.Model):
name = models.CharField(max_length=32)
tax_regions = models.ManyToManyField(SalesTaxRegion, blank=True, null=True, related_name="accounts")
However the SQL Django creates during syncdb seems to ignore the primary_key option. For example:
CREATE TABLE `airship_salestaxregion` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`name` varchar(256) NOT NULL,
`abbr` varchar(64) NOT NULL,
`rate` integer NOT NULL
)
;
CREATE TABLE `airship_account_tax_regions` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`account_id` integer NOT NULL,
`salestaxregion_id` integer NOT NULL,
UNIQUE (`account_id`, `salestaxregion_id`)
)
;
(note there is still an ID column in the first table and the relationship table references it)
As Like It says in the comment, you're getting your tables mixed up, you've given us python code for StateTax, not SalesTaxRegion.
I've tested your StateTax code with Django 1.4 and Postgres 9.2 and I get the following -
-- DROP TABLE testing_statetax;
CREATE TABLE testing_statetax
(
name character varying(256) NOT NULL,
abbr character varying(64) NOT NULL,
rate integer NOT NULL,
CONSTRAINT testing_statetax_pkey PRIMARY KEY (abbr)
)
There's no additional id field added.
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.
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.
Django 1.3
Python 2.7
In fixing one error, I've developed another. I'm not even sure where to begin, I'm so frustrated.
I got my app to show up in the Django admin section, and when I click on "add", it brings up the form. When I fill it out & submit it, it results in this error:
Exception Value: (1048, "Column 'user_id' cannot be null")
Seems simple & clear, but if I add user_id to my admin.py, then it results in the error I just fixed. If I leave it out and use the code that fixes the old error, then I get this error. Here's the relevant code:
admin.py (I commented out the code I was using, as that caused the earlier error)
from timeslip.models import Timeslip
from django.contrib import admin
class TimeslipAdmin(admin.ModelAdmin):
pass
#fields = ['user_id','day','hours_as_sec','part_of_day','drove','gas_money','notes']
admin.site.register(Timeslip, TimeslipAdmin)
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Timeslip(models.Model):
user=models.ManyToManyField(User)
day = models.DateField()
hours_as_sec = models.PositiveIntegerField()
part_of_day = models.CharField(max_length=16,choices=PART_O_DAY)
drove = models.BooleanField(default=False)
gas_money = models.DecimalField(max_digits=5,decimal_places=2)
notes = models.TextField()
class UserProfile(models.Model):
user = models.ForeignKey(User)
url = models.URLField("Website", blank=True)
position = models.CharField(max_length=50, blank=True)
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
If I make Timeslip's user=, into a ForeignKey(User) instead of a ManyToManyField(User), the error becomes:
Exception Value: (1062, "Duplicate entry '2' for key 'user_id'")
...which I understand, but that's exactly what I'm trying to do here now -- make multiple timeslips for each user.
How do I do that? I keep getting ridiculous errors no matter what I try, and it's hard finding documentation on this even though it seems like it would be a common & basic thing.
Update:
New question. Is Django caching stuff? I removed something from the model and reset everything but it still shows the old form. If it's showing me something it cached - How do I flush that cache?
Anyway, here's the output when I run manage.py sqlall timeslip
BEGIN;
CREATE TABLE `timeslip_timeslip` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`user_id` integer NOT NULL,
`day` date NOT NULL,
`hours_as_sec` integer UNSIGNED NOT NULL,
`part_of_day` varchar(16) NOT NULL,
`drove` bool NOT NULL,
`gas_money` numeric(5, 2) NOT NULL,
`notes` longtext NOT NULL
)
;
ALTER TABLE `timeslip_timeslip` ADD CONSTRAINT `user_id_refs_id_bb29929c` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`);
CREATE TABLE `timeslip_userprofile_user` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`userprofile_id` integer NOT NULL,
`user_id` integer NOT NULL,
UNIQUE (`userprofile_id`, `user_id`)
)
;
ALTER TABLE `timeslip_userprofile_user` ADD CONSTRAINT `user_id_refs_id_c73880a3` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`);
CREATE TABLE `timeslip_userprofile` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`url` varchar(200) NOT NULL,
`company` varchar(50) NOT NULL
)
;
ALTER TABLE `timeslip_userprofile_user` ADD CONSTRAINT `userprofile_id_refs_id_d05b8c80` FOREIGN KEY (`userprofile_id`) REFERENCES `timeslip_userprofile` (`id`);
CREATE INDEX `timeslip_timeslip_fbfc09f1` ON `timeslip_timeslip` (`user_id`);
COMMIT;