2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Django generic ForeignKey

Posted at

When you want add relation Many to one relation between models, Django provides ForeignKey. Take a look at simple example. Each clubteam has a owner and owner can be own a several clubteam (depending on his budget).
owner is associated to user class

class clubteam(models.Model):
  owner = models.ForeignKey(settings.AUTH_USER_MODEL, default = 1)

When you add model about player and each player belong to one clubteam.

Standard foreign key

python manage.py startapp player

player/models.py
from __future__ import unicode_literals
from django.conf import settings
from django.db import models
from club.models import clubteam
class Player(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
    clubteam = models.ForeignKey(clubteam)
    Age = models.PositiveIntegerField()

    def __str__(self):
        return str(self.user.username)
    def __unicode__(self):
        return str(self.user.username)

official link
https://docs.djangoproject.com/en/1.10/ref/contrib/contenttypes/

Generic Foreign Key

player/models.py
from __future__ import unicode_literals
from django.conf import settings
from django.db import models
from club.models import clubteam
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType


class Player(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    Age = models.PositiveIntegerField()

    def __str__(self):
        return str(self.user.username)
    def __unicode__(self):
        return str(self.user.username)

Here model.CASCADE means when the parent model is deleted, the child model is also instantly deleted.
when you want to show some players list in a specific clubteam, Here is a solution for that.

club/views.py
from django.shortcuts import render
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404
from player.models import Player
from club.models import clubteam

def player_viewr(request, clubteam_id = None):
    instance = get_object_or_404(clubteam, clubteam_id)
    content_type = ContentType.objects.get_for_model(Player)
    obj_id = instance.id
    players=Player.objects.filter(content_type=content_type, object_id = obj_id)
    context = {
        "instance": instance,
        "players": players
    }
    return render(request, "player_list.html", context)

The first thing, you need specific id for the clubteam, and check if that exist by instance = get_object_or_404(clubteam, clubteam_id)
content_type = ContentType.objects.get_for_model(Player)
This is for grabbing the specific type of contenttype and this is going to be feed to objects.filter, because the player models has the foreignkey of contenttype. And by the obj_id, player is filtered to some players who belong to specific clubteam.
Now we need to add something like this to the rendered html.

player_list.html
{% for player in  players%}
    <div>
        {{player.user.username}} | {{player.age}} 
    </div> </hr>
{% endfor %}

More sophisticated way to code generic foreign key

The fuct that whenever you want to grab player list, you need to code these line is not elegant way to code. Now is the time to add model manager.

player/models.py
from __future__ import unicode_literals
from django.conf import settings
from django.db import models
from club.models import clubteam
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

class PlayerManager(models.Manager):
    def filter_by_clubteam(self, instance):
        content_type = ContentType.objects.get_for_model(instance.__class__)
        #content_type = ContentType.objects.get_for_model(Player)
        qs = super(PlayerManager, self).filter(content_type=content_type, object_id = instance.id)
        return qs
class Player(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    Age = models.PositiveIntegerField()
    
    object=PlayerManager()
    def __str__(self):
        return str(self.user.username)
    def __unicode__(self):
        return str(self.user.username)

club/views.py
from django.shortcuts import render
from django.shortcuts import get_object_or_404
from player.models import Player
from club.models import clubteam

def player_viewr(request, clubteam_id = None):
    instance = get_object_or_404(clubteam, clubteam_id)
    players=Player.objects.filter_by_clubteam(instance)
    context = {
        "instance": instance,
        "players": players
    }
    return render(request, "player_list.html", context)

The other way to grab player list.

club/models.py
from __future__ import unicode_literals
from django.conf import settings
from django.db import models
from player.models import Player


# Create your models here.
class clubteam(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, default = 1)
    @property
    def playerlist(self):
        instance = self
        return Player.object.filter_by_clubteam(instance)
club/views.py
from django.shortcuts import render
from django.shortcuts import get_object_or_404
from player.models import Player
from club.models import clubteam

def player_viewr(request, clubteam_id = None):
    instance = get_object_or_404(clubteam, clubteam_id)
    players=instance.playerlist
    context = {
        "instance": instance,
        "players": players
    }
    return render(request, "player_list.html", context)

Create childkey form

club/models.py
from __future__ import unicode_literals
from django.conf import settings
from django.db import models
from player.models import Player
from django.contrib.contenttypes.models import ContentType

# Create your models here.
class clubteam(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, default = 1)
    @property
    def playerlist(self):
        instance = self
        return Player.object.filter_by_clubteam(instance)
    @property
    def get_content_type(self):
        instance = self
        content_type = ContentType.objects.get_for_model(instance.__class__)
        return content_type
player/forms.py
from django import forms

class PlayerForm(forms.Form):
    content_type = forms.CharField(widget=forms.HiddenInput)
    object_id = forms.IntegerField(widget=forms.HiddenInput)
    age = forms.IntegerField()
club/views.py
from django.shortcuts import render
from django.shortcuts import get_object_or_404
from django.contrib.contenttypes.models import ContentType
from player.models import Player
from club.models import clubteam
from player.forms import PlayerForm
def player_viewr(request, clubteam_id = None):
    instance = get_object_or_404(clubteam, clubteam_id)
    players=instance.playerlist
    initial_data = {
        "content_type":instance.get_content_type,
        "object_id":instance.id
    }
    player_enroll = PlayerForm(request.POST or None, initial = initial_data)
    if player_enroll.is_valid():
        c_type = player_enroll.cleaned_data.get("content_type")
        content_type = ContentType.objects.get(model=c_type)
        obj_id = player_enroll.cleaned_data.get("object_id")
        age_data = player_enroll.cleaned_data.get("age")
        new_player, created = Player.objects.get_or_create(
            user = request.user,
            content_type=content_type,
            object_id = obj_id,
            age = age_data,
        )
    context = {
        "instance": instance,
        "players": players,
        "player_enroll":player_enroll,
    }
    return render(request, "player_list.html", context)
player_list.html
<form method = "POST" action=".">
    {% csrf_token %}
    {{ player_enroll }}
    <input type="submit" value="Enroll">
</form>
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?