Django 1.9 updating model object makes a new object instance - python

I am working with Django 1.9 and noticed some strange behavior when working with the models. I know that the following code creates an object, saves it to the database, changes the field, then updates that same entry in the database:
cat = models.Cat(name="Bob")
cat.save()
cat.name = "Sally"
cat.save()
However, when I query all my objects using cats = models.Cat.objects.all() I find that rather than returning ["Sally"] it actually returns ["Bob", "Sally"]. Apparently cat.save() is creating a new element in the database rather than updating an existing one. I've worked with Django before, but never had this issue. One thing to note is that the name attribute is the primary key for the Cat model. Could this be why it's not updating, but creating a whole new entry?

The primary key is what Django uses to determine whether to update or create an item. Usually, that's an opaque ID which you don't modify; but in your case, it's part of your data. When you modify the value, Django has no way of knowing that the object refers to an existing row in the database.
Don't do this; stick with autoincremented IDs that have no relation to your actual data.

You're right, the issue here is that your primary key is the name field. Django will do an update if the pk value exists in the database, and an insert if it doesn't. For example:
class Cat(models.Model):
name = models.CharField(max_length=255)
cat = Cat(name='Bart')
cat.save() # This creates a new object
print(cat.pk)
> '1'
cat.name = 'Sally'
cat.save() # This updates the object, the pk will still be '1'
print(cat.pk)
> '1'
print(Cat.objects.all())
> [<Cat 'Sally'>]
fluffy = Cat(name='Fluffy')
fluffy.pk = 1
fluffy.save()
'''This will UPDATE the existing object, since an object
with that primary key already exists'''
print(Cat.objects.all())
> [<Cat 'Fluffy'>]
fluffy.pk = 2
fluffy.save()
'''This will CREATE a new object, since no object with
that primary key already exists'''
print(Cat.objects.all())
> [<Cat 'Fluffy'>, <Cat 'Fluffy'>]
If possible, I would recommend removing the primary_key=True attribute on the name field. If you want name to be unique, maybe just set unique=True instead?

Related

Python / Django: Make a lookup / database query inside a model class

What is the right way to lookup a table and use its last value as a value in a new model instance? Something like this:
class MyClass(models.Model):
id = models.AutoField(primary_key=True)
obj = MyClass.objects.latest('id')
my_field = models.IntegerField(default=(obj+1))
I need a db column, which keeps track of the primary_key, but is independent of it and can also be modified. Also I need to use its value as default when creating new instances of the Model.
you can use custom constructor as described in the docs:
https://docs.djangoproject.com/en/2.2/ref/models/instances/
you will need to define the obj field either as integer(to store the id of the previous record) or as a foreign key(if you want to reference the previous db record). In the second case you will need to pass the name of the model to the ForeignKeyField constructor as string('MyClass') and not directly(MyClass).

How to Fake django.contrib.auth User value for specific user?

I am writing a script and want to fake a User with an id (PK) of 2
var = Table(FKToUser=2)
var.save()
the problem is I'm getting :
"Table.FKToUser" must be a "User" instance
I've verified that the auth_user has a record with id=2
How can I fake this 2 value for testing purposes?
Thank you!
Assuming that Table is your model and FKToUser is a foreign key, there are two ways. The first is to set the FKToUser_id attribute to 2 and save the model. The other is to fetch the user and set the FKToUser attribute to the right user model instance.
This is also basically how a foreign key works. The actual column in the database is the FKToUser_id column, and it's a simple Integer foreign key to an id in another column. Django magic makes it possible to automatically retrieve the right instance by accessing FKToUser, and to set the right value by assigning a model instance to FKToUser.

Get model instance id from foreign key field without loading object

Say we have the following class:
Class Alert(models.Model):
contact = models.ForeignKey('emails.Contact',null=True,blank=True)
If I wanted to get the foreign key of the contact I would do somealert.contact.pk or somealert.contact_id. Do these commands pull down the whole contact object and then get the key? Or do any of them just yield the foreign key without pulling all off the attributes of the instance from the database. I worried about performance and would prefer to just get the key itself.
The first one - somealert.contact.pk - will get the Contact object. The second - somealert.contact_id - won't.
You can verify this in the shell by looking at the contents of django.db.connection.queries.

Django save or update model

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

How to save a ForeignKey without retrieving the foreign object instance?

I have a model in which I want to save a new instance (row). I have the primary key for the ForeignKey but I do not have the object itself (suppose it came from somewhere). Is there any way to save it without raw SQL and without having to get the instance?
Here is the model:
class UserLocale(models.Model):
user = models.ForeignKey(KingUser)
locale = models.ForeignKey(Locale)
Now suppose I have the locale primary key ('en_US') but I do not have the object itself. What I'm doing to save it is this:
def save_locale(user_instance, locale_name):
locale = Locale.objects.get(pk=locale_name)
UserLocale.objects.create(user=user_instance, locale=locale)
I know I can do it in one step with raw SQL, but I was wondering if I can do it in Django query model.
edit:
Yes, UserLocale is a M2M middle table. I know it would have been nicer to make a ManyToMany field, but it is legacy code.
Here is my SQL attempt (no tests, I'm writting by hand as I'm away from the code):
def save_raw_sql(user_instance, locale_name):
query = """
INSERT INTO
project_userlocale (user_id, locale_id)
values (%s, %s)
"""
UserLocale.objects.raw(query, [user_instance.id, locale_name])
You can just use the id field underlying the ForeignKey object:
UserLocale.objects.create(user=user_instance, locale_id=locale)
(I presume the reference to Locale in your last line should actually have been UserLocale).
def save_locale(user_instance, locale_name):
user_locale = UserLocale()
user_locale.user = user_instance
user_locale.locale_id = locale_name
user_locale.save()
if u invoke the object, you have the .locale atribute that is a reference to the class Locale, and .locale_id that contain the real value for the field.

Categories

Resources