Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
679 views
in Technique[技术] by (71.8m points)

mysql - Working with composite primary key in django project with legacy database

I have a legacy database, where some table contains composite primary key. The model I get by running manage.py inspectdb command looks like this.

class MyTable(models.Model):
    field1_id = models.IntegerField(db_column='field1id', primary_key=True)
    is_favorite = models.BooleanField(db_column='isfavorite', default=False, null=False)
    is_admin = models.BooleanField(db_column='isadmin', default=False, null=False)
    role = models.IntegerField(default=USER_GUEST, null=False)
    field2 = models.BooleanField(null=False, default=True)
    field3 = models.BooleanField(null=False, default=True)
    is_active = models.BooleanField(db_column='isactive', null=False, default=True)

    user = models.ForeignKey(
        CustomUser, models.DO_NOTHING, db_column='userid', primary_key=True)

    class Meta:
        managed = False
        db_table = 'mytable'
        unique_together = (('user', 'field1_id'),)

I can fetch data normally. However, the problem arises when I want to run save() command on some model instance. The query django executes is not correct one. For example:

>>> from web_services.apps.my_app.models import MyTable
>>> g = MyTable.objects.get(field1_id=12)
>>> g.is_active = True
>>> g.save()
>>> connection.queries[-1]
{'time': '0.000', 'sql': 'UPDATE `mytable` SET `isfavorite` = 0, `isadmin` = 1, `role` = 3, `field2` = 1, `field3` = 1, `isactive` = 1 WHERE `mytable`.`field1id` = 12'}

But I need:

{'time': '0.000', 'sql': 'UPDATE `mytable` SET `isfavorite` = 0, `isadmin` = 1, `role` = 3, `field2` = 1, `field3` = 1, `isactive` = 1 WHERE `mytable`.`field1id` = 12' AND `mytable`.`userid` = 1'}

Since django does not support composite primary key, what would be the best solution to overcome that problem? Note, it's legacy database table and it didn't have AutoField.

EDIT: adds legacy table structure

+------------+------------+------+-----+---------+-------+
| Field      | Type       | Null | Key | Default | Extra |
+------------+------------+------+-----+---------+-------+
| userid     | int(11)    | NO   | PRI | NULL    |       |
| field1id   | int(11)    | NO   | PRI | NULL    |       |
| isfavorite | int(11)    | NO   |     | 0       |       |
| isadmin    | int(11)    | NO   |     | 0       |       |
| role       | int(11)    | YES  |     | NULL    |       |
| field2     | tinyint(1) | NO   |     | 1       |       |
| field3     | tinyint(1) | NO   |     | 1       |       |
| isactive   | tinyint(4) | NO   |     | 1       |       |
+------------+------------+------+-----+---------+-------+
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Unfortunately django does not yet have a composite primary key (composite primary keys are also called multi column primary keys)

There is a third party library, unsurprisingly name django-compositekey that makes it possible to create a model that has a multi column primary key. However that project is not maintained and not compatible with the latest versions of django.

Your best bet, is to add a new column into your table which is a an AutoField and make it the new primary key. The fields that make up the primary key at present can then be marked as unique_together. While this may not be ideally for a few queries. It's good enough for most.

If you update your question with the current table structure (as shown in your sql console) I will update my answer to show what the model will look like.

Update There are lots of different ways in which you can drop the old composite primary key and replace it with a new auto increment primary key. Here is the easiest, it involves just SQL

CREATE TABLE newtable LIKE mytable;
ALTER TABLE newtable DROP PRIMARY KEY;
ALTER TABLE newtable ADD `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY;
RENAME TABLE mytable to mytable_old;
RENAME TABLE newtable to mytable;
INSERT INTO mytable(userid,field1id, rest of the fields) 
    SELECT * FROM mytable_old;

Then edit your model and remove the primary_key=True flag from those fields.

Footnote:
Some databases like sqlite for example does not support the LIKE clause in a CREATE TABLE. In these cases, you will have to look up the create table statement for the original table, and copy paste after editing the table name. Judging by your table structure you seem to be using mysql and it supports the LIKE clause.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...