Django / Factoryboy - unable to delete instances in test - python

I am writings tests for a django application and I've run into a problem were deleted objects still exist in the test database after I've supposedly deleted them.
I'm using the following factory
class CMSPageFactory(factory.DjangoModelFactory):
class Meta:
model = CMSPage
title = factory.Faker('company')
tenant = factory.SubFactory(TenantFactory)
key = factory.Faker('slug')
protected = False
in_navigation = False
active = True
This is the test I am running
def test_example_for_so(self):
page = CMSPageFactory()
page.delete()
self.assertFalse(page)
And it raises the following error:
AssertionError: <CMSPage: Fletcher LLC> is not false
I must be missing something very obvious but for the life of me I cannot figure out what. Does anyone know what I am doing wrong?

Are you sure, page still exists in the db?
Calling delete() on a django model instance (which your factory supposedly creates) will delete the database row, but nor your local python representation:
https://docs.djangoproject.com/en/2.1/ref/models/instances/#django.db.models.Model.delete
Issues an SQL DELETE for the object. This only deletes the object in the database; the Python instance will still exist and will still have data in its fields.

The object was deleted from the database but still exists in memory. From the Model delete docs:
Issues an SQL DELETE for the object. This only deletes the object in
the database; the Python instance will still exist and will still have
data in its fields. This method returns the number of objects deleted
and a dictionary with the number of deletions per object type
What you can do in the test is get the id and then try to get the object from the database, or count the objects in the database and expect 0.

Related

What is the correct way to use refresh_from_db in Django?

I'm using Django 1.8, Mezzanine, Cartridge, and I use Postgresql as the database.
I've updated the num_in_stock directly from the database. The quantities are all correct in the database but not on my website. I know the solution is here, but I don't know what to do with that. I really need it spelled out for me.
How exactly would you use this in Cartridge to refresh the num_in_stock?
This should be all you need to do to update one object. Replace object_name with your object.
object_name.refresh_from_db()
I assume you're using an F expression.
According to the documentation an F expression:
...makes it possible to refer to model field values and perform
database operations using them without actually having to pull them
out of the database into Python memory.
You're working directly in the database. Python knows nothing about the values of the model fields. There's nothing on memory, everything is happening on the database.
The documentation's example:
from django.db.models import F
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()
Although reporter.stories_filed = F('stories_filed') + 1 looks like a
normal Python assignment of value to an instance attribute, in fact
it’s an SQL construct describing an operation on the database.
So, for Python to know about this value you need to reload the object.
To access the new value saved this way, the object must be reloaded:
reporter = Reporters.objects.get(pk=reporter.pk)
# Or, more succinctly:
reporter.refresh_from_db()
In your example:
object_name.refresh_from_db()
And one more thing...
F() assignments persist after Model.save()
F() objects assigned to
model fields persist after saving the model instance and will be
applied on each save().
reporter = Reporters.objects.get(name='Tintin')
reporter.stories_filed = F('stories_filed') + 1
reporter.save()
reporter.name = 'Tintin Jr.'
reporter.save()
stories_filed will be updated twice in this case. If it’s initially
1, the final value will be 3. This persistence can be avoided by
reloading the model object after saving it, for example, by using
refresh_from_db().
I assume the num_in_stock is an attribute of your model class. If true you should get an instance of the class (i.e object_name) then
object_name.refresh_from_db()
After which, you can access it like object_name.num_in_stock

Django ORM doesn't save value when referencing to them by child attributes

Today I found something very tricky. I use django 1.7.7 (if this is important).
I have two models in relation: Car and Driver. I want to connect with each other instances of car object and driver object. Both objects seems(*) to be already created.
In both cases:
car = Car.objects.create()
car.save()
driver = Driver.objects.create()
driver.save()
self.garage.car = car
self.garage.save()
and next:
# doesn't work
self.garage.car.driver = driver
self.garage.car.save()
# self.object.car.driver is None
but:
# works
car = self.garage.car
car.driver = driver
car.save()
# self.object.car.driver is driver
(*) - To be honest I am not sure, whether these objects are fully created or not yet. Everything is going on in View, both objects have already own ids, however manual query to database shows, that those objects are not yet present. Transaction is not finished?
Because in your sample code, first self.object.car is not the same object you save in the line below. Check this out:
self.garage.car is self.garage.car
If above is False the only explanation is that car is a property and you get new instance of object every time you call this property.

Python peewee save() doesn't work as expected

