Неправильный тип. Ожидаемое значение pk, полученный str (поле «многие ко многим»)

#javascript #jquery #django #rest #serialization

#javascript #jquery #django #остальное #сериализация

Вопрос:

models.py

 import uuid
import os
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, 
                                        PermissionsMixin
from django.conf import settings


def recipe_image_file_path(instance, filename):
    """Generate file path for new recipe image"""
    ext = filename.split('.')[-1]
    filename = f'{uuid.uuid4()}.{ext}'

    return os.path.join('uploads/recipe/', filename)


class UserManager(BaseUserManager):

    def create_user(self, email, password=None, **extra_fields):
        """Creates and saves a new user"""
        if not email:
            raise ValueError('Users must have an email address')
        user = self.model(email=self.normalize_email(email), **extra_fields)
        user.set_password(password)
        user.save(using=self._db)

        return user

    def create_superuser(self, email, password):
        """Creates and saves a new super user"""
        user = self.create_user(email, password)
        user.is_staff = True
        user.is_superuser = True
        user.save(using=self._db)

        return user


class User(AbstractBaseUser, PermissionsMixin):
    """Custom user model that suppors using email instead of username"""
    email = models.EmailField(max_length=255, unique=True)
    name = models.CharField(max_length=255)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

    objects = UserManager()

    USERNAME_FIELD = 'email'


class Tag(models.Model):
    """Tag to be used for a recipe"""
    name = models.CharField(max_length=255)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )

    def __str__(self):
        return self.name


class Ingredient(models.Model):
    """Ingredient to be used in a recipe"""
    name = models.CharField(max_length=255)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE
    )

    def __str__(self):
        return self.name


class Recipe(models.Model):
    """Recipe object"""
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE
    )
    title = models.CharField(max_length=255)
    time_minutes = models.IntegerField()
    price = models.DecimalField(max_digits=5, decimal_places=2)
    link = models.CharField(max_length=255, blank=True)
    ingredients = models.ManyToManyField('Ingredient')
    tags = models.ManyToManyField('Tag')
    image = models.ImageField(null=True, upload_to=recipe_image_file_path)

    def __str__(self):
        return self.title
 

serializers.py

 from rest_framework import serializers
from django.contrib.auth import get_user_model

from core.models import Tag, Ingredient, Recipe


class TagSerializer(serializers.ModelSerializer):
    """Serializer for tag objects"""

    class Meta:
        model = Tag
        fields = ('id', 'name')
        read_only_fields = ('id',)


class IngredientSerializer(serializers.ModelSerializer):
    """Serializer for ingredient objects"""

    class Meta:
        model = Ingredient
        fields = ('id', 'name')
        read_only_fields = ('id',)


class RecipeSerializer(serializers.ModelSerializer):
    """Serialize a recipe"""
    ingredients = serializers.PrimaryKeyRelatedField(
        many=True,
        queryset=Ingredient.objects.all()
    )
    tags = serializers.PrimaryKeyRelatedField(
        many=True,
        queryset=Tag.objects.all()
    )

    class Meta:
        model = Recipe
        fields = (
            'id', 'title', 'ingredients', 'tags', 'time_minutes',
            'price', 'link' # Add user here
        )
        read_only_fields = ('id',)


class RecipeDetailSerializer(RecipeSerializer):
    """Serialize a recipe detail"""
    ingredients = IngredientSerializer(many=True, read_only=True)
    tags = TagSerializer(many=True, read_only=True)


class RecipeImageSerializer(serializers.ModelSerializer):
    """Serializer for uploading images to recipes"""

    class Meta:
        model = Recipe
        fields = ('id', 'image')
        read_only_fields = ('id',)
 

create.html

 {% extends 'base.html' %}
{% block content %}
<!DOCTYPE html>
<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>Create a Recipe</title>
  <script   src="https://code.jquery.com/jquery-3.5.1.min.js"   integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="   crossorigin="anonymous"></script>
  <script>

  $(document).ready(function() {

      // process the form
      $('form').submit(function(event) {
        var tags = $('select[name=tags]').val();
        var tagsS = tags.toString();
        var ingredients = $('select[name=ingredients]').val();
        var ingredientsS = ingredients.toString();
          // get the form data
          // there are many ways to get this data using jQuery (you can use the class or id also)
          var formData = {
              'title'              : $('input[name=T]').val(),
              'tags'             : tagsS,
              'ingredients'    : ingredientsS,
              'time_minutes'    : $('input[name=Time]').val(),
              'price'    : $('input[name=P]').val(),
              'link'    : $('input[name=link]').val(),
          };

          // process the form
          $.ajax({
              type        : 'POST', // define the type of HTTP verb we want to use (POST for our form)
              url         : '/api/recipe/recipes/', // the url where we want to POST
              data        : formData, // our data object
              dataType    : 'json', // what type of data do we expect back from the server
                          encode          : true
          })
              // using the done promise callback
              .done(function(data) {

                  // log data to the console so we can see
                  console.log(data);

                  // here we will handle errors and validation messages
              });

          // stop the form from submitting the normal way and refreshing the page
          event.preventDefault();
      });

  });
  </script>
</head>

<body class="">
<br>
<div class="jumbotron">
  <h1>Create a recipe</h1>
  <br>
  <form method="POST">
    {% csrf_token %}
    <div class="form-group">
    <label for="T">Title: </label>
    <input class="form-control" type="text" name="T" value="">
    </div>
    <div class="form-group">
    <label for="Ingr">Ingredients: </label>
    <select class="form-control" multiple id="select-ingr" name="ingredients">
      {% for ing in Ing %}
      <option value="{{ing.pk}}">{{ing}}</option>
      {%endfor%}
    </select>
  </div>
    <div class="form-group">
    <label for="Tag">Tags: </label>
    <select class="form-control" multiple id="select-tags" name="tags">
      {% for tag in Tag %}
      <option value="{{tag.pk}}">{{tag}}</option>
      {%endfor%}
    </select>
  </div>
    <div class="form-group">
    <label for="Time">Time: </label>
    <input class="form-control" type="text" name="Time" value="">
    </div>
  <div class="form-group">
    <label for="P">Price: </label>
    <input class="form-control" type="text" name="P" value="">
</div>
    <div class="form-group">
    <label for="link">Link: </label>
    <input class="form-control" type="text" name="link" value="">
    </div>
    <div class="form-group">
      <input class="btn btn-success" type="submit" name="submit" value="Create">
    </div>

  </form>
</div>
</body>

</html>
{%endblock%}
 

Я использовал метод toString, потому что без него форма отправляет массив как null / 0
, поэтому, если я выбираю более одного массива в форме, я получаю ‘Неправильный тип. Ожидаемое значение pk, полученная строка.’
но если я выберу только одно, форма будет успешно отправлена Заранее Благодарю вас: D
о, да, я все еще новичок, поэтому, если вы сможете объяснить, я был бы благодарен