如何在 Django 中添加 slug Web-application
How to add slug in Django Web-application
这是问题所在:我正在尝试 'fresh' 我的 django 网络博客,所以我不想 /post/2/
我想要重击 link 这与我的标题完全一样(像这样:/post/today-is-friday
这是一些代码,我尝试了一些方法,但没有任何效果:
models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
from django.template.defaultfilters import slugify
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
categories = models.ManyToManyField('Category', related_name='posts')
image = models.ImageField(upload_to='images/', default="images/None/no-img.jpg")
slug= models.SlugField(max_length=500, unique=True, null=True, blank=True)
def save(self, *args, **kwargs):
self.url= slugify(self.title)
super(Post, self).save(*args, **kwargs)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
class Category(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
urls.py
from django.urls import path
from django.conf.urls import include, url
from . import views
from .views import PostListView, PostDetailView, PostCreateView, PostUpdateView, PostDeleteView, UserPostListView
urlpatterns = [
#Blog section
path("", PostListView.as_view(), name='blog-home'),
path("user/<str:username>", UserPostListView.as_view(), name='user-posts'),
path("<slug:slug>/", PostDetailView.as_view(), name='post-detail'),
path("post/new/", PostCreateView.as_view(), name='post-create'),
path("<slug:slug>/update/", PostUpdateView.as_view(), name='post-update'),
path("<slug:slug>/delete/", PostDeleteView.as_view(), name='post-delete'),
path("about/", views.about, name="blog-about"),
path("<category>/", views.blog_category, name="blog_category"),
]
user_posts.html(这是为了访问博客 post 本身)
{% extends 'blog/base.html' %}
{% block content %}
<h1 class='mb-3'>Post by {{ view.kwargs.username }} ({{ page_obj.paginator.count }})</h1>
{% for post in posts %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ post.author.profile.image.url }}" alt="">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2 author_title" href="{% url 'user-posts' post.author.username %}">@{{ post.author }}</a>
<small class="text-muted">{{ post.date_posted|date:"N d, Y" }}</small>
<div>
<!-- category section -->
<small class="text-muted">
Categories:
{% for category in post.categories.all %}
<a href="{% url 'blog_category' category.name %}">
{{ category.name }}
</a>
{% endfor %}
</small>
</div>
</div>
<h2><a class="article-title" href="{% url 'post-detail' post.id %}">{{ post.title }}</a></h2>
<p class="article-content">{{ post.content|slice:200 }}</p>
</div>
</article>
{% endfor %}
{% endblock content %}
post_form.html(用于创建新的post,创建post后重定向有问题)
{% extends 'blog/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Blog Post</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post</button>
</div>
</form>
</div>
{% endblock content %}
如果您要在保存前更改 slug
字段的值,您可以使用信号。
此外,django 的 slugify
方法位于 django.utils.text
而不是 django.template.defaultfilters
。
urls.py
# ...
path('post/<slug:slug>/', PostDetailView.as_view(), name='post-detail'),
# ...
models.py
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils.text import slugify
import string
class Post(models.Model):
# ...
slug= models.SlugField(max_length=500, unique=True, null=True, blank=True)
# do not override save method here
def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
def unique_slug_generator(instance, new_slug=None):
if new_slug is not None:
slug = new_slug
else:
slug = slugify(instance.title)
class_ = instance.__class__
qs_exists = class_.objects.filter(slug=slug).exists()
if qs_exists:
new_slug = f"{slug}-{random_string_generator(size=5)}"
return unique_slug_generator(instance, new_slug=new_slug)
return slug
@receiver(pre_save, sender=Post)
def post_pre_save_receiver(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = unique_slug_generator(instance)
这两个函数,unique_slug_generator
和 random_string_generator
,一起将保证你不会在两个 post 上有相同的 slug,即使那些 [=70] 的标题=] 是一样的! (它会在最后添加一些随机生成的字符串)
编辑
在 user_posts.html
的 html 模板中,替换
<h2><a class="article-title" href="{% url 'post-detail' post.id %}">{{ post.title }}</a></h2>
与
<h2><a class="article-title" href="{% url 'post-detail' post.slug %}">{{ post.title }}</a></h2>
此外,在 post_form
的 视图 (不是模板)中,您应该像这样覆盖 get_success_url
:
def get_success_url(self):
return reverse('post-detail', kwargs={'slug': self.object.slug})
编辑 2(为了更清楚)
首先,每个post我们需要一个url,我们将其实现为:
path('post/<slug:slug>/', PostDetailView.as_view(), name='post-detail'),
接下来,您应该将之前的链接更改为 post-detail
。这些包括您的 1) 模板链接和 2) views/models:
中的链接
在模板中,只要有 {% url 'post-detail' post.pk %}
,就应该将其更改为 {% url 'post-detail' post.slug %}
。
并且在你的 views/models 中,你应该更改 reverse('post-detail', kwargs={'pk': self.pk})
tp reverse('post-detail', kwargs={'slug': self.slug})
(而不是 self.object.slug
)
这是问题所在:我正在尝试 'fresh' 我的 django 网络博客,所以我不想 /post/2/
我想要重击 link 这与我的标题完全一样(像这样:/post/today-is-friday
这是一些代码,我尝试了一些方法,但没有任何效果:
models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
from django.template.defaultfilters import slugify
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
categories = models.ManyToManyField('Category', related_name='posts')
image = models.ImageField(upload_to='images/', default="images/None/no-img.jpg")
slug= models.SlugField(max_length=500, unique=True, null=True, blank=True)
def save(self, *args, **kwargs):
self.url= slugify(self.title)
super(Post, self).save(*args, **kwargs)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
class Category(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
urls.py
from django.urls import path
from django.conf.urls import include, url
from . import views
from .views import PostListView, PostDetailView, PostCreateView, PostUpdateView, PostDeleteView, UserPostListView
urlpatterns = [
#Blog section
path("", PostListView.as_view(), name='blog-home'),
path("user/<str:username>", UserPostListView.as_view(), name='user-posts'),
path("<slug:slug>/", PostDetailView.as_view(), name='post-detail'),
path("post/new/", PostCreateView.as_view(), name='post-create'),
path("<slug:slug>/update/", PostUpdateView.as_view(), name='post-update'),
path("<slug:slug>/delete/", PostDeleteView.as_view(), name='post-delete'),
path("about/", views.about, name="blog-about"),
path("<category>/", views.blog_category, name="blog_category"),
]
user_posts.html(这是为了访问博客 post 本身)
{% extends 'blog/base.html' %}
{% block content %}
<h1 class='mb-3'>Post by {{ view.kwargs.username }} ({{ page_obj.paginator.count }})</h1>
{% for post in posts %}
<article class="media content-section">
<img class="rounded-circle article-img" src="{{ post.author.profile.image.url }}" alt="">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2 author_title" href="{% url 'user-posts' post.author.username %}">@{{ post.author }}</a>
<small class="text-muted">{{ post.date_posted|date:"N d, Y" }}</small>
<div>
<!-- category section -->
<small class="text-muted">
Categories:
{% for category in post.categories.all %}
<a href="{% url 'blog_category' category.name %}">
{{ category.name }}
</a>
{% endfor %}
</small>
</div>
</div>
<h2><a class="article-title" href="{% url 'post-detail' post.id %}">{{ post.title }}</a></h2>
<p class="article-content">{{ post.content|slice:200 }}</p>
</div>
</article>
{% endfor %}
{% endblock content %}
post_form.html(用于创建新的post,创建post后重定向有问题)
{% extends 'blog/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Blog Post</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post</button>
</div>
</form>
</div>
{% endblock content %}
如果您要在保存前更改 slug
字段的值,您可以使用信号。
此外,django 的 slugify
方法位于 django.utils.text
而不是 django.template.defaultfilters
。
urls.py
# ...
path('post/<slug:slug>/', PostDetailView.as_view(), name='post-detail'),
# ...
models.py
from django.db.models.signals import pre_save
from django.dispatch import receiver
from django.utils.text import slugify
import string
class Post(models.Model):
# ...
slug= models.SlugField(max_length=500, unique=True, null=True, blank=True)
# do not override save method here
def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
def unique_slug_generator(instance, new_slug=None):
if new_slug is not None:
slug = new_slug
else:
slug = slugify(instance.title)
class_ = instance.__class__
qs_exists = class_.objects.filter(slug=slug).exists()
if qs_exists:
new_slug = f"{slug}-{random_string_generator(size=5)}"
return unique_slug_generator(instance, new_slug=new_slug)
return slug
@receiver(pre_save, sender=Post)
def post_pre_save_receiver(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = unique_slug_generator(instance)
这两个函数,unique_slug_generator
和 random_string_generator
,一起将保证你不会在两个 post 上有相同的 slug,即使那些 [=70] 的标题=] 是一样的! (它会在最后添加一些随机生成的字符串)
编辑
在 user_posts.html
的 html 模板中,替换
<h2><a class="article-title" href="{% url 'post-detail' post.id %}">{{ post.title }}</a></h2>
与
<h2><a class="article-title" href="{% url 'post-detail' post.slug %}">{{ post.title }}</a></h2>
此外,在 post_form
的 视图 (不是模板)中,您应该像这样覆盖 get_success_url
:
def get_success_url(self):
return reverse('post-detail', kwargs={'slug': self.object.slug})
编辑 2(为了更清楚)
首先,每个post我们需要一个url,我们将其实现为:
path('post/<slug:slug>/', PostDetailView.as_view(), name='post-detail'),
接下来,您应该将之前的链接更改为 post-detail
。这些包括您的 1) 模板链接和 2) views/models:
在模板中,只要有 {% url 'post-detail' post.pk %}
,就应该将其更改为 {% url 'post-detail' post.slug %}
。
并且在你的 views/models 中,你应该更改 reverse('post-detail', kwargs={'pk': self.pk})
tp reverse('post-detail', kwargs={'slug': self.slug})
(而不是 self.object.slug
)