简体   繁体   English

将 Slug 添加到 URL 模式

[英]Adding Slugs to URL pattern

[Original Post] [原帖]

I want users to access posts using either the PK or Slug value.我希望用户使用 PK 或 Slug 值访问帖子。 I can get http://localhost:8000/post/8/ to work but not http://localhost:8000/post/working-in-malaysia/ .我可以让http://localhost:8000/post/8/工作,但不能让 http://localhost:8000/post/working-in-malaysia/ 工作

I have looked at a few posts on Stack Overflow but I don't want http://localhost:8000/post/8/working-in-malaysia .我看过一些关于 Stack Overflow 的帖子,但我不想要http://localhost:8000/post/8/working-in-malaysia And I don't want it to be a case of either the PK works.而且我不希望它成为任何一个 PK 作品的案例。 Or the slug works.或者蛞蝓起作用。 I want the user to be able to enter either.我希望用户能够输入。

Below you can see my failed attempts.下面你可以看到我失败的尝试。

urls.py网址.py

path('post/<int:pk>/', views.post_detail, name='post-detail'),
#path('post/<slug:the_slug>/', views.post_detail, name='post-detail-slug'),
#path('post/<slug:slug>/', views.post_detail, name='post-detail-slug'),
#path('post/(?P<slug>[-\w]+)/$', views.post_detail, name='post-detail-slug'),
path('<slug:slug>', views.post_detail, name='post-detail-slug'),

在此处输入图像描述

[Updates After Looking At Daniel Morell Comments] [查看 Daniel Morell 评论后的更新]

views.py视图.py

class PostDetailView(DetailView):
    model = Post

    def dispatch(pk_slug):
        if pk_slug.isdigit():
          post = Post.objects.filter(id=pk_slug).first()
        else:
          post = Post.objects.filter(slug=pk_slug).first()

        #post = get_object_or_404(Post)
        comments = post.comments.filter(active=True, slug=slug)
        new_comment = None

        if request.method == 'POST':
            comment_form = CommentForm(data=request.POST)
            if comment_form.is_valid():
                new_comment = comment_form.save(commit=False)
                new_comment.post = post
                new_comment.save()
            else:
                comment_form = CommentForm()
                return render(request, post_detail.html, {'post': post,
                                           'comments': comments,
                                           'new_comment': new_comment,
                                          'comment_form': comment_form})

urls.py网址.py

path('', PostListView.as_view(), name='blog-home'),
path('post/<slug:pk_slug>', views.post_detail, name='post-detail'),
path('post/new/', PostCreateView.as_view(), name='post-create'),

models.py模型.py

def get_absolute_url(self):
        #return reverse('article_detail', kwargs={'slug': self.slug})
        return reverse('post-detail', kwargs={'pk': self.pk})

[Updates After Looking At Daniel Morell 2nd Lot Of Comments] [查看丹尼尔莫雷尔第二批评论后的更新]

views.py视图.py

 pk_slug = self.kwargs.get(self.slug_url_kwarg)
        # If the pk_slug is not None and it is just digits treat as pk
        # Otherwise if it is not None treat as slug
        if pk_slug is not None and pk_slug.isdigit():
          queryset = queryset.filter(pk=pk_slug)
        else pk_slug is not None:
          slug_field = self.get_slug_field()
          queryset = queryset.filter(**{slug_field: pk_slug})

在此处输入图像描述

Command Prompt命令提示符

File "C:\Users\HP\django_project3\blog\views.py", line 55
    else pk_slug is not None:
               ^

SyntaxError: invalid syntax

[Problem Solved] [问题解决了]

For unknown reasons I had a FBV (function based view) and CBV (classed based view) with similar code.由于未知原因,我有一个 FBV(基于函数的视图)和 CBV(基于分类的视图),代码相似。 Daniel Morell original suggestion worked on the assumption that I was using a FBV. Daniel Morell 最初的建议是假设我使用的是 FBV。

I unfortunately wasted his time by showing him my code which had the CBV in it.不幸的是,我向他展示了我的代码,其中包含 CBV,这浪费了他的时间。 Which meant he re wrote his solution.这意味着他重新编写了他的解决方案。 Then I discovered that the FBV code was redundant and CBV code was correct.然后我发现 FBV 代码是多余的,而 CBV 代码是正确的。

Below is my final set of code.下面是我的最后一组代码。 This link is to Daniel Morell original suggestion - https://stackoverflow.com/revisions/61471979/1 .此链接指向 Daniel Morell 的原始建议 - https://stackoverflow.com/revisions/61471979/1

I am a beginner with Python and did not even know what the abbreviation CBV meant yesterday.我是 Python 的初学者,昨天甚至不知道缩写 CBV 的含义。

views.py视图.py

