Django Rest : Model field default value only while creating - django

I have a model with 2 char-fields. I want to give them default values when a row is getting created. This default values are sort of IDs which depends on time. This ID should never change. i.e. the default value should not be applied when subsequent updates happen on the row.
I'm trying to use update_or_create but, the defaults while creating & updating are not same. How can I put these Ids only while creating and ignore while updating?
I'm referring to this answer but no luck.
EDIT:
Following is the code for reference:
Model:
class UsersModel(models.Model):
id = models.CharField(db_column="id", max_length=25, primary_key=True)
key = models.CharField(db_column="key", max_length=100)
a = models.CharField(db_column="a",max_length=25, null=True, blank=True)
b = models.BigIntegerField(db_column="b", null=True, blank=True)
Views:
def post(self, request, format=None):
UsersModel.objects.update_or_create(a="a_val",defaults={"b":"b_val"})
Here, I want the id & key to take default values when the row is being created. When it is being updated, only b should get updated as shown in above code.

The same can be achieved by overiding the save function of models.Model to ensure that an id and key is given a value(default) in your case if the model is being created.
class UsersModel(models.Model):
id = models.CharField(db_column="id", max_length=25, primary_key=True)
key = models.CharField(db_column="key", max_length=100)
a = models.CharField(db_column="a",max_length=25, null=True, blank=True)
b = models.BigIntegerField(db_column="b", null=True, blank=True)
def save():
if not self.id:
self.id=get_default('id')
if not self.key:
self.key=get_default('key')
super(UsersModel, self).save(*args, **kwargs)
This will ensure when you create and update, the first time save is called, these variables are assigned a default value before getting saved in the db.

Related

Checking if a field in model is modified and creating instance of another model

