Django save or update model - python

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

Related

Django create_or_update get changes fields

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.

Django deserialize to model instance without saving to DB

I want to serialize an object to Json, then deserialize it as the object, without save it to DB (it's already saved). The reason is that the current state of the model may be different from the state when I serialized it.
This is how I currently serialize the object:
ser_obj = serializers.serialize("json", [self.instance])
Now, as I understand, in order to deserialize I can do something like:
for obj in serializers.deserialize("json", ser_obj):
obj.save()
But that will save the object to DB, which I don't want to.
I guess I can also do something like:
MyModel(field1 = ser_obj['field1'],
field2 = ser_obj['field2']
)
But it seems wrong.
So any idea how can I deserialize json object into Django model without saving to DB?
I did notice that if I use the 'save()', I can then use the obj.object to get the object.
You can deserialise without saving to the database.
for obj in serializers.deserialize("json", ser_obj):
do_something_with(obj)
This will return DeserializedObject instances. You can find more info in the Django docs
I wanted to solve a similar problem and found you can access the objects without saving by accessing the .object property:
for deserializedobject in serializers.deserialize("json", ser_obj):
obj = deserializedobject.object
print(object.field1)
Note that if objects accessed in this way have a related field (eg ForeignKey) then that will point to the current database object with that pk even if one has just been deserialised.
ie if A points to B and both A and B are serialised and later deserialised then A will point to B on the database which may have different values than the deserialised.
Essentially you cannot use this approach to freeze a snapshot of a group of related objects and interrogate them at a later date without executing a save().
(as at Django 3.2.3)

Making a copy of an object's initial state before saving

For my Django application, I'm looking to keep a full edit history for all objects. As part of this, I've overridden the model's save() method, part of which is shown below:
# Replicate the current version (from the db) with all attributes unchanged
new_ver = self.__class__.objects.get(pk=self.pk).save(force_insert=True)
# Update the current version in the database with the new attributes
super(CodexBaseClass, self).save(*args, force_update=True, **kwargs)
The 'self' that's passed to the save() method is the NEW version of the object that's been generated by the form. What this code is attempting to do is
(1) Make a copy of the object as it currently appears in the database (ie: copy the data as it was before the form modified it), then force an insert of this data so it's copied as a new row
(2) Update the existing row with the new version of the object that's been submitted through the form.
The problem is with the first line of the two lines of code - It generates a DoesNotExist exception. The object does exist, so I'm currently thinking that the issue is that the database row it's trying to read is currently locked.
So my question is: Is there a way I can modify/replace the first line so that I have a copy of the initial data, as it was before the form modified it?
Thanks.
If you want to insert a new object with same attributes, you only need to change the primary key of your object, and save it.
new_ver = self.__class__.objects.get(pk=self.pk)
new_ver.pk = None
new_ver.save()
Using None as primary key will auto generates it. You can have more information if you look at the django documentation.
If you need you can also make a copy of your object, be careful the cost can be expensive :
from copy import deepcopy
ver = self.__class__.objects.get(pk=self.pk)
new_ver = deepcopy(ver)
new_ver.pk = None
new_ver.save()
# Do what you need with ver object
You should take a look at django-reversion.
django-reversion is an extension to the Django web framework that
provides version control for model instances.
Documentation: link
Features
Roll back to any point in a model instance’s history.
Recover deleted model instances.
Simple admin integration.

How to get child model related data in parent model query

I have two models:
class BusinessCard(models.Model):
name = models.CharField(_("name"),null=True,max_length=50)
class Contacts(models.Model):
businesscard_id = models.OneToOneField(BusinessCard,null=True,blank=True,related_name='contact_detail',db_column="businesscard_id")
bcard_json_data = JsonField(null=True)
I just want access contacts model data using business card model:
target_bacard=BusinessCard.objects.filter(id=target_bacard_id).select_related()
When we access the target_bacard.contact_detail it gives key errors.
How can I get the contacts data using target_bacard queryset.
use get() instead of filter() like:
target_bacard = BusinessCard.objects.get(id=target_bacard_id)
target_bacard.contact_detail
If you want to access the Contacts instance that is in the 1-to-1 relationship with a BusinessCard instance bacard, use the related name you specified in Contacts:
contact = bacard.contact_detail
Also, you have some misleading names: Contacts should rather be Contact since an instance of this model represents only one contact. And its field businesscard_id would better be named businesscard (note that the table column will be called businesscard_id at the database level automatically in that case and store the id of the related businesssscard) because in the ORM you get a BusinessCard model instance when you access it, and not just its id.
You have not passed related model (field) argument to select_related()
target_bacard=BusinessCard.objects.filter(id=target_bacard_id).select_related('contact_detail')
Assuming id of BusinessCard is unique, you may want to use ...objects.get(id=target_bacard_id) inplace of ...objects.filter(id=target_bacard_id). Anyway select_related() will work on both ways.
select_related() is used for saving database query.
here is the documentation

Django debug error

I have the following in my model:
class info(models.Model):
add = models.CharField(max_length=255)
name = models.CharField(max_length=255)
An in the views when i say
info_l = info.objects.filter(id=1)
logging.debug(info_l.name)
i get an error saying name doesnt exist at debug statement.
'QuerySet' object has no attribute 'name'
1.How can this be resolved.
2.Also how to query for only one field instead of selecting all like select name from info.
1. Selecting Single Items
It looks like you're trying to get a single object. Using filter will return a QuerySet object (as is happening in your code), which behaves more like a list (and, as you've noticed, lacks the name attribute).
You have two options here. First, you can just grab the first element:
info_l = info.objects.filter(id=1)[0]
You could also use the objects.get method instead, which will return a single object (and raise an exception if it doesn't exist):
info_l = info.objects.get(id=1)
Django has some pretty good documentation on QuerySets, and it may be worth taking a look at it:
Docs on using filters
QuerySet reference
2. Retrieving Specific Fields
Django provides the defer and only methods, which will let you choose specific fields from the database, rather than fetching everything at once. These don't actually prevent the fields from being read; rather, it loads them lazily. defer is an "opt-in" mode, which lets you specify what fields should be lazily loaded. only is "out-out" -- you call it, and only the fields you pass will by eagerly loaded.
So in your example, you'd want to do something like this:
info_l = info.objects.filter(id=1).only('name')[0]
Though with a model as simple as the example you give, I wouldn't worry much at all about limiting fields.

Categories

Resources