You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中使用Slug(Form+Model)时未自动生成Slug的问题

问题分析与解决:Django中Recipe的slug字段未自动生成

问题现象

通过Django Admin或自定义表单添加Recipe时,slug字段始终为None,导致基于slug的路由访问报错。需求为slug无需用户手动输入,自动基于title字段生成,且已将slug字段设置为不可编辑、唯一。

模型代码

from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
from django.urls import reverse


class Ingredient(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return f'{self.name}'


class Recipe(models.Model):
    slug = models.SlugField(null=True, unique=True, blank=True, editable=False)
    title = models.CharField(null=False, max_length=100)
    description = models.CharField(null=False, max_length=250)
    preparation = models.TextField(null=False)
    score = models.IntegerField(null=True, validators=[MinValueValidator(1), MaxValueValidator(5)])
    last_update = models.DateField(auto_now=True)
    presentation_image = models.ImageField(upload_to='images', null=True, blank=True)
    ingredients = models.ManyToManyField(Ingredient)

    def __str__(self):
        return f'{self.slug}, {self.title}, {self.description}, {self.preparation}, {self.score}, {self.last_update}, {self.presentation_image}, {self.ingredients}'

表单代码

from django import forms
from django.core.validators import MinValueValidator, MaxValueValidator

from .models import Recipe
from .models import Ingredient


class RecipeForm(forms.ModelForm):
    class Meta:
        model = Recipe
        exclude = ['slug', 'last_update']
        widgets = {
            'preparation': forms.Textarea()
        }
        labels = {
            'title': 'Title',
            'description': 'Description',
            'preparation': 'Preparation',
            'score': 'Score',
            'presentation_image': 'Presentation Image',
            'ingredients': 'Ingredients'
        }

逻辑错误原因

Django不会自动为SlugField生成内容,哪怕你将其设为不可编辑。当前代码完全没有将title转换为slug并赋值的逻辑,因此每次保存Recipe时,slug字段会保持默认的null状态。

解决方法

方法1:重写Recipe模型的save方法

在模型的save方法中添加slug生成逻辑,同时处理重复slug的情况以保证唯一性:

from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
from django.utils.text import slugify


class Recipe(models.Model):
    slug = models.SlugField(null=True, unique=True, blank=True, editable=False)
    title = models.CharField(null=False, max_length=100)
    # 其余字段定义不变

    def save(self, *args, **kwargs):
        # 如果slug为空,基于title生成
        if not self.slug:
            base_slug = slugify(self.title)
            # 检查重复slug,存在则添加数字后缀
            slug_count = Recipe.objects.filter(slug__startswith=base_slug).count()
            if slug_count > 0:
                self.slug = f"{base_slug}-{slug_count + 1}"
            else:
                self.slug = base_slug
        super().save(*args, **kwargs)

    # 其余方法不变

方法2:使用Django的pre_save信号

通过信号在模型保存前自动生成slug,适合不想修改模型save方法的场景:

# 在models.py末尾添加
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils.text import slugify

@receiver(pre_save, sender=Recipe)
def generate_recipe_slug(sender, instance, **kwargs):
    if not instance.slug:
        base_slug = slugify(instance.title)
        slug_count = sender.objects.filter(slug__startswith=base_slug).count()
        if slug_count > 0:
            instance.slug = f"{base_slug}-{slug_count + 1}"
        else:
            instance.slug = base_slug

注意事项

  • 使用django.utils.text.slugify生成slug,它会自动将字符串转换为符合规则的格式(小写、空格转连字符、去除特殊字符)。
  • 必须处理slug重复情况,避免因不同title生成相同slug导致唯一约束报错。
  • 针对已存在的Recipe记录,需手动生成slug并更新数据库(可通过Django shell批量处理)。

内容的提问来源于stack exchange,提问作者Dragos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 01:05:00