From c238ed4cc4bb71d6c3017f06782b3c734cc09bbe Mon Sep 17 00:00:00 2001 From: Junwei Zhao Date: Fri, 21 Feb 2025 21:49:00 +1100 Subject: [PATCH] Update code to create posts --- data/db.sqlite3 | Bin 385024 -> 385024 bytes links/api_urls.py | 2 - links/serializers.py | 40 +++- openapi.yaml | 453 ------------------------------------------- static/openapi.yaml | 115 ++++++----- 5 files changed, 107 insertions(+), 503 deletions(-) delete mode 100644 openapi.yaml diff --git a/data/db.sqlite3 b/data/db.sqlite3 index 819d5883c08931e9a388549075723727127708b4..5762a6f0e4c273f13b7f78584c90bf1a7e4c7f49 100644 GIT binary patch delta 358 zcmZoTAl`65e1bHi*+dy*C)=(lU=GXgOa5HoMr&}X@`K!TP3 z76boJ{*U}G`5*D$;!EdK<$cIIlQ(#?p@AgtcGJ5oOPToDSXmet8M#~{nVCEpn1z}4Fikg1V3OM0AfUt~TFAx5pq}XHSdv;?qMKNdsaud=Trz!a3zM#) zsjh*Mu92aFp@Ef&nU$%ro~5CYk%6(1fe}_2WAp8Ml2|=>feyUQ!2gT?6aOpz$NabX z+W9>BxOh+Ub^;xk%e%d^mNkfppPhvf>^?RYMova9mg%qRS*3s)A2aa(=Ksq79_Y8n z{2%yt^WW!R#(!k9U_uxF^kx05wmhuNU~M3hW%~PmR#84y{-+H5fB0YXf8&43{{g7< YGXL}s^I2Kgfu3jPWMtV6F(+ph%-12io$x9Q&m z8-NG`0=xhN69T-mF)*3}7GMbn05>5SK|xb*ZDn+5X>MmAX>N44ykZ4N4HyFt>HrV= z5AqM{4+aj$4u=j&4hjv)4T-ZcFl`ODiG2l90+-pZ1r7l;m+F578VUms?*I?`5A_f4 zw-B%eiVu-c4wt5n1w9OKVQFLp2LlrUOiWFe?~er)2m=rB01x~R_O}p_1=SCefDjM2 I@Sg<`K+L03%K!iX diff --git a/links/api_urls.py b/links/api_urls.py index ff6ba7d..9558abf 100644 --- a/links/api_urls.py +++ b/links/api_urls.py @@ -7,8 +7,6 @@ from . import api_views # Create a router and register our viewsets with it router = DefaultRouter(trailing_slash=False) router.register('pages', page_views.PageViewSet, basename='api-pages') -# for API compatibility -router.register('newsletters', post_views.PostViewSet, basename='api-newsletters') router.register('posts', post_views.PostViewSet, basename='api-posts') router.register('music', api_views.MusicViewSet, basename='api-music') diff --git a/links/serializers.py b/links/serializers.py index fa8fadb..c464e3d 100644 --- a/links/serializers.py +++ b/links/serializers.py @@ -10,16 +10,52 @@ class PageSerializer(serializers.ModelSerializer): 'created_at', 'updated_at'] class PostSerializer(serializers.ModelSerializer): - tags = serializers.PrimaryKeyRelatedField(many=True, queryset=Tag.objects.all(), required=False) + tags = serializers.ListField(child=serializers.CharField(), required=False, write_only=True, help_text="List of tag slugs") + tag_details = serializers.SerializerMethodField(read_only=True) class Meta: model = Post - fields = ['id', 'title', 'content', 'tags', 'created_at', 'updated_at'] + fields = ['id', 'title', 'content', 'tags', 'tag_details', 'created_at', 'updated_at'] read_only_fields = ['id', 'created_at', 'updated_at'] extra_kwargs = { 'title': {'required': True}, 'content': {'required': True} } + + def get_tag_details(self, obj): + return [{'id': tag.id, 'name': tag.name, 'slug': tag.slug} for tag in obj.tags.all()] + + def create(self, validated_data): + tag_slugs = validated_data.pop('tags', []) + post = Post.objects.create(**validated_data) + + for tag_slug in tag_slugs: + try: + tag = Tag.objects.get(slug=tag_slug) + except Tag.DoesNotExist: + # If tag doesn't exist, create it with the slug as both name and slug + tag = Tag.objects.create(name=tag_slug, slug=tag_slug) + post.tags.add(tag) + + return post + + def update(self, instance, validated_data): + tag_slugs = validated_data.pop('tags', None) + for attr, value in validated_data.items(): + setattr(instance, attr, value) + + if tag_slugs is not None: + instance.tags.clear() + for tag_slug in tag_slugs: + try: + tag = Tag.objects.get(slug=tag_slug) + except Tag.DoesNotExist: + # If tag doesn't exist, create it with the slug as both name and slug + tag = Tag.objects.create(name=tag_slug, slug=tag_slug) + instance.tags.add(tag) + + instance.save() + return instance class ImageSerializer(serializers.ModelSerializer): url = serializers.SerializerMethodField() diff --git a/openapi.yaml b/openapi.yaml deleted file mode 100644 index bf2f800..0000000 --- a/openapi.yaml +++ /dev/null @@ -1,453 +0,0 @@ -openapi: 3.1.0 -info: - title: GoLinks API - description: API documentation for GoLinks service - version: 1.0.0 - -servers: - - url: http://localhost:8000 - description: Local development server - -paths: - /api/pages: - get: - summary: List all pages - description: Retrieve a paginated list of all pages - parameters: - - in: query - name: page - schema: - type: integer - description: Page number for pagination - - in: query - name: page_size - schema: - type: integer - description: Number of items per page - responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/PageList' - - post: - summary: Create a new page - description: Create a new page with the provided data - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PageCreate' - responses: - '201': - description: Page created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Page' - - /api/newsletters: - get: - summary: List all newsletters - description: Retrieve a paginated list of all newsletters - parameters: - - in: query - name: page - schema: - type: integer - description: Page number for pagination - - in: query - name: page_size - schema: - type: integer - description: Number of items per page - responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/NewsletterList' - - post: - summary: Create a new newsletter - description: Create a new newsletter with the provided data - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/NewsletterCreate' - responses: - '201': - description: Newsletter created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/Newsletter' - - /api/collections: - get: - summary: List all collections - description: Retrieve a paginated list of all image collections - parameters: - - in: query - name: page - schema: - type: integer - description: Page number for pagination - - in: query - name: page_size - schema: - type: integer - description: Number of items per page - responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/CollectionList' - - post: - summary: Create a new collection - description: Create a new image collection - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/CollectionCreate' - responses: - '201': - description: Collection created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/CollectionResponse' - - /api/collections/{collection_id}: - delete: - summary: Delete a collection - description: Delete a collection and all its images - parameters: - - in: path - name: collection_id - required: true - schema: - type: string - format: uuid - description: The ID of the collection to delete - responses: - '200': - description: Collection deleted successfully - content: - application/json: - schema: - $ref: '#/components/schemas/SuccessResponse' - - /api/collections/{collection_id}/upload_images: - post: - summary: Upload images to a collection - description: Upload one or more images to a collection with optional descriptions - parameters: - - in: path - name: collection_id - required: true - schema: - type: string - format: uuid - description: The ID of the collection to upload images to - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - properties: - file: - type: array - items: - type: string - format: binary - description: List of image files to upload. Only image/* content types are allowed. - descriptions: - type: array - items: - type: string - description: Optional list of descriptions for the uploaded images. Each description corresponds to the image at the same index. - required: - - file - responses: - '201': - description: Images uploaded successfully - content: - application/json: - schema: - type: object - properties: - status: - type: string - enum: [success] - example: success - message: - type: string - example: "Successfully uploaded 2 images" - data: - type: array - items: - $ref: '#/components/schemas/Image' - '400': - description: Bad request - invalid file or upload failed - content: - application/json: - schema: - type: object - properties: - status: - type: string - enum: [error] - example: error - message: - type: string - example: "Invalid file type. Only images are allowed." - - /api/images/{image_id}: - delete: - summary: Delete an image - description: Delete a specific image from a collection - parameters: - - in: path - name: image_id - required: true - schema: - type: string - format: uuid - description: The ID of the image to delete - responses: - '200': - description: Image deleted successfully - content: - application/json: - schema: - $ref: '#/components/schemas/SuccessResponse' - -components: - schemas: - Page: - type: object - properties: - id: - type: integer - url: - type: string - title: - type: string - summary: - type: string - content: - type: string - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - - PageCreate: - type: object - required: - - url - - title - properties: - url: - type: string - title: - type: string - summary: - type: string - content: - type: string - - PageList: - type: object - properties: - count: - type: integer - next: - type: string - nullable: true - previous: - type: string - nullable: true - results: - type: array - items: - $ref: '#/components/schemas/Page' - - Newsletter: - type: object - properties: - id: - type: integer - title: - type: string - type: - type: string - enum: [technology, world_news] - content: - type: string - created_at: - type: string - format: date-time - - NewsletterCreate: - type: object - required: - - title - - type - - content - properties: - title: - type: string - type: - type: string - enum: [technology, world_news] - content: - type: string - - NewsletterList: - type: object - properties: - count: - type: integer - next: - type: string - nullable: true - previous: - type: string - nullable: true - results: - type: array - items: - $ref: '#/components/schemas/Newsletter' - - Collection: - type: object - properties: - id: - type: string - format: uuid - name: - type: string - description: - type: string - image_count: - type: integer - created_at: - type: string - format: date-time - - CollectionCreate: - type: object - required: - - name - properties: - name: - type: string - description: - type: string - - CollectionList: - type: object - properties: - count: - type: integer - next: - type: string - nullable: true - previous: - type: string - nullable: true - results: - type: array - items: - $ref: '#/components/schemas/Collection' - - CollectionResponse: - type: object - properties: - status: - type: string - enum: [success] - message: - type: string - data: - $ref: '#/components/schemas/Collection' - - Image: - type: object - properties: - id: - type: string - format: uuid - collection: - type: string - format: uuid - title: - type: string - description: The original filename of the uploaded image - description: - type: string - nullable: true - description: Optional description for the image - file_key: - type: string - description: Internal storage key for the image - content_type: - type: string - description: MIME type of the image - example: "image/jpeg" - size: - type: integer - description: Size of the image in bytes - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time - - ImageUploadResponse: - type: object - properties: - status: - type: string - enum: [success] - message: - type: string - data: - type: array - items: - $ref: '#/components/schemas/Image' - - SuccessResponse: - type: object - properties: - status: - type: string - enum: [success] - message: - type: string - - ErrorResponse: - type: object - properties: - status: - type: string - enum: [error] - message: - type: string diff --git a/static/openapi.yaml b/static/openapi.yaml index 571b3d4..b61daf0 100644 --- a/static/openapi.yaml +++ b/static/openapi.yaml @@ -49,10 +49,10 @@ paths: schema: $ref: '#/components/schemas/Page' - /api/newsletters: + /api/posts: get: - summary: List all newsletters - description: Retrieve a paginated list of all newsletters + summary: List all posts + description: Retrieve a paginated list of all posts parameters: - in: query name: page @@ -70,24 +70,24 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/NewsletterList' + $ref: '#/components/schemas/PostList' post: - summary: Create a new newsletter - description: Create a new newsletter with the provided data + summary: Create a new post + description: Create a new post with the provided data requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/NewsletterCreate' + $ref: '#/components/schemas/PostCreate' responses: '201': - description: Newsletter created successfully + description: Post created successfully content: application/json: schema: - $ref: '#/components/schemas/Newsletter' + $ref: '#/components/schemas/Post' /api/collections: get: @@ -151,8 +151,8 @@ paths: /api/collections/{collection_id}/upload_images: post: - summary: Upload images to collection - description: Upload one or multiple images to a collection + summary: Upload images to a collection + description: Upload one or more images to a collection with optional descriptions parameters: - in: path name: collection_id @@ -160,7 +160,7 @@ paths: schema: type: string format: uuid - description: The ID of the collection + description: The ID of the collection to upload images to requestBody: required: true content: @@ -173,13 +173,33 @@ paths: items: type: string format: binary + description: List of image files to upload. Only image/* content types are allowed. + descriptions: + type: array + items: + type: string + description: Optional list of descriptions for the uploaded images. Each description corresponds to the image at the same index. + required: + - file responses: '201': description: Images uploaded successfully content: application/json: schema: - $ref: '#/components/schemas/ImageUploadResponse' + type: object + properties: + status: + type: string + enum: [success] + example: success + message: + type: string + example: "Successfully uploaded 2 images" + data: + type: array + items: + $ref: '#/components/schemas/Image' /api/images/{image_id}: delete: @@ -254,38 +274,43 @@ components: items: $ref: '#/components/schemas/Page' - Newsletter: + Post: type: object properties: id: type: integer title: type: string - type: - type: string - enum: [technology, world_news] content: type: string + tag_details: + type: array + items: + $ref: '#/components/schemas/Tag' created_at: type: string format: date-time + updated_at: + type: string + format: date-time - NewsletterCreate: + PostCreate: type: object required: - title - - type - content properties: title: type: string - type: - type: string - enum: [technology, world_news] content: type: string + tags: + type: array + items: + type: string + description: List of tag slugs to associate with the post - NewsletterList: + PostList: type: object properties: count: @@ -299,7 +324,19 @@ components: results: type: array items: - $ref: '#/components/schemas/Newsletter' + $ref: '#/components/schemas/Post' + + Tag: + type: object + properties: + id: + type: integer + name: + type: string + slug: + type: string + description: + type: string Collection: type: object @@ -360,30 +397,25 @@ components: id: type: string format: uuid + collection: + type: string + format: uuid title: type: string + description: + type: string content_type: type: string size: type: integer - url: - type: string created_at: type: string format: date-time - - ImageUploadResponse: - type: object - properties: - status: + updated_at: type: string - enum: [success] - message: + format: date-time + url: type: string - data: - type: array - items: - $ref: '#/components/schemas/Image' SuccessResponse: type: object @@ -393,12 +425,3 @@ components: enum: [success] message: type: string - - ErrorResponse: - type: object - properties: - status: - type: string - enum: [error] - message: - type: string