如何在 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:&nbsp;
              {% for category in post.categories.all %}
              <a href="{% url 'blog_category' category.name %}">
                {{ category.name }}
              </a>&nbsp;
              {% 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_generatorrandom_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