41

Context

I have the models AppVersion, App & DeployApp. In the AppVersion model users can upload APK files to the filesystem. I am using a pre_save signal to prevent uploading APK files with the same version_code for a specific App like this:

@receiver(pre_save, sender=AppVersion)
def prevent_duplicate_version_code(sender, instance, **kwargs):
    qs = AppVersion.objects.filter(app_uuid=instance.app_uuid, version_code=instance.version_code)
    if qs.exists():
        raise FileExistsError("Version code has to be unique for a specific app")

This signal does what I want, except it also raises the error when I am trying to create an object in the bridge-table DeployApp.

Models

# models.py

class App(models.Model):
    app_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
    app_name = models.CharField(max_length=100)


class AppVersion(models.Model):
    app_version_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
    app_uuid = models.ForeignKey(App, on_delete=models.CASCADE, related_name='app_versions')
    app_version_name = models.CharField(max_length=100)
    version_code = models.IntegerField(blank=True, null=True, editable=False)
    source = models.FileField(upload_to=get_app_path, storage=AppVersionSystemStorage()) 


class DeployApp(models.Model):
    deploy_app_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
    app_version = models.ForeignKey(AppVersion, on_delete=models.CASCADE)
    device_group = models.ForeignKey(DeviceGroup, on_delete=models.CASCADE)
    release_date = UnixDateTimeField()

My guess is that when creating an object of DeployApp the related AppVersion is also saved and thus the pre_save signal is called and raises the Exception.

I also tried to override the save() method for the AppVersion model but the results are the same.

How do I make sure that the Exception only happens upon creating a new AppVersion instance and does not happen when adding or editing a DeployApp instance?

Stevy
  • 3,228
  • 7
  • 22
  • 38
  • 3
    why not https://docs.djangoproject.com/en/2.2/ref/models/options/#unique-together unique_together unique_together = ['app_uuid', 'app_version_name']? – Brown Bear May 07 '19 at 13:48
  • 1
    no, the related model isn't saved if it already exists and all you're doing is creating a `DeployApp` object. That's not possible. There must be something in your code that explicitly saves a `AppVersion` object. – dirkgroten May 07 '19 at 13:48
  • 8
    @BearBrown `unique_together` will soon become obsolete. Have a look at [UniqueConstraint](https://docs.djangoproject.com/en/2.2/ref/models/constraints/#uniqueconstraint) – zar3bski Aug 26 '19 at 08:26

1 Answers1

94

Solved it thanks to Bear Brown his suggestion. I removed the signal and added UniqueConstraint to the AppVersion model like this:

class Meta:
    db_table = 'app_version'
    constraints = [
        models.UniqueConstraint(fields=['app_uuid', 'version_code'], name='unique appversion')
    ]
Stevy
  • 3,228
  • 7
  • 22
  • 38
  • Using UniqueConstraint() in Models, I am getting this issue : (models.W044) SQL Server does not support unique constraints on expressions. Can you please help with this ? – sodmzs1 May 02 '23 at 06:17