django rest framwork で request.get() が取れない場合. PrimaryKeyRelatedField とか

django/djangorestframework で JOIN する 親から子、子から親
https://qiita.com/uturned0/items/973b32be719a52947f3c の続き

# background

GETはrelationしてるobjectを全部、POSTは relationsしているobjectのpkのみを受け取る、というのをしようとして↓を勉強。ありがとう。

https://sakataharumi.hatenablog.jp/entry/2018/10/20/010806

やってみると、 update のときに item_id が None になる問題にハマっていた. 理解するためにまず non-relation で勉強

modelはこれ

```
class Item(models.Model):
    name = models.CharField(max_length=100)
    tel = models.CharField(max_length=100)

    def __str__(self):
        return self.name
```

serializer 

```py
class ItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = ['id', 'name', 'tel']

    def create(self, request):
        return Item.objects.create(**request)

    def update(self, instance, request):
        item_id = request.pop('item_id', None)

        if item_id:
        	# ここで適当な処理がしたい的な要望
            instance.item = Item.objects.all(pk=item_id)

        return super().update(instance, request)
```


# DRFよくあるエラー


    "detail": "Method \"PUT\" not allowed."
    "detail": "Method \"PATCH\" not allowed."

    request URL が list/ になってて、 list/1 のようにupdate対象のpkを指定していない


# custom field を POST で受け取りたい場合

   DRFのcreate() にくる request はvalidated_data なので、不要なdictは来ない
model/serializerにいない post requestを受け付けるには以下のunnecessaryな行が必要


```py
class ItemSerializer(serializers.ModelSerializer):
    unnecessary = serializers.CharField(write_only=True)      # <-- ここに書くと request に含まれる対象として認識される
    class Meta:
        model = Item
        fields = ['id', 'name', 'type', 'unnecessary']      # <-------------


    def create(self, request):
        unnecessary = request.pop("unnecessary")      # <-- するとここにデータが入ってくる。modelsにはないfieldなので、 pop でrequestから削除するのが必須
        return Item.objects.create(**request)
```

## fields / create / update ... の不思議
> write_onlyを指定することによりこのフィールドをGET時には出さないようにしておきます。

fields にはget/post関わらず使う可能性のあるものをすべて書く。
postのときはあると困る・・とか考えなくていい。

write_only / read_only がそのスイッチで、DRFがいい感じに fields を解釈してくれる。

read_only = GET only 
write_only = only POST, PATCH, PUT, DELETE と思えばよさそう



# requestにやってくるデータの型はserializerのclass変数で来まる

## CharField = str

```py
class ItemSerializer(serializers.ModelSerializer):
    item_id = serializers.CharField(   #<---------- CharField = str()
        write_only=True,
    )

    class Meta:
        model = Item
        fields = ['id', 'name', 'tel']

    def create(self, request):
        item_id = request.get('item_id', None)   #<--- str で値が入る
        return Item.objects.create(job_history=item_id, **request)
```

## UUIDField = uuid()

```py
class ItemSerializer(serializers.ModelSerializer):
    item_id = serializers.UUIDField( #<------------- UUID
        write_only=True,
    )
    
    ...

    def create(self, request):
        item_id = request.get('item_id', None)   # uuid型になる
		...
```

# ハマった理由

fieldの指定をrelation系のものにすると、relationが見つからない場合は None になるようだ

```py
class ItemSerializer(serializers.ModelSerializer):
    parrent_id = serializers.PrimaryKeyRelatedField( 
    	 とか
    parrent_id = serializers.StringsRelatedField( 
    	とか
	,,,

    def create(self, request):
	    parrent_id = request.get('parrent_id', None)   # Noneになる
		...

```

見つかった場合は、instance object が入る。

```
    def create(self, request):
	    parrent = request.get('parrent_id', None)  
	    print(parrent.pk)           # instanceの中に各fieldが入ってくる
		...

```


# serializer の relation を理解する

わからないこと： PrimaryKeyRelatedField って、pkとpkをつないだあと、何を返すの？
親を返すの？子を返すの？ どっち？

