Reputation: 2358
I have a foreign key relationship in my Django (v3) models:
class Example(models.Model):
title = models.CharField(max_length=200) # this is irrelevant for the question here
not_before = models.DateTimeField(auto_now_add=True)
...
class ExampleItem(models.Model):
myParent = models.ForeignKey(Example, on_delete=models.CASCADE)
execution_date = models.DateTimeField(auto_now_add=True)
....
Can I have code running/triggered whenever an ExampleItem
is "added to the list of items in an Example
instance"? What I would like to do is run some checks and, depending on the concrete Example
instance possibly alter the ExampleItem
before saving it.
To illustrate
Let's say the Example
's class not_before
date dictates that the ExampleItem
's execution_date
must not be before not_before
I would like to check if the "to be saved" ExampleItem
's execution_date
violates this condition. If so, I would want to either change the execution_date
to make it "valid" or throw an exception (whichever is easier). The same is true for a duplicate execution_date
(i.e. if the respective Example
already has an ExampleItem
with the same execution_date
).
So, in a view, I have code like the following:
def doit(request, example_id):
# get the relevant `Example` object
example = get_object_or_404(Example, pk=example_id)
# create a new `ExampleItem`
itm = ExampleItem()
# set the item's parent
itm.myParent = example # <- this should trigger my validation code!
itm.save() # <- (or this???)
The thing is, this view is not the only way to create new ExampleItem
s; I also have an API for example that can do the same (let alone that a user could potentially "add ExampleItems
manually via REPL). Preferably the validation code must not be duplicated in all the places where new ExampleItems
can be created.
I was looking into Signals
(Django docu), specifically pre_save
and post_save
(of ExampleItem
) but I think pre_save
is too early while post_save
is too late... Also m2m_changed
looks interesting, but I do not have a many-to-many relationship.
What would be the best/correct way to handle these requirements? They seem to be rather common, I imagine. Do I have to restructure my model?
Upvotes: 0
Views: 45
Reputation: 77912
The obvious solution here is to put this code in the ExampleItem.save()
method - just beware that Model.save()
is not invoked by some queryset bulk operations.
Using signals handlers on your own app's models is actually an antipattern - the goal of signal is to allow for your app to hook into other app's lifecycle without having to change those other apps code.
Also (unrelated but), you can populate your newly created models instances directly via their initializers ie:
itm = ExampleItem(myParent=example)
itm.save()
and you can even save them directly:
# creates a new instance, populate it AND save it
itm = ExampleItem.objects.create(myParent=example)
This will still invoke your model's save method so it's safe for your use case.
Upvotes: 1