init
This commit is contained in:
31
backend/api/schemas/__init__.py
Normal file
31
backend/api/schemas/__init__.py
Normal file
@@ -0,0 +1,31 @@
|
||||
"""Aggregate exports for API schemas and shared response payloads."""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from ninja import Schema
|
||||
|
||||
from api.schemas.auth import *
|
||||
from api.schemas.blog import *
|
||||
from api.schemas.gallery import *
|
||||
from api.schemas.events import *
|
||||
from api.schemas.communications import *
|
||||
from api.schemas.certificates import *
|
||||
|
||||
|
||||
class MessageSchema(Schema):
|
||||
"""Basic success response containing a message."""
|
||||
message: str
|
||||
|
||||
|
||||
class ErrorSchema(Schema):
|
||||
"""Standard error payload with optional details."""
|
||||
error: str
|
||||
details: Optional[str] = None
|
||||
|
||||
|
||||
def rebuild_comment_schema() -> None:
|
||||
"""Ensure the self-referential CommentSchema is fully initialized."""
|
||||
CommentSchema.model_rebuild()
|
||||
|
||||
|
||||
rebuild_comment_schema()
|
||||
129
backend/api/schemas/auth.py
Normal file
129
backend/api/schemas/auth.py
Normal file
@@ -0,0 +1,129 @@
|
||||
"""Authentication-related API schemas."""
|
||||
|
||||
from ninja import Schema, ModelSchema
|
||||
from typing import Optional
|
||||
|
||||
from users.models import User
|
||||
|
||||
|
||||
class UserRegistrationSchema(Schema):
|
||||
username: str
|
||||
email: str
|
||||
password: str
|
||||
first_name: Optional[str] = None
|
||||
last_name: Optional[str] = None
|
||||
university: Optional[str] = None
|
||||
student_id: Optional[str] = None
|
||||
year_of_study: Optional[int] = None
|
||||
major: Optional[str] = None
|
||||
|
||||
class UserLoginSchema(Schema):
|
||||
email: str
|
||||
password: str
|
||||
|
||||
class UserProfileSchema(ModelSchema):
|
||||
profile_picture: Optional[str] = None
|
||||
student_id: Optional[str] = None
|
||||
major: Optional[str] = None
|
||||
university: Optional[str] = None
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = [
|
||||
'id',
|
||||
'username',
|
||||
'email',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'student_id',
|
||||
'year_of_study',
|
||||
'major',
|
||||
'university',
|
||||
'bio',
|
||||
'date_joined',
|
||||
'is_email_verified',
|
||||
'is_active',
|
||||
'is_staff',
|
||||
'is_superuser',
|
||||
'is_deleted',
|
||||
'deleted_at',
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def resolve_major(obj):
|
||||
return obj.get_major_display()
|
||||
|
||||
@staticmethod
|
||||
def resolve_university(obj):
|
||||
return obj.get_university_display()
|
||||
|
||||
@staticmethod
|
||||
def resolve_profile_picture(obj, context):
|
||||
"""
|
||||
Resolves the absolute URL for the profile picture.
|
||||
`context` contains the request object, which is needed for build_absolute_uri.
|
||||
"""
|
||||
request = context['request']
|
||||
if obj.profile_picture and hasattr(obj.profile_picture, 'url'):
|
||||
return request.build_absolute_uri(obj.profile_picture.url)
|
||||
return None
|
||||
|
||||
|
||||
class UserListSchema(ModelSchema):
|
||||
major: Optional[str] = None
|
||||
university: Optional[str] = None
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = [
|
||||
'id',
|
||||
'username',
|
||||
'email',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'is_active',
|
||||
'is_staff',
|
||||
'is_superuser',
|
||||
'date_joined',
|
||||
'major',
|
||||
'university',
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def resolve_full_name(obj):
|
||||
return obj.get_full_name()
|
||||
|
||||
@staticmethod
|
||||
def resolve_major(obj):
|
||||
return obj.get_major_display()
|
||||
|
||||
@staticmethod
|
||||
def resolve_university(obj):
|
||||
return obj.get_university_display()
|
||||
|
||||
class UserUpdateSchema(Schema):
|
||||
first_name: Optional[str] = None
|
||||
last_name: Optional[str] = None
|
||||
bio: Optional[str] = None
|
||||
year_of_study: Optional[int] = None
|
||||
major: Optional[str] = None
|
||||
university: Optional[str] = None
|
||||
student_id: Optional[str] = None
|
||||
|
||||
class TokenSchema(Schema):
|
||||
access_token: str
|
||||
refresh_token: str
|
||||
token_type: str = "bearer"
|
||||
|
||||
class TokenRefreshIn(Schema):
|
||||
refresh_token: str
|
||||
|
||||
class PasswordResetRequestSchema(Schema):
|
||||
email: str
|
||||
|
||||
class PasswordResetConfirmSchema(Schema):
|
||||
token: str
|
||||
new_password: str
|
||||
|
||||
class UsernameCheckSchema(Schema):
|
||||
exists: bool
|
||||
87
backend/api/schemas/blog.py
Normal file
87
backend/api/schemas/blog.py
Normal file
@@ -0,0 +1,87 @@
|
||||
"""Blog API schemas."""
|
||||
|
||||
from ninja import Schema, ModelSchema
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
|
||||
from blog.models import Category, Tag, Comment
|
||||
|
||||
|
||||
class CategorySchema(ModelSchema):
|
||||
class Config:
|
||||
model = Category
|
||||
model_fields = ['id', 'name', 'slug', 'description']
|
||||
|
||||
class TagSchema(ModelSchema):
|
||||
class Config:
|
||||
model = Tag
|
||||
model_fields = ['id', 'name', 'slug']
|
||||
|
||||
class AuthorSchema(Schema):
|
||||
id: int
|
||||
username: str
|
||||
first_name: str
|
||||
last_name: str
|
||||
profile_picture: Optional[str] = None
|
||||
|
||||
@staticmethod
|
||||
def resolve_profile_picture(obj, context):
|
||||
request = context['request']
|
||||
if obj.profile_picture and hasattr(obj.profile_picture, 'url'):
|
||||
return request.build_absolute_uri(obj.profile_picture.url)
|
||||
return None
|
||||
|
||||
class PostListSchema(Schema):
|
||||
id: int
|
||||
title: str
|
||||
slug: str
|
||||
excerpt: str
|
||||
author: AuthorSchema
|
||||
featured_image: Optional[str] = None
|
||||
status: str
|
||||
published_at: Optional[datetime] = None
|
||||
category: Optional[CategorySchema] = None
|
||||
tags: List[TagSchema]
|
||||
is_featured: bool
|
||||
created_at: datetime
|
||||
reading_time: int
|
||||
|
||||
class PostDetailSchema(PostListSchema):
|
||||
content: str
|
||||
content_html: str
|
||||
|
||||
class PostCreateSchema(Schema):
|
||||
title: str
|
||||
content: str
|
||||
excerpt: Optional[str] = None
|
||||
category_id: Optional[int] = None
|
||||
tag_ids: Optional[List[int]] = []
|
||||
status: str = "draft"
|
||||
is_featured: bool = False
|
||||
|
||||
class CommentSchema(ModelSchema):
|
||||
author: AuthorSchema
|
||||
replies: List['CommentSchema'] = []
|
||||
post_id: int
|
||||
post_title: str
|
||||
post_slug: str
|
||||
|
||||
class Config:
|
||||
model = Comment
|
||||
model_fields = ['id', 'content', 'created_at', 'is_approved']
|
||||
|
||||
@staticmethod
|
||||
def resolve_post_id(obj):
|
||||
return obj.post_id
|
||||
|
||||
@staticmethod
|
||||
def resolve_post_title(obj):
|
||||
return obj.post.title
|
||||
|
||||
@staticmethod
|
||||
def resolve_post_slug(obj):
|
||||
return obj.post.slug
|
||||
|
||||
class CommentCreateSchema(Schema):
|
||||
content: str
|
||||
parent_id: Optional[int] = None
|
||||
70
backend/api/schemas/certificates.py
Normal file
70
backend/api/schemas/certificates.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""API payloads for certificate operations."""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from ninja import Schema
|
||||
|
||||
|
||||
class SkillSchema(Schema):
|
||||
id: int
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
|
||||
|
||||
class CertificateTemplateOut(Schema):
|
||||
id: int
|
||||
event_id: int
|
||||
event_title: str
|
||||
image_url: Optional[str]
|
||||
skill_ids: List[int]
|
||||
skills: List[SkillSchema]
|
||||
|
||||
|
||||
class CertificateGenerationItem(Schema):
|
||||
user_id: int
|
||||
score: int
|
||||
title: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
skill_ids: Optional[List[int]] = None
|
||||
issued_at: Optional[datetime] = None
|
||||
expires_at: Optional[datetime] = None
|
||||
|
||||
|
||||
class CertificateGenerationPayload(Schema):
|
||||
entries: List[CertificateGenerationItem]
|
||||
default_title: Optional[str] = None
|
||||
default_description: Optional[str] = None
|
||||
|
||||
|
||||
class UserCertificateOut(Schema):
|
||||
id: int
|
||||
user_id: int
|
||||
user_name: str
|
||||
event_id: int
|
||||
title: str
|
||||
certificate_id: str
|
||||
certificate_code: str
|
||||
score: int
|
||||
score_label: str
|
||||
image_url: Optional[str]
|
||||
|
||||
|
||||
class CertificateGenerationResponse(Schema):
|
||||
certificates: List[UserCertificateOut]
|
||||
|
||||
|
||||
class CertificateVerificationOut(Schema):
|
||||
certificate_id: str
|
||||
certificate_code: str
|
||||
user_id: int
|
||||
user_name: str
|
||||
event_id: int
|
||||
event_title: str
|
||||
title: str
|
||||
score: int
|
||||
score_label: str
|
||||
issued_at: datetime
|
||||
expires_at: Optional[datetime] = None
|
||||
image_url: Optional[str] = None
|
||||
skills: List[str]
|
||||
124
backend/api/schemas/communications.py
Normal file
124
backend/api/schemas/communications.py
Normal file
@@ -0,0 +1,124 @@
|
||||
"""Schemas for communications-related endpoints."""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional, List
|
||||
|
||||
from ninja import Schema, ModelSchema
|
||||
|
||||
from api.schemas import AuthorSchema
|
||||
from communications.models import (
|
||||
Announcement,
|
||||
NewsletterSubscription,
|
||||
PushNotificationDevice
|
||||
)
|
||||
|
||||
|
||||
class AnnouncementSchema(ModelSchema):
|
||||
author: AuthorSchema
|
||||
content_html: str
|
||||
|
||||
class Config:
|
||||
model = Announcement
|
||||
model_fields = [
|
||||
'id', 'title', 'content', 'announcement_type', 'priority',
|
||||
'is_published', 'publish_date', 'send_email', 'send_push',
|
||||
'target_audience', 'email_sent', 'push_sent', 'created_at', 'updated_at'
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def resolve_content_html(obj):
|
||||
return obj.content_html
|
||||
|
||||
class AnnouncementListSchema(Schema):
|
||||
id: int
|
||||
title: str
|
||||
content: str
|
||||
announcement_type: str
|
||||
priority: str
|
||||
author: AuthorSchema
|
||||
is_published: bool
|
||||
publish_date: Optional[datetime] = None
|
||||
target_audience: str
|
||||
created_at: datetime
|
||||
|
||||
class AnnouncementCreateSchema(Schema):
|
||||
title: str
|
||||
content: str
|
||||
announcement_type: str = "general"
|
||||
priority: str = "normal"
|
||||
target_audience: str = "all"
|
||||
is_published: bool = False
|
||||
publish_date: Optional[datetime] = None
|
||||
send_email: bool = False
|
||||
send_push: bool = False
|
||||
|
||||
class AnnouncementUpdateSchema(Schema):
|
||||
title: Optional[str] = None
|
||||
content: Optional[str] = None
|
||||
announcement_type: Optional[str] = None
|
||||
priority: Optional[str] = None
|
||||
target_audience: Optional[str] = None
|
||||
is_published: Optional[bool] = None
|
||||
publish_date: Optional[datetime] = None
|
||||
send_email: Optional[bool] = None
|
||||
send_push: Optional[bool] = None
|
||||
|
||||
class NewsletterSubscriptionSchema(ModelSchema):
|
||||
user: Optional[AuthorSchema] = None
|
||||
|
||||
class Config:
|
||||
model = NewsletterSubscription
|
||||
model_fields = [
|
||||
'id', 'email', 'is_active', 'subscribed_categories',
|
||||
'confirmed_at', 'created_at'
|
||||
]
|
||||
|
||||
class NewsletterSubscribeSchema(Schema):
|
||||
email: str
|
||||
subscribed_categories: Optional[List[str]] = []
|
||||
|
||||
class NewsletterUnsubscribeSchema(Schema):
|
||||
email: str
|
||||
|
||||
class PushDeviceSchema(ModelSchema):
|
||||
user: AuthorSchema
|
||||
|
||||
class Config:
|
||||
model = PushNotificationDevice
|
||||
model_fields = [
|
||||
'id', 'device_token', 'device_type', 'is_active', 'created_at'
|
||||
]
|
||||
|
||||
class PushDeviceCreateSchema(Schema):
|
||||
device_token: str
|
||||
device_type: str = "web"
|
||||
|
||||
class PushDeviceUpdateSchema(Schema):
|
||||
is_active: bool
|
||||
|
||||
class PushNotificationSchema(Schema):
|
||||
title: str
|
||||
body: str
|
||||
data: Optional[dict] = None
|
||||
target_audience: str = "all"
|
||||
|
||||
class MessageResponseSchema(Schema):
|
||||
"""Simple message payload for API responses."""
|
||||
message: str
|
||||
success: bool = True
|
||||
|
||||
class AnnouncementStatsSchema(Schema):
|
||||
"""Summary statistics for announcements."""
|
||||
total_announcements: int
|
||||
published_announcements: int
|
||||
draft_announcements: int
|
||||
urgent_announcements: int
|
||||
email_sent_count: int
|
||||
push_sent_count: int
|
||||
|
||||
class NewsletterStatsSchema(Schema):
|
||||
"""Summary statistics for newsletter subscriptions."""
|
||||
total_subscriptions: int
|
||||
active_subscriptions: int
|
||||
confirmed_subscriptions: int
|
||||
recent_subscriptions: int
|
||||
247
backend/api/schemas/events.py
Normal file
247
backend/api/schemas/events.py
Normal file
@@ -0,0 +1,247 @@
|
||||
"""Event and gallery API schemas."""
|
||||
|
||||
from uuid import UUID
|
||||
from ninja import ModelSchema, Schema
|
||||
from pydantic import field_validator
|
||||
from typing import Literal, Optional, List
|
||||
from datetime import datetime
|
||||
|
||||
from api.schemas.blog import AuthorSchema
|
||||
from events.models import Event, Registration
|
||||
from gallery.models import Gallery
|
||||
from payments.models import Payment
|
||||
|
||||
|
||||
class EventGallerySchema(ModelSchema):
|
||||
"""Schema representing gallery items associated with an event."""
|
||||
uploaded_by: AuthorSchema
|
||||
file_size_mb: float
|
||||
markdown_url: str
|
||||
absolute_image_url: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
model = Gallery
|
||||
model_fields = ['id', 'title', 'description', 'image', 'alt_text',
|
||||
'width', 'height', 'is_public', 'created_at']
|
||||
|
||||
@staticmethod
|
||||
def resolve_absolute_image_url(obj, context):
|
||||
request = context['request']
|
||||
if obj.image and hasattr(obj.image, 'url'):
|
||||
return request.build_absolute_uri(obj.image.url)
|
||||
return None
|
||||
|
||||
class EventSchema(ModelSchema):
|
||||
"""Schema providing full event details for API responses."""
|
||||
gallery_images: List[EventGallerySchema]
|
||||
description_html: str
|
||||
registration_count: int
|
||||
absolute_featured_image_url: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
model = Event
|
||||
model_fields = [
|
||||
'id', 'title', 'slug', 'description', 'featured_image', 'event_type',
|
||||
'address', 'location', 'online_link', 'start_time', 'end_time',
|
||||
'registration_start_date', 'registration_end_date', 'registration_success_markdown',
|
||||
'capacity', 'price', 'status', 'created_at', 'updated_at'
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def resolve_absolute_featured_image_url(obj, context):
|
||||
request = context['request']
|
||||
if obj.featured_image and hasattr(obj.featured_image, 'url'):
|
||||
return request.build_absolute_uri(obj.featured_image.url)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def resolve_registration_count(obj):
|
||||
return obj.registrations.filter(status__in=[Registration.StatusChoices.CONFIRMED, Registration.StatusChoices.ATTENDED]).count()
|
||||
|
||||
@staticmethod
|
||||
def resolve_description_html(obj):
|
||||
return obj.description_html
|
||||
|
||||
|
||||
class EventListSchema(Schema):
|
||||
"""Condensed event representation for list endpoints."""
|
||||
id: int
|
||||
title: str
|
||||
slug: str
|
||||
featured_image: Optional[str] = None
|
||||
absolute_featured_image_url: Optional[str] = None
|
||||
event_type: str
|
||||
start_time: datetime
|
||||
end_time: datetime
|
||||
registration_start_date: Optional[datetime] = None
|
||||
registration_end_date: Optional[datetime] = None
|
||||
capacity: Optional[int] = None
|
||||
price: Optional[float] = None
|
||||
status: str
|
||||
registration_count: int
|
||||
created_at: datetime
|
||||
|
||||
@staticmethod
|
||||
def resolve_absolute_featured_image_url(obj, context):
|
||||
request = context['request']
|
||||
if obj.featured_image and hasattr(obj.featured_image, 'url'):
|
||||
return request.build_absolute_uri(obj.featured_image.url)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def resolve_registration_count(obj):
|
||||
return obj.registrations.filter(status__in=[Registration.StatusChoices.CONFIRMED, Registration.StatusChoices.ATTENDED]).count()
|
||||
|
||||
class EventCreateSchema(Schema):
|
||||
"""Payload for creating events via the API."""
|
||||
title: str
|
||||
description: str
|
||||
event_type: str
|
||||
address: Optional[str] = None
|
||||
location: Optional[str] = None
|
||||
online_link: Optional[str] = None
|
||||
start_time: datetime
|
||||
end_time: datetime
|
||||
registration_start_date: Optional[datetime] = None
|
||||
registration_end_date: Optional[datetime] = None
|
||||
capacity: Optional[int] = None
|
||||
price: Optional[float] = None
|
||||
status: str = "draft"
|
||||
gallery_image_ids: Optional[List[int]] = []
|
||||
|
||||
class EventUpdateSchema(Schema):
|
||||
"""Payload for updating events via the API."""
|
||||
title: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
event_type: Optional[str] = None
|
||||
address: Optional[str] = None
|
||||
location: Optional[str] = None
|
||||
online_link: Optional[str] = None
|
||||
start_time: Optional[datetime] = None
|
||||
end_time: Optional[datetime] = None
|
||||
registration_start_date: Optional[datetime] = None
|
||||
registration_end_date: Optional[datetime] = None
|
||||
capacity: Optional[int] = None
|
||||
price: Optional[float] = None
|
||||
status: Optional[str] = None
|
||||
gallery_image_ids: Optional[List[int]] = None
|
||||
|
||||
class RegistrationSchema(ModelSchema):
|
||||
"""Schema describing a registration entry with event context."""
|
||||
user: AuthorSchema
|
||||
event: EventListSchema
|
||||
discount_code: str | None = None
|
||||
|
||||
class Config:
|
||||
model = Registration
|
||||
model_fields = [
|
||||
'id',
|
||||
'status',
|
||||
'registered_at',
|
||||
'ticket_id',
|
||||
'discount_amount',
|
||||
'final_price',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def resolve_discount_code(obj):
|
||||
return obj.discount_code.code if obj.discount_code else None
|
||||
|
||||
|
||||
class AdminUserSchema(Schema):
|
||||
id: int
|
||||
username: str
|
||||
first_name: str
|
||||
last_name: str
|
||||
email: str
|
||||
|
||||
|
||||
class PaymentAdminSchema(Schema):
|
||||
id: int
|
||||
authority: Optional[str]
|
||||
ref_id: Optional[str]
|
||||
status: int
|
||||
status_label: str
|
||||
base_amount: int
|
||||
discount_amount: int
|
||||
amount: int
|
||||
verified_at: Optional[datetime]
|
||||
created_at: datetime
|
||||
discount_code: Optional[str]
|
||||
user: AdminUserSchema
|
||||
|
||||
@field_validator("discount_code", mode="before")
|
||||
def normalize_discount_code(cls, value):
|
||||
if value is None:
|
||||
return None
|
||||
if hasattr(value, "code"):
|
||||
return value.code
|
||||
return str(value)
|
||||
|
||||
|
||||
class RegistrationAdminSchema(Schema):
|
||||
id: int
|
||||
ticket_id: UUID
|
||||
status: str
|
||||
status_label: str
|
||||
registered_at: datetime
|
||||
final_price: Optional[int]
|
||||
discount_amount: Optional[int]
|
||||
user: AdminUserSchema
|
||||
payments: List[PaymentAdminSchema]
|
||||
|
||||
|
||||
class EventAdminDetailSchema(EventSchema):
|
||||
registrations: List[RegistrationAdminSchema] = []
|
||||
|
||||
@staticmethod
|
||||
def resolve_registrations(obj):
|
||||
return obj.registrations.select_related("user").prefetch_related(
|
||||
"payments__discount_code"
|
||||
).order_by("-registered_at")
|
||||
|
||||
class PaginatedRegistrationSchema(Schema):
|
||||
count: int
|
||||
next: Optional[str] = None
|
||||
previous: Optional[str] = None
|
||||
results: List[RegistrationAdminSchema]
|
||||
|
||||
class RegistrationStatusUpdateSchema(Schema):
|
||||
status: str
|
||||
|
||||
class RegisterationDetailSchema(Schema):
|
||||
"""Detailed registration information with associated event metadata."""
|
||||
event_image: Optional[str]
|
||||
event_title: str
|
||||
event_type: str
|
||||
ticket_id: UUID
|
||||
status: str
|
||||
registered_at: datetime
|
||||
success_markdown: Optional[str]
|
||||
|
||||
class EventBriefSchema(Schema):
|
||||
"""Minimal event representation used for nested responses."""
|
||||
id: int
|
||||
title: str
|
||||
slug: str
|
||||
start_date: datetime
|
||||
end_date: Optional[datetime] = None
|
||||
location: Optional[str] = None
|
||||
price: int
|
||||
absolute_image_url: Optional[str] = None
|
||||
|
||||
class MyEventRegistrationOut(Schema):
|
||||
"""Registration information as returned to authenticated users."""
|
||||
id: int
|
||||
created_at: datetime
|
||||
status: Literal["pending", "confirmed", "cancelled", "attended"]
|
||||
event: EventBriefSchema
|
||||
|
||||
class RegistrationStatusOut(Schema):
|
||||
is_registered: bool
|
||||
|
||||
|
||||
class RegistrationCreateSchema(Schema):
|
||||
discount_code: Optional[str] = None
|
||||
27
backend/api/schemas/gallery.py
Normal file
27
backend/api/schemas/gallery.py
Normal file
@@ -0,0 +1,27 @@
|
||||
"""Schemas for gallery resources."""
|
||||
|
||||
from ninja import Schema, ModelSchema
|
||||
from typing import Optional
|
||||
|
||||
from api.schemas.blog import AuthorSchema
|
||||
from gallery.models import Gallery
|
||||
|
||||
|
||||
class GallerySchema(ModelSchema):
|
||||
"""Serialized representation of a gallery image."""
|
||||
uploaded_by: AuthorSchema
|
||||
file_size_mb: float
|
||||
markdown_url: str
|
||||
|
||||
class Config:
|
||||
model = Gallery
|
||||
model_fields = ['id', 'title', 'description', 'image', 'alt_text',
|
||||
'width', 'height', 'is_public', 'created_at']
|
||||
|
||||
|
||||
class GalleryCreateSchema(Schema):
|
||||
"""Payload for creating a gallery entry."""
|
||||
title: str
|
||||
description: Optional[str] = None
|
||||
alt_text: Optional[str] = None
|
||||
is_public: bool = True
|
||||
35
backend/api/schemas/payments.py
Normal file
35
backend/api/schemas/payments.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from ninja import Schema
|
||||
|
||||
|
||||
class CreatePaymentIn(Schema):
|
||||
event_id: int
|
||||
description: str
|
||||
discount_code: str | None = None
|
||||
mobile: str | None = None
|
||||
email: str | None = None
|
||||
|
||||
|
||||
class CreatePaymentOut(Schema):
|
||||
start_pay_url: str | None = None
|
||||
authority: str | None = None
|
||||
base_amount: int
|
||||
discount_amount: int
|
||||
amount: int
|
||||
|
||||
class PaymentDetailOut(Schema):
|
||||
ref_id: str | None = None
|
||||
authority: str | None = None
|
||||
base_amount: int
|
||||
discount_amount: int
|
||||
amount: int
|
||||
status: str
|
||||
verified_at: str | None = None
|
||||
event: dict
|
||||
|
||||
class CouponVerifyIn(Schema):
|
||||
event_id: int
|
||||
code: str
|
||||
|
||||
class CouponVerifyOut(Schema):
|
||||
discount_amount: int
|
||||
final_price: int
|
||||
Reference in New Issue
Block a user