manual

https://www.django-rest-framework.org/api-guide/relations/


ここによると、PrimaryKeyRelatedField は親で使っている。子で使うとどうなるんだ？


## 親 → 子 relation

```py:models.py
class Album(models.Model):
    album_name = models.CharField(max_length=100)
    artist = models.CharField(max_length=100)

class Track(models.Model):
    album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)
    order = models.IntegerField()
    title = models.CharField(max_length=100)
    duration = models.IntegerField()

    class Meta:
        unique_together = ['album', 'order']
        ordering = ['order']

    def __str__(self):
        return '%d: %s' % (self.order, self.title)
```


```py:serializers.py
class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

class TrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Track
        fields = ['order', 'title', 'duration']

```

```py:input-data  
python manage.py makemigrations
python manage.py migrate
python manage.py shell

import django
django.setup()

from myapp.models import *
from myapp.serializers import * 

album = Album.objects.create(album_name="The Grey Album", artist='Danger Mouse')
Track.objects.create(album=album, order=1, title='Public Service Announcement', duration=245)
Track.objects.create(album=album, order=2, title='What More Can I Say', duration=264)
Track.objects.create(album=album, order=3, title='Encore', duration=159)
serializer = AlbumSerializer(instance=album)
serializer.data
```

```json:result-of-album
{
    "album_name": "The Grey Album",
    "artist": "Danger Mouse",
    "tracks": [
        1,
        2,
        3
    ]
}
```

子のpkが入りました。



## 子　→　親 relation

```py:models.py
class AnotherAlbum(models.Model):
    album_name = models.CharField(max_length=100)
    artist = models.CharField(max_length=100)

class AnotherTrack(models.Model):
    album = models.ForeignKey(AnotherAlbum, related_name='tracks', on_delete=models.CASCADE)
    order = models.IntegerField()
    title = models.CharField(max_length=100)
    duration = models.IntegerField()

    class Meta:
        unique_together = ['album', 'order']
        ordering = ['order']

    def __str__(self):
        return '%d: %s' % (self.order, self.title)
```


```py:serializers.py
class AnotherAlbumSerializer(serializers.ModelSerializer):
    #ここをやめて tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True) 

    class Meta:
        model = AnotherAlbum
        fields = ['album_name', 'artist'
            # , 'tracks'
        ]

class AnotherTrackSerializer(serializers.ModelSerializer):
    album = serializers.PrimaryKeyRelatedField(read_only=True) # 子に追加
    class Meta:
        model = AnotherTrack
        fields = ['order', 'title', 'duration', 'album']
```

```py:input-data  
python manage.py makemigrations
python manage.py migrate
python manage.py shell

import django
django.setup()

from myapp.models import *
from myapp.serializers import * 

album = AnotherAlbum.objects.create(album_name="The Grey Album", artist='Danger Mouse')
AnotherTrack.objects.create(album=album, order=1, title='Public Service Announcement', duration=245)
AnotherTrack.objects.create(album=album, order=2, title='What More Can I Say', duration=264)
AnotherTrack.objects.create(album=album, order=3, title='Encore', duration=159)
serializer = AnotherAlbumSerializer(instance=album)
serializer.data
```

```json:result-of-track
[
    {
        "order": 1,
        "title": "Public Service Announcement",
        "duration": 245,
        "album": 1
    },
    {
        "order": 2,
        "title": "What More Can I Say",
        "duration": 264,
        "album": 1
    },
    {
        "order": 3,
        "title": "Encore",
        "duration": 159,
        "album": 1
    }
]
```

親のpkが入りました。


## 結論

PrimaryKeyRelatedField は　親に使えば子のpkを返すし、子に使えば親のpkを返す。





# 結果、どうなったか
relationしてる子をcreateするとき、parent objectを取るのをやめた。
create() では UUIDField でただの変数として取得。
その値を使って親のobjectを取得、 Item.object.create(parent=parent, **request)  するようにした。

Noneになるよってエラーより、このほうがjoin先のpkが見つからないエラーもわかりやすかった。