DJC Slugify 完整使用指南

📋 功能概览

  1. 多语言支持:自动处理中英文(中文转拼音)
  2. 智能唯一性检测:自动识别模型约束
  3. 多种集成方式:Mixin、手动调用、模型方法
  4. 灵活配置:全局配置 + 单次调用配置
  5. 批量处理:支持批量生成和更新
  6. 分组支持:支持同一分组内保持唯一

🚀 快速开始

安装依赖

 
pip install python-slugify pypinyin
 
 

放置文件

djc_slugify.py放在项目的 utils/目录下。

🎯 基础用法

1. 基本转换

 
from utils.djc_slugify import djc_slugify

英文

slug1 = djc_slugify("Hello World!") # hello-world

中文(自动转拼音)

slug2 = djc_slugify("你好,世界!") # ni-hao-shi-jie

混合

slug3 = djcslugify("Python编程教程") # python-bian-cheng-jiao-cheng

 
 

2. 在模型中集成(Mixin方式)

场景1:全局唯一slug
 
from django.db import models
from utils.djcslugify import SlugMixin

继承SlugMixin,自动获得slug字段

class Article(SlugMixin, models.Model): title = models.CharField(maxlength=200, verbosename="标题") content = models.TextField(verbose_name="内容")

# 可选:指定生成slug的源字段,默认是'title'
slug_source_field = 'title'

def __str__(self):
    return self.title

使用

article = Article(title="Python基础教程") article.save() # 自动生成slug: python-ji-chu-jiao-cheng print(article.slug)

 
 
场景2:分组内唯一slug
 
from django.db import models
from utils.djc_slugify import GroupSlugMixin

class Tag(GroupSlugMixin, models.Model): name = models.CharField(maxlength=100, verbosename="标签名") group = models.CharField(maxlength=50, verbosename="分组")

# 指定源字段
slug_source_field = 'name'
# 指定分组字段
slug_group_field = 'group'

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

使用

tag1 = Tag(name="Python", group="language") tag1.save() # slug: python

tag2 = Tag(name="Python", group="language") tag2.save() # slug: python-1(同一分组内自动添加后缀)

tag3 = Tag(name="Python", group="framework") tag3.save() # slug: python(不同分组,可以相同)

 
 

⚙️ 高级用法

3. 手动调用生成

基本生成
 
from utils.djc_slugify import DJCSlugify
from blog.models import Article

方式1:使用便捷函数

slug1 = djc_slugify("测试标题")

方式2:使用类方法

slug2 = DJCSlugify.generateslug("测试标题")

 
 
带唯一性检查
 
# 检查全局唯一性(Article.slug字段是unique=True)
slug = DJCSlugify.generateslug(
    text="新的文章标题",
    model=Article,               # 指定模型
    instancepk=None,            # 新建时为None
    fieldname='slug',           # slug字段名
    # unique_by=None             # 自动检测模型约束
)

检查分组唯一性

