I use the save() method to override the models.Model method in order to manipulate a bit of the properties before saving them in the DB (for example, issuing a Google MAPS API call to get geolocation of an address).
However, I would not like to issue a call to Google everytime I update an entry via the admin panel, but only when I insert a new entry.
Is there any easy way to do it (instead of running a query against the DB inside the save() method and check if the object already exists)?
Meir
check the primary key of that said object on save()
def save(self, *args, **kwargs):
if self.pk:
# this is executed when updating (pk exists)
else:
# this is executed when inserting
super(Model, self).save(*args, **kwargs)
something like that
source
read the link thoroughly, theres a gotcha there
EDIT: gotchas, read the comments for more gotchas
https://docs.djangoproject.com/en/dev/ref/models/querysets/#get-or-create
basically, with this you can check if your object exists and run your update, or create a new one if the object isn't int your database
Related
How to use PUT method for creating an object on particular id if no object is available
on that id in Django Rest Framework?
You can try update_or_create()
e.g:
class YourAPIView(APIView):
def put(self, request, **kwargs):
serializer = YourSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
obj, created = YourModel.objects.update_or_create(
id=kwargs['id'],
defaults=serializer.validated_data)
return Response()
A RESTFUL API should error out for a PUT request on an object that doesn't exist. The idea being that if it had existed at one point to create the id, it has since been deleted. It makes more sense to keep it deleted than to re-create it.
This is especially true if the id is auto-generated. And even more so if it's an auto-incrementing integer id like the default id of Django models. If you were to support this functionality in that case, a user would create an instance of data with an id that the table hasn't incremented over yet potentially leading to errors like this.
I am using Django 1.9, and now trying to override save method behaviour. The problem is that when I do instance.some_field = some_value, the self object is already modified, whereas I need to know that was the original value of some_field. To do this, the only way out seems to be fetching object from database by self's pk.
So I wonder what is GENERIC syntax - if there's any - to fetch instance from the database? By saying GENERIC, I imply that I don't want to explicitly type the model name (like MYMODEL.objects.get(...)), but rather make Django figure out the right model based on target instance's model.
To make the question clearer, let me illustrate my goal in a pseudo-code:
def save_extented(self, *args, **kwargs):
original_object = (self model).objects.get(pk = self.pk)
Is it possible ? Or maybe I don't need this, and there's some smart Django hack to fetch the instance with rolled back field values ?
You can use django-dirtyfields and pre_save signal:
#receiver(pre_save, sender=MyModel)
def pre_save_logic(sender, instance, **kwargs):
if 'my_field' in instance.get_dirty_fields():
do_some_logic()
Is there a way, hopefully without breaking admin, to disable editing existing model instances on the ORM level?
I'm not talking about removing 'Save' and 'Save and continue' buttons from templates - there should be no operations that can change the values of a committed instance of a model.
Preferably, the 'Save As' option should work instead.
Overwrite the save function for your model like so:
class MyModel(models.Model):
def save(self, *args, **kwargs):
if self.pk is None:
super(MyModel, self).save(*args, **kwargs)
This function only call the superclass save function (which actually saves the change) if there is no pk, e.g. the model instance is new.
You could override your model class's save() (do nothing if self.pk) and delete (always do nothing)
But really, the database level is the safest place for that. For example, in PostgreSQL you could write two simple rules:
CREATE RULE noupd_myapp_mymodel AS ON UPDATE TO myapp_mymodel
DO NOTHING;
CREATE RULE nodel_myapp_mymodel AS ON DELETE TO myapp_mymodel
DO NOTHING;
Either way, the admin wouldn't know anything about this, so everything still looks editable. See my answer to Whole model as read-only for an attempt at making a model read-only in the admin. For your purposes, keep the add permission as-is, and only declare all fields read-only when not adding.
EDIT: One reason why overriding delete() in your model class is not safe, is the fact that "bulk delete" (Queryset.delete(), e.g. admin checkboxes action) will not call the individual instances' delete() method, it will go straight to SQL: https://docs.djangoproject.com/en/dev/topics/db/queries/#deleting-objects
For those who need to prevent MyModel.objects.filter(pk=123).update(name="bob"):
class NoUpdateQuerySet(models.QuerySet):
def update(self, *args, **kwargs):
pass
class MyModel(models.Model):
objects = NoUpdateQuerySet.as_manager()
...
Django docs - link
Sorry for some crazy subj.
I'd like to override django models save method and call some additional code if the model instance is newly created.
Sure I can use signals or check if the model have empty pk field and if yes, create temporary variable and later call a code:
Class EmailModel(models.Model):
email = models.EmailField()
def save(self, *args, **kwargs)
is_new = self.pk is None
super(EmailModel, self).save(*args, **kwargs)
# Create necessary objects
if is_new:
self.post_create()
def post_create(self):
# do job, send mails
pass
But I like to have some beautiful code and avoid using temporary variable in save method.
So the question is: is it possible to find if the instance of model is newly created object just after super save_base parent method call?
I've checked django sources can't find how to do that in right way.
Thanks
We have related post
For real - signals are best approch in this case.
You could use post_save() signal and in the listener just check if the credit_set exist for current model instance and if not - create one. That would be my choice - there is no need to overdo such a simple task.
Of course if you really need to know exactly when the model was initiated (I doubt it) use post_init() signal. You don't need to override save() method just to set some additional variables. Just catch post_init() signal, or pre_save(), and just change/add what you want. IMHO there is no sense to override save() method and check if this is new instance or not - that's why the signals are there.
Not sure how to phrase question, but here goes.
Say i have a table of events, and each event links to a object using a generic foreign key.
I dont want to show two events that link to the same foreign object, only the most recent event out of those which link to the same object.
Can anyone give any hints?
I think it may be very hard to do what you are asking. Since you are looking for a list of events and using generic relations, your only bet is probably to add some SQL via the .extra() filter. I'm not sure I would even know what SQL you would need to run for this since it would probably need GROUP BY and HAVING clauses.
As an alternative though, consider adding a BooleanField to your event object called latest. Then in the save method of your event, write some code like this:
def save(self, *args, **kwargs):
similar_events = Event.objects.filter(content_type=self.content_type,
object_id=self.object_id)
later_events = similar_events.filter(date__gt=self.date)
if later_events:
self.latest = False
else:
self.latest = True
similar_events.filter(date__lte=self.date).update(latest=False)
super(Event, self).save(*args, **kwargs)
Then to get your list of events simply do this:
Event.objects.filter(latest=True)
When you use foreign keys to point to a model, the model which is pointed to gets a descriptor referring to all the models that point to it. By default this is modelname_set, or event_set in this case, unless you've changed it.
You can use this descriptor to get the most recent event for the object. Since the descriptor returns a query set, you can order it by the date field, and take the first one.
latest_event_for_obj = obj.entry_set.order_by('-your_date_field')[0]