В своё время, когда я изучал Джанго, сломал много копий на этом простом моменте. Решений находил множество, но все они в конкретно моём случае не подходили. Итак. Если вы получили ошибку типа:
NoReverseMatch at /admin/r/7/1/
Reverse for 'f_view_bog' not found. 'f_view_bog' is not a valid view function or pattern name...
То возможная причина в том, что вы неправильно используете функцию reverse() в get_absolute_url. На многих сайтах первый аргумент, который должен являться view-функцией оборачивают в кавычки. Однако, это сработает лучше, если вы не будете так делать, а вместо этого просто импортируете view-функцию вначале файла models.py
Чтобы было ещё чуточку понятнее, я приведу небольшоё демо-пример. У нас имеется приложение Blog, файлы которого выглядят следующем образом:
urls.py:
from django.urls import path
from . import views
from .views import f_view_bog
# pattern for the index page, all products and each product
urlpatterns = [
path('post/<int:post_id>/', f_view_bog, name="post_url"),
]
views.py:
from django.http import HttpResponse
from django.shortcuts import render
# Create your views here.
def f_view_bog(request, post_id):
return HttpResponse(f"<h1>Текст статьи</h1>{post_id}</p>")
models.py:
from django.db import models
from django.urls import reverse
from blog.views import f_view_bog
# Create your models here.
class Blog(models.Model):
name = models.CharField(max_length=255, unique=True, verbose_name='Название поста')
slug = models.SlugField(max_length=200, unique=True, verbose_name='Постоянная ссылка')
content = models.TextField(blank=True, verbose_name='Текст')
def get_absolute_url(self):
return reverse(f_view_bog, kwargs={'post_id': self.pk})
def __str__(self):
return self.name
class Meta:
verbose_name = 'Запись'
verbose_name_plural = 'Записи'
Обратите внимание на строки 3 и 12 кода выше. В третей строке мы импортируем вьюху, а в 12-й ссылаемся на неё, но не через return reverse('f_view_bog', kwargs={'post_id': self.pk}), а через return reverse(f_view_bog, kwargs={'post_id': self.pk})
Если данный вариант не подошел
Однако, приведённый выше пример не идеален и имеет свои недостатки. По этой причине предлагаю вам ещё один вариант:
urls.py:
from django.urls import path
from .views import show_post_view, show_category_view
# pattern for the index page, all products and each product
urlpatterns = [
path('post/<slug:post_slug>/', show_post_view, name="post_url"),
path('category/<slug:cat_slug>/', show_category_view, name="category_url")
]
views.py:
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404, redirect
from .models import *
# Create your views here.
def show_post_view(request, post_slug):
post = get_object_or_404(PostModel, slug=post_slug)
# в post помещаем все данные о текущей записи в блоге. в дальнейшем в шаблоне будем вызывать так: post.name
# где name - название одного из полей записи из созданной нами модели в models.py
context = {
'post': post
}
return render(request, 'blog-single-page.html', context=context)
def show_category_view(request, cat_slug):
category = get_object_or_404(CategoryModel, slug=cat_slug)
# в category помещаем все данные о текущей категории. в дальнейшем в шаблоне будем вызывать так: product.name
# где name - название одного из полей категории из созданной нами модели в models.py
context = {
'category': category
}
return render(request, 'blog-single-category.html', context=context)
admin.py:
from django.contrib import admin
from mptt.admin import MPTTModelAdmin
from .models import *
# Register your models here.
class BlogAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('name',)} # автозаполнение слага из названия
class Meta:
verbose_name = 'Запись'
verbose_name_plural = 'Записи'
class BlogCategoryAdmin(MPTTModelAdmin):
prepopulated_fields = {'slug': ('name',)} # автозаполнение слага из названия
admin.site.register(PostModel, BlogAdmin) # PostModel - имя класса из модели
admin.site.register(CategoryModel, BlogCategoryAdmin) # CategoryModel - имя класса из модели
models.py:
from django.db import models
from django.urls import reverse
from mptt.fields import TreeForeignKey
from mptt.models import MPTTModel
# Create your models here.
class CategoryModel(MPTTModel):
name = models.CharField(max_length=50, unique=True, verbose_name='Название категории')
slug = models.SlugField(max_length=150, verbose_name='slug', null=False, unique=True)
parent = TreeForeignKey('self', on_delete=models.PROTECT, null=True, blank=True, related_name='children',
db_index=True, verbose_name='Родительская категория')
class MPTTMeta:
order_insertion_by = ['name']
def get_absolute_url(self):
return reverse('category_url', kwargs={'cat_slug': self.slug})
def __str__(self):
return self.name
class Meta:
verbose_name = 'Категория'
verbose_name_plural = 'Категории'
class PostModel(models.Model):
name = models.CharField(max_length=255, unique=True, verbose_name='Название поста')
slug = models.SlugField(max_length=200, unique=True, verbose_name='Постоянная ссылка')
category = TreeForeignKey(CategoryModel, on_delete=models.PROTECT, null=False, verbose_name='Категория')
content = models.TextField(blank=True, verbose_name='Текст')
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('post_url', kwargs={'post_slug': self.slug})
class Meta:
verbose_name = 'Запись'
verbose_name_plural = 'Записи'
В данном примере мы заключаем в одинарные кавычки первый аргумент функции reverse. Значение этого аргумента соответствует значению name функции path из файла urls.py