Django Проблема при выводе статей по категориям через slug

Govard

Новичок
Пользователь
Апр 21, 2020
22
4
3
Здравствуйте. Пару раз вы мне помогли и сейчас в очередной раз я прошу у вас помощи... Я учусь...

Я нашел на просторах интернета один способ в котором описывалось как подкрутить слаг для статей на сайте. Всё получилось на ура и я хотел этот способ прикрутить к категориям статей. Слаг прикрутился к категориям, но вот вывод статей по категориям теперь никак работать не хочет почему-то...

models.py

Python:
from django.db import models
from django.urls import reverse
from django.utils.text import slugify


class News(models.Model):
     title = models.CharField(max_length=250, verbose_name='Наименование')
     content = models.TextField(blank=True, verbose_name='Контент')
     created_ad = models.DateTimeField(auto_now_add=True, verbose_name='Дата публикации')
     updated_at = models.DateTimeField(auto_now=True, verbose_name='Обновлено')
     photo = models.ImageField(upload_to='photos/%Y/%m/%d/', verbose_name='Картинка', blank=True)
     is_published = models.BooleanField(default=True, verbose_name='Опубликовано')
     category = models.ForeignKey('Category', verbose_name='Категория', on_delete=models.PROTECT) # on_delete=models.PROTECT - защита от удаления статей привязанных к категории.
     views = models.IntegerField(default=0, verbose_name='Просмотры')
     slug = models.SlugField(max_length=250, unique=True, verbose_name='Слаг статей')

     def get_absolute_url(self):
          return reverse('view_news', kwargs={"slug": self.slug})

     # Строковое представление объекта
     def __str__(self):
          return self.title

     def save(self, *args, **kwargs):
         value = self.title
         self.slug = slugify(value, allow_unicode=True)
         super().save(*args, **kwargs)

     class Meta:
          verbose_name = 'Новость'
          verbose_name_plural = 'Новости'
          ordering = ['-created_ad']  # '-created_ad' - посты в обратном порядке. Свежие первые.


class Category(models.Model):
     title = models.CharField(max_length=150, db_index=True, verbose_name='Наименование каегории')
     slug = models.SlugField(max_length=150, unique=True, verbose_name='Слаг категорий')

     def get_absolute_url(self):
          return reverse('category', kwargs={"slug": self.slug})

     def __str__(self):
          return self.title

     def save(self, *args, **kwargs):
         value = self.title
         self.slug = slugify(value, allow_unicode=True)
         super().save(*args, **kwargs)

     class Meta:
          verbose_name = 'Категория'
          verbose_name_plural = 'Категории'
          ordering = ['title']

views.py

Python:
from django.db.models import F

from django.shortcuts import render, get_object_or_404, redirect
from django.views.generic import ListView, DetailView, CreateView
from django.urls import reverse_lazy
from django.contrib.auth.mixins import LoginRequiredMixin

from .models import News, Category
from .forms import NewsForm, UserRegisterForm, UserLoginForm, ContactForm
from django.contrib import messages
from django.contrib.auth import login, logout
from django.core.mail import send_mail


def register(request):
    if request.method == 'POST':
        form = UserRegisterForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            messages.success(request, 'Вы успешно зарегистрировались!')
            return redirect('home')
        else:
            messages.error(request, 'Ошибка регистрации')
    else:
        form = UserRegisterForm()
    return render(request, 'news/register.html', {"form": form})


def user_login(request):
    if request.method == 'POST':
        form = UserLoginForm(data=request.POST)
        if form.is_valid():
            user = form.get_user()
            login(request, user)
            return redirect('home')
    else:
        form = UserLoginForm()
    return render(request, 'news/login.html', {"form": form})


def user_logout(request):
    logout(request)
    return redirect('login')


def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            mail = send_mail(form.cleaned_data['subject'], form.cleaned_data['content'], 'web-supsu@yandex.ru', ['kornyukhov94@mail.ru'], fail_silently=True)
            if mail:
                messages.success(request, 'Письмо отправлено!')
                return redirect('contact')
            else:
                messages.error(request, 'Ошибка отправки')
        else:
            messages.error(request, 'Не верно заполнено одно из полей')
    else:
        form = ContactForm()
    return render(request, 'news/test.html', {"form": form})


def search(request):
    search_query = request.GET.get('search', '')

    if search_query:
        ser = News.objects.filter(title__icontains=search_query)
    else:
        ser = News.objects.all()
    return render(request, 'news/search.html', {"ser": ser})


class HomeNews(ListView):
    model = News
    template_name = 'news/home_news_list.html'
    context_object_name = 'news'
    extra_context = {'title': 'Статьи'}
    paginate_by = 5

    def get_queryset(self):
        return News.objects.filter(is_published=True).select_related('category') # .select_related('category') Убирает дублирование SQL запросов.


class ArticleListView(ListView):
    template_name = 'news/home_page.html'
    context_object_name = 'news'
    extra_context = {'title': 'Главная'}
    paginate_by = 5

    def get_queryset(self):
        return News.objects.filter(is_published=True).select_related('category')


