Evgenii Legotckoi
Evgenii Legotckoi08 квітня 2017 р. 17:52

Django - Підручник 022. Додавання системи закладок (вибране) на сайт

На сайті додана можливість помічати статті, коментарі, теми та відповіді на форумі як обране. При цьому позначка як обране не передбачає перезавантаження сторінки, оскільки для цих дій використовується механізм AJAX-запитів.

Для того, щоб реалізувати систему закладок, необхідно:

  • Додати таблицю, яка реалізує відношення Many-to-Many між користувачем та статтею чи коментарем.
  • Додати view, який оброблятиме цей запит.
  • Додати url для обробки запиту на додавання або вилучення об'єкта з обраного.
  • Написати html-код, який відповідатиме за відображення лічильника доданого в закладки.
  • Додати javascript обробник, який викликатиме AJAX-запит.

На цьому сайті як іконка лічильника використовується іконка зірки з Bootstrap.


Many-to-Many таблиця для закладок

Зробимо можливість додавання закладок для статей та коментарів. Для цього зробимо спільну абстрактну модель , від якої будемо успадковуватися для конкретного типу контенту на сайті. В абстрактній моделі створимо поле користувача, яке буде єдиним для всіх моделей закладок.

class BookmarkBase(models.Model):
    class Meta:
        abstract = True

    user = models.ForeignKey(User, verbose_name="Пользователь")

    def __str__(self):
        return self.user.username

Далі успадковуємося від цієї моделі, щоб створити дві окремі моделі даних для коментарів та статей.

Тут додамо поле obj , яке відповідатиме за зовнішній ключ на таблицю контенту: Article або Comment. Важливо, щоб поле називалося однаково обох моделях. Тоді можна буде написати один перегляд для всіх таблиць закладок.

class BookmarkArticle(BookmarkBase):
    class Meta:
        db_table = "bookmark_article"

    obj = models.ForeignKey(Article, verbose_name="Статья")


class BookmarkComment(BookmarkBase):
    class Meta:
        db_table = "bookmark_comment"

    obj = models.ForeignKey(Comment, verbose_name="Комментарий")

views.py

Для обробки запиту на додавання в закладки або видалення із закладок створимо view, яке зможе працювати з будь-якою таблицею закладок, яка задовольнятиме загальний вигляд, представлений вище.

# -*- coding: utf-8 -*-

import json

from django.contrib import auth
from django.http import HttpResponse
from django.views import View


class BookmarkView(View):
    # в данную переменную будет устанавливаться модель закладок, которую необходимо обработать
    model = None

    def post(self, request, pk):
        # нам потребуется пользователь
        user = auth.get_user(request)
        # пытаемся получить закладку из таблицы, или создать новую
        bookmark, created = self.model.objects.get_or_create(user=user, obj_id=pk)
        # если не была создана новая закладка, 
        # то считаем, что запрос был на удаление закладки
        if not created:
            bookmark.delete()

        return HttpResponse(
            json.dumps({
                "result": created,
                "count": self.model.objects.filter(obj_id=pk).count()
            }),
            content_type="application/json"
        )

Зауважте, що в даному коді використовувалося порівняння obj_id=pk , що означає, що ми намагаємося знайти запис у таблиці закладок щодо id об'єкта. Оскільки у всіх моделях це поле однакове, то проблем виникнути не повинно з подібним синтаксисом.

urls.py

А тепер подивимося, як виглядатимуть url для обробки запитів на додавання контенту до закладок.

# -*- coding: utf-8 -*-

from django.conf.urls import url
from django.contrib.auth.decorators import login_required

from . import views
from users.models import BookmarkArticle, BookmarkComment

app_name = 'ajax'
urlpatterns = [
    url(r'^article/(?P<pk>\d+)/bookmark/$',
        login_required(views.BookmarkView.as_view(model=BookmarkArticle)),
        name='article_bookmark'),
    url(r'^comment/(?P<pk>\d+)/bookmark/$',
        login_required(views.BookmarkView.as_view(model=BookmarkComment)),
        name='comment_bookmark'),
]

Щоб виконати цей запит, користувач повинен бути авторизований, за що відповідає декоратор login_required .

URL в даному випадку визначає, який тип контенту додається в закладки, також визначає pk цього контенту, а також дію, яку необхідно зробити. Адже крім додавання до закладок, можна додати систему лайків, ріпостів тощо. за тим самим принципом.

html

У моєму випадку html-код виглядає так:

<div data-id="{{ like_obj.id }}" data-type="article" data-action="bookmark" title="Избранное">
    <span class="glyphicon glyphicon-star"></span>
    <span data-count="bookmark">{{ like_obj.get_bookmark_count }}</span>
</div>

Тут є кілька кастомних атрибутів:

  • data-id - відповідає за pk контенту, який можна додавати до закладок.
  • data-type - тип контенту, ця сама назва фігурує і в url.
  • data-action - дія, яку потрібно вчинити, в даному випадку додавання до закладок
  • data-count - лічильник, що показує скільки користувачів додали контент в закладки

Що стосується наступного коду like_obj.get_bookmark_count, це теж одноманітний метод, яка додається в моделі контенту, наприклад для статей виглядатиме таким чином:

def get_bookmark_count(self):
    return self.bookmarkarticle_set.all().count()

javascript

AJAX-запити для цього функціоналу створюються за допомогою бібліотеки jQuery .

Під час роботи з AJAX необхідно враховувати кілька нюансів:

  1. Якщо у вас мультимовний сайт, у якого розрізняються url за поточною мовою, то краще зробити окреме api для AJAX, яке буде незалежно від мови, інакше потрібно буде враховувати мову в url під час створення AJAX-запиту. Якщо не враховувати мову, то буде здійснюватись редирект AJAX-запиту на поточний URL з урахуванням мови, і запит не спрацьовуватиме. Тобто редиректів не повинно бути.
  2. Django не прийме AJAX-запиту, якщо він не буде налаштований на використання CSRF токена, який служить для боротьби з підробкою міжсайтових запитів. При кожному запиті сторінки Django підмішує CSRF токен в Cookies, звідти його можна буде взяти.

Налаштування AJAX на використання CSRF токена

Наступний код можна включити до скрипту на кожній сторінці сайту, де потрібно використовувати AJAX. Він автоматично налаштовуватиме AJAX на використання CSRF Токена.

// Получение переменной cookie по имени
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

// Настройка AJAX
$(function () {
    $.ajaxSetup({
        headers: { "X-CSRFToken": getCookie("csrftoken") }
    });
});

Обробники додавання в закладки та їх підключення

У наступному коді формується AJAX-запит на виклик додавання або видалення із закладок контенту. У цьому випадку код буде універсальним як для статей, так і для коментарів.

В url запиту можна спостерігати наступний рядок "/api/" + type + "/" + pk + "/" + action + "/", який означає, що модуль для AJAX запитів висить на префіксі /api / , тобто цей url він підключений в основному urls.py файлі проекту, далі йде тип контенту, його первинний ключ і дію яке потрібно зробити. Оскільки всі ці дані забираються з атрибутів data, , то підсумковий url буде виглядати наступним чином:

  • /api/article/112/bookmark/ - для статей
  • /api/comment/14/bookmark/ - для коментарів

У обробці успішного результату можна додати підсвічування зірочки закладки для поточного користувача і т.д.

function to_bookmarks()
{
    var current = $(this);
    var type = current.data('type');
    var pk = current.data('id');
    var action = current.data('action');

    $.ajax({
        url : "/api/" + type + "/" + pk + "/" + action + "/",
        type : 'POST',
        data : { 'obj' : pk },

        success : function (json) {
            current.find("[data-count='" + action + "']").text(json.count);
        }
    });

    return false;
}

// Подключение обработчика
$(function() {
    $('[data-action="bookmark"]').click(to_bookmarks);
});

Для Django рекомендую VDS-сервера хостера Timeweb .

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

ИМ
  • 04 лютого 2018 р. 04:40

Доброго времени суток Евгений. Не подскажете как сделать иконки (лайк-дизлайк) кликабельными?

ИМ
  • 02 грудня 2018 р. 04:57

Доброго времени суток Евгений. Не ясен метод:

def get_bookmark_count(self):
    return self.bookmarkarticle_set().all().count()

Не могли бы вы растолковать откуда моя модель статьи знает о этой функции?:

self.bookmarkarticle_set()

Когда применяю данный метод то выскакивает ошибка:

__call__() missing 1 required keyword-only argument: 'manager'

И второй вопрос каким образом можно подсвечивать закладку (допустим менять ее цвет когда она добавлена пользователем, ну как у вас на сайте)?

Evgenii Legotckoi
  • 03 грудня 2018 р. 04:57

Добрый день, Игорь!

Вообще это стандартное поведение в моделях, если на данную модель какая-то иная модель имеет внешний ключ (ForeignKey)

В данном конкретном случае есть модель Article и BookmarkArticle , поскольку BookmarkArticle имеет внешний ключ на Article, то модель Article автоматически получает метод bookmarkarticle_set, который возвращает query set  всех закладок, которые имеют внешний ключ на конкретный объект статьи.

Здесь я по ходу допустил ошибку, напишите так

self.bookmarkarticle_set.all().count()

Для подсветки я использую фильтр в шаблоне, которые ищет пользователя в наборе

@register.filter
def user_in(objects, user):
    if user.is_authenticated:
        return objects.filter(user=user).exists()
    return False

а вот его применение

<span data-icon="favorite" class="mdi mdi-star mr-1 {% if obj.bookmarks.all|user_in:user %}text-success{% endif %}"></span>

ИМ
  • 03 грудня 2018 р. 22:55

self.bookmarkarticle_set.all().count()

Да, так все работает! Спасибо вам огромное.

Еще один вопрос:

Я подключил фильтр:

@register.filter
def user_in(objects, user):
    if user.is_authenticated:
        return objects.filter(user=user).exists()
    return False

в templatetags/movie_extras.py

в шаблоне:

{% load movie_extras %}
<span style="cursor: pointer" class="fa fa-bookmark-o fa-4x{% if movie.bookmarks.all|user_in:user %}fa-2x{% endif %}"></span>

При этом ошибка:

 File "/home/chunk/public/web-dev/testproj/movie/templatetags/movie_extras.py", line 8, in user_in
    return objects.filter(user=user).exists()
AttributeError: 'NoneType' object has no attribute 'filter'

что я делаю не так Евгений?







Evgenii Legotckoi
  • 03 грудня 2018 р. 23:58

мне кажется, вы неправильно записали это

movie.bookmarks.all

Как у вас называется модель закладок для фильмов? BookmarkArticle? BookmarkMovie?

Я вам дал код из сайта, а здесь уже на десять раз всё переписано и выглядит иначе. У вас скорее всего как-то так должно быть.

movie.bookmarkmovie_set.all

Или что-то типо того

ИМ
  • 04 грудня 2018 р. 00:10

Все верно.

Я вам дал код из сайта, а здесь уже на десять раз всё переписано и выглядит иначе. У вас скорее всего как-то так должно быть.
self.bookmarkarticle_set.all().count()

Так все работает. Только у меня:

self.bookmarkmovie_set.all().count()

Фильтр к сожалению не работает.












Evgenii Legotckoi
  • 04 грудня 2018 р. 00:22

так перепишите его так

{% if movie.bookmarkmovie_set.all|user_in:user %}fa-2x{% endif %}
ИМ
  • 04 грудня 2018 р. 00:26

