Django exclude field from all queries

Viewed 5577

I am running Django on Heroku with zero-downtime feature. This means that during deployment there are two version of code running (old and new) on the same database. That's why we need to avoid any backward incompatible migrations.

It there a possibility to exclude a field from Django query on a given model?

Let say we have a model (version 1):

class Person(models.Model):
    name = models.CharField()
    address = models.TextField()

In some time in the future we want to move address to the separate table. We know that we should not delete a field for older code to work so Person model may look like (version 2):

class Person(models.Model):
    name = models.CharField()
    address = models.ForeignKey(Address)
    _address = models.TextField(db_name='address')

This way if old code will query for address it will get it from Person table even if database has been migrated (it will be an old value, but let assume thats not a big issue).

How now I can safetly delete _address field? If we will deploy version 3 with _address field deleted then code for version 2 will still try to fetch _address on select, even if it's not used anywhere and will fail with "No such column" exception.

Is there a way to prevent this and mark some field as "non-fetchable" within the code for version 2? So version 2 will not delete field, but will not fetch it anymore and version 3 will delete field.

2 Answers

You can use custom object manager for defer your specific field/fields for all the queryset.

class CustomManager(models.Manager):
    def get_queryset(self):
        return super(CustomManager, self).get_queryset().defer('_address',)
class Person(models.Model):
    name = models.CharField()
    address = models.ForeignKey(Address)
    _address = models.TextField(db_name='address')

    objects = CustomManager()

after that in your any queryset against Person model will not include _address field in query by default.

Yes, you can do it:

QuerySet.defer():

"In some complex data-modeling situations, your models might contain a lot of fields, some of which could contain a lot of data (for example, text fields), or require expensive processing to convert them to Python objects. If you are using the results of a queryset in some situation where you don’t know if you need those particular fields when you initially fetch the data, you can tell Django not to retrieve them from the database." - docs

Entry.objects.defer("headline", "body")

OR

With django 1.8 onwards: use values_list. You can only include fields that you want. You can also use Queryset.only() and Queryset.defer() to refine your queryset queries. You can chain defer() calls as well

Entry.objects.values_list('id', 'headline')

Related