I have two models
Project Model
class Project(models.Model):
name = models.CharField(max_length=200)
workflow = models.ForeignKey("WorkflowType", null=True, blank=True, on_delete=models.SET_NULL)
created_on = models.DateTimeField(auto_now_add=True)
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True)
def __str__(self):
return self.name
Workflow Instance Model
class WorkflowInstance(models.Model):
workflow_step = models.ForeignKey('WorkflowStep', null=True, blank=True, on_delete=models.CASCADE)
project = models.ForeignKey('Project', null=True, blank=True, on_delete=models.SET_NULL)
I want to check if the value of workflow field in "Project" models is added or changed for a particular project.
I am approaching the problem in following manner:
Checking if the previous and the new value of the "workflow" field in a project are different. If yes (modifies), then create the new instance of a project.
#receiver(pre_save, sender=Project)
def projectToBeUpdated(sender, instance, **kwargs):
if instance.id is None:
pass
else:
previous = Project.objects.get(id=instance.id)
if previous.workflow != instance.workflow:
print("workflow value modified. Please create a WorkflowInstance")
Problem: The comparison for previous and new value of the "workflow" field are happening in "pre_save" signal. But my new instance creation for workflowInstance is to be created in "post_save" signal. How can I do this?
Also, ideally I would like to store the previous value of workflow field in "pre_save" and get the new value of the field in "post_save". Reason being, save() method might fail for any reason, while I am comparing the previous and new value in "pre_save" method itself. Making changes in the database without confirming if the save() method executed successfully would be a wrong approach in my view.
You can override the model save method itself and use the following code logic, additionally if you want to identify what fields got modified you can use the dirtyfields package as well.
def save(self, *args, **kwargs):
if not self._state.adding:
changed_attr = self.get_dirty_fields()
else:
is_new = True
super().save(*args, **kwargs)```

How to obtain foreign key field name in post method?

I have an API which I pass some sample data. I am overwriting the post method by copying the Queryset so I can manipulate the output data. I would like to access the value of a foreign key relationship but I can only access the foreign key itself.
Like so:
def post(self, request, *args, **kwargs):
data_request = request.data
data_request = data_request.copy()
machine_na = data_request.__getitem__('project')
##this gives me the id of the foreign key
data_request.__setitem__('project', str('my value'))
return Response(data_request, *args, **kwargs)
Now that works. But I can't access the name of the foreign key.
My models as Foreign Keys:
class Machine(models.Model):
machine_name = models.CharField(max_length=120, primary_key=True)
def __str__(self):
return self.machine_name
class Project(models.Model):
project_name = models.CharField(max_length=120, primary_key=True)
def __str__(self):
return self.project_name
My model that refers to the others:
class Simulation(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default=1)
machine = models.ForeignKey(Machine, on_delete=models.CASCADE, default=1)
project
I hope I am clear enough. I would like to access the specific name of, for example, foreign key 2.
calling 'project.project_name' doesn't work and everything else I tried also not.
Any help is highly appreciated. Thanks in advance
Get the project object using the key and then get the name from the object.
project=Project.objects.get(id=int(self.request.POST['project']))
machine_na = project.project_name

Django models - create items which have to pass various tasks

I am trying to create a model for the following scenario:
An item is delivered and has to pass through various tasks in a certain order.
As soon as the item has passed through a task, the task is marked as completed.
The names of the tasks are known in advance.
For example:
Create item
task 1 'register the item'
task 2 'install the item'
task 3 'check logs of item'
I started with a simple model like this, but it looks very static to me and not really i what i am looking for.
models.py
class Item(models.Model):
item_id = models.PositiveIntegerField()
item_priority = models.PositiveSmallIntegerField(default=2)
item_date_created = models.DateField(auto_now_add=True)
task1_name = models.CharField(max_length=128)
task1_done_by = models.CharField(max_length=64, blank=True)
task1_done_date = models.DateField(null=True, blank=True)
task2_name = models.CharField(max_length=128)
task2_done_by = models.CharField(max_length=64, blank=True)
task2_done_date = models.DateField(null=True, blank=True)
# ... next 20 tasks
def __str__(self):
return str(self.item_id)
Trying Relational fields, for example a ManyToMany Field to pre define the tasks, i ended up with this:
class Task(models.Model):
name = models.CharField(max_length=128)
done_by = models.CharField(max_length=64, blank=True)
done_date = models.DateField(null=True, blank=True)
class Item(models.Model):
item_id = models.PositiveIntegerField()
item_priority = models.PositiveSmallIntegerField(default=2)
item_date_created = models.DateField(auto_now_add=True)
tasks = models.ManyToManyField(Task)
But, if i create two items with the same tasks and mark the task of the first item as done it will be marked as done in the second item as well -- this is not what i want.
How to assign a task to a specific item? Or which other model would best fit my scenario?
For executing code at object creation time you generally override the save() method. So when an Item gets created, in the save() method you create and link dynamically the Tasks to Item.tasks
class Item(models.Model):
item_id = models.PositiveIntegerField()
item_priority = models.PositiveSmallIntegerField(default=2)
item_date_created = models.DateField(auto_now_add=True)
# remove m2m !!!
def save(self, *args, **kwargs):
# If it has no pk then it is a *new* item
if not self.pk:
# Save by manually
# Call save of parent class (models.Model) creating the custom class
super(Item, self).save(*args, **kwargs)
# Now you have the PK
t1 = Task( name='T1', item=self )
t1.save()
#...
class Task(models.Model):
name = models.CharField(max_length=128)
done_by = models.CharField(max_length=64, blank=True)
done_date = models.DateField(null=True, blank=True)
# Add Item FK
item = models.ForeignKey(Item)
Note on Code Update:
1) I changed the direction: the task links to the item, not the other way round. See https://docs.djangoproject.com/en/1.11/topics/db/examples/many_to_one/
2) I still check if it is a new instance, then call super save and then I create the task for this item.
Make sure to check the self.pk value otherwise the code will get executed also when updating the object.
Also be aware of that you have to create a new Task object linked to an Item object every time. If you link one task object to two items then when item_a completes a task the task would appear to be completed also for item_b.
What you need to do is create task1 for item_a and task2 for item_b. This way you separate the tasks.
Read on:
https://docs.djangoproject.com/en/1.11/ref/models/instances/#saving-objects
https://docs.djangoproject.com/en/1.11/topics/db/models/#relationships

Auto increment django model field per user

I have this model:
class Invoice(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
data = models.TextField(default=None, blank=True, null=True)
number = models.PositiveIntegerField(default=0, null=False)
What I need is to auto-increment the field number for each separated user. The rationale is that each user has a list of Invoice, starting from number=1 to number=latest.number+1.
I do known about F() expressions, but can't figure out how to reference the latest/greatest number for each specific user. Maybe Invoice.objects.filter(owner=request.user).aggregate(Max('number')) is the path, but how do I ensure there is no race conditions between Max() and F()?
You can achieve this and similar functions by overriding save method in model and writing your custom logics to it.
class Invoice(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
data = models.TextField(default=None, blank=True, null=True)
number = models.PositiveIntegerField(default=0, null=False)
def save(self, *args, **kwargs):
if self.pk:
self.number += 1
# Write all your logic here, like handeling max value etc
return super(Invoice, self).save(*args, **kwargs)
you can get your first or last object like this:
# For last Object
Model.objects.latest('field') # Field can be id or pk or ...
# For first Object
Model.objects.all().first() # You can also use it on filter
A simple solution is you can make the number field as the primary key since its nature would be similar.
class Invoice(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
data = models.TextField(default=None, blank=True, null=True)
number = models.IntegerField(primary_key=True)
Or, you can make number as AutoField or BigAutoField.
number = models.AutoField()

Django based marketplace, creating a transaction history

I'm trying to create a transaction history for each transaction on a Django based marketplace.
I thought the best way of keeping track of this data was to override the save() function and create a Transaction record.
class Transaction(models.Model):
item = models.ManyToManyField(Item, blank=True)
buyer = models.ManyToManyField(User, related_name='buyer')
seller = models.ManyToManyField(User, related_name='seller')
description = models.CharField(max_length=500)
purchase_date = models.DateField(auto_now_add=True)
value = models.DecimalField(max_digits=7, decimal_places=2)
def save(self, *args, **kwargs):
self.buyer.money+=self.value
self.seller.money-=self.value
super(Transaction, self).save(*args, **kwargs)
Am I going about this all wrong? Currenlty I get...
'Transaction' instance needs to have a primary key value before a many-to-many relationship can be used.
You have to save your object before you can go through many-to-many relationships.
Please explain how you can have multiple buyers and sellers on a single transaction. (For the rest of this answer, I'm assuming that there aren't and you meant for these to be ForeignKey fields.)
The related names for buyer and seller are not clear. See below.
I'm not sure what description is for. Is it different from the item list?
item should be called items, since it can be plural, and you might want to create a custom junction table (using the "through" parameter) with a quantity field.
You forgot to save the related objects.
Modified version:
class Transaction(models.Model):
items = models.ManyToManyField(Item, through='TransactionItem', blank=True)
buyer = models.ForeignKey(User, related_name='transactions_as_buyer')
seller = models.ForeignKey(User, related_name='transactions_as_seller')
description = models.CharField(max_length=500)
purchase_date = models.DateField(auto_now_add=True)
value = models.DecimalField(max_digits=7, decimal_places=2)
def save(self, *args, **kwargs):
super(Transaction, self).save(*args, **kwargs)
self.buyer.money += self.value
self.buyer.save()
self.seller.money -= self.value
self.seller.save()
class TransactionItem(models.Model):
transaction = models.ForeignKey(Transaction)
item = models.ForeignKey(Item)
quantity = models.IntegerField()
The buyer and seller fields are many to many fields so self.buyer will never work, I think you were meaning to use ForeignKey instead of ManyToManyField.