Django QuerySet API check if is unique for this id - django

This is how my model looks like:
class Catalog(models.Model):
global_id = models.IntegerField(unique=True)
name = models.CharField(max_length=30)
short_name = models.CharField(max_length=10)
description = models.CharField(max_length=200, blank=True)
version = models.CharField(max_length=10)
class Meta:
constraints = [
models.CheckConstraint(
check=models.Q(
//
CHECK IF VERSION IS UNIQUE FOR THIS PARTICULAR GLOBAL_ID
//
),
name="%(app_label)s_%(class)s_unique_version",
)
]
As you can see, I need to make sure that version models are unique for a particular global_id, I just don't know how. Help.

The code below allows you to set a unique constraint against the two fields.
Note for this to work the unique constraint is removed from the global_id field.
class Catalog(models.Model):
global_id = models.IntegerField(max_length=10)
version = models.CharField(max_length=10)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['global_id', 'version'],
name='unique_global_id_for_version'
)
]
>>> Catalog.objects.create(global_id=80, version=80)
<Catalog: Catalog object (10)>
>>> Catalog.objects.create(global_id=90, version=80)
<Catalog: Catalog object (11)>
>>> Catalog.objects.create(global_id=80, version=80)
django.db.utils.IntegrityError: UNIQUE constraint failed: ...
>>> Catalog.objects.create(global_id=80, version=90)
<Catalog: Catalog object (12)>
Above, when trying to create a Catalog with global_id=80 it failed as it already existed for version=80, but later a Catalog with global_id=80 was able to be created for a Catalog with version=90.
The docs for this constraint

According to the docs you can use the "unique" option for django 3.1 :
If True, this field must be unique throughout the table.
This is enforced at the database level and by model validation. If you
try to save a model with a duplicate value in a unique field, a
django.db.IntegrityError will be raised by the model’s save() method.
Which would make your declaration like this :
version = models.CharField(max_length=10, unique=True)

Related

ForeignKey updates aren't cascading on Django created tables in Postgres

This is the error that I get when I try to update the value in the "parent" table that the foreign key is looking at in a related table:
ERROR: update or delete on table "product" violates foreign key constraint "pd_family_product_guid_ada83db3_fk_product_guid" on table "pd_family"
DETAIL: Key (guid)=(902d30b8-26ba-ea11-a812-000d3a5bbb60) is still referenced from table "pd_family".
SQL state: 23503
This is what I have for my models:
class Product(models.Model):
guid = models.UUIDField(primary_key=True)
product = models.CharField(max_length=10)
year = models.IntegerField()
previous_product_name = models.CharField(max_length=10)
class StandardProductFieldsMixin(models.Model):
product_guid = models.ForeignKey('Product', on_delete=models.CASCADE, db_column='product_guid')
class Meta:
abstract = True
class ActiveFieldMixin(models.Model):
active = models.BooleanField()
class Meta:
abstract = True
class Family(StandardProductFieldsMixin, ActiveFieldMixin):
new_code = models.IntegerField(null=True)
code = models.DecimalField(max_digits=20, decimal_places=2)
title = models.CharField(max_length=255)
desc = models.TextField(null=True)
fam_desc = models.TextField(null=True)
When I try to change a value of guid in Product, my expectation is that it would automatically change it in Family as well. I'm trying to change it with something like:
UPDATE product
SET guid = '902D30B8-26BA-EA11-A812-000D3A5BBB6B'
WHERE guid = '902D30B8-26BA-EA11-A812-000D3A5BBB60'
I guess I was under the wrong impression. Do I need to do something additional in the model? Looked at the documentation for something like on_update, but not seeing that either an **options or as a parameter for models.ForeignKey.
From what I gather after reading about it for more than an hour, if I want this kind of functionality I just need to add it Postgres manual, by dropping the constraint and adding it back with ON UPDATE CASCADE.
Apparently I'm under the wrong impression:
https://stackoverflow.com/a/61648910/3123109
Sounds like Django model migrations implement neither ON DELETE nor ON UPDATE database CASCADES options. I guess I'd need to drop into the database and implement them there.

Bypassing the unique constraint when defining a foreign key to just one field in Django

