chinskiy
chinskiy

Reputation: 2715

How to check that Django model instance have preselected related data

Before Django 2.X I use this function for checking that ForeignKey and OneToOneField were prefetched to the Django model instance.

def ensure_related(obj, field):
    field_obj = obj._meta.get_field(field)

    if hasattr(obj, field_obj.get_cache_name()):
        return True
    return False

So for example

In [1]: permission = Permission.objects.last()

SELECT "auth_permission"."id",
       "auth_permission"."name",
       "auth_permission"."content_type_id",
       "auth_permission"."codename"
  FROM "auth_permission"
 INNER JOIN "django_content_type"
    ON ("auth_permission"."content_type_id" = "django_content_type"."id")
 ORDER BY "django_content_type"."app_label" DESC,
          "django_content_type"."model" DESC,
          "auth_permission"."codename" DESC
 LIMIT 1


In [2]: ensure_related(permission, 'content_type')

Out[2]: False

In [3]: permission = Permission.objects.prefetch_related('content_type').last()

SELECT "auth_permission"."id",
       "auth_permission"."name",
       "auth_permission"."content_type_id",
       "auth_permission"."codename"
  FROM "auth_permission"
 INNER JOIN "django_content_type"
    ON ("auth_permission"."content_type_id" = "django_content_type"."id")
 ORDER BY "django_content_type"."app_label" DESC,
          "django_content_type"."model" DESC,
          "auth_permission"."codename" DESC
 LIMIT 1


SELECT "django_content_type"."id",
       "django_content_type"."app_label",
       "django_content_type"."model"
  FROM "django_content_type"
 WHERE "django_content_type"."id" IN (1)


In [3]: ensure_related(permission, 'content_type')

Out[4]: True

But since Django 2.X this method always return True.

My questions is: how since Django 2.X implement this check?

Upvotes: 1

Views: 294

Answers (1)

NekitoSP
NekitoSP

Reputation: 164

You can use fields_cache in obj._state (instance of ModelState)

>>> Car.objects.first()._state.fields_cache
{}

>>> Car.objects.prefetch_related('manufacturer').first()._state.fields_cache
{'manufacturer': None}

also works with lazy loading:

>>> car = Car.objects.first()
>>> car._state.fields_cache
{}
>>> car.manufacturer
>>> car._state.fields_cache
{'manufacturer': None}

so ensure_related should be like this:

def ensure_related(obj, field):
    return field in obj._state.fields_cache

Upvotes: 4

Related Questions