@habrocut

Как исправить ошибку при записи в БД (связанные таблицы)?

Добрый день!
Подскажите пожалуйста, что я делаю не так.

Проект на python 3.6 + django 2.0 + rest_framework.
База данных - sqlite3.
В проекте 2 связанные модели (соответственно 2 связанные таблицы в базе): Event (встречи) + Place (места, где пройдут встречи).
В таблице Event есть поле place - оно должно содержать id из таблицы Place.

Код из файла models.py:
class Place(models.Model):
    title_p = models.CharField(max_length=100)
    type_p = models.CharField(max_length=100)
    adress_p = models.CharField(max_length=200)
    link_p = models.CharField(max_length=255)

    class Meta:
        ordering = ('title_p',)
        
class Event(models.Model):
    title = models.CharField(max_length=100)
    description = models.CharField(max_length=200)
    price = models.CharField(max_length=100)
    place = models.ForeignKey(Place, on_delete=models.PROTECT)
    link = models.CharField(max_length=255)
    tags = models.CharField(max_length=200)
    date = models.DateField()
    time = models.TimeField()
    created_at = models.DateTimeField(auto_now_add=True)
    modified_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ('created_at',)


Код из serializers.py:
class PlaceSerializer(serializers.ModelSerializer):
    class Meta:
        model = Place
        fields = ('id', 'title_p', 'type_p', 'adress_p', 'link_p')
        
class EventSerializer(serializers.ModelSerializer):
    place = PlaceSerializer(read_only=True)
    class Meta:
        model = Event
        fields = ('id', 'title', 'description', 'price', 'place', 'link', 'date', 'time')


С таким подходом получается GET-ом вывести данные из таблицы Event (c вложенными значениями из таблицы Place):

HTTP 200 OK
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 1,
    "title": "Встреча1",
    "description": "Описание1",
    "price": "100",
    "place": {
        "id": 1,
        "title_p": "Место1",
        "type_p": "ТипМеста1",
        "adress_p": "Адрес1",
        "link_p": "http://vk.com/111/"
    },
    "link": "http://vk.com/11111/",
    "date": null,
    "time": "23:00:00"
}


Но если попытаемся добавить в Event новую запись (например через httpie), появляется ошибка:
http -a admin:???????? POST http://localhost:8000/events/ title=Встреча2 description=Описание2 price=200 place=1 link=http://vk.com/22222/ date=2018-01-23 time=23:00


Exception Type: IntegrityError at /events/
Exception Value: NOT NULL constraint failed: events_event.place_id


Как исправить эту ошибку?
  • Вопрос задан
  • 150 просмотров
Решения вопроса 1
@javedimka
Хочу сока
К сожалению не помогло, ошибка та же:

Вариант раз, подходит для вложенных сериалайзеров

Пишешь два сериалайзера, один для GET, с твоим, расширенным представлением для связанного Place, другой для POST/PUT с стандартной реализацией которая сериализует связанный объект как его id, потом во вью возвращаешь либо первый либо второй, т.е.:

class EventGETSerializer(serializers.ModelSerializer):
    place = PlaceSerializer(read_only=True)
    class Meta:
        model = Event
        fields = ('id', 'title', 'description', 'price', 'place', 'link', 'date', 'time')

class EventSerializer(serializers.ModelSerializer):
    class Meta:
        model = Event
        fields = ('id', 'title', 'description', 'price', 'place', 'link', 'date', 'time')

В вью переопределяешь get_serializer_class метод:
from rest_framework import permissions


class EventList(generics.ListCreateAPIView):
    queryset = Event.objects.all()

    def get_serializer_class(self):
        if self.request.method in permissions.SAFE_METHODS:
            return EventGETSerializer
        else:
            return EventSerializer



update:
Вариант два, странно работает, но не нарушает принципов DRY и возможно подходит если устраивает стандартное представление данных с depth=1

Развивая тему с depth, решил попробовать:
Без блока else не корректно себя ведет, не знаю почему.
Необходимо использовать дженерики/вьюсеты или самому передавать объект запроса в сериалайзер.
from rest_framework.permissions import SAFE_METHODS


class EventSerializer(serializers.ModelSerializer):

    def __init__(self, *args, **kwargs):
        request = kwargs.get('context', {}).get('request')
        if request is not None and request.method not in SAFE_METHODS:
            self.Meta.depth = 0
        else:
            self.Meta.depth = 1
        super().__init__(*args, **kwargs)

    class Meta:
        model = Event
        fields = ('id', 'title', 'description', 'price', 'place', 'link',
                  'date', 'time')

Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы