I'm using Django create_or_update function.
In case of update, Is there a way to know the list of changed fields.
Obviously I can use the get_or_create function before and in case, after this, I can update the model.. but I'm looking for a way to have this using a single query.
Is it possible?
update_or_create(defaults=None, **kwargs)
The update_or_create method tries to fetch an object from database based on the given kwargs. If a match is found, it updates the fields passed in the defaults dictionary.
The query doesn't care if the updated fields have changed or not, all the field in "default" are updated
Returns a tuple of (object, created), where object is the created or updated object and created is a boolean specifying whether a new object was created.
https://docs.djangoproject.com/en/3.0/ref/models/querysets/#update-or-create
Maybe it's not the best solution but I think it gets the job done. You could retrieve the instance that will be updated and then compute the fields that have changed using filter() and lambda functions, as suggested in this answer by Rahul Gupta.
Let's suppose you can identify the instance through say first_name and last_name as reported in the docs:
old_instance = Person.objects.filter(first_name=first_name, last_name=last_name)
old_instance = old_instance[0] if old_instance else None
new_instance, created = Person.objects.update_or_create(
first_name=first_name, last_name=last_name,
defaults={'first_name': 'Bob'},
)
# it's been updated and we have the old instance
if not created and old_instance:
# get a list of the model's fields
fields = Person._meta.get_all_field_names()
# compute the fields which have changed
diff_fields = filter(lambda field: getattr(old_instance,field,None)!=getattr(new_instance,field,None), fields)
The diff_fields list at this point should only contain first_name.
Related
According to the Django REST Framework docs, passing default=CreateOnlyDefault(<arg>) to a serializer field
can be used to only set a default argument during create operations. During updates the field is omitted.
It takes a single argument, which is the default value or callable that should be used during create operations.
How is this different from declaring read_only=True, default=<arg>, where <arg> is that single argument you would pass to CreateOnlyDefault?
When CreateOnlyDefault is used, the value will only be used on creation not on updates.
For example:
class MySerializer:
created = serializers.DateTimeField(
read_only=True,
default=serializers.CreateOnlyDefault(timezone.now)
)
modified = serializers.DateTimeField(
read_only=True,
default=timezone.now
)
With that serializer, modified field will be updated every on every update whereas created field will stay the same from creation.
It's mean it can be provided by the API client or not, so when it's checked by the validator if nothing is provided its create a 'Default' value.
u = UserDetails.objects.create(first_name='jake',last_name='sullivan')
u.save()
UserDetails.objects.create() and u.save() both perform the same save() function. What is the difference? Is there any extra check or benefit in using create() vs save()?
Similar questions:
What's the best way to create a model object in Django?
Django: Difference between save() and create() from transaction perspective
Django Model() vs Model.objects.create()
The Django documentation says it is the same. It is just more convenient to make it on one line. You could make a save() on one line too, but it would be more verbose and less readable -- it is clear you are creating a new object with the create() method.
create(**kwargs)
A convenience method for creating an object and saving it all in one
step. Thus:
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
and:
p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)
are equivalent.
The force_insert parameter is documented elsewhere, but all it means
is that a new object will always be created. Normally you won’t need
to worry about this. However, if your model contains a manual primary
key value that you set and if that value already exists in the
database, a call to create() will fail with an IntegrityError since
primary keys must be unique. Be prepared to handle the exception if
you are using manual primary keys.
Similar question:
Django Model() vs Model.objects.create()
The difference between Model() vs Model.objects.create() are summarized as below.
.save() perform internally as either INSERT or UPDATE object to db, while .objects.create() perform only INSERT object to db.
Model.save() perform ....
UPDATE → If the object’s primary key attribute is set to a value that evaluates to True
INSERT →
If the object’s primary key attribute is not set or if the UPDATE didn’t update anything (e.g. if primary key is set to a value that doesn’t exist in the database).
If primary key attribute is set to a value then Model.save() perform UPDATE but Model.objects.create raise IntegrityError.
eg.
models.py
class Subject(models.Model):
subject_id = models.PositiveIntegerField(primary_key=True, db_column='subject_id')
name = models.CharField(max_length=255)
max_marks = models.PositiveIntegerField()
1) Insert/Update to db with Model.save()
physics = Subject(subject_id=1, name='Physics', max_marks=100)
physics.save()
math = Subject(subject_id=1, name='Math', max_marks=50) # Case of update
math.save()
Output:
Subject.objects.all().values()
<QuerySet [{'subject_id': 1, 'name': 'Math', 'max_marks': 50}]>
2) Insert to db with Model.objects.create()
Subject.objects.create(subject_id=1, name='Chemistry', max_marks=100)
IntegrityError: UNIQUE constraint failed: m****t.subject_id
Explanation: Above math.save() is case of update since subject_id is primary key and subject_id=1 exists django internally perform UPDATE, name Physics to Math and max_marks from 100 to 50 for this, but objects.create() raise IntegrityError
Model.objects.create() not equivalent to Model.save() however same can be achieved with force_insert=True parameter on save method i.e Model.save(force_insert=True).
Model.save() return None where Model.objects.create() return model instance i.e. package_name.models.Model
Conclusion: Model.objects.create() internally do model initialization and perform save with force_insert=True.
source-code block of Model.objects.create()
def create(self, **kwargs):
"""
Create a new object with the given kwargs, saving it to the database
and returning the created object.
"""
obj = self.model(**kwargs)
self._for_write = True
obj.save(force_insert=True, using=self.db)
return obj
The following links can be followed for more details:
https://docs.djangoproject.com/en/stable/ref/models/querysets/#create
https://github.com/django/django/blob/2d8dcba03aae200aaa103ec1e69f0a0038ec2f85/django/db/models/query.py#L440
Note: Above answer is from question.
I am using Django 1.5.1 and I want to save or update model.
I read the django document and I met the get_or_create method which provides saving or updating. There is a usage like;
Model.objects.get_or_create(name='firstName',surname='lastName',defaults={'birthday': date(1990, 9, 21)})
defaults field is using only for getting. While it is setting phase, name and surname are only set. That is what I understand from the document.
So I want to do something different that setting name,surname and birthDay, but getting name and surname excluding birthdate. I could not see the way to do that in the document and another place.
How can I do this?
Thank you!
get_or_create provides a way of getting or creating. Not saving or updating. Its idea is: I want to get a model, and if it doesn't exist, I want to create it and get it.
In Django, you don't have to worry about getting the name or the surname or any attribute. You get an instance of the model which has all the attributes, I.e.
instance = Model.objects.get(name='firstName',surname='lastName')
print instance.birthday
print instance.name
print instance.surname
An overview of the idea could be: a Model is a data structure with a set of attributes, an instance is a particular instance of a model (uniquely identified by a primary_key (pk), a number) which has a specific set of attributes (e.g. name="firstName").
Model.objects.get is used to go to the database and retrieve a specific instance with a specific attribute or set of attributes.
Since Django 1.7 there's update_or_create:
obj, created = Person.objects.update_or_create(
first_name='John',
last_name='Lennon',
defaults=updated_values
)
The parameters you give are the ones that will be used to find an existing object, the defaults are the parameters that will be updated on that existing or newly created object.
A tuple is returned, obj is the created or updated object and created is a boolean specifying whether a new object was created.
Docs: https://docs.djangoproject.com/en/1.8/ref/models/querysets/#update-or-create
I have a function which currently calls Models.object.get(), which returns either 0 or 1 model objects:
if it returns 0, I create a new model instance in the except DoesNotExist clause of the function.
Otherwise, I would like to update the fields in the pre-existing
instance, without creating a new one.
I was originally attempting to
call .update() on the instance which was found, but .update()
seems to be only callable on a QuerySets. How do I get around
changing a dozen fields, without calling .filter() and comparing
the lengths to know if I have to create or update a pre-existing
instance?
With the advent of Django 1.7, there is now a new update_or_create QuerySet method, which should do exactly what you want. Just be careful of potential race conditions if uniqueness is not enforced at the database level.
Example from the documentation:
obj, created = Person.objects.update_or_create(
first_name='John', last_name='Lennon',
defaults={'first_name': 'Bob'},
)
The update_or_create method tries to fetch an object from database
based on the given kwargs. If a match is found, it updates the
fields passed in the defaults dictionary.
Pre-Django 1.7:
Change the model field values as appropriate, then call .save() to persist the changes:
try:
obj = Model.objects.get(field=value)
obj.field = new_value
obj.save()
except Model.DoesNotExist:
obj = Model.objects.create(field=new_value)
# do something else with obj if need be
if you want only to update model if exist (without create it):
Model.objects.filter(id = 223).update(field1 = 2)
mysql query:
UPDATE `model` SET `field1` = 2 WHERE `model`.`id` = 223
As of Django 1.5, there is an update_fields property on model save. eg:
obj.save(update_fields=['field1', 'field2', ...])
https://docs.djangoproject.com/en/dev/ref/models/instances/
I prefer this approach because it doesn't create an atomicity problem if you have multiple web app instances changing different parts of a model instance.
I don't know how good or bad this is, but you can try something like this:
try:
obj = Model.objects.get(id=some_id)
except Model.DoesNotExist:
obj = Model.objects.create()
obj.__dict__.update(your_fields_dict)
obj.save()
Here's a mixin that you can mix into any model class which gives each instance an update method:
class UpdateMixin(object):
def update(self, **kwargs):
if self._state.adding:
raise self.DoesNotExist
for field, value in kwargs.items():
setattr(self, field, value)
self.save(update_fields=kwargs.keys())
The self._state.adding check checks to see if the model is saved to the database, and if not, raises an error.
(Note: This update method is for when you want to update a model and you know the instance is already saved to the database, directly answering the original question. The built-in update_or_create method featured in Platinum Azure's answer already covers the other use-case.)
You would use it like this (after mixing this into your user model):
user = request.user
user.update(favorite_food="ramen")
Besides having a nicer API, another advantage to this approach is that it calls the pre_save and post_save hooks, while still avoiding atomicity issues if another process is updating the same model.
As #Nils mentionned, you can use the update_fields keyword argument of the save() method to manually specify the fields to update.
obj_instance = Model.objects.get(field=value)
obj_instance.field = new_value
obj_instance.field2 = new_value2
obj_instance.save(update_fields=['field', 'field2'])
The update_fields value should be a list of the fields to update as strings.
See https://docs.djangoproject.com/en/2.1/ref/models/instances/#specifying-which-fields-to-save
I am using the following code in such cases:
obj, created = Model.objects.get_or_create(id=some_id)
if not created:
resp= "It was created"
else:
resp= "OK"
obj.save()
update:
1 - individual instance :
get instance and update manually get() retrieve individual object
post = Post.objects.get(id=1)
post.title = "update title"
post.save()
2 - Set of instances :
use update() method that works only with queryset that what would be returned by filter() method
Post.objects.filter(author='ahmed').update(title='updated title for ahmed')
Apologies, I am completely new to Django and Python.
I have 2 questions. First, how would I go about getting the last object created (or highest pk) in a list of objects? For example, I know that I could use the following to get the first object:
list = List.objects.all()[0]
Is there a way to get the length of List.objects? I've tried List.objects.length but to no avail.
Second, is it possible to create simultaneous filters or combine lists? Here is an example:
def findNumber(request, number)
phone_list = Numbers.objects.filter(cell=number)
I want something like the above, but more like:
def findNumber(request, number)
phone_list = Numbers.objects.filter(cell=number or home_phone=number)
What is the correct syntax, if any?
I haven't tried this yet, but I'd look at the latest() operator on QuerySets:
latest(field_name=None)
Returns the latest object in the
table, by date, using the field_name
provided as the date field.
This example returns the latest Entry
in the table, according to the
pub_date field:
Entry.objects.latest('pub_date')
If your model's Meta specifies
get_latest_by, you can leave off the
field_name argument to latest().
Django will use the field specified in
get_latest_by by default.
Like get(), latest() raises
DoesNotExist if an object doesn't
exist with the given parameters.
Note latest() exists purely for
convenience and readability.
And the model docs on get_latest_by:
get_latest_by
Options.get_latest_by
The name of a DateField or DateTimeField in the model. This specifies the default field to use in your model Manager's latest method.
Example:
get_latest_by = "order_date"
See the docs for latest() for more.
Edit: Wade has a good answer on Q() operator.
this works!
Model.objects.latest('field') - field can be id. that will be the latest id
Since Django 1.6 - last
last()
Works like first(), but returns the last object in the queryset.
Returns the last object matched by the queryset, or None if there is no matching object. If the QuerySet has no ordering defined, then the queryset is automatically ordered by the primary key.
list = List.objects.last() gives you the last object created
For the largest primary key, try this:
List.objects.order_by('-pk')[0]
Note that using pk works regardless of the actual name of the field defined as your primary key.
You can use the count() method on a query set the get the number of items.
list = List.objects.all()
list.count()
Arguments to filter are "AND"ed together. If you need to do OR filters look at Q objects.
http://docs.djangoproject.com/en/dev/topics/db/queries/#complex-lookups-with-q-objects
alternative for the latest object created:
List.objects.all()[List.objects.count()-1]
It is necessary to add an AssertionError for the case when there are no items in the list.
except AssertionError:
...
I am working on Django version is 1.4.22, neither last nor lastet is working.
My way to solve with minimum db loading is like:
latest = lambda model_objects, field : model_objects.values_list( field, flat = True ).order_by( "-" + field )[ 0 ]
latest_pk = latest( List.objects, "pk" )
This function accepts query_set as input.
You may bind this function dynamically by doing:
import types
List.objects.latest = types.MethodType( latest, List.objects )
Then you should be able to get the last object by this latest pk easily.
Try this:
InsertId= (TableName.objects.last()).id