Initial Commit

This commit is contained in:
2024-05-20 19:10:57 +02:00
commit fc8d2bb57b
7 changed files with 425 additions and 0 deletions

228
.gitignore vendored Normal file
View File

@@ -0,0 +1,228 @@
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Linux template
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/

14
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,14 @@
image: python:3.8
stages:
- publish
pack:
stage: publish
script:
- pip install twine
- python setup.py sdist bdist_wheel
- python -m twine upload -u gitlab-ci-token -p $CI_JOB_TOKEN --repository-url $CI_API_V4_URL/projects/$CI_PROJECT_ID/packages/pypi dist/*
only:
- tags

26
README.md Normal file
View File

@@ -0,0 +1,26 @@
# Enhanced Django Rest Framework JSON Api
This is a library aimed to fix some classes in Django Rest Framework and Django Rest Framework JSON Api libraries.
## Contents
### enhanced_drf_jsonapi module
#### API
- ##### class PreloadIncludesMixin
Overwrites the method get_queryset(self, *args, **kwarg)
- ##### class ReasonableModelViewSet
Overwrites the attribute http_method_names
- ##### class ReasonableModelSerializer
Overwrites the method get_field_names(self, declared_fields, info)
#### PAGINATION
- ##### class NgxJsonApiPageNumberPagination
Overwrites the method get_paginated_response(self, data)
## Build the library
In root directory, run `python setup.py bdist_wheel`. This will create a wheel file in `dist` folder.
## Install
Run this command in the desired python environment `pip install path/to/wheelfile.whl`.

View File

View File

@@ -0,0 +1,88 @@
from rest_framework import viewsets
from rest_framework_json_api import serializers
from rest_framework_json_api.utils import get_included_resources
from rest_framework_json_api.views import AutoPrefetchMixin, RelatedMixin
basic_filter = ('exact', 'isnull')
text_filter = ('exact', 'contains', 'iexact', 'icontains', 'startswith', 'istartswith', 'endswith', 'iendswith')
date_filter = ('exact', 'gte', 'lte')
int_filter = ('exact', 'gte', 'lte')
class PreloadIncludesMixin(object):
"""
This mixin provides a helper attributes to select or prefetch related models
based on the include specified in the URL.
__all__ can be used to specify a prefetch which should be done regardless of the include
.. code:: python
# When MyViewSet is called with ?include=author it will prefetch author and authorbio
class MyViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
prefetch_for_includes = {
'__all__': [],
'category.section': ['category']
}
select_for_includes = {
'__all__': [],
'author': ['author', 'author__authorbio'],
}
"""
def get_select_related(self, include):
return getattr(self, 'select_for_includes', {}).get(include, None)
def get_prefetch_related(self, include):
return getattr(self, 'prefetch_for_includes', {}).get(include, None)
def get_queryset(self, *args, **kwargs):
qs = super(PreloadIncludesMixin, self).get_queryset(*args, **kwargs)
included_resources = get_included_resources(self.request)
for included in included_resources + ['__all__']:
select_related = self.get_select_related(included)
if select_related is not None:
qs = qs.select_related(*select_related)
prefetch_related = self.get_prefetch_related(included)
if prefetch_related is not None:
if not isinstance(prefetch_related, list):
qs = qs.prefetch_related(*prefetch_related(self))
else:
qs = qs.prefetch_related(*prefetch_related)
return qs
class ReasonableModelViewSet(AutoPrefetchMixin,
PreloadIncludesMixin,
RelatedMixin,
viewsets.ModelViewSet):
http_method_names = ['get', 'post', 'patch', 'delete', 'head', 'options']
class ReasonableModelSerializer(serializers.ModelSerializer):
"""
Reusable class to extend DJA ModelSerializer and make it more JSONAPI compatible
"""
def get_field_names(self, declared_fields, info):
method = self.context['request'].method
included_rel_fields = []
if method == 'GET':
included_rel_fields = [qp for qp in (
self.context['request'].query_params['include'].split(',')
if 'include' in self.context['request'].query_params else []
) if qp in (self.included_serializers if hasattr(self, 'included_serializers') else [])]
elif method == 'POST' or method == 'PATCH':
included_rel_fields = [irel for irel in (
self.context['request'].data.keys()
) if irel in (self.included_serializers if hasattr(self, 'included_serializers') else [])]
fields = super().get_field_names(declared_fields, info)
return fields + included_rel_fields

View File

@@ -0,0 +1,37 @@
from collections import OrderedDict
from rest_framework.response import Response
from rest_framework_json_api.pagination import JsonApiPageNumberPagination
class NgxJsonApiPageNumberPagination(JsonApiPageNumberPagination):
def get_paginated_response(self, data):
next = None
previous = None
if self.page.has_next():
next = self.page.next_page_number()
if self.page.has_previous():
previous = self.page.previous_page_number()
return Response(
{
"results": data,
"meta": OrderedDict(
[
("page", self.page.number),
("pages", self.page.paginator.num_pages),
("total_resources", self.page.paginator.count),
("resources_per_page", self.page.paginator.per_page),
]
),
"links": OrderedDict(
[
("first", self.build_link(1)),
("last", self.build_link(self.page.paginator.num_pages)),
("next", self.build_link(next)),
("prev", self.build_link(previous)),
]
),
}
)

32
setup.py Normal file
View File

@@ -0,0 +1,32 @@
from setuptools import setup, find_packages
import pathlib
HERE = pathlib.Path(__file__).parent
VERSION = '1.0.4'
PACKAGE_NAME = 'enhanced_drf_jsonapi'
LICENSE = 'MIT'
DESCRIPTION = 'Patch library for django rest framework json api'
LONG_DESCRIPTION = (HERE / "README.md").read_text(encoding='utf-8')
LONG_DESC_TYPE = "text/markdown"
INSTALL_REQUIRES = [
'djangorestframework-jsonapi~=6.0.0'
]
setup(
name=PACKAGE_NAME,
version=VERSION,
description=DESCRIPTION,
long_description=LONG_DESCRIPTION,
long_description_content_type=LONG_DESC_TYPE,
author=AUTHOR,
author_email=AUTHOR_EMAIL,
url=URL,
install_requires=INSTALL_REQUIRES,
license=LICENSE,
packages=find_packages(),
include_package_data=True
)