Django generic ForeignKey

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`


```python: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 
```python: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.


```python: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.

```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.

```python: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)

```

```python: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.

```python: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)
```

```python: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
```python: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
```
```python: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()
```
```python: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)
```


```html:player_list.html
<form method = "POST" action=".">
    {% csrf_token %}
    {{ player_enroll }}
    <input type="submit" value="Enroll">
</form>
```