Django Rest Framework でこれまでに経験した中で最も困難な問題に出くわしました。最初にモデルを示し、次に説明します。
class Stampcardformat(models.Model):
workunit = models.ForeignKey(
Workunit,
on_delete=models.CASCADE
)
uuid = models.UUIDField(
default=uuid.uuid4,
editable=False,
unique=True
)
limit = models.PositiveSmallIntegerField(
default=10
)
category = models.CharField(
max_length=255
)
class Stampcard(models.Model):
stampcardformat = models.ForeignKey(
Stampcardformat,
on_delete=models.CASCADE
)
user = models.ForeignKey(
User,
on_delete=models.CASCADE
)
uuid = models.UUIDField(
default=uuid.uuid4,
editable=False,
unique=True
)
class Stamp(models.Model):
stampcardformat = models.ForeignKey(
Stampcardformat,
on_delete=models.CASCADE
)
stampcard = models.ForeignKey(
Stampcard,
on_delete=models.CASCADE,
blank=True,
null=True
)
uuid = models.UUIDField(
default=uuid.uuid4,
editable=False,
unique=True
)
これらのモデルは、単純なスタンプカード モデルを表しています。スタンプカードは、スタンプカード形式の制限数が指示するのと同じ数のスタンプが関連付けられている場合、foreignkey を介していっぱいであると見なされます。次のことを行うビューを作成する必要があります。
- ビューは、uuid で構成されるスタンプ (以下を参照) のリストを取り込みます。
- 次に、指定されたスタンプごとに適切なスタンプカード形式を見つける必要があります。
次に、リクエストしたユーザーが対応する stampcardformat のスタンプカードを持っているかどうかを確認する必要があります。
a) ある場合は、スタンプカードがいっぱいかどうかを確認する必要があります。
i)満杯の場合は、指定された形式の新しいスタンプカードを作成し、作成したスタンプカードにスタンプ stampcard-foreignkey を更新する必要があります。
ii)いっぱいでない場合は、スタンプ stampcard-foreignkey を見つかったスタンプカードに更新する必要があります
b)ユーザーが指定された stampcardformat のスタンプカードを取得していない場合は、新しいスタンプカードを作成し、スタンプ stampcard-foreignkey を作成したスタンプカードに更新する必要があります。
スタンプのリクエスト本文リストは次のとおりです。
[
{
"stamp_uuid": "62c4070f-926a-41dd-a5b1-1ddc2afc01b2"
},
{
"stamp_uuid": "4ad6513f-5171-4684-8377-1b00de4d6c87"
},
...
]
クラス ベースのビューは、この動作をサポートしていないようです。クラスベースのビューを変更しようとしましたが、役に立ちませんでした。ビューがエラーをスローするため、多くの点に加えて失敗します:
AssertionError: Expected view StampUpdate to be called with a URL keyword argument named "pk". Fix your URL conf, or set the `.lookup_field` attribute on the view correctly.
編集
追加のコンテキスト: pk、スラッグなどを含まない URL が必要です。したがって、URL は次のようになります。
/api/stampcards/stamps/
それにプット(またはボディと機能を持つ任意のリクエスト)を実行します。私が書いたルートは次のとおりです。
url(r'^stamps/$', StampUpdate.as_view(), name='stamp-api-update'),
編集:大規模な更新。それで、私はうまく機能するビューをまとめました。最初に、スタンプカード モデルを次のように更新しました (いっぱいになったかどうかを追跡するために、新しいフィールド 'done' を追加しました)。
class Stampcard(models.Model):
stampcardformat = models.ForeignKey(
Stampcardformat,
on_delete=models.CASCADE
)
user = models.ForeignKey(
User,
on_delete=models.CASCADE
)
uuid = models.UUIDField(
default=uuid.uuid4,
editable=False,
unique=True
)
done = models.BooleanField(default=False)
次に、次のようにビューを書きました。
class StampUpdate(APIView):
permission_classes = (IsAuthenticated,)
def get_object(self, uuid):
try:
return Stamp.objects.get(uuid=uuid)
except Stamp.DoesNotExist():
raise Http404
def put(self, request, format=None):
for stamp_data in request.data:
stamp = self.get_object(stamp_data['stamp_uuid'])
if stamp.stampcard==None:
user_stampcard = self.request.user.stampcard_set.exclude(done=True).filter(stampcardformat=stamp.stampcardformat)
if user_stampcard.exists():
earliest_stampcard = user_stampcard.earliest('timestamp')
stamp.stampcard = earliest_stampcard
stamp.save()
if earliest_stampcard.stamp_set.count() == earliest_stampcard.stampcardformat.limit:
earliest_stampcard.done=True
earliest_stampcard.save()
else:
new_stampcard = Stampcard(stampcardformat=stamp.stampcardformat, user=self.request.user)
new_stampcard.save()
stamp.stampcard = new_stampcard
stamp.save()
new_stampcards = Stampcard.objects.exclude(done=True).filter(user=self.request.user)
last_full_stampcard = Stampcard.objects.filter(user=self.request.user).filter(done=True)
if last_full_stampcard.exists():
last_full_stampcard_uuid=last_full_stampcard.latest('updated').uuid
last_full_stampcard = Stampcard.objects.filter(uuid=last_full_stampcard_uuid)
stampcards = new_stampcards | last_full_stampcard
else:
stampcards = new_stampcards
print(stampcards)
stampcard_serializer = StampcardSerializer(stampcards, many=True)
return Response(stampcard_serializer.data)
しかし、このコードには 2 つの問題があります。
- 私の直感では、モデル インスタンスで save() を呼び出すだけの部分 (例:
stamp.save()
) は、API にとって非常に安全ではないことがわかります。最初にデータをシリアル化することができませんでした。私の質問は: このビューはこのように大丈夫ですか? それとも何か改善できますか?たとえば、使用されるジェネリッククラスベースは使用しませんが、ここでそれらを使用する方法がわかりません... - この方法でスタンプカードがいっぱいになった場合は、スタンプカードも返却したいと思います。しかし、関係のないスタンプカードもすべて除外したいので、 を呼び出し
.exclude(done=True)
ました。残念ながらいっぱいになったスタンプカードは、do=True ですが! 途中で埋まったスタンプカードを戻り値に加えるにはどうすればよいですか?