Populating django field with pre_save()?

PythonDatabaseDjangoModelTriggers

Python Problem Overview


class TodoList(models.Model):
    title = models.CharField(maxlength=100)
    slug = models.SlugField(maxlength=100)
    def save(self):
        self.slug = title
        super(TodoList, self).save()

I'm assuming the above is how to create and store a slug when a title is inserted into the table TodoList, if not, please correct me!

Anyhow, I've been looking into pre_save() as another way to do this, but can't figure out how it works. How do you do it with pre_save()?

is it like

def pre_save(self):
     self.slug = title

I'm guessing no. What is the code to do this?

Thanks!

Python Solutions


Solution 1 - Python

Most likely you are referring to django's pre_save signal. You could setup something like this:

from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.template.defaultfilters import slugify

@receiver(pre_save)
def my_callback(sender, instance, *args, **kwargs):
    instance.slug = slugify(instance.title)

If you dont include the sender argument in the decorator, like @receiver(pre_save, sender=MyModel), the callback will be called for all models.

You can put the code in any file that is parsed during the execution of your app, models.py is a good place for that.

Solution 2 - Python

@receiver(pre_save, sender=TodoList)
def my_callback(sender, instance, *args, **kwargs):
    instance.slug = slugify(instance.title)

Solution 3 - Python

you can use django signals.pre_save:

from django.db.models.signals import post_save, post_delete, pre_save

class TodoList(models.Model):
    @staticmethod
    def pre_save(sender, instance, **kwargs):
        #do anything you want

pre_save.connect(TodoList.pre_save, TodoList, dispatch_uid="sightera.yourpackage.models.TodoList") 

Solution 4 - Python

The pre_save() signal hook is indeed a great place to handle slugification for a large number of models. The trick is to know what models need slugs generated, what field should be the basis for the slug value.

I use a class decorator for this, one that lets me mark models for auto-slug-generation, and what field to base it on:

from django.db import models
from django.dispatch import receiver
from django.utils.text import slugify

def autoslug(fieldname):
    def decorator(model):
        # some sanity checks first
        assert hasattr(model, fieldname), f"Model has no field {fieldname!r}"
        assert hasattr(model, "slug"), "Model is missing a slug field"

        @receiver(models.signals.pre_save, sender=model, weak=False)
        def generate_slug(sender, instance, *args, raw=False, **kwargs):
            if not raw and not instance.slug:
                source = getattr(instance, fieldname)
                slug = slugify(source)
                if slug:  # not all strings result in a slug value
                    instance.slug = slug
        return model
    return decorator

This registers a signal handler for specific models only, and lets you vary the source field with each model decorated:

@autoslug("name")
class NamedModel(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField()

@autoslug("title")
class TitledModel(models.Model):
    title = models.CharField(max_length=255)
    slug = models.SlugField()

Note that no attempt is made to generate a unique slug value. That would require checking for integrity exceptions in a transaction or using a randomised value in the slug from a large enough pool as to make collisions unlikely. Integrity exception checking can only be done in the save() method, not in signal hooks.

Solution 5 - Python

Receiver functions must be like this:

def my_callback(sender, **kwargs):
    print("Request finished!")

Notice that the function takes a sender argument, along with wildcard keyword arguments **(kwargs); all signal handlers must take these arguments.

All signals send keyword arguments, and may change those keyword arguments at any time.

Reference here.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDerekView Question on Stackoverflow
Solution 1 - PythonBernhard VallantView Answer on Stackoverflow
Solution 2 - PythonLeandro SouzaView Answer on Stackoverflow
Solution 3 - PythonEyal ChView Answer on Stackoverflow
Solution 4 - PythonMartijn PietersView Answer on Stackoverflow
Solution 5 - PythonRockalliteView Answer on Stackoverflow