def post_detail(request, pk_slug):
    template_name = 'post_detail.html'



    if pk_slug.isdigit():
      post = Post.objects.filter(id=pk_slug).first()
    else:
      post = Post.objects.filter(url=pk_slug).first()

    comments = Comment.objects.filter(post=post.id ,active=True)
    #post = Post.objects.get(pk=pk)
    new_comment = None
    # Comment posted
    if request.method == 'POST':
        comment_form = CommentForm(data=request.POST)
        if comment_form.is_valid():

            # Create Comment object but don't save to database yet
            new_comment = comment_form.save(commit=False)
            # Assign the current post to the comment
            new_comment.post = post
            # Save the comment to the database
            new_comment.save()
    else:
        comment_form = CommentForm()

    return render(request, template_name, {'post': post,
                                           'comments': comments,
                                           'new_comment': new_comment,
                                           'comment_form': comment_form})

urls.py网址.py

path('post/<slug:pk_slug>/', views.post_detail, name='post-detail'),

You can achieve that using djangos re_path method and using regular expressions.您可以使用 djangos re_path方法和使用正则表达式来实现。 Docs:文件:

https://docs.djangoproject.com/en/3.0/topics/http/urls/#nested-arguments https://docs.djangoproject.com/en/3.0/ref/urls/#re-path https://docs.djangoproject.com/en/3.0/topics/http/urls/#nested-arguments https://docs.djangoproject.com/en/3.0/ref/urls/#re-path

It would be something like this, although this won't work, just to give you an idea:它会是这样的,虽然这不起作用,只是为了给你一个想法:

re_path(r'^post/(?P<pk:int>\d+)|(?P<slug:slug>\w+)/$', ...)

You can try the regex with tools like https://regexr.com/ .您可以使用https://regexr.com/等工具尝试正则表达式。 Just create some of the urls you would expect and use the platform to find the right regex pattern.只需创建一些您期望的 url 并使用该平台找到正确的正则表达式模式。 I haven't tried this myself, so can't tell you exactly how it's done.我自己没有尝试过,所以不能告诉你它是如何完成的。

There is a trick to simplifying your URL configuration.有一个技巧可以简化您的 URL 配置。 <slug:__> will match any string of ASCII letters, numbers, hyphens or underscores. <slug:__>将匹配任何 ASCII 字母、数字、连字符或下划线的字符串。 This means you can use slug to match your primary key.这意味着您可以使用slug来匹配您的主键。

This is a little different.这有点不同。 So you should add a comment to your urlpatterns explaining that it matches the pk and the slug .因此,您应该在urlpatterns中添加注释,说明它与pkslug匹配。

Here is an example.这是一个例子。

# urls.py

urlpatterns = [
    # This matches both the primary key and the slug.
    path('post/<slug:pk_slug>/', views.PostDetailView.as_view(), name='post-detail')
]
# views.py

from django.db.models import Q

class PostDetailView(DetailView):
    model = Post
    slug_url_kwarg = 'pk_slug'

    def get_object(self, queryset=None):
        # This function overrides DetialView.get_object()

        # Use a custom queryset if provided; this is required for subclasses
        if queryset is None:
            queryset = self.get_queryset()

        # Next, look up our primary key or slug
        pk_slug = self.kwargs.get(self.slug_url_kwarg)

        # If the pk_slug is not None and it is just digits treat as pk
        if pk_slug is not None and pk_slug.isdigit():
            queryset = queryset.filter(pk=pk_slug)

        # Otherwise if it is not None treat as slug
        elif pk_slug is not None:
            slug_field = self.get_slug_field()
            queryset = queryset.filter(**{slug_field: pk_slug})

        # Raise an error if there is no pk_slug in the URLconf
        if pk_slug is None:
            raise AttributeError(
                "Generic detail view %s must be called with an object "
                "pk_slug in the URLconf." % self.__class__.__name__
            )

        try:
            # Get the single item from the filtered queryset
            obj = queryset.get()
        except queryset.model.DoesNotExist:
            raise Http404(_("No %(verbose_name)s found matching the query") %
                          {'verbose_name': queryset.model._meta.verbose_name})
        return obj

The isdiget() method returns true if each character in the string is a digit and the string has at least one character.如果字符串中的每个字符都是数字并且字符串至少包含一个字符,则isdiget()方法返回 true。

Thus the following URLs will all be used to match the primary key...因此,以下 URL 将全部用于匹配主键...

  • /post/123/
  • /post/4/
  • /post/80/

And the following will all be interpreted as slugs...而以下都将被解读为蛞蝓……

  • /post/my-first-post/
  • /post/23-a-post-slug/
  • /post/123b/

Note: this will not work with a slug that is made up of all digits.注意:这不适用于由所有数字组成的 slug。

You should add a validation rule to your post form to make sure the slug has a non-digit character in it.您应该在帖子表单中添加验证规则,以确保 slug 中包含非数字字符。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM