Arhn - архитектура программирования

PyQt5 и Django: как загрузить изображение с помощью HTTP-запроса (многочастная форма)?

в настоящее время я работаю над проектом по созданию приложения продавца, в котором продавец может загрузить изображение меню и некоторую дополнительную информацию, такую ​​как цена и название меню. но я остановился на методе запроса POST, потому что в это время мне нужно загрузить изображение с некоторыми текстовыми данными, а не только с текстовыми данными.

Я использую PyQt5.

Это мой веб-сервер API. Я использую Django Rest Framework для его создания.

[Структура API

Это модель меню:

class Menu(models.Model):
    image = models.ImageField(upload_to=path_and_rename)
    name = models.CharField(max_length=100)
    price = models.IntegerField()
    category = models.IntegerField()
    availability = models.BooleanField(default=False)
    # booked = models.IntegerField()
    sellerID = models.ForeignKey(Seller, on_delete=models.PROTECT)

Это сериализаторы меню:

class MenuSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Menu
        fields = ('id', 'image', 'name', 'price', 
                  'category','availability', 'sellerID')

Это вид меню:

class MenuViewset(viewsets.ModelViewSet):
    queryset = models.Menu.objects.all()
    serializer_class = serializers.MenuSerializer

Я попытался использовать метод в этом ответе: здесь

Это моя реализация этого метода:

def upload(self):
    file1 = QFile("/home/shalahuddin/Desktop/jamu.jpg")
    file1.open(QFile.ReadOnly)
    url = "http://127.0.0.1:8000/api/menu/"

    nama = QByteArray()
    nama.append("ABCD")

    harga = QByteArray()
    harga.append(str(999))

    kategori = QByteArray()
    kategori.append(str(0))

    ada = QByteArray()
    ada.append(str(True))

    idseller = QByteArray()
    idseller.append(str(2))


    data = {"name": nama, "price": harga, "category": kategori, "availability": ada, "sellerID": idseller}
    files = {"image": file1}
    multipart = self.construct_multipart(data, files)
    request_qt = QNetworkRequest(QUrl(url))
    request_qt.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader,
                         'multipart/form-data; boundary=%s' % multipart.boundary())
    self.manager = QtNetwork.QNetworkAccessManager()
    self.manager.finished.connect(self.handleResponseMenu)
    self.manager.post(request_qt, multipart)

def handleResponseMenu(self, reply):
    er = reply.error()
    data = json.loads(str(reply.readAll(), 'utf-8'))
    file = open("/home/shalahuddin/Desktop/testfile.txt", "w")

    file.write(str(reply))
    if er == QtNetwork.QNetworkReply.NoError:
        bytes_string = reply.readAll()
        data = json.loads(str(bytes_string, 'utf-8'))
        # print(data)
        QMessageBox.information(self, "Menu", "Upload Success!")

    else:
        errorMessage = "Error occured: " + str(er) + "\n" + str(reply.errorString())
        QMessageBox.critical(self, "Error Done", errorMessage)

def construct_multipart(self, data, files):
    multiPart = QHttpMultiPart(QHttpMultiPart.FormDataType)
    for key, value in data.items():
        textPart = QtNetwork.QHttpPart()
        textPart.setHeader(QNetworkRequest.ContentDispositionHeader,
                           "form-data; name=\"%s\"" % key)
        textPart.setBody(value)
        multiPart.append(textPart)

    for key, file in files.items():
        imagePart = QtNetwork.QHttpPart()
        # imagePart.setHeader(QNetworkRequest::ContentTypeHeader, ...);
        fileName = QFileInfo(file.fileName()).fileName()
        imagePart.setHeader(QtNetwork.QNetworkRequest.ContentDispositionHeader,
                            "form-data; name=\"%s\"; filename=\"%s\"" % (key, fileName))
        imagePart.setBodyDevice(file)
        multiPart.append(imagePart)
    return multiPart

Но в результате я получил ошибку 302 Bad Request. Данные JSON, которые я получаю:

{
  'image': ['No file was submitted.'], 
  'name': ['Ensure this field has no more than 100 characters.', 
           'Null characters are not allowed.'], 
  'price': ['This field is required.'], 
  'category': ['This field is required.'], 
  'sellerID': ['This field is required.']
}

Поэтому я хотел бы знать, где проблема? Я пытался отследить это неоднократно, но все равно не дал ответа. Это мой первый раз, когда я использую составной запрос.


  • Если я тестирую сервер с помощью Post-man с форматом данных формы в теле, он работает 13.03.2019
  • @eyllanesc конечно, вот код: github.com/shalahuddinn/CanteenWebServer 13.03.2019

