Adding generic relations to your models

In generic relations, ContentType objects play the role of pointing to the model used for the relationship. You will need three fields to set up a generic relation in a model:

  • A ForeignKey field to ContentType: This will tell us the model for the relationship
  • A field to store the primary key of the related object: This will usually be a PositiveIntegerField to match Django's automatic primary key fields
  • A field to define and manage the generic relation using the two previous fields: The content types framework offers a GenericForeignKey field for this purpose

Edit the models.py file of the actions application and make it look like this:

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey

class Action(models.Model):
user = models.ForeignKey('auth.User',
related_name='actions',
db_index=True,
on_delete=models.CASCADE)
verb = models.CharField(max_length=255)
target_ct = models.ForeignKey(ContentType,
blank=True,
null=True,
related_name='target_obj',
on_delete=models.CASCADE)
target_id = models.PositiveIntegerField(null=True,
blank=True,
db_index=True)
target = GenericForeignKey('target_ct', 'target_id')
created = models.DateTimeField(auto_now_add=True,
db_index=True)

class Meta:
ordering = ('-created',)

We have added the following fields to the Action model:

  • target_ct: A ForeignKey field that points to the ContentType model
  • target_id: A PositiveIntegerField for storing the primary key of the related object
  • target: A GenericForeignKey field to the related object based on the combination of the two previous fields

Django does not create any field in the database for GenericForeignKey fields. The only fields that are mapped to database fields are target_ct and target_id. Both fields have blank=True and null=True attributes so that a target object is not required when saving Action objects.

You can make your applications more flexible by using generic relationships instead of foreign keys when it makes sense to have a generic relation.

Run the following command to create initial migrations for this application:

python manage.py makemigrations actions

You should see the following output:

Migrations for 'actions':
actions/migrations/0001_initial.py
- Create model Action

Then, run the next command to sync the application with the database:

python manage.py migrate

The output of the command should indicate that the new migrations have been applied, as follows:

Applying actions.0001_initial... OK

Let's add the Action model to the administration site. Edit the admin.py file of the actions application and add the following code to it:

from django.contrib import admin
from .models import Action

@admin.register(Action)
class ActionAdmin(admin.ModelAdmin):
list_display = ('user', 'verb', 'target', 'created')
list_filter = ('created',)
search_fields = ('verb',)

You just registered the Action model in the administration site. Run the python manage.py runserver command to initialize the development server and open http://127.0.0.1:8000/admin/actions/action/add/ in your browser. You should see the page for creating a new Action object, as follows:

As you would notice in the preceding screenshot, only the target_ct and target_id fields that are mapped to actual database fields are shown. The GenericForeignKey field does not appear in the form. The target_ct field allows you to select any of the registered models of your Django project. You can restrict the content types to choose from a limited set of models using the limit_choices_to attribute in the target_ct field: the limit_choices_to attribute allows you to restrict the content of ForeignKey fields to a specific set of values.

Create a new file inside the actions application directory and name it utils.py. We will define a shortcut function that will allow us to create new Action objects in a simple way. Edit the new utils.py file and add the following code to it:

from django.contrib.contenttypes.models import ContentType
from .models import Action

def create_action(user, verb, target=None):
action = Action(user=user, verb=verb, target=target)
action.save()

The create_action() function allows us to create actions that optionally include a target object. We can use this function anywhere in our code as a shortcut to add new actions to the activity stream.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.134.79.121