简体   繁体   中英

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. 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. I've been following tutorials to build an API to handle data and everyone seems to recommend using DRF. All of my views use ListAPIView , RetrieveAPIView , and RetrieveUpdateAPIView . 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.

Database Table:

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

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:

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.

When I head to that URL, I am able to pull down a single instance. But when I go to change the value, I am just met with that error about inserting a duplicate key; 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.

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:

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 . It just seems like it's just there to prevent duplicates of LocationID and Setting together.

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. In your case the error is telling you location_id=3 and setting='Next Measurement' already exists.

To resolve this, either remove the constraint and run migrations or handle an Integrity error when adding/updating these fields.

Since you said

LocationID and Setting both as primary keys

You need to update the setting field to be unique as well.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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