Ответы:


1

В следующем примере я покажу вам создание запроса:

import json
from PyQt5 import QtCore, QtNetwork, QtWidgets

class MenuWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(MenuWidget, self).__init__(parent)
        self._manager = QtNetwork.QNetworkAccessManager()
        self._manager.finished.connect(self.handleResults)

        self.init_ui()

    def init_ui(self):
        self.filepath_lineedit = QtWidgets.QLineEdit()
        select_button = QtWidgets.QPushButton(
            text="Select File",
            clicked=self.select_file
        )
        self.name_lineedit = QtWidgets.QLineEdit()
        self.price_spinbox = QtWidgets.QSpinBox()
        self.category_spinbox = QtWidgets.QSpinBox()
        self.availability_checkbox = QtWidgets.QCheckBox()
        self.seller_id_spinbox = QtWidgets.QSpinBox()
        self.log_textedit = QtWidgets.QTextBrowser()
        upload_button = QtWidgets.QPushButton(
            text="Upload",
            clicked=self.upload
        )

        hlay = QtWidgets.QHBoxLayout()
        hlay.addWidget(self.filepath_lineedit)
        hlay.addWidget(select_button)
        lay = QtWidgets.QFormLayout(self)
        lay.addRow("Image:", hlay)
        lay.addRow("Name:", self.name_lineedit)
        lay.addRow("Price:", self.price_spinbox)
        lay.addRow("Category:", self.category_spinbox)
        lay.addRow("Availability:", self.availability_checkbox)
        lay.addRow("SellerID:", self.seller_id_spinbox)
        lay.addRow(self.log_textedit)
        lay.addRow(upload_button)

    @QtCore.pyqtSlot()
    def select_file(self):
        filename, _ = QtWidgets.QFileDialog.getOpenFileName(
            self, 
            "Open Image", 
            QtCore.QDir.currentPath(), 
            "Image Files (*.png *.jpg *.bmp)"
        )
        if filename:
            self.filepath_lineedit.setText(filename)

    @QtCore.pyqtSlot()
    def upload(self):   
        data = {
            "name": self.name_lineedit.text(),
            "price": self.price_spinbox.value(),
            "category": self.category_spinbox.value(),
            "availability": self.availability_checkbox.isChecked(),
            "sellerID": self.seller_id_spinbox.value()
        }
        path = self.filepath_lineedit.text()
        files = {"image": path}
        multi_part = self.construct_multipart(data, files)
        if multi_part:
            url = QtCore.QUrl("http://127.0.0.1:8000/api/menu/")
            request = QtNetwork.QNetworkRequest(url)
            reply = self._manager.post(request, multi_part)
            multi_part.setParent(reply)

    @QtCore.pyqtSlot(QtNetwork.QNetworkReply)
    def handleResults(self, reply):
        parsed = json.loads(reply.readAll().data())
        text = json.dumps(parsed, indent=4, sort_keys=True)
        self.log_textedit.setText(text)
        if reply.error() == QtNetwork.QNetworkReply.NoError:
            QtWidgets.QMessageBox.information(self, "Menu", "Upload Success!")
        else:
            errorMessage = "Error occured: {}".format(reply.errorString())
            QMessageBox.critical(self, "Error Done", errorMessage)
        reply.deleteLater()

    def construct_multipart(self, data, files):
        multi_part = QtNetwork.QHttpMultiPart(QtNetwork.QHttpMultiPart.FormDataType)
        for key, value in data.items():
            post_part = QtNetwork.QHttpPart()
            post_part.setHeader(QtNetwork.QNetworkRequest.ContentDispositionHeader, 
                "form-data; name=\"{}\"".format(key))
            post_part.setBody(str(value).encode())
            multi_part.append(post_part)
        for field, filepath in  files.items():
            file = QtCore.QFile(filepath)
            if not file.open(QtCore.QIODevice.ReadOnly):
                break
            post_part = QtNetwork.QHttpPart()
            post_part.setHeader(QtNetwork.QNetworkRequest.ContentDispositionHeader,
                "form-data; name=\"{}\"; filename=\"{}\"".format(field, file.fileName()))
            post_part.setBodyDevice(file)
            file.setParent(multi_part)
            multi_part.append(post_part)
        return  multi_part


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = MenuWidget()
    w.show()
    sys.exit(app.exec_())

введите здесь описание изображения

13.03.2019
  • @eyllansec Привет, братан, я просто кое-что вспомнил, возможен ли запрос PATCH? Например, я хотел бы редактировать только данные (без загрузки новой картинки). 01.07.2019
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..