Все работет Евгений. Спасибо вам.

ИМ
  • 05 грудня 2018 р. 15:44

Доброго времени суток Евгений. Можно поинтересоваться каким образом выводите закладки в профиле пользователя?

Evgenii Legotckoi
  • 05 грудня 2018 р. 15:47

Добрый день, Игорь.

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

user.bookmarkmovie_set.all()



ИМ
  • 05 грудня 2018 р. 15:54

Спасибо Евгений, буду пробовать.

ИМ
  • 05 грудня 2018 р. 16:16

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

Evgenii Legotckoi
  • 05 грудня 2018 р. 16:20

Покажите шаблон, где выводите закладки, но без контента

ИМ
  • 05 грудня 2018 р. 16:22
{% extends 'base/base.html' %}
{% block content %}
<div class="userprofile">
<div class="row">
    <div class="col-md-3">
		{% if user.avatar %}
			<img src="{{ user.avatar.url }}">
		{% else %}
			<img src="/static/img/noavatar.png">
		{% endif %}
	</div>
    <div class="col-md-9">
		{{ profile.username }}
		<ul class="nav nav-tabs" id="myTab" role="tablist">
  <li class="nav-item">
    <a class="nav-link active" id="home-tab" data-toggle="tab" href="#home" role="tab" aria-controls="home" aria-selected="true">Home</a>
  </li>
  <li class="nav-item">
    <a class="nav-link" id="profile-tab" data-toggle="tab" href="#profile" role="tab" aria-controls="profile" aria-selected="false">Закладки {{ user.bookmarkmovie_set.all.count }}</a>
  </li>
  <li class="nav-item">
    <a class="nav-link" id="contact-tab" data-toggle="tab" href="#contact" role="tab" aria-controls="contact" aria-selected="false">Contact</a>
  </li>
</ul>
<div class="tab-content" id="myTabContent">
  <div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">...</div>
  <div class="tab-pane fade" id="profile" role="tabpanel" aria-labelledby="profile-tab">...</div>
  <div class="tab-pane fade" id="contact" role="tabpanel" aria-labelledby="contact-tab">...</div>
</div>
		<p></p>
	</div>
</div>
</div>
{% endblock %}
Evgenii Legotckoi
  • 05 грудня 2018 р. 16:30

хм.. для вывода тогда должно быть как-то так

{% for bookmark in user.bookmarkmovie_set.all %}
    {{ bookmark .movie.title }}
{% endfor %}

Думаю, что догадаетесь как вывести нужную информацию о фильме

bookmark.movie - означает, что вы берёте поле внешнего ключа на фильм, которое должно быть в закладке. полагаю, что онго должно называться movie

ИМ
  • 05 грудня 2018 р. 16:32

Ага цикл. Спасибо за разъяснение Евгений.

ab
  • 26 жовтня 2019 р. 12:02

здраствуйте. У меня почему-то выходит так

ab
  • 26 жовтня 2019 р. 12:02

здраствуйте. У меня почему-то выходит так

Evgenii Legotckoi
  • 26 жовтня 2019 р. 23:58

url или не подключили, или подключили неправильно

progammist
  • 29 квітня 2020 р. 17:37

Подскажите пожалуйста, что значит

#app_name = 'ajax'

в urls.py ??
Это надо новое приложение создать?

Evgenii Legotckoi
  • 29 квітня 2020 р. 17:46

Да, для такого функционала лучше создать отдельное приложение. Причина предельно простая. Обычно, когда делают сайт на Django, то очень часто делают его мультиязычным. Поэтому всё, что касается ajax взаимодействия, лучше пускать мимо дистпечеризации urls с поддержкой мультиязычности. Такая проблема возникает вследствие того, что производится переадресация на url, который зависит от языка. Из-за переадресации неправильно работает js.

Тут два решения проблемы:

  1. Выковыривать язык из страницы, чтобы сформировать url и биться со следующими багами
  2. Пустить этот функционал в обход мультиязычности, что гораздо проще и с меньшим количеством последующих багов
progammist
  • 29 квітня 2020 р. 17:48
  • (відредаговано)

ок, понял, спасибо
А в новом приложении строку app_name = 'ajax' указывать в урл?

Evgenii Legotckoi
  • 29 квітня 2020 р. 17:51

Да, нужно будет добавить файл urls, поскольку обычно при использование команды startapp это файл не создаётся, и потом в нём обязательно добавить app_name, можете присовить своё имя для него. app_name отвечает за контекс генерирования url

progammist
  • 29 квітня 2020 р. 18:01
  • (відредаговано)

подскажите еще пж, я правильно понимаю, что функцию:

def get_bookmark_count(self):
    return self.bookmarkarticle_set.all().count()

Нужно добавить в модель статьи?

И еще второй вопрос, вроде всё сделал, при клике на звездочку (кстати у меня она не отображется, просто пустое место, но при клике что-то срабатывает) ничего не происходит, в консоле: Not Found: /api/article//bookmark/

Evgenii Legotckoi
  • 29 квітня 2020 р. 18:06
  1. Да
  2. У вас тут должен быть id статьи, то есть эта запись /api/article//bookmark/ должна выглядеть например так /api/article/666/bookmark/
    Так что ищите косяк где-то в js
progammist
  • 29 квітня 2020 р. 18:06

Ок, а посмотреть добавленные закладки на каком урл можно будет после добавления?

Evgenii Legotckoi
  • 29 квітня 2020 р. 18:08

на том, который сами наваяете, если на пользовательском фронтенде, а вообще добавляйте модель закладок в панель администрирования в файле admin.py, тогда сможете увидеть в админке, добавилось там что-нибудь или нет.

progammist
  • 29 квітня 2020 р. 23:34

Не получилось у меня сделать с ajax, по всей видимости пробелма с получением ID поста. Попробовал без ajax, по сути таже ошбика(какие-то проблемы с id). Создал новый вопрос: https://evileg.com/ru/forum/topic/1329/ , не знаю как пофиксить.

progammist
  • 30 квітня 2020 р. 16:52

Подскажите, а дейтсвие:

data-action - действие, которое нужно совершить, в данном случае добавление в закладки
data-action="bookmark"

В какой части кода описываем?

Evgenii Legotckoi
  • 30 квітня 2020 р. 17:00
ction to_bookmarks()
{
    var current = $(this);
    var type = current.data('type');
    var pk = current.data('id');
    var action = current.data('action'); // ЗДЕСЬ

    $.ajax({
        url : "/api/" + type + "/" + pk + "/" + action + "/",
        type : 'POST',
        data : { 'obj' : pk },

        success : function (json) {
            current.find("[data-count='" + action + "']").text(json.count);
        }
    });

    return false;
}
progammist
  • 30 квітня 2020 р. 17:23
  • (відредаговано)

Вообщем решил сделать не по id, а по slug, что в итоге вышло:

urls.py