class NewsByCategory(ListView):
    model = News
    context_object_name = 'news'
    paginate_by = 5

    def get_queryset(self):
        return News.objects.filter(slug=self.kwargs['slug'], is_published=True).select_related('category')


class ArticleDetailView(DetailView):
    model = News
    context_object_name = 'news_item'

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.object.views += 1
        self.object.save()
        context = self.get_context_data(object=self.object)
        return self.render_to_response(context)


class CreateNews(LoginRequiredMixin, CreateView):
    login_url = '/admin/login/' # Перенаправление в админку для авторизации
    form_class = NewsForm
    template_name = 'news/add_news.html'


urls.py

Python:
from django.urls import path
from django.views.decorators.cache import cache_page

from .views import *

from news.views import ArticleListView, ArticleDetailView

urlpatterns = [
    path('register/', register, name='register'),
    path('login/', user_login, name='login'),
    path('logout/', user_logout, name='logout'),
    path('contact/', contact, name='contact'),
    path('search/', search, name='search'),
    # Страница с кэшем   path('', cache_page(60)(HomeNews.as_view()), name='home'),
    path('articles/', HomeNews.as_view(), name='articles'),
    path('', ArticleListView.as_view(), name='home'),
    path('news/<slug>/', ArticleDetailView.as_view(), name='view_news'),
    path('category/<slug>/', NewsByCategory.as_view(), name='category'),
    path('add-news/', CreateNews.as_view(), name='add_news'),
]

Шаблон в котором должны отображаться и фильтроваться статьи по категориям news_list.html

HTML:
{% extends 'base.html' %}

{% block title %}
{{ title }}
{% endblock %}

{% block sidebar %}

{% include 'inc/_sidebar.html' %}

{% endblock %}

{% block content %}
{% for item in object_list %}
<div class="card mb-3">
  <div class="card-header">
    Категория: <a href="{% url 'category' item.category.slug %}">{{ item.category }}</a>
  </div>
  <div class="card-body">
    <div class="media">
      {% if item.photo %}
      <img src="{{ item.photo.url }}" alt="" width="350" class="mr-3">
      {% else %}
      <img src="" alt="" class="mr-3">
      {% endif %}
      <div class="media-body">
        <h5 class="card-title text-center">{{ item.title }}</h5>
        <p class="card-text">{{ item.content | safe | linebreaks | truncatewords:50 }}</p>
        <a href="{{ item.get_absolute_url }}" class="btn btn-primary">Читать далее...</a>
      </div>
    </div>
  </div>
  <div class="card-footer text-muted">
    {{ item.created_ad|date:"Y-m-d:i:s" }}
  </div>
</div>
{% endfor %}
{% endblock %}

Подскажите пожалуйста, как мне сделать так, чтобы при переходе по ссылке категории меня перекидывало на страницу где размещены как раз те статьи на которую категорию я перешел.

Статьи - Google Chrome.jpg

Когда навожу на ссылку, то слаг работает, но когда перехожу на ссылку, то статьи пропадают:
127.0.0.18000categoryмагия-в-интернете - Google Chrome.jpg

Не закидывайте пожалуйста яйцами и помидорами. Уже 2 дня пытаюсь работаться, много вариантов перепробовал, гугл перерыл, ничего не получается((
 

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
Подскажите пожалуйста, как мне сделать так, чтобы при переходе по ссылке категории меня перекидывало на страницу где размещены как раз те статьи на которую категорию я перешел.
Можно примерно так.
В модели News для категории укажите related_name:
Python:
category = models.ForeignKey('Category', verbose_name='Категория', on_delete=models.PROTECT, related_name='news')

Во вьюхе ArticleListView (в методе get_queryset) определите название категории и получите связанные с ней новости:
Python:
def get_queryset(self):   
    # попытаться получить категорию
    category = get_object_or_404(Category, slug__iexact=self.kwargs.get('slug'))
    # вывести новости из категории
    queryset = category.news.all()
    return queryset
 
Последнее редактирование:
  • Мне нравится
Реакции: Govard

Govard

Новичок
Пользователь
Апр 21, 2020
22
4
3
Можно примерно так.
В модели News для категории укажите related_name:
Python:
category = models.ForeignKey('Category', verbose_name='Категория', on_delete=models.PROTECT, related_name='news')

Во вьюхе ArticleListView (в методе get_queryset) определите название категории и получите связанные с ней новости:
Python:
def get_queryset(self):  
    # попытаться получить категорию
    category = get_object_or_404(Category, slug__iexact=self.kwargs.get('slug'))
    # вывести новости из категории
    queryset = category.news.all()
    return queryset

Блин вы меня опять спасли)))))))))))))))))))))))))))
Огромнейшее спасибо вам, всё работает))))))))))))))
Буду сейчас разбираться как это заработало)))))))
Вы лучший))))))))))))))))))
 

Форум IT Специалистов