简体   繁体   English

Django 无法修改 SQL 服务器中具有多个主键的行

[英]Django unable to modify a row in SQL Server with multiple primary keys

I am using Django 3.0 , DjangoRestFramework 3.12 and django-mssql-backend 2.8.1 to build a webapp version of a current desktop app that stores everything in a legacy MS SQL Server database.我正在使用Django 3.0DjangoRestFramework 3.12django-mssql-backend 2.8.1来构建当前桌面应用程序的 webapp 版本,该版本将所有内容存储在旧版 MS SQL 服务器数据库中。 Due to this I am limited to database modifications that don't alter the table structure already in place.因此,我仅限于不会改变已经存在的表结构的数据库修改。

I built my Django models using the built-in legacy database model-generator and then made final modifications on my own.我使用内置的遗留数据库模型生成器构建了我的 Django 模型,然后自己进行了最终修改。 I've been following tutorials to build an API to handle data and everyone seems to recommend using DRF.我一直在按照教程构建一个 API 来处理数据,每个人似乎都建议使用 DRF。 All of my views use ListAPIView , RetrieveAPIView , and RetrieveUpdateAPIView .我的所有视图都使用ListAPIViewRetrieveAPIViewRetrieveUpdateAPIView Now, when I try to build part of the API to allow me to alter settings in one of the tables, I'm running into an error about inserting a duplicate key value.现在,当我尝试构建 API 的一部分以允许我更改其中一个表中的设置时,我遇到了关于插入重复键值的错误。

Database Table:数据库表:

dbo.systemDateTimeSettings
  - LocationID (PK, FK, int, not null)  
  - Setting (PK, nvarchar(50), not null)
  - Value (datetime, null)

Model: Model:

class SystemDatetimeSettings(models.Model):
    location_id = models.OneToOneField(Locations, models.DO_NOTHING, db_column='LocationID', primary_key=True)
    setting = models.CharField(db_column='Setting', max_length=50)
    value = models.DateTimeField(db_column='Value', blank=True, null=True)
    
    def get_api_url(self, request=None):
        return reverse("api:datetime-settings-update",
                        kwargs={
                            'location_id': int(self.location_id.location_id),
                            'setting': self.setting
                        },
                        request=request)

    class Meta:
        managed = False
        db_table = 'SystemDateTimeSettings'
        unique_together = (('location_id', 'setting'),)

Serializer:序列化器:

class SystemDatetimeSettingsSerializer(serializers.ModelSerializer):
    url = serializers.SerializerMethodField(read_only=True)
    
    class Meta:
        model = SystemDatetimeSettings
        fields = [
            'url',
            'location_id',
            'setting',
            'value'
        ]
        read_only_fields = [
            'location_id',
            'setting',
        ]
        
    def get_url(self, obj):
        request = self.context.get("request")
        return obj.get_api_url(request=request)

Url: Url:

path('locations/<int:location_id>/settings/datetime/<str:setting>/update', DatetimeSettingsUpdate.as_view(), name='datetime-settings-update'),

View:看法:

class DatetimeSettingsUpdate(RetrieveUpdateAPIView):
    lookup_field        = 'setting'
    serializer_class    = SystemDatetimeSettingsSerializer
    permission_classes  = [permissions.IsAuthenticated]
    # queryset            = SystemDatetimeSettings.objects.all()
    
    def get_object(self):
        location_id = self.kwargs.get('location_id')
        setting = self.kwargs.get('setting')
        return get_object_or_404(SystemDatetimeSettings, location_id=location_id, setting=setting)

The error I am getting is:我得到的错误是:

IntegrityError at /api/locations/3/settings/datetime/Next Measurement/update
('23000', "[23000] [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]Violation of PRIMARY KEY constraint 'aaaaaSystemDateTimeSettings_PK'. Cannot insert duplicate key in object 'dbo.SystemDateTimeSettings'. The duplicate key value is (3, Next Measurement). (2627) (SQLExecDirectW); [23000] [Microsoft][ODBC Driver 13 for SQL Server][SQL Server]The statement has been terminated. (3621)")

What I am reading from all of this is that the real, underlying issue is that the SQL Server database is using LocationID and Setting both as primary keys, but Django doesn't allow multiple primary keys.我从所有这些中读到的是,真正的潜在问题是 SQL 服务器数据库正在使用 LocationID 和 Setting 两者作为主键,但 Django 不允许多个主键。

When I head to that URL, I am able to pull down a single instance.当我前往那个 URL 时,我能够拉下一个实例。 But when I go to change the value, I am just met with that error about inserting a duplicate key;但是当我 go 更改值时,我只是遇到了关于插入重复键的错误; Which is unusual as I am not creating a new key value, just trying to modify a preexisting one.这是不寻常的,因为我没有创建一个新的键值,只是试图修改一个预先存在的键值。

I've looked around here for other instances where people have had multiple primary keys in the database like here , here , and here , but they all seem to mention using unique_together , which I have, but don't mention how to make use of that constraint.我在此处查看了人们在数据库中有多个主键的其他实例,例如此处此处此处,但他们似乎都提到了使用unique_together ,我有,但没有提到如何使用那个约束。

Is there any way to resolve this?有没有办法解决这个问题? Or do I need to modify the legacy database in some way?还是我需要以某种方式修改旧数据库?

EDIT: I scripted the aaaaaSystemDateTimeSettings_PK constraint in SQL Server and got:编辑:我在 SQL 服务器中编写了aaaaaSystemDateTimeSettings_PK约束并得到:

ALTER TABLE [dbo].[SystemDateTimeSettings] ADD  CONSTRAINT [aaaaaSystemDateTimeSettings_PK] PRIMARY KEY CLUSTERED 
(
    [LocationID] ASC,
    [Setting] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO

I tried deleting the constraint (local copy - don't panic) and then tried the update page.我尝试删除约束(本地副本 - 不要惊慌),然后尝试更新页面。 It went through but overwrote every Setting and Value that had the same LocationID with the same setting name and new value .它通过但覆盖了具有相同LocationID和相同setting名称和新value的每个SettingValue It just seems like it's just there to prevent duplicates of LocationID and Setting together.似乎它只是为了防止LocationIDSetting重复。

Just so that I am being crystal clear in what I'm trying to do, here is what my table looks like beforehand:只是为了让我对我想做的事情一清二楚,这就是我的桌子事先看起来的样子:

Before modifications修改前

And here is what I want to be able to do with the update page:这是我希望能够对更新页面执行的操作:

After modifying the database修改数据库后

Your error is telling you that you are trying to add/update a row in the database which already exists with your unique_together = (('location_id', 'setting'),) constraint in models.py To avoid this, you must ensure the values you are adding/updating do not already exist in the database.您的错误是告诉您您正在尝试添加/更新数据库中的一行,该行已经存在于models.py中的unique_together = (('location_id', 'setting'),)约束为避免这种情况,您必须确保您正在添加/更新的值在数据库中尚不存在。 In your case the error is telling you location_id=3 and setting='Next Measurement' already exists.在您的情况下,错误告诉您location_id=3setting='Next Measurement'已经存在。

To resolve this, either remove the constraint and run migrations or handle an Integrity error when adding/updating these fields.要解决此问题,请删除约束并运行迁移,或者在添加/更新这些字段时处理Integrity错误。

Since you said既然你说

LocationID and Setting both as primary keys LocationID 和将两者都设置为主键

You need to update the setting field to be unique as well.您还需要将设置字段更新为唯一。

setting = models.CharField(db_column='Setting', max_length=50, unique=True)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM