Intruder
IntruderFeb. 3, 2020, 5:47 a.m.

QTableView и список значений в ячейке

QTableView, QList, QVector

Всем доброго времени суток!

Подскажите, пожалуйста, как лучше сделать:
1. Имеется модель данных, посторенная с помощью QAbstractTableView. Сама таблица отражает объект в плоском виде.
2. В некоторых колонках нужно отобразить список значений, потому что в самом объекте присутствуют QList/QVector, которые в свою очередь тоже содержат некоторые свойства.

Вопрос у меня возникает следующий, как сделать так, чтобы можно было записывать в модель данных объект, который имеет в своем составе массив, как свойство. Если бы это была простая БД, то особых проблем бы не было, я просто нормализовал бы данные, связал связями и получил обычный Select через первичные и внешние ключи.
Но тут у меня объект программный и для него нужна модель данных. Модель я построил. Но вопрос остался с ячейками, где должны храниться массивы. Как тут быть?

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

10
Evgenii Legotckoi
  • Feb. 3, 2020, 6:20 a.m.

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

    Intruder
    • Feb. 3, 2020, 7:35 a.m.

    Вот это модель данных:

    QVariant FeedThruTableModel::headerData(int section, Qt::Orientation orientation, int role) const
    {
        if(role != Qt::DisplayRole){
            return QVariant();
        }
    
        if(orientation == Qt::Vertical){
            return section + 1;
        }
        switch(section){
        case HOLEIDENT:
            return tr("Hole Identification");
        case FUNCTIONALITEMNUMBER:
            return tr("Functional Item Number Identifier");
        case FUNCTIONALITEMTYPE:
            return tr("Functional Item Type");
        case IDFUNCTIONALITEMREF:
            return tr("Identifier");
        case INSTALLATIONIDENT:
            return tr("Installation Identifier");
        case APPLICREFID:
            return tr("Applicability Information");
        case CHANGETYPE:
            return tr("Change Type");
        case CHANGEMARK:
            return tr("Change Mark");
        case REASONFORUPDATEREFIDS:
            return tr("Reason For Update Ref Ids");
        case SECURITYCLASSIFICATION:
            return tr("Security Classification");
        case COMMERCIALCLASSIFICATION:
            return tr("Commercial Classification");
        case CAVEAT:
            return tr("Caveat");
        case AUTHORITYNAME:
            return tr("Authority Name");
        case AUTHORITYDOCUMENT:
            return tr("Authority Document");
        case CONTEXTIDENT:
            return tr("Context Identification");
        case MANUFACTURERCODEVALUE:
            return tr("CAGE Code");
        case ITEMORIGINATOR:
            return tr("Functional Item Originator");
        case NAMEVALUE:
            return tr("Functional Item Name");
        case CHANGETYPENAME:
            return tr("Change Type");
        case CHANGEMARKNAME:
            return tr("Change Mark");
        case REASONFORUPDATEREFIDSNAME:
            return tr("Reason For Update Ref Ids");
        case SHORTNAMEVALUE:
            return tr("Alternate Name");
        case DMREF: // Вот это поле объявлено как QVector<dmRef> *vectorDmRef
            return tr("References to data modules");
        case PMREF: // Вот это поле объявлено как QVector<pmRef> *vectorPmRef
            return tr("References to publications");
        case EXTERNALPUBREF: // Вот это поле как QVector<externalPubRef> *vectorExternalPubRef
            return tr("References to external documents");
        }
        return QVariant();
    }
    
    

    Сам объект описан вот так:

        // Attributes of the tag <funcitonalItemRef>
        QMap<QString, QString> *functionalItemRefAttrMap = nullptr;
        // Tag <name>
        Name *nameObject = nullptr;
        // Tag <shortName>
        QString shortNameValue;
        // Tag <refs>
        Refs *refsObject = nullptr;
    

    Объект Refs:

        // The tag <dmRef>
        QVector<DmRef> *dmRefVector = nullptr;
        // The tag <pmRef>
        // Здесь пока ничего нет, но по аналогии с dmRef
        // The tag <externalPubRef>
        // Здесь пока ничего нет, но по аналогии с dmRef
    

    Или я что-то неправильно понял?

      Evgenii Legotckoi
      • Feb. 3, 2020, 4:32 p.m.

      Немного не догоняю, вот этот вектор dmRefVector должен как-то мапиться во все эти enum?

      Может тогда лучше QMap ? А потом уже возвращать и устанавливать данные в зависимости от этого enum?

        Intruder
        • Feb. 3, 2020, 11 p.m.

        Евгений, добрый день.

        Я планировал для элемента dmRef получать агрегатное значение и его отображать. Иными словами в модели будет храниться его подготовленное значение. Если представлять его в каком-то виде, то я предполагал взять QListView для этих целей. А зачем QMap ?
        Но, сейчас у меня возник еще один вопрос, а как эти данные готовить. С однозначными(атомарными) значениями все понятно, а тут? Я могу показать весь класс для этой модели, если нужно.

          Intruder
          • Feb. 4, 2020, 1:37 a.m.

          Евгений, у меня с обычными таблицами проблем вообще никаких не возникло, разобрался как это работает и в путь. Но тут получается, что объект, который я кладу в модель данных - он составной и в его составе могут быть другие обеъкты и/или массивы/списки/словари. А вот как в этом случае строить модель данных для меня не совсем понятно.
          Я тут поразмыслил и мне пришла в голову мысль, что нужно как-то модернизировать эти объекты-"матрешки". Например, вместо объекта, который входит в состав хранить не сам объект, а его идентификатор-ключ. В объекте, который будет включаться ввести дополнительное свойство, идентификатор. Вот только придется следить за уникальностью этих идентификаторов программно при создании.
          Вот только одного я не учел. В файл мне нельзя класть эти идентификаторы иначе я не пройду валидацию. Как вариант придется ломать формат и вводить комментарии или элементы, которые не влияют на валидацию.

            Evgenii Legotckoi
            • Feb. 4, 2020, 3:08 a.m.
            • (edited)

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

            Для управления уникальными указателями попробуйте использовать shared_ptr

              Intruder
              • Feb. 4, 2020, 3:21 a.m.

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

                Intruder
                • Feb. 5, 2020, 6:32 a.m.

                Евгений, добрый день.

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

                  Intruder
                  • Feb. 5, 2020, 12:53 p.m.

                  Решил пойти сначала. Создал тестовый проект.
                  Тестовая форма
                  Здесь отображаются данные в верхней части, это данные основного объекта, а в нижней части отображаются данные объекта, который входит в состав основного объекта. Этим объектом является QVector<составной_объект>.
                  Классы, которые описывают основной и составной объект достаточно просты:
                  Составной объект class Contact и его описание

                  #ifndef CONTACT_H
                  #define CONTACT_H
                  
                  #include <QMap>
                  
                  class Contact
                  {
                  public:
                      Contact();
                      ~Contact(){}
                  
                      QMap<QString, QString> *getMap() const;
                      void setMap(QMap<QString, QString> *value);
                  private:
                      QMap<QString, QString> *map = nullptr;
                  };
                  
                  #endif // CONTACT_H
                  
                  #include "contact.h"
                  Contact::Contact() {}
                  
                  QMap<QString, QString> *Contact::getMap() const {return map;}
                  
                  void Contact::setMap(QMap<QString, QString> *value) {map = value;}
                  

                  Основной объект class ConnectionList и его описание

                  #ifndef CONNECTIONLIST_H
                  #define CONNECTIONLIST_H
                  
                  #include "contact.h"
                  
                  class ConnectionList
                  {
                  public:
                      ConnectionList();
                      ~ConnectionList();
                  
                      QString getAttrValue() const;
                      void setAttrValue(const QString &value);
                  
                      QVector<Contact> *getContactList() const;
                      void setContactList(QVector<Contact> *value);
                  
                  private:
                      QString attrValue;
                      QVector<Contact> *contactList = nullptr;
                  };
                  
                  #endif // CONNECTIONLIST_H
                  
                  #include "connectionlist.h"
                  
                  ConnectionList::ConnectionList(){}
                  
                  QString ConnectionList::getAttrValue() const {return attrValue;}
                  
                  void ConnectionList::setAttrValue(const QString &value) {attrValue = value;}
                  
                  QVector<Contact> *ConnectionList::getContactList() const {return contactList;}
                  
                  void ConnectionList::setContactList(QVector<Contact> *value) {contactList = value;}
                  

                  Модель данных для отображения таблицы "Contact List" и описание:

                  #ifndef CONTACTMODEL_H
                  #define CONTACTMODEL_H
                  
                  #include "contact.h"
                  
                  #include <QAbstractTableModel>
                  
                  class ContactModel : public QAbstractTableModel
                  {
                      Q_OBJECT
                  
                  public:
                      ContactModel(QObject *parent = nullptr);
                  
                      void appendContact(Contact *item);
                      void resetModel();
                      QVector<Contact> getContactList();
                  
                  private:
                      enum Cols{
                          CONTACTIDENT = 0,
                          CONTACTFUNCTION,
                          CONTACTTYPE,
                          CONNECTEDFLAG,
                          CONTACTPARTNUMBER,
                          WIREINSTALLATION,
                          COLUMNCOUNT
                      };
                  
                      typedef QHash<Cols, QVariant> ContactData;
                      typedef QList<ContactData> ContactGroup;
                      ContactGroup m_contactGroup;
                  
                  
                      // QAbstractItemModel interface
                  public:
                      int rowCount(const QModelIndex &parent) const;
                      int columnCount(const QModelIndex &parent) const;
                      QVariant data(const QModelIndex &index, int role) const;
                  
                      // QAbstractItemModel interface
                  public:
                      QVariant headerData(int section, Qt::Orientation orientation, int role) const;
                  
                      // QAbstractItemModel interface
                  public:
                      Qt::ItemFlags flags(const QModelIndex &index) const;
                  };
                  
                  #endif // CONTACTMODEL_H
                  
                  
                  #include "contactmodel.h"
                  
                  ContactModel::ContactModel(QObject *parent)
                      : QAbstractTableModel(parent)
                  {
                  }
                  
                  QVariant ContactModel::headerData(int section, Qt::Orientation orientation, int role) const
                  {
                      if(role != Qt::DisplayRole){
                          return QVariant();
                      }
                  
                      if(orientation == Qt::Vertical){
                          return section + 1;
                      }
                      switch(section){
                      case CONTACTIDENT:
                          return tr("CONTACTIDENT");
                      case CONTACTFUNCTION:
                          return tr("CONTACTFUNCTION");
                      case CONTACTTYPE:
                          return tr("CONTACTTYPE");
                      case CONNECTEDFLAG:
                          return tr("CONNECTEDFLAG");
                      case CONTACTPARTNUMBER:
                          return tr("CONTACTPARTNUMBER");
                      case WIREINSTALLATION:
                          return tr("WIREINSTALLATION");
                      }
                      return QVariant();
                  }
                  
                  int ContactModel::rowCount(const QModelIndex &parent) const
                  {
                      Q_UNUSED(parent)
                      return m_contactGroup.count();
                  
                  }
                  
                  int ContactModel::columnCount(const QModelIndex &parent) const
                  {
                      Q_UNUSED(parent)
                      return COLUMNCOUNT;
                  }
                  
                  
                  void ContactModel::appendContact(Contact *item)
                  {    ContactData contactItem;
                       if(item->getMap()->contains("1")){
                           contactItem[CONTACTIDENT] = item->getMap()->value(item->getMap()->key("1"));
                       }
                       if(item->getMap()->contains("2")){
                           contactItem[CONTACTFUNCTION] = item->getMap()->value(item->getMap()->key("2"));
                       }
                       if(item->getMap()->contains("3")){
                           contactItem[CONTACTTYPE] = item->getMap()->value(item->getMap()->key("3"));
                       }
                       if(item->getMap()->contains("4")){
                           contactItem[CONNECTEDFLAG] = item->getMap()->value(item->getMap()->key("4"));
                       }
                       if(item->getMap()->contains("5")){
                           contactItem[CONTACTPARTNUMBER] = item->getMap()->value(item->getMap()->key("5"));
                       }
                       if(item->getMap()->contains("6")){
                           contactItem[WIREINSTALLATION] = item->getMap()->value(item->getMap()->key("6"));
                       }
                       int row = m_contactGroup.count();
                       beginInsertRows(QModelIndex(), row, row);
                       m_contactGroup.append(contactItem);
                       endInsertRows();
                  }
                  
                  
                  Qt::ItemFlags ContactModel::flags(const QModelIndex &index) const
                  {
                      Qt::ItemFlags flags = QAbstractTableModel::flags(index);
                      if(index.isValid()){
                          flags |= Qt::ItemIsEditable;
                      }
                  
                      return flags;
                  }
                  
                  
                  QVariant ContactModel::data(const QModelIndex &index, int role) const
                  {
                      if(!index.isValid() ||
                              m_contactGroup.count() <= index.row() ||
                              (role != Qt::DisplayRole && role != Qt::EditRole)){
                          return QVariant();
                      }
                      return m_contactGroup[index.row()][Cols(index.column())];
                  }
                  
                  

                  Модель данных для отображения верхней таблицы:

                  #ifndef CONNECTIONLISTMODEL_H
                  #define CONNECTIONLISTMODEL_H
                  
                  #include "connectionlist.h"
                  
                  #include <QAbstractTableModel>
                  #include <QObject>
                  
                  class ConnectionListModel : public QAbstractTableModel
                  {
                      Q_OBJECT
                  public:
                      ConnectionListModel(QObject *parent = nullptr);
                  
                      void appendConnection(ConnectionList *item);
                      void resetModel();
                      QVector<ConnectionList> getConnectionList();
                  
                  private:
                      enum Columns{
                          CONNECTION = 0,
                          CONTACTLIST,
                          COLUMNCOUNT
                      };
                  
                      typedef QHash<Columns, QVariant> ConnectionData;
                      typedef  QMap<QString, QVariant> ContactData;
                      typedef  QVariantList ContactList;
                      typedef QList<ConnectionData> ConnectionGroup;
                      ConnectionGroup m_connectionGroup;
                      ContactData m_contactMap;
                      ContactList m_contactList;
                  
                      // QAbstractItemModel interface
                  public:
                      int rowCount(const QModelIndex &parent) const;
                      int columnCount(const QModelIndex &parent) const;
                      QVariant data(const QModelIndex &index, int role) const;
                      QVariant headerData(int section, Qt::Orientation orientation, int role) const;
                      Qt::ItemFlags flags(const QModelIndex &index) const;
                  };
                  
                  #endif // CONNECTIONLISTMODEL_H
                  
                  
                  #include "connectionlistmodel.h"
                  
                  ConnectionListModel::ConnectionListModel(QObject *parent)
                      : QAbstractTableModel(parent)
                  {
                  
                  }
                  
                  void ConnectionListModel::appendConnection(ConnectionList *item)
                  {
                      ConnectionData connectionItem;
                      if(!item->getAttrValue().isEmpty()){
                          connectionItem[CONNECTION] = item->getAttrValue();
                      }
                      if(item->getContactList() != nullptr){
                          m_contactMap.insert(item->getContactList()->at(0).getMap()->key("1"), item->getContactList()->at(0).getMap()->value("1"));
                          m_contactMap.insert(item->getContactList()->at(0).getMap()->key("2"), item->getContactList()->at(0).getMap()->value("2"));
                          m_contactList.append(m_contactMap);
                          m_contactList.append(m_contactMap);
                          connectionItem[CONTACTLIST] = QVariant::fromValue(m_contactList);
                      }
                  
                      int row = m_connectionGroup.count();
                      beginInsertRows(QModelIndex(), row, row);
                      m_connectionGroup.append(connectionItem);
                      endInsertRows();
                  
                  }
                  
                  
                  int ConnectionListModel::rowCount(const QModelIndex &parent) const
                  {
                      Q_UNUSED(parent)
                      return m_connectionGroup.count();
                  }
                  
                  int ConnectionListModel::columnCount(const QModelIndex &parent) const
                  {
                      Q_UNUSED(parent)
                      return COLUMNCOUNT;
                  }
                  
                  QVariant ConnectionListModel::data(const QModelIndex &index, int role) const
                  {
                      if(!index.isValid() ||
                              m_connectionGroup.count() <= index.row() ||
                              (role != Qt::DisplayRole && role != Qt::EditRole)){
                          return QVariant();
                      }
                      return m_connectionGroup[index.row()][Columns(index.column())];
                  }
                  
                  QVariant ConnectionListModel::headerData(int section, Qt::Orientation orientation, int role) const
                  {
                      if(role != Qt::DisplayRole){
                          return QVariant();
                      }
                  
                      if(orientation == Qt::Vertical){
                          return section + 1;
                      }
                      switch(section){
                      case CONNECTION:
                          return tr("CONNECTION");
                      case CONTACTLIST:
                          return tr("CONTACTLIST");
                      }
                      return QVariant();
                  }
                  
                  Qt::ItemFlags ConnectionListModel::flags(const QModelIndex &index) const
                  {
                      Qt::ItemFlags flags = QAbstractTableModel::flags(index);
                      if(index.isValid()){
                          flags |= Qt::ItemIsEditable;
                      }
                  
                      return flags;
                  }
                  
                  

                  Логика работы формы:

                  #ifndef MAINWINDOW_H
                  #define MAINWINDOW_H
                  
                  #include "contactmodel.h"
                  #include "connectionlist.h"
                  #include "connectionlistmodel.h"
                  
                  #include <QMainWindow>
                  
                  namespace Ui {
                  class MainWindow;
                  }
                  
                  class MainWindow : public QMainWindow
                  {
                      Q_OBJECT
                  
                  public:
                      explicit MainWindow(QWidget *parent = nullptr);
                      ~MainWindow();
                  
                  private:
                      Ui::MainWindow *ui;
                      ContactModel *_cModel = nullptr;
                      ConnectionListModel *_connListModel = nullptr;
                  
                  };
                  
                  #endif // MAINWINDOW_H
                  
                  #include "mainwindow.h"
                  #include "ui_mainwindow.h"
                  
                  MainWindow::MainWindow(QWidget *parent) :
                      QMainWindow(parent),
                      ui(new Ui::MainWindow)
                  {
                      ui->setupUi(this);
                      _cModel = new ContactModel();
                      ui->contactTable->setModel(_cModel);
                      ui->contactTable->horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
                  
                      _connListModel = new ConnectionListModel();
                      ui->connectionTable->setModel(_connListModel);
                      ui->connectionTable->horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
                  
                      Contact *cont1 = new Contact();
                      Contact *cont2 = new Contact();
                      ConnectionList *newConnectionList = new ConnectionList();
                  
                      auto attrMap = new QMap<QString, QString>();
                      attrMap->insert("1", "1");
                      attrMap->insert("2", "2");
                      attrMap->insert("3", "3");
                      attrMap->insert("4", "4");
                      attrMap->insert("5", "5");
                      attrMap->insert("6", "6");
                      cont1->setMap(attrMap);
                      cont2->setMap(attrMap);
                      _cModel->appendContact(cont1);
                      _cModel->appendContact(cont2);
                  
                      newConnectionList->setAttrValue("direct");
                      QVector<Contact> *contactList = new QVector<Contact>();
                      contactList->append(*cont1);
                      contactList->append(*cont2);
                      newConnectionList->setContactList(contactList);
                      _connListModel->appendConnection(newConnectionList);
                  
                  }
                  
                  MainWindow::~MainWindow()
                  {
                      delete ui;
                  }
                  
                  

                  Так вот при запуске проекта я получаю список в объекте m_connectionGroup в ячейке CONTACTLIST. Но данные этой ячейки не отображаются в таблице.
                  В принципе я добился того, что положил данные объекта в модель данных, но теперь я не понимаю:
                  1. Как отобразить эти данные в ячейку?
                  2. Если отобразить данные в ячейку не получится, то как связать верхнюю таблицу с нижней? С тем расчетом, что двигаясь по строкам верхней таблицы менялись значения в нижней.
                  3. Как в этом случае редактировать(добавлять/удалять) данные из модели?
                  4. Думал про делегаты, типа в ячейку добавить кнопку, которая бы вызывала дочернюю форму, а там отображались бы данные из списка. Но не знаю, правильное ли решение это будет? Да и с делегатами пока еще не работал.

                  Еще раз оговорюсь, что с простыми таблицами вроде бы все понятно. Я вроде бы разобрался, как это работает, а вот с таког рода объектами... Темный лес в общем.

                  Файл проекта приложил. Спасибо всем, кто поможет разобраться в такой задачке.
                  TestTableView.7z TestTableView.7z

                    Intruder
                    • Feb. 6, 2020, 8:25 a.m.

                    Итак, продолжаю вести что-то вроде рассказа о том, как и что у меня получается. В общем решил задачу с отоображением данных в основной и дочерней таблице. Если в крадце, то я сохранил в ячейку список QVariantList внутрь которого положил QMap . Эту колонку, которая содержит список элементов я не отображаю, но теперь я могу легко взять этот список элементов. Дальше на выбор сторки в повесил построение дочерней модели.
                    Как все сделаю, то напишу итоговый результат.
                    Просто я смотрю, много информации по работе с таблицами из БД, а чтобы это делалось программно такой информации практически нет.

                      Comments

                      Only authorized users can post comments.
                      Please, Log in or Sign up
                      AD

                      C ++ - Test 004. Pointers, Arrays and Loops

                      • Result:50points,
                      • Rating points-4
                      m

                      C ++ - Test 004. Pointers, Arrays and Loops

                      • Result:80points,
                      • Rating points4
                      m

                      C ++ - Test 004. Pointers, Arrays and Loops

                      • Result:20points,
                      • Rating points-10
                      Last comments
                      ИМ
                      Игорь МаксимовNov. 22, 2024, 11:51 a.m.
                      Django - Tutorial 017. Customize the login page to Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
                      Evgenii Legotckoi
                      Evgenii LegotckoiOct. 31, 2024, 2:37 p.m.
                      Django - Lesson 064. How to write a Python Markdown extension Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
                      A
                      ALO1ZEOct. 19, 2024, 8:19 a.m.
                      Fb3 file reader on Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
                      ИМ
                      Игорь МаксимовOct. 5, 2024, 7:51 a.m.
                      Django - Lesson 064. How to write a Python Markdown extension Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
                      d
                      dblas5July 5, 2024, 11:02 a.m.
                      QML - Lesson 016. SQLite database and the working with it in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                      Now discuss on the forum
                      m
                      moogoNov. 22, 2024, 7:17 a.m.
                      Mosquito Spray System Effective Mosquito Systems for Backyard | Eco-Friendly Misting Control Device & Repellent Spray - Moogo ; Upgrade your backyard with our mosquito-repellent device! Our misters conce…
                      Evgenii Legotckoi
                      Evgenii LegotckoiJune 24, 2024, 3:11 p.m.
                      добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
                      t
                      tonypeachey1Nov. 15, 2024, 6:04 a.m.
                      google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
                      NSProject
                      NSProjectJune 4, 2022, 3:49 a.m.
                      Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…

                      Follow us in social networks