Cómo usar GenericForeignKey en Django

VM Vera Mazhuga Vera Mazhuga

Vera Mazhuga

Software Developer
2 min read.


A veces, cuando la solución del problema está asociado con ciertas... uhm... dificultades, sobre todo cuando todavía se está en el proceso de superarlas, después de una ardua victoria, llega el deseo natural de decirle al mundo lo que en realidad debía hacerse. Por lo tanto, vamos a ver: cómo usar GenericForeignKey en Django...


Inicialmente tenemos tres modelos: página, artículo y encuesta. Aunque podría haber más, aquí la idea es relacionar alguna encuesta con alguna página o artículo que tengamos (para saber dónde mostrarla).

from django.db import models


class Page(models.Model):
    name = models.CharField(max_length=255)
    # ...


class Article(models.Model):
    title = models.CharField(max_length=255)
    # ...


class Poll(models.Model):
    question = models.CharField(max_length=500)
    answers = # ...
    # ...


Entonces, queremos que una encuesta tenga un "ForeignKey" a una página o un artículo. ¿Cómo relacionar un objeto con diferentes modelos?
La respuesta es - vía
un framework para conectar tipos”de contenido y las relaciones genéricas.


Content Types Framework guarda en la tabla contenttypes la información sobre los modelos: nombre de aplicación, nombre de modelo, tipo. La aplicación se encuentra en django.contrib.contenttypes en INSTALLED_APPS de forma predeterminada. Dicho esto: podemos obtener el objeto de ContentType por el nombre de aplicación y modelo:

from django.contrib.contenttypes.models import ContentType
user_type = ContentType.objects.get(app_label='auth', model='user')


o a partir del modelo:

user_type = ContentType.objects.get_for_model(User)


u obtener un modelo por el
ContentType:

model = user_type.model_class()


u obtener el objeto:

user_type.get_object_for_this_type(username='demo')


Con
ContentType podemos obtener el tipo de modelo que queremos asociar con las encuestas.


La solución será de la siguiente forma:

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class Poll(models.Model):
    question = models.CharField(max_length=500)
    answers = # ...
    # ...
    limit = models.Q(app_label='miapp', model='page') | 
        models.Q(app_label='miapp', model='article')
    content_type = models.ForeignKey(
        ContentType,
        verbose_name=_('content page'),
        limit_choices_to=limit,
        null=True,
        blank=True,
    )
    object_id = models.PositiveIntegerField(
        verbose_name=_('related object'),
        null=True,
    )
    content_object = generic.GenericForeignKey('content_type', 'object_id')


El el campo
object_id se guarda el id del objeto relacionado (página o artículo). Con la condición del campo limit podemos filtrar por los modelos que queremos asociar a encuestas.


Ahora mostramos cómo exponerlo muy bonito en la interfaz de administración. Para no preocuparnos por el autocomplete, usamos
Django Grappelli

class PollAdmin(admin.ModelAdmin):

    fieldsets = (
        (None, {
            'fields': (
                'question'
                # ...
            )
        }),
        (_('page/article'), {
            'classes': ('grp-collapse grp-open',),
            'fields': ('content_type', 'object_id', )
        }),
    )

    autocomplete_lookup_fields = {
        'generic': [['content_type', 'object_id']],
    }

Luego, para obtener la encuesta, dependiendo del objeto, podemos usar el siguiente template tag:

from django import template
from django.contrib.contenttypes.models import ContentType
from miapp.models import Poll


register = template.Library()

@register.inclusion_tag('miapp/_poll.html')
def get_poll(related_object):
    related_object_type = ContentType.objects.get_for_model(related_object)
    poll = PollQuestion.objects.filter(
        content_type__pk=related_object_type.id,
        object_id=related_object.id,
    ).first()
    return {
        'poll': poll,
    }

Written by Vera Mazhuga

VM Vera Mazhuga Vera Mazhuga

Vera specializes in writing and maintaining code for various applications. Her focus on problem-solving and efficient programming ensures reliable and effective software solutions.

Newsletter

Subscribe to our newsletter:

Read more

The technology job gap

$500 Billion: The potential salaries lost, as over the next 10 years there will be 1 million more computing jobs than there ...

1 min read.

Build Once. Own Forever.