尝试从 Django 博客的标题帖子中删除 url
Triyng to slugify url from title posts in a Django blog
为期货 url 创建了 slug 变量并执行了 ./makemigrations 和 migrate 并且参数出现在管理面板中,但是当我在进行空的“theblog”迁移后尝试迁移时我收到此错误:
class Migration(migrations.Migration):
File "xxx/theblog/models.py", line 104, in Migration
migrations.RunPython(generate_slugs_for_old_posts, reverse=reverse_func),
TypeError: __init__() got an unexpected keyword argument 'reverse'
将 slug 参数从 null 和空白更改为唯一,但现在这似乎不是问题所在。我知道问题出在 get_success_url 但真的不知道如何解决。
models.py:
from django.utils.text import slugify
class Post(models.Model):
title= models.CharField(max_length=100)
header_image = models.ImageField(null=True , blank=True, upload_to="images/")
title_tag= models.CharField(max_length=100)
author= models.ForeignKey(User, on_delete=models.CASCADE)
body = RichTextUploadingField(extra_plugins=
['youtube', 'codesnippet'], external_plugin_resources= [('youtube','/static/ckeditor/youtube/','plugin.js'), ('codesnippet','/static/ckeditor/codesnippet/','plugin.js')])
post_date = models.DateTimeField(auto_now_add=True)
category = models.CharField(max_length=50, default='uncategorized')
slug = models.SlugField(unique=True)
snippet = models.CharField(max_length=200)
status = models.IntegerField(choices=STATUS, default=0)
likes = models.ManyToManyField(User, blank=True, related_name='blog_posts')
def save(self, *args, **kwargs):
self.slug = self.generate_slug()
return super().save(*args, **kwargs)
def generate_slug(self, save_to_obj=False, add_random_suffix=True):
generated_slug = slugify(self.title)
random_suffix = ""
if add_random_suffix:
random_suffix = ''.join([
random.choice(string.ascii_letters + string.digits)
for i in range(5)
])
generated_slug += '-%s' % random_suffix
if save_to_obj:
self.slug = generated_slug
self.save(update_fields=['slug'])
return generated_slug
def generate_slugs_for_old_posts(apps, schema_editor):
Post = apps.get_model("theblog", "Post")
for post in Post.objects.all():
post.slug = slugify(post.title)
post.save(update_fields=['slug'])
def reverse_func(apps, schema_editor):
pass # just pass
class Migration(migrations.Migration):
dependencies = []
operations = [
migrations.RunPython(generate_slugs_for_old_posts, reverse=reverse_func),
]
首先,您必须向 Post
模型添加可为空的 slug 字段。这是 slug 字段上的 Django docs。您也必须实现生成 slug 值的方法。您可以在您的模型上实现 generate_slug(...)
方法,例如:
import string # for string constants
import random # for generating random strings
# other imports ...
from django.utils.text import slugify
# other imports ...
class Post(models.Model):
# ...
slug = models.SlugField(null=True, blank=True, unique=True)
# ...
def save(self, *args, **kwargs):
self.slug = self.generate_slug()
return super().save(*args, **kwargs)
def generate_slug(self, save_to_obj=False, add_random_suffix=True):
"""
Generates and returns slug for this obj.
If `save_to_obj` is True, then saves to current obj.
Warning: setting `save_to_obj` to True
when called from `.save()` method
can lead to recursion error!
`add_random_suffix ` is to make sure that slug field has unique value.
"""
# We rely on django's slugify function here. But if
# it is not sufficient for you needs, you can implement
# you own way of generating slugs.
generated_slug = slugify(self.title)
# Generate random suffix here.
random_suffix = ""
if add_random_suffix:
random_suffix = ''.join([
random.choice(string.ascii_letters + string.digits)
for i in range(5)
])
generated_slug += '-%s' % random_suffix
if save_to_obj:
self.slug = generated_slug
self.save(update_fields=['slug'])
return generated_slug
现在,在保存每个对象时,您都会自动为您的对象生成 slug。
处理没有设置 slug
字段的旧 post。您必须使用 RunPython
(Django docs):
创建自定义迁移
首先运行这个命令
python manage.py makemigrations <APP_NAME> --empty
将 替换为 Post
模型所在的实际应用名称。
它将生成一个空的迁移文件:
from django.utils.text import slugify
from django.db import migrations
def generate_slugs_for_old_posts(apps, schema_editor):
Post = apps.get_model("<APP_NAME>", "Post") # replace <APP_NAME> with actual app name
# dummy way
for post in Post.objects.all():
# Do not try to use `generate_slug` method
# here, you probably will get error saying
# that Post does not have method called `generate_slug`
# as it is not the actual class you have defined in your
# models.py!
post.slug = slugify(post.title)
post.save(update_fields=['slug'])
def reverse_func(apps, schema_editor):
pass # just pass
class Migration(migrations.Migration):
dependencies = []
operations = [
migrations.RunPython(generate_slugs_for_old_posts, reverse=reverse_func),
]
之后您可以更改 slug 字段并使其不可为空:
class Post(models.Model):
# ...
slug = models.SlugField(unique=True)
# ...
现在 python manage.py migrate
,这将使 slug 字段在以后的 post 中不可为空,但它可能会警告您正在尝试创建现有列
不可为空。在这里你有一个选项,上面写着“我已经创建了自定义迁移”或类似的东西。 Select它。
现在,当您的 post 有 slug 时,您必须修复您的视图,以便它们接受来自 url 的 slug 参数。这里的技巧是确保您的 posts 也被 ID 接受。因为有人可能已经有一个 link 到一些 post 的 ID。如果您删除带有 ID 参数的 URL,那么有人可能无法再使用旧的 link。
您也可以将旧网址重定向到新网址
urls.py
[..]
from django.views.generic.base import RedirectView
urlpatterns = [
# Redirect old links:
path('article/<int:pk>', RedirectView.as_view(url='article/<slug:url>', permanent=True)),
# You won't need this path any more
# path('article/<int:pk>', ArticleDetailView.as_view(), name="article-detail"),
# The new path with slug
path('article/<slug:url>', ArticleDetailView.as_view(), name="article-detail"),
[..]
]
参考https://docs.djangoproject.com/en/3.1/ref/class-based-views/base/#redirectview
为期货 url 创建了 slug 变量并执行了 ./makemigrations 和 migrate 并且参数出现在管理面板中,但是当我在进行空的“theblog”迁移后尝试迁移时我收到此错误:
class Migration(migrations.Migration):
File "xxx/theblog/models.py", line 104, in Migration
migrations.RunPython(generate_slugs_for_old_posts, reverse=reverse_func),
TypeError: __init__() got an unexpected keyword argument 'reverse'
将 slug 参数从 null 和空白更改为唯一,但现在这似乎不是问题所在。我知道问题出在 get_success_url 但真的不知道如何解决。
models.py:
from django.utils.text import slugify
class Post(models.Model):
title= models.CharField(max_length=100)
header_image = models.ImageField(null=True , blank=True, upload_to="images/")
title_tag= models.CharField(max_length=100)
author= models.ForeignKey(User, on_delete=models.CASCADE)
body = RichTextUploadingField(extra_plugins=
['youtube', 'codesnippet'], external_plugin_resources= [('youtube','/static/ckeditor/youtube/','plugin.js'), ('codesnippet','/static/ckeditor/codesnippet/','plugin.js')])
post_date = models.DateTimeField(auto_now_add=True)
category = models.CharField(max_length=50, default='uncategorized')
slug = models.SlugField(unique=True)
snippet = models.CharField(max_length=200)
status = models.IntegerField(choices=STATUS, default=0)
likes = models.ManyToManyField(User, blank=True, related_name='blog_posts')
def save(self, *args, **kwargs):
self.slug = self.generate_slug()
return super().save(*args, **kwargs)
def generate_slug(self, save_to_obj=False, add_random_suffix=True):
generated_slug = slugify(self.title)
random_suffix = ""
if add_random_suffix:
random_suffix = ''.join([
random.choice(string.ascii_letters + string.digits)
for i in range(5)
])
generated_slug += '-%s' % random_suffix
if save_to_obj:
self.slug = generated_slug
self.save(update_fields=['slug'])
return generated_slug
def generate_slugs_for_old_posts(apps, schema_editor):
Post = apps.get_model("theblog", "Post")
for post in Post.objects.all():
post.slug = slugify(post.title)
post.save(update_fields=['slug'])
def reverse_func(apps, schema_editor):
pass # just pass
class Migration(migrations.Migration):
dependencies = []
operations = [
migrations.RunPython(generate_slugs_for_old_posts, reverse=reverse_func),
]
首先,您必须向 Post
模型添加可为空的 slug 字段。这是 slug 字段上的 Django docs。您也必须实现生成 slug 值的方法。您可以在您的模型上实现 generate_slug(...)
方法,例如:
import string # for string constants
import random # for generating random strings
# other imports ...
from django.utils.text import slugify
# other imports ...
class Post(models.Model):
# ...
slug = models.SlugField(null=True, blank=True, unique=True)
# ...
def save(self, *args, **kwargs):
self.slug = self.generate_slug()
return super().save(*args, **kwargs)
def generate_slug(self, save_to_obj=False, add_random_suffix=True):
"""
Generates and returns slug for this obj.
If `save_to_obj` is True, then saves to current obj.
Warning: setting `save_to_obj` to True
when called from `.save()` method
can lead to recursion error!
`add_random_suffix ` is to make sure that slug field has unique value.
"""
# We rely on django's slugify function here. But if
# it is not sufficient for you needs, you can implement
# you own way of generating slugs.
generated_slug = slugify(self.title)
# Generate random suffix here.
random_suffix = ""
if add_random_suffix:
random_suffix = ''.join([
random.choice(string.ascii_letters + string.digits)
for i in range(5)
])
generated_slug += '-%s' % random_suffix
if save_to_obj:
self.slug = generated_slug
self.save(update_fields=['slug'])
return generated_slug
现在,在保存每个对象时,您都会自动为您的对象生成 slug。
处理没有设置 slug
字段的旧 post。您必须使用 RunPython
(Django docs):
首先运行这个命令
python manage.py makemigrations <APP_NAME> --empty
将 Post
模型所在的实际应用名称。
它将生成一个空的迁移文件:
from django.utils.text import slugify
from django.db import migrations
def generate_slugs_for_old_posts(apps, schema_editor):
Post = apps.get_model("<APP_NAME>", "Post") # replace <APP_NAME> with actual app name
# dummy way
for post in Post.objects.all():
# Do not try to use `generate_slug` method
# here, you probably will get error saying
# that Post does not have method called `generate_slug`
# as it is not the actual class you have defined in your
# models.py!
post.slug = slugify(post.title)
post.save(update_fields=['slug'])
def reverse_func(apps, schema_editor):
pass # just pass
class Migration(migrations.Migration):
dependencies = []
operations = [
migrations.RunPython(generate_slugs_for_old_posts, reverse=reverse_func),
]
之后您可以更改 slug 字段并使其不可为空:
class Post(models.Model):
# ...
slug = models.SlugField(unique=True)
# ...
现在 python manage.py migrate
,这将使 slug 字段在以后的 post 中不可为空,但它可能会警告您正在尝试创建现有列
不可为空。在这里你有一个选项,上面写着“我已经创建了自定义迁移”或类似的东西。 Select它。
现在,当您的 post 有 slug 时,您必须修复您的视图,以便它们接受来自 url 的 slug 参数。这里的技巧是确保您的 posts 也被 ID 接受。因为有人可能已经有一个 link 到一些 post 的 ID。如果您删除带有 ID 参数的 URL,那么有人可能无法再使用旧的 link。
您也可以将旧网址重定向到新网址
urls.py
[..]
from django.views.generic.base import RedirectView
urlpatterns = [
# Redirect old links:
path('article/<int:pk>', RedirectView.as_view(url='article/<slug:url>', permanent=True)),
# You won't need this path any more
# path('article/<int:pk>', ArticleDetailView.as_view(), name="article-detail"),
# The new path with slug
path('article/<slug:url>', ArticleDetailView.as_view(), name="article-detail"),
[..]
]
参考https://docs.djangoproject.com/en/3.1/ref/class-based-views/base/#redirectview