app_name = 'ajax'
urlpatterns = [ url('<slug:slug>/', login_required(views.BookmarkView.as_view(model=BookmarkArticle)), name='article_bookmark'),

в .js

function to_bookmarks()
{
    var current = $(this);
    var type = current.data('type');
    var pk = current.data('id');
    var action = current.data('action');

    $.ajax({
        url :   "/" + pk + "/" ,
        type : 'POST',
        data : { 'obj' : pk },

        success : function (json) {
            current.find("[data-count='" + action + "']").text(json.count);
        }
    });

    return false;
}

// Подключение обработчика
$(function() {
    $('[data-action="bookmark"]').click(to_bookmarks);
});

В шаблоне:

<div data-id="{{ post.slug }}" data-type="post" data-action="bookmark" title="Избранное">
    <span class="far fa-heart"></span>
    <span data-count="bookmark">{{ post.get_bookmark_count }}</span>
</div>

В итоге при клике на иконку в консоле получаю:

[30/Apr/2020 16:21:00] "POST /novayastatya/ HTTP/1.1" 200 7405

Счётчик при клике на иконку никак не обновляется 0 всегда, также в БД таблица bookmark_article пустая. Может быть с урлами что-то?

progammist
  • 30 квітня 2020 р. 17:29
  • (відредаговано)

Также попробовал нажать на иконку для неавторизованного пользователя, аналогично:

[30/Apr/2020 16:21:00] "POST /novayastatya/ HTTP/1.1" 200 7405

То есть @login_required не срабатывает

progammist
  • 01 травня 2020 р. 05:17
  • (відредаговано)

Евгений, можете подсказать почему может быть так?

Evgenii Legotckoi
  • 04 травня 2020 р. 03:22

А вы js код где подключаете?

файл js с to_bookmarks можно загрузить в head теге, а вот этот код

$(function() {
    $('[data-action="bookmark"]').click(to_bookmarks);
});

должен вызываться в конце страницы, когда html загружен

progammist
  • 04 травня 2020 р. 06:19
  • (відредаговано)

Загрузил в конце страницы - ситуация аналогичная. Клик нажимается, в консоле

 "POST /novayastatya/ HTTP/1.1" 200 7405

Но счетчик не обновляется и в базе нет записи. У меня подозрения, что не срабатаывает url.

urls.py

app_name = 'ajax'

urlpatterns = [
    url('/api/favourites/', login_required(views.BookmarkView.as_view(model=BookmarkArticle)),
        name='article_bookmark'),
]

Урл нигде не перподключал, создал новое app "ajax" в нём добавил urls.py и в него добавил код выше. Может быть надо где-то include url вставить?

И еще немного симптомов, если я убираю во views.py (ajax app) класс BookmarkView(View) , то ничего не меняется (никаких ошибок нет). Но в коносле POST запрос ("POST /novayastatya/ HTTP/1.1" 200 7405) при клике всё равно пишет. Получается урл не срабатывает или view, а может и с моделями что-то...

Evgenii Legotckoi
  • 04 травня 2020 р. 12:54

А вы эти urls вообще подключили в самом главном файле urls, который обычно находится рядом с файлом settings.py?

progammist
  • 04 травня 2020 р. 13:05

нет, а как?)

Evgenii Legotckoi
  • 04 травня 2020 р. 13:25

также, как подключили сами статьи в файле urls.py, который лежит рядом с settings.py. Статьи же у вас работают...

progammist
  • 04 травня 2020 р. 13:36
  • (відредаговано)

я не понимаю, как нужно подкллючить url из ajax app. Сатьи у меня лежат в главном приложении

Статьи я подключил вот так:

path('<slug:slug>/', views.post_detail, name='post_detail')

А как подключить url'ы приложения ajax app ?

Evgenii Legotckoi
  • 04 травня 2020 р. 13:47
  • (відредаговано)

боюсь, что вам это слабо поможет, но обычно url из других app подключают подобным образом

urlpatterns = [
    path('api/', include('ecore.urls')),
]

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

progammist
  • 08 травня 2020 р. 02:04

За неделю я разобрался и нашел где была ошибка, сейчас закладки добавляются успешно.

Сейчас новый вопрос, как можно их вывывести?
Пытаюсь вывести в шаблоне:

{% for bookmark in user.bookmarkarticle_set.all %} 
{{ bookmark.post.title }} #модель статьи Post
{% endfor %}

Ничего не выводится. Вьюху не создавал, не имею понятия как. Подскажите как вывести закладки юзера?

Evgenii Legotckoi
  • 08 травня 2020 р. 20:12

