Как упоминалось в ответе Бабака, вы, вероятно, захотите использовать поле слизняков. Если вы хотите, чтобы slugfield автоматически заполнялся на основе значения заголовка, вам необходимо переопределить метод save
вашей модели (это потому, что значение slugfield должно быть установлено после того, как значение поля title
уже известно). Кроме того, вы, вероятно, захотите изменить метод модели get_absolute_url
. Вы можете, например, сделать это:
# models.py
from django.db import models
from django.urls import reverse
from django.utils.text import slugify #new
class BlogPost(models.Model): #changed
postTitle=models.CharField(max_length=150)
postContent=models.TextField(blank=False,default="Not found")
category=models.CharField(max_length=50)
author=models.CharField(max_length=100)
updated=models.DateTimeField(auto_now=True,auto_now_add=False)
timestamp=models.DateTimeField(auto_now=False,auto_now_add=True)
slug = models.SlugField() # new
def __unicode__(self):
return self.postTitle
def __str__(self):
return self.postTitle
def get_absolute_url(self):
return reverse('details',kwargs={"id":self.id, "slug":self.slug}) #changed
def save(self, *args, **kwargs): # new
self.slug = slugify(self.postTitle)
super().save(*args, **kwargs)
(обратите внимание, что я переименовал модель с blogPost
в BlogPost
в соответствии с Python соглашениями об именах для классов)
Вы Вам также нужно будет изменить файл urls.py, чтобы он работал с измененной моделью. Вот предложение (с использованием функции path
вместо функции url
, которую вы использовали в своем коде):
# urls.py
from django.urls import path #changed
from . import views
urlpatterns=[
path('details/<slug:slug>/<int:id>/',views.blog_detail,name="details"), #changed
]
Если вы действительно используете этот подход, обратите внимание, что вам также необходимо изменить свое представление, так что он принимает аргумент slug (хотя представление на самом деле не должно делать что-либо с переменной slug):
# views.py
from django.shortcuts import render, get_object_or_404
from .models import BlogPost #changed
def blog_detail(request, slug, id=None): #changed
instance=get_object_or_404(BlogPost,id=id)
args={"instance":instance}
return render(request,'blog/blogDetail.html',args)
Вот несколько тестов, которые я написал, чтобы проверить, что модель и view работают должным образом. Я включаю их сюда на случай, если они помогут прояснить поведение / что происходит.
# tests.py
from django.test import TestCase, Client
from django.urls import reverse
from .models import BlogPost
class ModelsTestCase(TestCase):
def setUp(self):
self.bp1 = BlogPost.objects.create(postTitle='First post',
postContent='This is a post',
category='Fun category',
author='First author')
def tearDown(self):
pass
def test_blogpost_slug(self):
bp = BlogPost.objects.get(id=1)
self.assertEqual(bp.slug, 'first-post')
def test_blogpost_url(self):
bp = BlogPost.objects.get(id=1)
self.assertEqual(bp.get_absolute_url(), '/blog/details/first-post/1/')
class ViewsTestCase(TestCase):
def setUp(self):
self.bp1 = BlogPost.objects.create(postTitle='First post',
postContent='This is a post',
category='Fun category',
author='First author')
self.client = Client()
def tearDown(self):
pass
def test_blog_detail_view_responds_with_status_code_200(self):
firstpost_url = self.bp1.get_absolute_url()
resp = self.client.get(firstpost_url)
self.assertEqual(resp.status_code, 200)
Все три теста выполнены успешно.
Конечно, должно быть больше элегантное решение, когда дело доходит до шаблонов URL-адресов, которое не требует передачи в представление ненужного аргумента slug.