How to enforce uniqueness constraint on a whole column in SQLAlchemy? - database

I have the following MySQL class.
class MyClassA(db.Model):
b = db.Column(db.String(12))
c = db.Column(db.String(12))
I want to make sure that the values of b are unique over the whole column. How can I change this declaration to enforce that constraint?

Well, you can use the flag "unique=True" like flask sql alchemy's documentation:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50), **unique=True**)
email = Column(String(120), unique=True)
Flask SQL Alchemy
I hope this helps

Related

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)

Django: UUID in automatic ManyToMany fields

I want to use UUIDs for database ids instead of autoincrement integers. I understand that this can be done by overriding the id in the Model class, for example:
from django.db import models
class Publication(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=30)
class Article(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
But, there is still a problem. The automatically generated table for the ManyToMany field uses an auto-incremented id, not a UUID.
Of course, this can address this by defining a "through" table as follows:
...
class Article(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication, through="ArticlePublication")
class ArticlePublication(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
article = models.ForeignKey(Article, on_delete=models.CASCADE)
publication = models.ForeignKey(Publication, on_delete=models.CASCADE)
But, according to the Django docs, when you use a "through" table: "Unlike normal many-to-many fields, you can’t use add(), create(), or set() to create relationships". Also, not "DRY".
I want to use add, create and set , and I like the "DRY" feature of the ManyToMany field. I also want to use UUIDs as ids.
I looked for a parameter to ManyToMany to pass in the definition of the automatically created "id" field, but there isn't one.
Am I missing something, or is this a limitation of Django's support for UUID as primary key?

Foreign key related to self. Creating an instance

I have a Foreign key that relates to self
class Fok(models.Model):
name = models.TextField(max_length=50)
fok = models.ForeignKey('self', related_name='foks')
trying to create a first instance of this class ending up with an error: fok.fok_id can not be NULL.
Where do I mistake? What should I do?
Your first instance doesn't have a related fok, therefore you must allow null values in this relation.
fok = models.ForeignKey('self', related_name='foks', null=True)

Django: How to have a multiple foreign keys references the same table in one table

I know that normally Django would create a foreign key called user_id if I simply do something like
from django.db import models
class Order(models.Model):
user = models.ForeignKey(User)
comments = models.CharField(max_length=400)
date_created = models.DateTimeField('created date')
class User(models.Model):
name = models.CharField(max_length=200)
age = models.IntegerField()
but what if I need three distinct foreign key in Order that all points to User? The three foreign keys would be user_created, user_modified, and user_status.
The solution is actually straight forward:
class Order(models.Model):
user_status = models.ForeignKey(User, related_name='orders_status')
user_created = models.ForeignKey(User, related_name='orders_created')
user_modified = models.ForeignKey(User, related_name='orders_modified')
You just need to define separate related_names to avoid ambiguity when accessing the Order from the User object.

Compound/Composite primary/unique key with Django

How can you create models (and thus tables) with a compound (composite) primary/unique key using Django?
Django does not support compound primary keys. You can create a single compound unique key with Meta.unique_together.
if you want only unique mixed fields together use belowcode:
class MyTable(models.Model):
class Meta:
unique_together = (('key1', 'key2'),)
key1 = models.IntegerField()
key2 = models.IntegerField()
But if you want unique together and one of column be primary, set primary argument for model column, similar below code:
class MyTable(models.Model):
class Meta:
unique_together = (('key1', 'key2'),)
key1 = models.IntegerField(primary_key=True)
key2 = models.IntegerField()

Resources