I've a model as shown below:
class Instance(models.Model):
data_version = models.IntegerField(default=1, unique=True)
name = models.CharField(max_length=200)
The data_version field has to be related to all other models in the application:
class Source(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
version = models.ForeignKey(Instance, on_delete=models.CASCADE, to_field='data_version', null=True)
The problem here is that Django requires the field data_version to be a unique field for me to be able to define such a relationship but that simply doesn't fit into my use case. I need to have multiple Instance objects in the app, each with version numbers starting from 1.
What I'd like is to have a unique constraint on the combination of name and data_version but then Django doesn't allow defining Foreign key relationships as shown above. Is there a way I can bypass this restriction?

Setting default value of field in model to another model instance

Model
class SlackPermission(models.Model):
#fields
class GithubPermission(models.Model):
#fields
class Employee(models.Model):
#fields
slack_permission = models.OneToOneField(SlackPermission, on_delete=models.CASCADE, related_name='Slack',default=SlackPermission.objects.get(pk=1))
github_permission = models.OneToOneField(GithubPermission, on_delete=models.CASCADE, related_name='Github',default=GithubPermission.objects.get(pk=1))
Error:
ValueError: Cannot serialize: <GithubPermission: GithubPermission object (1)>
There are some values Django cannot serialize into migration files.
I am creating API just to create Employee. Where there is not option of giving slackpermissions and githubpermissions. How do I give default value in there?
The problem is that the default is calculated immediately, and for migrations, it can not really serialize that.
That bing said, it is not very useful to do this anyway. You can just pass the primary key as default value. This is specified in the documentation on the default=… parameter [Django-doc]:
For fields like ForeignKey that map to model instances, defaults should be the value of the field they reference (pk unless to_field is set) instead of model instances.
So we can write this as:
class Employee(models.Model):
full_name = models.CharField(max_length=100)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
slack_permission = models.OneToOneField(
SlackPermission,
on_delete=models.CASCADE,
related_name='Slack',
default=1
)
github_permission = models.OneToOneField(
GithubPermission,
on_delete=models.CASCADE,
related_name='Github',
default=1
)
Note that you should ensure that there exists an object with that primary key. Therefore it might not be ideal to do that.
The issue here is that you are attempting to set a field value to an object instance. So your default value should be just 1 if you are certain of the pk.
Also, I am not sure the advantage of creating two separate models for these permission values. Seems like they can just be fields in your employee model. Seems like these permissions share identical fields as well which will allow you to flatten them a bit.

m2m for existing table (through/unique_together)

I have found in internet different examples on how to handle m2m relations with existing DB models, such as ex1 or here ex2, however I'm still not able to solve the error I get.
My models are depicted below. Basically, all the tables where created manually.
I got the following error message:
OperationalError: (1054, "Unknown column 'supervisor_project.id' in 'field list'").
I'm still a bit confused on when to use unique_together with through. Do you see any errors in the model below? The table supervisor_project has no id field and its PK is composed actually of two FK's, i.e. surrogate PK.
class Supervisor(models.Model):
name = models.CharField(max_length=45, blank=True, null=True, help_text="Name, e.g. John Smith")
class Meta:
managed = False
db_table = 'supervisor'
def __unicode__(self):
return self.name
class Project(models.Model):
title = models.CharField(max_length=45, blank=True, null=True)
supervisors = models.ManyToManyField(Supervisor, through='SupervisorProject', through_fields=('project', 'supervisor'))
class SupervisorProject(models.Model):
supervisor = models.ForeignKey('Supervisor', on_delete=models.CASCADE)
project = models.ForeignKey('Project', on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'supervisor_project'
unique_together = (('supervisor', 'project'),)
Django requires each model to have exactly one primary key field. It doesn't support multiple-column primary keys yet.
Since you haven't explicitly defined a primary key on the SupervisorProject model, Django assumes that there is an automatic primary key field id. When it includes the id field in a query, you get the error because it doesn't exist.
If possible, I would add an auto-incrementing id column to each intermediate table. There isn't a way to get Django to add the column to the tables automatically. You have set managed=False, so Django expects you to manage the database table.

Is it possible to instruct Django to save a model instance to a particular table based on its fields?

I'm attempting to construct a Django application that models an existing set of tables. These tables all have the same fields, plus custom fields per table. What I'm wanting to do is model this structure, and have records save to a particular table based on what table model they are attached to.
These tables can be created quite often, so it is unfeasible to construct new models per table.
Perhaps the code will demonstrate what I'm trying to do more clearly:
class CustomField(models.Model):
column_name = models.CharField(max_length=100)
description = models.CharField(max_length=255, blank=True, null=True)
class CustomData(models.Model):
custom_field = models.ForeignKey(CustomField)
value = models.CharField(max_length=100, blank=True, null=True)
# value will always be a nullable varchar(100)
class Table(models.Model):
table_name = models.CharField(max_length=255)
name = models.CharField(max_length=100)
custom_fields = models.ManyToManyField(CustomField)
class Record(models.Model):
table = models.ForeignKey(Table)
... list of common fields omitted ...
custom_values = models.ManyToManyField(CustomData)
When saving a new record that has a foreign key to 'table_1', I would like the eventual operation to be along the lines of insert into table_1 (..fields..) values (..field values..)
Is this possible? I guess I could hook into signals or the save method, but I'd like to find the simplest approach if such exists.
You can create unmanaged models dynamically. You just need to create a dict mapping column names to the data values. Once you have that, you can do the following:
from django.db import models
# This is the dict you created, mapping column names to values
values = {col_1: value_1, col_2: value_2, col_3: value_3, ... }
# Create a dict defining the custom field types, eg {col_name: django_field}
attrs = dict((c, models.CharField(max_length=100, blank=True, null=True)) for c in values)
# Add a Meta class to define the table name, eg table_1
class Meta:
app_label = myapp
db_table = 'table_1'
managed = False
attrs['Meta'] = Meta
attrs['__module__'] = 'path.to.your.apps.module'
DynamicModel = type('MyModel', (models.Model,), attrs)
# Save your data
DynamicModel.objects.create(**values)
Wrap this up in a function, and put it in your .save() method on Record. If you have any common fields, you can add them to attrs, or even better: create an abstract model with all the common fields and inherit that in the last line above instead of models.Model.