Добрый день.
То, что вы добавили как статью, следовало добавить как вопрос на форуме. ЭТо не статья, вы описали, что сделали и спрашивайте, что делать дальше. Поэтому задайте именно вопрос на форуме, в противном случае я не могу пропустить ту статью, чтобы другие пользователи имели к ней доступ. Она не соответсвует наименованию статьи.

Но суть вашей проблемы в том случае заключается в том, что вы не забираете список постов в соответствии с пользователем.
Поскольку никак не передаёте информацию о пользователе во view

progammist
  • 09 травня 2020 р. 22:19

ок, немного промазал при создании)
создал вопрос к статье: https://evileg.com/ru/forum/topic/1345/

progammist
  • 09 травня 2020 р. 22:27
  • (відредаговано)
> Но суть вашей проблемы в том случае заключается в том, что вы не забираете список постов в соответствии с пользователем.
Поскольку никак не передаёте информацию о пользователе во view

Вы можете показать пример как это сделать?

progammist
  • 10 травня 2020 р. 19:01

всё, разобрался

progammist
  • 10 травня 2020 р. 19:23

Оставлю для тех, у кого возникнет подобный вопрос. Посты выводим циклом:

{% for bookmark in user.bookmarkarticle_set.all %} 
{{ bookmark.obj.title }} 
{% endfor %}

Так как мы ссылаемся на обьект статьи в модели BookmarkArticle через переменную obj , то и доступ к статье получаем через неё:

{{ bookmark.obj.title }} 
progammist
  • 13 травня 2020 р. 17:20
  • (відредаговано)

Как я могу вывести добавленные посты в обратном порядке? сейчас выводятся новодобавленные в конец. Пыатюсь добавить order_by('-created_on') :

views.py

    object_list =  User.bookmarkarticle_set.all().order_by('-created_on')

Ошибка:

Cannot resolve keyword 'created_on' into field.
Evgenii Legotckoi
  • 13 травня 2020 р. 17:24

Может так?

object_list =  User.bookmarkarticle_set.all().order_by('-obj__created_on')
progammist
  • 13 травня 2020 р. 17:30

да, спасибо)

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
B
  • Bogdannn
  • 28 березня 2024 р. 05:21

C++ - Тест 002. Константы

  • Результат:16бали,
  • Рейтинг балів-10
B
  • Bogdannn
  • 28 березня 2024 р. 05:15

C++ - Тест 001. Первая программа и типы данных

  • Результат:46бали,
  • Рейтинг балів-6
FL

C++ - Тест 006. Перечисления

  • Результат:80бали,
  • Рейтинг балів4
Останні коментарі
k
kmssr09 лютого 2024 р. 05:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 12:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 21:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 19:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik19 грудня 2023 р. 08:01
Qt/C++ - Урок 056. Підключення бібліотеки Boost в Qt для компіляторів MinGW і MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
P
Pisych27 лютого 2023 р. 15:04
Как получить в массив значения из связанной модели? Спасибо, разобрался:))
AC
Alexandru Codreanu19 січня 2024 р. 22:57
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…
BlinCT
BlinCT27 грудня 2023 р. 19:57
Растягивать Image на парент по высоте Ну и само собою дял включения scrollbar надо чтобы был Flickable. Так что выходит как то так Flickable{ id: root anchors.fill: parent clip: true property url linkFile p…
Дмитрий
Дмитрий10 січня 2024 р. 15:18
Qt Creator загружает всю оперативную память Проблема решена. Удалось разобраться с помощью утилиты strace. Запустил ее: strace ./qtcreator Начал выводиться весь лог работы креатора. В один момент он начал считывать фай…
Evgenii Legotckoi
Evgenii Legotckoi12 грудня 2023 р. 17:48
Побуквенное сравнение двух строк Добрый день. Там случайно не высылается этот сигнал textChanged ещё и при форматировани текста? Если решиать в лоб, то можно просто отключать сигнал/слотовое соединение внутри слота и …

Слідкуйте за нами в соціальних мережах