Cómo usar GenericForeignKey en Django
Vera Mazhuga
Software Developer
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
Vera specializes in writing and maintaining code for various applications. Her focus on problem-solving and efficient programming ensures reliable and effective software solutions.