from blog.models import Tag slug = DJCSlugify.generateslug( text="Python", model=Tag, instancepk=None, fieldname='slug', uniqueby={'group': 'language'} # 只检查language分组内的唯一性 )

不检查唯一性

slug = DJCSlugify.generateslug( text="标题", model=Article, uniqueby=False # 即使slug字段是unique,也不检查 )

 
 
为已有实例生成
 
from utils.djcslugify import slugifyfor_model

article = Article.objects.get(pk=1) newslug = slugifyformodel( instance=article, sourcefield='title', # 从title字段生成 slugfield='slug' # 保存到slug字段 ) article.slug = newslug article.save()

 
 

4. 自定义配置

全局配置(推荐在settings.py中)
 
# settings.py
from utils.djc_slugify import DJCSlugify

DJCSlugify.configure( maxlength=100, # 最大长度 separator='', # 分隔符 language='auto', # 语言检测:auto/zh/en lowercase=True, # 转为小写 stopwords=('a', 'an', 'the', '的', '是', '了'), replacements=[ # 自定义替换 ('&', 'and'), ('@', 'at'), ('#', 'sharp'), ] )

 
 
单次调用配置
 
slug = djcslugify(
    "Python&Django",
    maxlength=150,
    separator='-',
    lowercase=False,  # 不转为小写
    replacements=[('&', 'and')],
    uniqueby=False
)
 
 

🔄 批量处理

5. 批量生成

 
# 批量生成slug
titles = ["文章一", "Article Two", "Python教程"]
slugs = DJCSlugify.bulkgenerateslugs(
    texts=titles,
    model=Article,  # 可选:检查唯一性
    uniqueby=None
)

结果: ['wen-zhang-yi', 'article-two', 'python-jiao-cheng']

 
 

6. 更新已有数据

在Django Shell中:
 
python manage.py shell
 
 
 
# 更新所有文章的slug

from utils.djc_slugify import DJCSlugify from blog.models import Article

result = DJCSlugify.updateexistingslugs( modelclass=Article, sourcefield='title', slugfield='slug', batchsize=100, # 每批100条 verbose=True # 显示进度 )

print(f"更新了 {result['updated']} 条,跳过了 {result['skipped']} 条")

 
 
创建管理命令:
 
# management/commands/updateslugs.py
from django.core.management.base import BaseCommand
from utils.djcslugify import DJCSlugify
from blog.models import Article, Category, Tag

class Command(BaseCommand): help = '更新所有模型的slug'

def handle(self, *args, **options):
    models = [Article, Category, Tag]

    for model in models:
        self.stdout.write(f"更新 {model.__name__} ...")
        result = DJCSlugify.update_existing_slugs(
            model_class=model,
            verbose=False
        )
        self.stdout.write(
            self.style.SUCCESS(
                f"  ✓ 完成: 更新{result['updated']}条,跳过{result['skipped']}条"
            )
        )</code></pre></div><div class="hyc-code-scrollbar__track" style="bottom:4px;height:7px;left:4px;position:absolute;right:4px;"><div class="hyc-code-scrollbar__thumb" style="display:block;height:100%;position:relative;width:0px;">&nbsp;</div></div><div style="border-radius:3px;bottom:2px;position:absolute;right:2px;top:2px;width:6px;"><div style="background-color:rgba(0, 0, 0, 0.2);border-radius:inherit;cursor:pointer;display:block;height:0px;position:relative;width:100%;">&nbsp;</div></div></div></div><div class="ybc-p">运行命令:</div><div class="hyc-common-markdown__code"><div class="hyc-common-markdown__code__hd">&nbsp;</div><div class="hyc-code-scrollbar" style="height:100%;overflow:hidden;position:relative;width:100%;"><div class="hyc-code-scrollbar__view" style="inset:0px;margin-bottom:-13px;margin-right:-13px;overflow:scroll;position:relative;"><pre><code class="language-bash">python manage.py update_slugs</code></pre></div><div class="hyc-code-scrollbar__track" style="bottom:4px;height:7px;left:4px;position:absolute;right:4px;"><div class="hyc-code-scrollbar__thumb" style="display:block;height:100%;position:relative;width:0px;">&nbsp;</div></div><div style="border-radius:3px;bottom:2px;position:absolute;right:2px;top:2px;width:6px;"><div style="background-color:rgba(0, 0, 0, 0.2);border-radius:inherit;cursor:pointer;display:block;height:0px;position:relative;width:100%;">&nbsp;</div></div></div></div><hr><h2>🎪 实战示例</h2><h3>示例1:CMS文章系统</h3><div class="hyc-common-markdown__code"><div class="hyc-common-markdown__code__hd">&nbsp;</div><div class="hyc-code-scrollbar" style="height:100%;overflow:hidden;position:relative;width:100%;"><div class="hyc-code-scrollbar__view" style="inset:0px;margin-bottom:-13px;margin-right:-13px;overflow:scroll;position:relative;"><pre><code class="language-python"># models.py

from django.db import models from utils.djc_slugify import SlugMixin

class Category(SlugMixin, models.Model): name = models.CharField(maxlength=100, verbosename="分类名") slugsourcefield = 'name' # 从name生成slug

class Meta:
    verbose_name = "分类"
    verbose_name_plural = "分类"

class Article(SlugMixin, models.Model): title = models.CharField(maxlength=200, verbosename="标题") content = models.TextField(verbosename="内容") category = models.ForeignKey(Category, ondelete=models.CASCADE, verbosename="分类") published = models.BooleanField(default=False, verbosename="已发布")

def __str__(self):
    return self.title

class Meta:
    verbose_name = "文章"
    verbose_name_plural = "文章"
    ordering = ['-id']

使用

category = Category(name="编程技术") category.save() # 自动生成slug: bian-cheng-ji-shu

article = Article( title="Python入门指南", content="...", category=category ) article.save() # 自动生成slug: python-ru-men-zhi-nan

 
 

示例2:多用户标签系统

 
# models.py
from django.db import models
from utils.djc_slugify import GroupSlugMixin
from django.contrib.auth.models import User

class UserTag(GroupSlugMixin, models.Model): name = models.CharField(maxlength=100, verbosename="标签名") user = models.ForeignKey(User, ondelete=models.CASCADE, verbosename="用户") slugsourcefield = 'name' # 从name生成slug sluggroupfield = 'user_id' # 按用户分组

class Meta:
    verbose_name = "用户标签"
    verbose_name_plural = "用户标签"
    # 同一个用户的标签名唯一
    unique_together = ['user', 'name']

def __str__(self):
    return f"{self.name} (用户: {self.user.username})"

使用

user1 = User.objects.get(username="alice") user2 = User.objects.get(username="bob")

不同用户可以创建同名标签

tag1 = UserTag(name="重要", user=user1) tag1.save() # slug: zhong-yao

tag2 = UserTag(name="重要", user=user1) tag2.save() # slug: zhong-yao-1(同一用户下唯一)

tag3 = UserTag(name="重要", user=user2) tag3.save() # slug: zhong-yao(不同用户,可以相同)

 
 

示例3:在视图中使用

 
# views.py
from django.views.generic import CreateView, UpdateView
from django.shortcuts import getobjector404
from utils.djcslugify import slugifyformodel
from .models import Article

class ArticleCreateView(CreateView): model = Article templatename = 'articleform.html' fields = ['title', 'content', 'category']

def form_valid(self, form): article = form.save(commit=False)

# 方法1:自动生成(推荐) article.slug = slugifyformodel(article)

# 方法2:手动控制 # article.slug = djcslugify( # article.title, # model=Article, # maxlength=150 # )

article.save() return super().form_valid(form)

class ArticleUpdateView(UpdateView): model = Article templatename = 'articleform.html' fields = ['title', 'content', 'category']

def form_valid(self, form): article = form.save(commit=False)

# 如果标题改变了,重新生成slug if 'title' in form.changeddata: article.slug = slugifyfor_model(article)

article.save() return super().formvalid(form)

 
 

示例4:在Form中验证

 
# forms.py
from django import forms
from utils.djcslugify import DJCSlugify
from .models import Tag

class TagForm(forms.ModelForm): class Meta: model = Tag fields = ['name', 'group']

def clean(self): cleaneddata = super().clean() name = cleaneddata.get('name') group = cleaned_data.get('group')

if name and group: # 生成slug slug = DJCSlugify.generateslug( text=name, model=Tag, instancepk=self.instance.pk if self.instance else None, unique_by={'group': group} )

# 检查是否已存在 qs = Tag.objects.filter(group=group, slug=slug) if self.instance and self.instance.pk: qs = qs.exclude(pk=self.instance.pk)

if qs.exists(): raise forms.ValidationError(f"分组 '{group}' 中已存在标签 '{name}'")

cleaned_data['slug'] = slug

return cleaneddata

 
 

🔧 维护和优化

1. 数据迁移脚本

 
# 迁移脚本:为已有数据添加slug
def migrateslugs():
    from utils.djc_slugify import DJCSlugify
    from blog.models import Article, Category

print("开始迁移Article...") result1 = DJCSlugify.updateexistingslugs(Article, verbose=True)

print("\n开始迁移Category...") result2 = DJCSlugify.updateexistingslugs(Category, verbose=True)

print(f"\n✅ 迁移完成!") print(f"Article: 更新{result1['updated']}条") print(f"Category: 更新{result2['updated']}条")

 
 

2. 性能优化

 
# 处理大量数据时
from utils.djc_slugify import DJCSlugify
from django.db import transaction

@transaction.atomic def updateallarticlesslugs(): articles = Article.objects.all() batchsize = 500

for i in range(0, articles.count(), batch_size):
    batch = articles[i:i+batch_size]

    for article in batch:
        # 只更新没有slug的
        if not article.slug:
            article.slug = DJCSlugify.generate_slug_for_model(article)
            article.save(update_fields=['slug'])

    print(f"已处理: {i+batch_size}/{articles.count()}")</code></pre></div><div class="hyc-code-scrollbar__track" style="bottom:4px;height:7px;left:4px;position:absolute;right:4px;"><div class="hyc-code-scrollbar__thumb" style="display:block;height:100%;position:relative;width:0px;">&nbsp;</div></div><div style="border-radius:3px;bottom:2px;position:absolute;right:2px;top:2px;width:6px;"><div style="background-color:rgba(0, 0, 0, 0.2);border-radius:inherit;cursor:pointer;display:block;height:0px;position:relative;width:100%;">&nbsp;</div></div></div></div><h3>3. 自定义语言处理器</h3><div class="hyc-common-markdown__code"><div class="hyc-common-markdown__code__hd">&nbsp;</div><div class="hyc-code-scrollbar" style="height:100%;overflow:hidden;position:relative;width:100%;"><div class="hyc-code-scrollbar__view" style="inset:0px;margin-bottom:-13px;margin-right:-13px;overflow:scroll;position:relative;"><pre><code class="language-python"># 自定义中文处理(首字母)

def firstletterhandler(text, config): from pypinyin import lazypinyin, Style pinyinlist = lazypinyin(text, style=Style.FIRSTLETTER) return ''.join(pinyin_list).lower()

注册

DJCSlugify.LANGHANDLERS['zh-first'] = firstletter_handler

使用

slug = DJCSlugify.generate_slug("你好世界", language='zh-first')

结果: "nhsj"

 
 

🆘 常见问题

Q1: slug重复了怎么办?

 
# 检查并修复重复的slug

from django.db.models import Count from blog.models import Article

duplicates = Article.objects.values('slug').annotate( count=Count('id') ).filter(count__gt=1)

for dup in duplicates: articles = Article.objects.filter(slug=dup['slug']) for i, article in enumerate(articles): if i > 0: # 保留第一个 newslug = DJCSlugify.generateslugformodel(article) article.slug = newslug article.save() print(f"修复: {article.id} -> {newslug}")

 
 

Q2: 如何修改已有slug的规则?

 
# 临时修改配置
originalmaxlength = DJCSlugify.DEFAULTCONFIG['maxlength']
DJCSlugify.configure(max_length=200)

更新数据

DJCSlugify.updateexistingslugs(Article)

恢复配置

DJCSlugify.configure(maxlength=originalmaxlength)

 
 

Q3: 如何禁用自动生成?

 
# 在模型中覆盖save方法
class Article(models.Model):
    title = models.CharField(maxlength=200)
    slug = models.SlugField(max_length=200, blank=True)

def save(self, *args, **kwargs):
    # 不自动生成slug
    if not self.slug and self.title:
        # 留空或手动处理
        pass
    super().save(*args, **kwargs)</code></pre></div><div class="hyc-code-scrollbar__track" style="bottom:4px;height:7px;left:4px;position:absolute;right:4px;"><div class="hyc-code-scrollbar__thumb" style="display:block;height:100%;position:relative;width:0px;">&nbsp;</div></div><div style="border-radius:3px;bottom:2px;position:absolute;right:2px;top:2px;width:6px;"><div style="background-color:rgba(0, 0, 0, 0.2);border-radius:inherit;cursor:pointer;display:block;height:0px;position:relative;width:100%;">&nbsp;</div></div></div></div><hr><h2>📊 最佳实践总结</h2><ol class="ybc-ol-component ybc-ol-component_1"><li class="ybc-li-component ybc-li-component_ol"><div class="ybc-p"><span class="ybc-li-component_content"><strong>新项目</strong>:使用 </span><code class="hyc-common-markdown__code__inline"><span class="ybc-li-component_content">SlugMixin</span></code><span class="ybc-li-component_content">或 </span><code class="hyc-common-markdown__code__inline"><span class="ybc-li-component_content">GroupSlugMixin</span></code></div></li><li class="ybc-li-component ybc-li-component_ol"><div class="ybc-p"><span class="ybc-li-component_content"><strong>现有项目</strong>:使用 </span><code class="hyc-common-markdown__code__inline"><span class="ybc-li-component_content">DJCSlugify.update_existing_slugs()</span></code><span class="ybc-li-component_content">迁移</span></div></li><li class="ybc-li-component ybc-li-component_ol"><div class="ybc-p"><span class="ybc-li-component_content"><strong>批量操作</strong>:使用批量方法,设置合理的 </span><code class="hyc-common-markdown__code__inline"><span class="ybc-li-component_content">batch_size</span></code></div></li><li class="ybc-li-component ybc-li-component_ol"><div class="ybc-p"><span class="ybc-li-component_content"><strong>唯一性</strong>:根据业务需求设置正确的 </span><code class="hyc-common-markdown__code__inline"><span class="ybc-li-component_content">unique_by</span></code></div></li><li class="ybc-li-component ybc-li-component_ol"><div class="ybc-p"><span class="ybc-li-component_content"><strong>性能</strong>:大量数据时分批次处理</span></div></li><li class="ybc-li-component ybc-li-component_ol"><div class="ybc-p"><span class="ybc-li-component_content"><strong>备份</strong>:重要操作前备份数据</span></div></li></ol>

核心功能

快速找到您需要的专业解决方案

查看全部

Djacore CMS是什么?主要解决什么问题?

核心功能 25 次浏览

Djacore CMS是基于Django 5.2开发的企业级智能内容管理系统,专门为处理千万级数据量场景设计。它提供安全、高效、可扩展的内容管理解决方案,帮助企业快速构建和维护数字内容平台,支持智能缓存优化、模块化扩展和AI内容生成。


DjacoreCMS 的 SEO 功能有多强?

SEO优化 11 次浏览 2026-04-12

所有内容模型均基于标准 SEO 基类构建,不仅自动满足基础的 TDK(标题、描述、关键词)设置,还完整支持 Open Graph 协议和 Twitter Card 标准,确保内容在搜索引擎和社交媒体中获得最佳展示效果。


DjacoreCMS 的 SEO 优化功能有哪些?

SEO优化 11 次浏览 2026-04-11

所有内容模型均基于标准 SEO 基类构建,完整支持 TDK 设置、Open Graph 协议和 Twitter Card 标准,确保在搜索引擎和社交媒体获得最佳展示效果。


DjacoreCMS的SEO优化功能包括哪些?

SEO优化 11 次浏览 2026-04-12

1. 所有内容模型基于标准SEO基类构建2. 完整的TDK(标题、描述、关键词)支持3. 完整支持Open Graph协议和Twitter Card标准4. 优秀的Tag逻辑设计5. 灵活的URL设计,支持规则化路径、泛目录、ID防爬等


DjacoreCMS 适合建设哪些类型的网站?

核心功能 11 次浏览 2026-04-12

适合企业官网、内容发布平台、电商系统、在线教育平台、知识管理系统、资源下载平台等,满足多样化网站建设需求。


如何为 DjacoreCMS 的不同页面(首页、列表页、详情页)设置不同的 `<title>`?

模板开发 10 次浏览 2026-04-11

在基础模板(如 `base.html`)中,将 `` 标签内容定义在一个 block 中:`{% block title %}默认标题{% endblock %}`。然后,在各个子模板(如 `home.html`, `article_detail.html`)中,使用 `{% block ti...