From 356695f594fefc70b9e5b4aa051309596c088aa2 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 17 Dec 2022 23:03:19 -0500 Subject: [PATCH] new data model: add OpenAPI prototype --- catalog/api.py | 105 +++++++++++++++++++++++++++++-- catalog/book/models.py | 8 +-- catalog/common/jsondata.py | 2 + catalog/common/models.py | 10 ++- catalog/templates/item_base.html | 2 +- catalog/urls.py | 4 +- 6 files changed, 115 insertions(+), 16 deletions(-) diff --git a/catalog/api.py b/catalog/api.py index 467b0811..a2f44cb2 100644 --- a/catalog/api.py +++ b/catalog/api.py @@ -1,11 +1,104 @@ from ninja import NinjaAPI -from .models import Podcast +from .models import * +from .common import * +from .sites import * from django.conf import settings +from datetime import date +from ninja import Schema +from typing import List, Optional +from django.utils.baseconv import base62 +from django.shortcuts import render, get_object_or_404, redirect, reverse +from django.http import Http404 + +api = NinjaAPI(title=settings.SITE_INFO['site_name'], version="1.0.0", description=f"{settings.SITE_INFO['site_name']} API
Learn more") -api = NinjaAPI(title=settings.SITE_INFO['site_name'], version="1.0.0", description=settings.SITE_INFO['site_name']) +class ItemIn(Schema): + title: str + brief: str - -@api.get("/podcasts/{item_id}") -def get_item(request, item_id: int): - return Podcast.objects.filter(pk=item_id).first() + +class ItemOut(Schema): + uuid: str + title: str + brief: str + url: str + api_url: str + category: str + + +class EditionIn(ItemIn): + subtitle: str = None + orig_title: str = None + author: list[str] + translator: list[str] + language: str = None + pub_house: str = None + pub_year: int = None + pub_month: int = None + binding: str = None + price: str = None + pages: str = None + series: str = None + imprint: str = None + + +class EditionOut(ItemOut): + subtitle: str = None + orig_title: str = None + author: list[str] + translator: list[str] + language: str = None + pub_house: str = None + pub_year: int = None + pub_month: int = None + binding: str = None + price: str = None + pages: str = None + series: str = None + imprint: str = None + + +@api.post("/catalog/fetch", response=ItemOut) +def fetch_edition(request, url: str): + site = SiteManager.get_site_by_url(url) + if not site: + return Http404() + resource = site.get_resource_ready() + if not resource: + return Http404() + return site.get_item() + + +@api.post("/book/") +def create_edition(request, payload: EditionIn): + edition = Edition.objects.create(**payload.dict()) + return {"id": edition.uuid} + + +@api.get("/book/{uuid}/", response=EditionOut) +def get_edition(request, uuid: str): + edition = get_object_or_404(Edition, uid=base62.decode(uuid)) + return edition + + +# @api.get("/book", response=List[EditionOut]) +# def list_editions(request): +# qs = Edition.objects.all() +# return qs + + +@api.put("/book/{uuid}/") +def update_edition(request, uuid: str, payload: EditionIn): + edition = get_object_or_404(Item, uid=base62.decode(uuid)) + for attr, value in payload.dict().items(): + setattr(edition, attr, value) + edition.save() + return {"success": True} + + +@api.delete("/book/{uuid}/") +def delete_edition(request, uuid: str): + edition = get_object_or_404(Edition, uid=base62.decode(uuid)) + edition.delete() + return {"success": True} diff --git a/catalog/book/models.py b/catalog/book/models.py index 8cff2878..1e441fe9 100644 --- a/catalog/book/models.py +++ b/catalog/book/models.py @@ -57,16 +57,16 @@ class Edition(Item): orig_title = jsondata.CharField(null=True, blank=True, default=None) author = jsondata.ArrayField(_('作者'), null=False, blank=False, default=list) translator = jsondata.ArrayField(_('译者'), null=True, blank=True, default=list) - language = jsondata.ArrayField(_("语言"), null=True, blank=True, default=list) - pub_house = jsondata.ArrayField(_('出版方'), null=True, blank=True, default=list) + language = jsondata.CharField(_("语言"), null=True, blank=True, default=None) + pub_house = jsondata.CharField(_('出版方'), null=True, blank=True, default=None) pub_year = jsondata.IntegerField(_("发表年份"), null=True, blank=True) pub_month = jsondata.IntegerField(_("发表月份"), null=True, blank=True) binding = jsondata.CharField(null=True, blank=True, default=None) pages = jsondata.IntegerField(blank=True, default=None) series = jsondata.CharField(null=True, blank=True, default=None) contents = jsondata.CharField(null=True, blank=True, default=None) - price = jsondata.FloatField(_("发表月份"), null=True, blank=True) - imprint = jsondata.FloatField(_("发表月份"), null=True, blank=True) + price = jsondata.CharField(_("价格"), null=True, blank=True) + imprint = jsondata.CharField(_("发表月份"), null=True, blank=True) @property def isbn10(self): diff --git a/catalog/common/jsondata.py b/catalog/common/jsondata.py index 2cd1d398..f20188e3 100644 --- a/catalog/common/jsondata.py +++ b/catalog/common/jsondata.py @@ -123,6 +123,8 @@ class BooleanField(JSONFieldMixin, fields.BooleanField): class CharField(JSONFieldMixin, fields.CharField): + def from_json(self, value): # TODO workaound some bad data in migration, should be removed after clean up + return value if isinstance(value, str) else None pass diff --git a/catalog/common/models.py b/catalog/common/models.py index bbf765ae..aa2775cf 100644 --- a/catalog/common/models.py +++ b/catalog/common/models.py @@ -195,7 +195,7 @@ class Item(SoftDeleteMixin, PolymorphicModel): self.primary_lookup_id_type = None def __str__(self): - return f"{self.id}|{self.url_id}{' ' + self.primary_lookup_id_type + ':' + self.primary_lookup_id_value if self.primary_lookup_id_value else ''} ({self.title})" + return f"{self.id}|{self.uuid}{' ' + self.primary_lookup_id_type + ':' + self.primary_lookup_id_value if self.primary_lookup_id_value else ''} ({self.title})" @classmethod def get_best_lookup_id(cls, lookup_ids): @@ -222,12 +222,16 @@ class Item(SoftDeleteMixin, PolymorphicModel): self.merged_to_item = to_item @property - def url_id(self): + def uuid(self): return base62.encode(self.uid.int) @property def url(self): - return f'/{self.url_path}/{self.url_id}/' + return f'/{self.url_path}/{self.uuid}/' + + @property + def api_url(self): + return '/api' + self.url @property def class_name(self): diff --git a/catalog/templates/item_base.html b/catalog/templates/item_base.html index 89254e90..08b795c7 100644 --- a/catalog/templates/item_base.html +++ b/catalog/templates/item_base.html @@ -75,7 +75,7 @@ {% trans '评分:评分人数不足' %} {% endif %} -
uid: {{item.url_id}}
+
uuid: {{item.uuid}}
class: {{item.class_name}}
category: {{item.category}}
id type: {{item.primary_id_type}}
diff --git a/catalog/urls.py b/catalog/urls.py index 4f43cd6a..133905d1 100644 --- a/catalog/urls.py +++ b/catalog/urls.py @@ -15,7 +15,7 @@ def _get_all_url_paths(): urlpatterns = [ - re_path(r'item/(?P[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12})/', retrieve_by_uuid, name='retrieve_by_uuid'), - re_path(r'(?P' + _get_all_url_paths() + ')/(?P[A-Za-z0-9]{21,22})/', retrieve, name='retrieve'), + re_path(r'^item/(?P[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12})/', retrieve_by_uuid, name='retrieve_by_uuid'), + re_path(r'^(?P' + _get_all_url_paths() + ')/(?P[A-Za-z0-9]{21,22})/', retrieve, name='retrieve'), path("api/", api.urls), ]