I'm inserting/updating objects into a MySQL database using the peewee ORM for Python. I have a model like this:
class Person(Model):
person_id = CharField(primary_key=True)
name = CharField()
I create the objects/rows with a loop, and each time through the loop have a dictionary like:
pd = {"name":"Alice","person_id":"A123456"}
Then I try creating an object and saving it.
po = Person()
for key,value in pd.items():
setattr(po,key,value)
po.save()
This takes a while to execute, and runs without errors, but it doesn't save anything to the database -- no records are created.
This works:
Person.create(**pd)
But also throws an error (and terminates the script) when the primary key already exists. From reading the manual, I thought save() was the function I needed -- that peewee would perform the update or insert as required.
Not sure what I need to do here -- try getting each record first? Catch errors and try updating a record if it can't be created? I'm new to peewee, and would normally just write INSERT ... ON DUPLICATE KEY UPDATE or even REPLACE.
Person.save(force_insert=True)
It's documented: http://docs.peewee-orm.com/en/latest/peewee/models.html#non-integer-primary-keys-composite-keys-and-other-tricks
I've had a chance to re-test my answer, and I think it should be replaced. Here's the pattern I can now recommend; first, use get_or_create() on the model, which will create the database row if it doesn't exist. Then, if it is not created (object is retrieved from db instead), set all the attributes from the data dictionary and save the object.
po, created = Person.get_or_create(person_id=pd["person_id"],defaults=pd)
if created is False:
for key in pd:
setattr(fa,key,pd[key])
po.save()
As before, I should mention that these are two distinct transactions, so this should not be used with multi-user databases requiring a true upsert in one transaction.
I think you might try get_or_create()? http://peewee.readthedocs.org/en/latest/peewee/querying.html#get-or-create
You may do something like:
po = Person()
for key,value in pd.items():
setattr(po,key,value)
updated = po.save()
if not updated:
po.save(force_insert=True)

How does Django foreign key access work

Say I have a model like this.
class Job(models.Model):
client = models.ForeignKey(Contacts, null=True)
and lets say I have job j. I know I can access the client belonging to j like this
j.client
but there is also
j.client_id
So my question is how does accessing j.client work?
Does django store client__id then when j.client is called it does a query to find the correct object?
Or is the object reference stored to j and accessing client__id is getting the id from the Contact object?
I've looked around the source code a bit but couldn't find the answer to my question
What you are probably talking about is client and client_id (single underscore).
The client_id attribute is a regular (integer) attribute. This is the foreign key that is saved to the database. You will only ever see a client_id column in the database, even though you specify the ForeignKey as client.
The client attribute is an object descriptor instance. It is a special class that overrides the __get__ and __set__ methods, so settings and accessing that attributes invokes that class's methods. This is the magic that gives you access to the actual related model instance. __get__ will retrieve the correct model instance from the database if it isn't loaded already, based on the client_id attribute. __set__ will also set the client_id attribute to the primary key of the related object, so that client_id is always up-to-date.
Note that this attribute is also available in query lookups, and is quite handy. E.g., if you have just the primary key of a foreign object, and not the model instance itself, the following queries look very similar:
job = Job.objects.filter(client__id=pk)
job = Job.objects.filter(client_id=pk)
However, underneath the first query accesses an attribute on the related object (double underscore) and performs an OUTER JOIN. The second query only ever accesses a local attribute, thus not having to perform the OUTER JOIN statement and saving performance.
This is explained in the docs:
https://docs.djangoproject.com/en/dev/ref/models/fields/#database-representation
In the database there is only client_id field (single underscore)
On the model instance you will have client attribute, when you access it this will cause Django to load the related object from the db and instantiate as another model instance.
You will also have client_id attribute (one underscore) which has the primary key value of the related object, as stored in the db field.
When doing ORM queries you are able to use client__id (double underscore) syntax to lookup against fields on the related model, eg you could also do client__name if Client model had a name field. This will become a SQL JOIN query across both models.
eg
Job.objects.get(client__id=1)
Job.objects.filter(client__name='John')
client = Client.objects.get(pk=1)
Job.objects.get(client=client)
j.client gives you the models.Model object. You can access it's properties like ...
client = j.client
id = client.id
name = client.name
But there should not be a j.client__id field. You should use j.client.id to get the id field. Although you can use j.client__id field to do filters and such.
So,
id = j.client.id # good
id = j.client__id # bad
and
job = Job.objects.get(client__id=1) # good
job = Job.objects.get(client.id=1) # bad

Mongoengine insert function failed

I have a problem about insert function. If i have a array of objects to insert[bad,good,good]. if the first object is bad, and object insert action will fail, then the rest of the objects will never hit the database even the object is good.
How can i deal with it ?
You can validate the model instances before saving to ensure they are valid eg:
valid_docs = [d for d in docs if d.validate()]
Or pass in the continue_on_error=True as a write_options eg:
Doc.objects.insert(docs, write_options={"continue_on_error": True})

Categories