During a django model's object creation, how to pop out and save a value from a JSON array stored in JSONField of another model's object?

Question:

I am building a website in django where users can purchase lets say activation keys for a software. Now there are a limited number of these keys, all of whome are right now stored in a json array in a JSONField of a django model named Software.

Whenever a user purchases a key, an object of PurchasedKey model is created. During this, an activation key from the list of available keys must be saved in the attribute named activation_key of this object. After saving the key it must also be deleted from the list of available keys. This is the part I am not sure how to do.

I could just manipulate the JSON to retrieve one key from the list, remove it from the list and the update and save the Software object. But is there a better way to do this with probably also a better way to store the available keys instead of using JSONField.

# models.py
from django.db import models
from django.contrib.auth.models import User

class Software(models.Model):
    name=models.CharField(max_length=20)
    available_keys=models.JSONField()

class PurchasedKey(models.Model):
    purchased_by=models.ForeignKey(User, on_delete=models.CASCADE)
    software=models.ForeignKey(Software, on_delete=models.CASCADE)
    activation_key= What to do here
Asked By: HimDek

||

Answers:

Create a new model to hold the software keys

SoftwareKey(models.Model):
   software_key = models.CharFiled(length=256)
   // or put the json field here
   active = models.BooleanField(default=True)

Purchase(models.Model):
  purchased_by=models.ForeignKey(User, on_delete=models.CASCADE)
  software=models.ForeignKey(Software, on_delete=models.CASCADE)
  activation_key= models.ForeignKey(SoftwareKeys, on_delete=models.SET_NULL)
  active = models.BooleanField(default=True)

Then in the Purchase model add some unique partial indexes(https://pypi.org/project/django-partial-index/) to restrict adding the same key again.

Also you can make the activation_key 1-1, So activation key can be added only once.

activation_key= models.OneToOneField(SoftwareKeys, on_delete=models.SET_NULL)

Additionally you can add a new field in SoftwareKey

purchased = models.BooleanField(default=False)

and make it true if it purchased.
Deleting the purchased keys from the model is not a good practice, keep it there for future reference, use some flags to know whether its purchased or not.

Answered By: Jisson

Just override the Create Method of Purchase-Model when you are creating, in which you can modified the other model instance using save method or if you are using DRF then it would lot easier because in it you can use perform_create.

Answered By: HaxSpyHunter