PYRSS-Website/apps/api/serializers.py
Corban-Lee Jones 90b3dde68d saved guilds api
- active flag
- allow PUT method
- typo fix
- remove settings from savedguild serialiser
2024-08-16 18:44:52 +01:00

217 lines
7.1 KiB
Python

# -*- encoding: utf-8 -*-
import logging
from urllib.parse import unquote
from rest_framework import serializers
from apps.home.models import SubChannel, Filter, Subscription, SavedGuilds, TrackedContent, ArticleMutator, GuildSettings
log = logging.getLogger(__name__)
# This DynamicModelSerializer is from a StackOverflow user in an obscure thread.
# I wish that I could remember which thread, because god bless that man.
class DynamicModelSerializer(serializers.ModelSerializer):
"""
For use with GET requests, to specify which fields to include or exclude
Mimics some graphql functionality.
Usage: Inherit your ModelSerializer with this class. Add "only_fields" or
"exclude_fields" to the query parameters of your GET request.
This also works with nested foreign keys, for example:
?only_fields=name,age&company__only_fields=id,name
Some more examples:
?only_fields=company,name&company__exclude_fields=name
?exclude_fields=name&company__only_fields=id
?company__exclude_fields=name
Note: the Foreign Key serializer must also inherit from this class
"""
def only_keep_fields(self, fields_to_keep):
fields_to_keep = set(fields_to_keep.split(","))
all_fields = set(self.fields.keys())
for field in all_fields - fields_to_keep:
self.fields.pop(field, None)
def exclude_fields(self, fields_to_exclude):
fields_to_exclude = fields_to_exclude.split(",")
for field in fields_to_exclude:
self.fields.pop(field, None)
def remove_unwanted_fields(self, dynamic_params):
if fields_to_keep := dynamic_params.pop("only_fields", None):
self.only_keep_fields(fields_to_keep)
if fields_to_exclude := dynamic_params.pop("exclude_fields", None):
self.exclude_fields(fields_to_exclude)
def get_or_create_dynamic_params(self, child):
if "dynamic_params" not in self.fields[child]._context:
self.fields[child]._context.update({"dynamic_params": {}})
return self.fields[child]._context["dynamic_params"]
@staticmethod
def split_param(dynamic_param):
crumbs = dynamic_param.split("__")
return crumbs[0], "__".join(crumbs[1:]) if len(crumbs) > 1 else None
def set_dynamic_params_for_children(self, dynamic_params):
for param, fields in dynamic_params.items():
child, child_dynamic_param = self.split_param(param)
if child in set(self.fields.keys()):
dynamic_params = self.get_or_create_dynamic_params(child)
dynamic_params.update({child_dynamic_param: fields})
@staticmethod
def is_param_dynamic(p):
return p.endswith("only_fields") or p.endswith("exclude_fields")
def get_dynamic_params_for_root(self, request):
query_params = request.query_params.items()
return {k: v for k, v in query_params if self.is_param_dynamic(k)}
def get_dynamic_params(self):
"""
When dynamic params get passed down in set_context_for_children
If the child is a subclass of ListSerializer (has many=True)
The context must be fetched from ListSerializer Class
"""
if isinstance(self.parent, serializers.ListSerializer):
return self.parent._context.get("dynamic_params", {})
return self._context.get("dynamic_params", {})
def __init__(self, *args, **kwargs):
request = kwargs.get("context", {}).get("request")
super().__init__(*args, **kwargs)
is_root = bool(request)
if is_root:
if request.method != "GET":
return
dynamic_params = self.get_dynamic_params_for_root(request)
self._context.update({"dynamic_params": dynamic_params})
def to_representation(self, *args, **kwargs):
if dynamic_params := self.get_dynamic_params().copy():
self.remove_unwanted_fields(dynamic_params)
self.set_dynamic_params_for_children(dynamic_params)
return super().to_representation(*args, **kwargs)
class Meta:
abstract = True
class SubChannelSerializer(DynamicModelSerializer):
"""
Serializer for SubChannel Model.
"""
class Meta:
model = SubChannel
fields = ("id", "channel_id", "channel_name", "subscription")
class FilterSerializer(DynamicModelSerializer):
"""
Serializer for the Filter Model.
"""
class Meta:
model = Filter
fields = ("id", "name", "matching_algorithm", "match", "is_insensitive", "is_whitelist", "guild_id")
class ArticleMutatorSerializer(DynamicModelSerializer):
class Meta:
model = ArticleMutator
fields = ("id", "name", "value")
class SubscriptionSerializer_GET(DynamicModelSerializer):
"""
Serializer for the Subscription Model.
"""
article_title_mutators = ArticleMutatorSerializer(many=True)
article_desc_mutators = ArticleMutatorSerializer(many=True)
active = serializers.BooleanField(initial=True)
class Meta:
model = Subscription
fields = (
"id", "name", "url", "guild_id", "channels_count", "creation_datetime", "extra_notes", "filters",
"article_title_mutators", "article_desc_mutators", "article_fetch_image", "published_threshold", "embed_colour", "active"
)
class SubscriptionSerializer_POST(DynamicModelSerializer):
"""
Serializer for the Subscription Model.
"""
class Meta:
model = Subscription
fields = (
"id", "name", "url", "guild_id", "channels_count", "creation_datetime", "extra_notes", "filters",
"article_title_mutators", "article_desc_mutators", "article_fetch_image", "published_threshold", "embed_colour", "active"
)
class SavedGuildSerializer(DynamicModelSerializer):
"""
Serializer for the SavedGuild model.
"""
class Meta:
model = SavedGuilds
fields = ("id", "guild_id", "name", "icon", "added_by", "permissions", "owner")
class GuildSettingsSerializer(DynamicModelSerializer):
"""
Serializer for the GuildSettings model.
"""
class Meta:
model = GuildSettings
fields = ("id", "guild_id", "default_embed_colour", "active")
class TrackedContentSerializer_GET(DynamicModelSerializer):
"""
Serializer for the TrackedContent model.
"""
subscription = SubscriptionSerializer_GET()
class Meta:
model = TrackedContent
fields = ("id", "guid", "title", "url", "subscription", "channel_id", "message_id", "blocked", "creation_datetime")
# def to_representation(self, instance):
# representation = super().to_representation(instance)
# log.info(representation.get("guid", "nothing"))
# if 'guid' in representation:
# representation['guid'] = unquote(representation['guid'])
# log.info(representation.get("guid", "nothing"))
# return representation
class TrackedContentSerializer_POST(DynamicModelSerializer):
"""
Serializer for the TrackedContent model.
"""
class Meta:
model = TrackedContent
fields = ("id", "guid", "title", "url", "subscription", "channel_id", "message_id", "blocked", "creation_datetime")