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:
And here is what I want to be able to do with the update page:
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.